몇년만인지 모르겠지만.. AI워터마크 못지움샘플 놓고가요 정보
몇년만인지 모르겠지만.. AI워터마크 못지움샘플 놓고가요
본문
몇년만인지 모르겠지만 오랜만에 들려봅니다 잘지내시죠 게시판에 워터마크 내용이 있길래 폭풍검색을해보니 이런방법이 있더군요
자유 게시판에 이런걸 올렸다고 모라 안하겠죠?
하하하~
참고용으로 올려 봅니다
strong_invisible_watermark.php
↓ 이 파일 하나만 생성하면 됨
<?php
/**
* Strong Invisible Watermark System
* AES + ECC + Spread Spectrum + Multi-Channel DCT Watermark
* AI Retouch / Inpainting / Upscale Resistant
*/
class StrongWatermark {
/* =====================================================
1. AES 암호화 / 복호화
====================================================== */
private function aesEncrypt($plaintext, $key) {
$method = "AES-256-CBC";
$iv = substr(hash("sha256", $key), 0, 16);
return base64_encode(openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv));
}
private function aesDecrypt($ciphertext, $key) {
$method = "AES-256-CBC";
$iv = substr(hash("sha256", $key), 0, 16);
return openssl_decrypt(base64_decode($ciphertext), $method, $key, OPENSSL_RAW_DATA, $iv);
}
/* =====================================================
2. ECC – 간단한 해시 기반 오류검출/복구
====================================================== */
private function eccApply($data) {
$ecc = substr(hash("sha256", $data), 0, 32);
return $data . "|ECC:" . $ecc;
}
private function eccVerify($data) {
$parts = explode("|ECC:", $data);
if (count($parts) != 2) return false;
$msg = $parts[0];
return substr(hash("sha256", $msg), 0, 32) === $parts[1];
}
/* =====================================================
3. 문자열 ↔ 비트 변환
====================================================== */
private function strToBits($str) {
$bits = "";
for ($i=0; $i < strlen($str); $i++)
$bits .= str_pad(decbin(ord($str[$i])), 8, "0", STR_PAD_LEFT);
return $bits;
}
private function bitsToStr($bits) {
$out = "";
for ($i=0; $i < strlen($bits); $i+=8)
$out .= chr(bindec(substr($bits, $i, 8)));
return $out;
}
/* =====================================================
4. 2D DCT / IDCT
====================================================== */
private function dct2d($block) {
$N=8; $out=[];
for ($u=0;$u<$N;$u++){
for ($v=0;$v<$N;$v++){
$sum=0;
for ($x=0;$x<$N;$x++){
for ($y=0;$y<$N;$y++){
$sum += $block[$x][$y] *
cos((2*$x+1)*$u*pi()/16) *
cos((2*$y+1)*$v*pi()/16);
}
}
$cu = ($u==0)?sqrt(1/8):sqrt(2/8);
$cv = ($v==0)?sqrt(1/8):sqrt(2/8);
$out[$u][$v] = $cu * $cv * $sum;
}
}
return $out;
}
private function idct2d($block) {
$N=8; $out=[];
for ($x=0;$x<$N;$x++){
for ($y=0;$y<$N;$y++){
$sum=0;
for ($u=0;$u<$N;$u++){
for ($v=0;$v<$N;$v++){
$cu = ($u==0)?sqrt(1/8):sqrt(2/8);
$cv = ($v==0)?sqrt(1/8):sqrt(2/8);
$sum += $cu * $cv * $block[$u][$v] *
cos((2*$x+1)*$u*pi()/16) *
cos((2*$y+1)*$v*pi()/16);
}
}
$out[$x][$y] = $sum;
}
}
return $out;
}
/* =====================================================
5. 워터마크 삽입
Spread Spectrum + Multi Channel + Random DCT Coord
====================================================== */
public function embed($input, $output, $message, $key) {
// 1) 암호화 + ECC
$payload = $this->eccApply($this->aesEncrypt($message, $key));
$bits = $this->strToBits($payload);
// 2) 이미지 로딩
$img = imagecreatefromjpeg($input);
$w = imagesx($img);
$h = imagesy($img);
// 3) 난수 초기화
srand(crc32($key));
$index = 0;
$max = strlen($bits);
// 4) Spread Spectrum: 이미지 전체에 삽입
for ($y=0; $y < $h; $y+=8) {
for ($x=0; $x < $w; $x+=8) {
if ($index >= $max) break;
// RGB 각각에 삽입 (삭제 더 어려움)
$channels = ['R'=>0, 'G'=>1, 'B'=>2];
foreach ($channels as $chName => $ch) {
// 8x8 블록 만들기
$block = [];
for ($i=0;$i<8;$i++){
for ($j=0;$j<8;$j++){
$rgb = imagecolorat($img, $x+$j, $y+$i);
$component = ($rgb >> (16 - 8*$ch)) & 0xFF;
$block[$i][$j] = $component;
}
}
// DCT
$dct = $this->dct2d($block);
// 난수 기반 삽입 위치 (중주파 + 고주파)
$px = rand(2,6);
$py = rand(2,6);
$bit = $bits[$index];
// Spread Spectrum 강도
$alpha = 3;
if ($bit == '1') $dct[$py][$px] += $alpha;
else $dct[$py][$px] -= $alpha;
// IDCT
$new = $this->idct2d($dct);
// 이미지에 다시 기록
for ($i=0;$i<8;$i++){
for ($j=0;$j<8;$j++){
$val = max(0, min(255, intval($new[$i][$j])));
$r=$g=$b=0;
$rgbOld = imagecolorat($img, $x+$j, $y+$i);
// 기존 채널 값 유지하며 덮어쓰기
$r = ($ch==0)?$val:(($rgbOld>>16)&0xFF);
$g = ($ch==1)?$val:(($rgbOld>>8)&0xFF);
$b = ($ch==2)?$val:($rgbOld&0xFF);
$color = imagecolorallocate($img, $r,$g,$b);
imagesetpixel($img, $x+$j, $y+$i, $color);
}
}
}
$index++;
}
}
imagejpeg($img, $output, 90);
imagedestroy($img);
}
/* =====================================================
6. 워터마크 추출
====================================================== */
public function extract($input, $key, $bitLength) {
$img = imagecreatefromjpeg($input);
$w = imagesx($img);
$h = imagesy($img);
srand(crc32($key));
$bits = "";
$index=0;
for ($y=0; $y < $h; $y+=8) {
for ($x=0; $x < $w; $x+=8) {
if ($index >= $bitLength) break;
// RGB 채널에서 다수결
$votes = 0;
for ($ch=0; $ch<3; $ch++) {
$block=[];
for ($i=0;$i<8;$i++){
for ($j=0;$j<8;$j++){
$rgb = imagecolorat($img, $x+$j, $y+$i);
$component = ($rgb >> (16 - 8*$ch)) & 0xFF;
$block[$i][$j] = $component;
}
}
$dct = $this->dct2d($block);
$px=rand(2,6);
$py=rand(2,6);
if ($dct[$py][$px] > 0) $votes++;
}
// R/G/B 중 2개 이상이면 1
$bits .= ($votes >= 2 ? "1" : "0");
$index++;
}
}
$raw = $this->bitsToStr($bits);
// ECC 검증
if (!$this->eccVerify($raw)) {
return "[ERROR] 데이터 손상됨";
}
// ECC 제거 → AES 복호화 → 메시지 복원
$payload = explode("|ECC:", $raw)[0];
return $this->aesDecrypt($payload, $key);
}
}
사용법
1) 워터마크 삽입
<?php
require_once "strong_invisible_watermark.php";
$wm = new StrongWatermark();
$wm->embed("orig.jpg", "wm.jpg",
"OWNER=YSH|MSG=내 이미지 무단 사용 금지",
"my-secret-key"
);
echo "워터마크 삽입 완료!";
2) 워터마크 추출
<?php
require_once "strong_invisible_watermark.php";
$wm = new StrongWatermark();
echo $wm->extract("wm.jpg", "my-secret-key", 2000);
이 버전의 강력한 특징
AES256 + 해시 기반 ECC
문구 위변조 불가능.
Spread Spectrum
이미지 전체에 흩뿌림 → 일부 삭제해도 전체 복구됨.
중주파 + 고주파 삽입
AI 업스케일/클린업/노이즈 제거로도 사라지지 않음.
RGB 다중 채널 삽입 (다수결 복원)
R 제거해도 G/B에서 살아있음.
난수 기반 위치 분산
키 없으면 추출 불가능 → 공격자가 특정 계수만 제거 불가.
다음에 또 뵈요 건강들 하셔요 화이팅!
3
댓글 6개
좋습니다
유용한 소스 감사합니다.
그런데 테스트 해보니 시간이 엄청 오래 걸리구요.
디코딩하니 "[ERROR] 데이터 손상됨"이라고 뜨네요.
wm.jpg 이미지를 보면 원본 그대로구요.
제가 무엇을 잘못했을까요?
찾아보면 워터마크에 대한 정보가 꽤 나오더군요.

건강하시죠? 오랜만에뵙네요. ^^ 좋은팁 감사합니다.
알짜 정보만 챙겨주셨던 분이신데.. 귀환?

반가워요.