2026, 새로운 도약을 시작합니다.

몇년만인지 모르겠지만.. AI워터마크 못지움샘플 놓고가요

· 1개월 전 · 234 · 6

몇년만인지 모르겠지만 오랜만에 들려봅니다 잘지내시죠 게시판에 워터마크 내용이 있길래 폭풍검색을해보니 이런방법이 있더군요

자유 게시판에 이런걸 올렸다고 모라 안하겠죠?

하하하~

참고용으로 올려 봅니다 

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 이미지를 보면 원본 그대로구요.

제가 무엇을 잘못했을까요? 

찾아보면 워터마크에 대한 정보가 꽤 나오더군요.

건강하시죠? 오랜만에뵙네요. ^^ 좋은팁 감사합니다.

알짜 정보만 챙겨주셨던 분이신데.. 귀환?

반가워요.

댓글 작성

댓글을 작성하시려면 로그인이 필요합니다.

로그인하기

자유게시판

번호 제목 글쓴이 날짜 조회
공지 5일 전 조회 427
200425 어제 조회 44
200424 2일 전 조회 79
200423 3일 전 조회 72
200422 3일 전 조회 120
200421 4일 전 조회 144
200420 4일 전 조회 56
200419 4일 전 조회 157
200418 4일 전 조회 119
200417 4일 전 조회 153
200416 4일 전 조회 86
200415 4일 전 조회 92
200414 4일 전 조회 185
200413 4일 전 조회 109
200412 5일 전 조회 220
200411 5일 전 조회 175
200410 5일 전 조회 279
200409 5일 전 조회 191
200408 5일 전 조회 145
200407 5일 전 조회 215
200406 5일 전 조회 155
200405 6일 전 조회 227
200404 6일 전 조회 115
200403 6일 전 조회 125
200402 6일 전 조회 100
200401 6일 전 조회 82
200400 6일 전 조회 118
200399 1주 전 조회 261
200398 1주 전 조회 184
200397 1주 전 조회 161
200396 1주 전 조회 296
🐛 버그신고