몇년만인지 모르겠지만.. 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에서 살아있음.
난수 기반 위치 분산
키 없으면 추출 불가능 → 공격자가 특정 계수만 제거 불가.
다음에 또 뵈요 건강들 하셔요 화이팅!
댓글 작성
댓글을 작성하시려면 로그인이 필요합니다.
로그인하기
댓글 6개
좋습니다
유용한 소스 감사합니다.
그런데 테스트 해보니 시간이 엄청 오래 걸리구요.
디코딩하니 "[ERROR] 데이터 손상됨"이라고 뜨네요.
wm.jpg 이미지를 보면 원본 그대로구요.
제가 무엇을 잘못했을까요?
찾아보면 워터마크에 대한 정보가 꽤 나오더군요.
건강하시죠? 오랜만에뵙네요. ^^ 좋은팁 감사합니다.
알짜 정보만 챙겨주셨던 분이신데.. 귀환?
반가워요.