замислих се за техническо решение позволяващо да се напъха скрита информация в картинка под форма на безобиден шум, устойчиво на загубна компресия. това да е устойчиво на компресия е ключово, иначе задачата е безинтересна и дори сигурно рабин ще се справи. в нашия форум джонката позволява пнг, но у фейсбука всичко се джипегва, пнг не работи добре за снимки. та идеята е - правя си набор ортогонални вектори с размерност равна на броя пиксели, по един вектор на всеки бит информация която искам да предам. ако бита е 1, добавям неговия вектор, ако е 0 вадя. всичко добре, но ортогонализацията на векторите е квадратична, понеже трябва да "изправям" всеки нов вектор срещу всички досегашни. затова се спрях на практично решение - разделям картинката на блокове и еднократно правя 32 вектора с по-ниска размерност равна на големината на блока, и просто ги преизползвам върху всеки блок. всичко това в среда с удобни инструменти се прави за минути, ето малко код и тестови резултати:
създаване на базиса: 1.запълвам със случайни числа, 2.балансираме до 0 3.ортогонализация, 4. нормализация
void init(int dim,int oct,int seed){
n=dim;
r.init(seed);
U.SetSize(oct*8);
for(int i=0;i<U.count();i++){
F& v=U[i];
v.SetSize(n);
double S=0;
for(int j=0;j<n;j++)S+=(v[j]=r.frnd()-0.5);
float z=S/n;
for(int j=0;j<n;j++)v[j]-=z;// balans do 0
for(int j=0;j<i;j++)add(v.data,j,-dot(j,v.data));
mul(v,1/sqrt(dot(i,v.data)));// normalizaciq
}
}
кодиране:
void encode(float *dst,const char* text,float A){
int u0=0;// otmestwaneto w bazisa
for(const char* p=text;;p++){
for(int b=0;b<8;b++)add(dst,u0+b,A*SGNBOOL((*p>>b)&1));
if(!*p)break;// izlizame sled zapis na 0, za da ne pechata gluposti pri dekodiraneto
u0+=8;
if(u0>=U.count()){// izrazhodili sme kapaciteta na bloka ot n stojnosti, minawame na now
u0=0;
dst+=n;
}
}
}
декодиране:
float decode(float *data,int dsz){
char buf[256];
memset(buf,0,sizeof(buf));
int rn=0;// prieti simwoli
float S=0;// amplitudata na prietiq signal
int bits=0;// broj blokowe ot n stojnosti
for(int bl=0;bl<dsz/n;bl++,data+=n){// za wseki whoden blok
for(int i=0;i<U.count();i++,bits++){
float d=dot(i,data);
if(d>0)buf[(bits>>3)]|=1<<(bits&7);
S+=d*d;
}
}
float A=sqrtf(S/bits);
LOG::Write("A:%f DEC: [%s]\n",A,buf);
return A;
}
употреба:
int n=I.Pixels();
lb.init(n/2,4,234234);// ползвам 32 вектора с размерност половината пиксели, така ще имам 6 блока побиращи по 4 байта
float *data=&I.rgb->r;
float A=lb.decode(data,3*n);// викам декодиране за да измеря наличния шум
lb.encode(data,"Расстрелять",A*20);// кодирам с ниво 20 пъти над шума за надеждност
lb.decode(data,3*n);// тест на записа и проверка на нивото
I.SaveJpg("c:/temp/encoded.jpg",SJ_LINEAR);
изход в конзолата при кодиране, вижда се че сигнала е със сила 3.67:
A:0.258966 DEC: EЁЛSuўЂme„ПzdЭа<Л
A:3.674595 DEC: Расстрелять
декодиране след компресия, вижда се сумарна амплитуда 1.05 т.е. компресията подтиска силата значително
A:1.057775 DEC: Расстрелять
картинката с вмъкнатото съобщение, нарочно е по-късо за да се вижда граница с оригинала. при реална употреба разбира се това е крайно недопустимо :)
сега докато го писах това се усетих че все пак текущата техническа реализация подлежи на детектване (т.е. може да привличе внимание), ако се търси специално. любопитно ми е кой ще се усети (аз залагам на делегате)