Недавно на хабре появилось несколько интересных статей про голографическое кодирование и декодирование информации — Эксперимент с голографическим кодированием и декодированием информации и я, разумеется, сразу же захотел реализовать это дело под OpenCV 🙂
Алгоритм производит действия, аналогичные процессу получения голограммы:
В определённой области пространства (каждом пикселе изображения) складывают две волны: одна из них идёт непосредственно от источника (опорная волна — постоянное значение: 127.5), а другая отражается от объекта записи (объектная волна — суммарное значение весов всех пикселей исходной картинки). В этой же области размещают фотопластинку (или иной регистрирующий материал — пустая картинка), в результате на этой пластинке возникает сложная картина полос потемнения, которые соответствуют картине интерференции в этой области пространства. Если теперь эту пластинку осветить волной, близкой к опорной, то она преобразует эту волну в волну, близкую к объектной. Таким образом, мы будем видеть (с той или иной степенью точности) такой же свет, какой отражался бы от объекта записи.
Реализацию алгоритма на php смотрите в первой статье, а на паскале во второй(ссылки внизу), вот же что получилось у меня:
// // голографическое кодирование и декодирование информации // // на основе статьи: Эксперимент с голографическим кодированием и декодированием информации // http://habrahabr.ru/blogs/algorithm/120051/ // http://habrahabr.ru/blogs/algorithm/122318/ // // Сложность алгоритма оценивается как O(n^2) // поэтому экспериментировать стоит только на маленьких картинках ;) // // https://robocraft.ru // #include <cv.h> #include <highgui.h> #include <stdlib.h> #include <stdio.h> #include <math.h> #include <string> // получить голограмму изображения // src - исходное изображение // wave_length - длина волны // base_light - опорный свет IplImage* cvImageHologram(const IplImage* src, float wave_length, float base_light = 127.5 ); // получить изображение по голограмме IplImage* cvImageDehologram(const IplImage* src, float wave_length); int main(int argc, char* argv[]) { IplImage *image = 0, *dst = 0, *dst2 = 0; char img_name[] = "Image0.jpg"; // имя картинки задаётся первым параметром char* image_filename = argc >= 2 ? argv[1] : img_name; // получаем картинку image = cvLoadImage(image_filename, 1); printf("[i] image: %s\n", image_filename); if(!image){ printf("[!] Error: cant load image: %s\n", image_filename); return -1; } // покажем изображение cvNamedWindow( "image"); cvShowImage( "image", image ); //for(int i=1; i<1000; i++){ //float wave_len = 2; //0.01*i; char* tmp_ptr=0; float wave_len = argc >= 3 ? (float)strtod(argv[2], &tmp_ptr) : 0.0001; printf("%f\n", wave_len); #if 1 // получить голограмму dst = cvImageHologram(image, wave_len); // раскодируем полученную голограмму dst2 = cvImageDehologram(dst, wave_len); #else // декодирование голограммы dst = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); cvConvertImage(image, dst, CV_BGR2GRAY); dst2 = cvImageDehologram(dst, wave_len); #endif // покажем результат cvNamedWindow( "Hol"); cvShowImage( "Hol", dst ); cvNamedWindow( "deHol"); cvShowImage( "deHol", dst2 ); #if 1 // сохраняем результат std::string dst_file_name = image_filename; dst_file_name += "-hol.png"; cvSaveImage(dst_file_name.c_str(), dst); dst_file_name = image_filename; dst_file_name += "-dehol.png"; cvSaveImage(dst_file_name.c_str(), dst2); #endif #if 1 // увеличенные картинки для отображения результатов int resW = image->width * 3; int resH = image->height * 3; IplImage* dst3 = cvCreateImage( cvSize(resW, resH), IPL_DEPTH_8U, 3); IplImage* dst4 = cvCreateImage( cvSize(resW, resH), IPL_DEPTH_8U, 1); // показываем картинки cvNamedWindow( "obj"); cvResize(image, dst3, CV_INTER_NN); cvShowImage( "obj", dst3 ); cvNamedWindow( "Hologram"); cvResize(dst, dst4, CV_INTER_NN); cvShowImage( "Hologram", dst4 ); cvNamedWindow( "deHologram"); cvResize(dst2, dst3, CV_INTER_NN); cvShowImage( "deHologram", dst3 ); cvReleaseImage(&dst3); cvReleaseImage(&dst4); #endif // ждём нажатия клавиши cvWaitKey(0); // освобождаем ресурсы cvReleaseImage(&image); cvReleaseImage(&dst); cvReleaseImage(&dst2); //} // удаляем окна cvDestroyAllWindows(); return 0; } // получить голограмму изображения // src - исходное изображение // wave_length - длина волны // base_light - опорный свет IplImage* cvImageHologram(const IplImage* src, float wave_length, float base_light) { if(!src){ return 0; } float resize = 1; float W = wave_length; //длина волны источника света if(W<=0){ return 0; } IplImage* dst = cvCreateImage(cvSize(src->width*resize, src->height*resize), IPL_DEPTH_32F, 1); if(!dst){ return 0; } cvZero(dst); printf("[i] Start...\n"); // пробегаемся по всем пикселям получаемого изображения for( int y1=0; y1height; y1++ ) { float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep); for( int x1=0; x1 width; x1++ ) { // яркость каждой точки искомого изображения - // равна сумме света пришедшего ОТО ВСЕХ точек исходного изображения // пробегаемся по всем пикселям исходного изображения for( int y0=0; y0 height; y0++ ) { uchar* ptr_src = (uchar*) (src->imageData + y0 * src->widthStep); for( int x0=0; x0 width; x0++ ) { // расстояние от точки (x0;y0) до точки (x1;y1) считаем что везде z=1, плоская голограмма float D = sqrt( 1.0f + (x0*resize-x1)*(x0*resize-x1) + (y0*resize-y1)*(y0*resize-y1) ); // Фаза волны достигшей этой точки float Phase = D/W; // доля света от очередной точки объекта: // суммируем свет от очередной точки // (в соответствии с фазой световой волны пришедшей от этой точки) // на выходе имеем - интенсивность света в точке (x1;y1) // 3 канала ptr_dst[x1] += ptr_src[3*x0]*cos(Phase) + // B - синий ptr_src[3*x0+1]*cos(Phase) + // G - зелёный ptr_src[3*x0+2]*cos(Phase); // R - красный } } } } printf("[i] Done.\n"); // находим максимальное значение float max = 0; for( int y1=0; y1 height; y1++ ) { float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep); for( int x1=0; x1 width; x1++ ) { if(ptr_dst[x1]>max){ max = ptr_dst[x1]; } } } printf("[i] max: %f \n", max); // результирующее изображение IplImage* temp = cvCreateImage(cvSize(src->width*resize, src->height*resize), src->depth, 1); // пробегаемся по всем пикселям получаемого изображения for( int y1=0; y1 height; y1++ ) { float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep); uchar* ptr_temp = (uchar*) (temp->imageData + y1 * temp->widthStep); for( int x1=0; x1 width; x1++ ) { float val = ptr_dst[x1]; if(max!=0){ // добавляем опорный свет val = abs( pow( (ptr_dst[x1]/max)*127.5 + base_light, 2 )/255 ); } if(val>255.0){ val = 255; } ptr_temp[x1] = (uchar)val; } } cvReleaseImage(&dst); return temp; } // получить изображение по голограмме // src - исходное изображение голограммы // wave_length - длина волны IplImage* cvImageDehologram(const IplImage* src, float wave_length) { if(!src){ return 0; } float resize = 1; IplImage* dst = cvCreateImage(cvSize(src->width*resize, src->height*resize), IPL_DEPTH_32F, 3); if(!dst){ return 0; } cvZero(dst); float W = wave_length; //длина волны источника света if(W<=0){ W=0.1; } printf("[i] Start...\n"); // пробегаемся по всем пикселям получаемого изображения for( int y1=0; y1 height; y1++ ) { float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep); for( int x1=0; x1 width; x1++ ) { // пробегаемся по всем пикселям исходного изображения for( int y0=0; y0 height; y0++ ) { uchar* ptr_src = (uchar*) (src->imageData + y0 * src->widthStep); for( int x0=0; x0 width; x0++ ) { // расстояние от точки (x0;y0) до точки (x1;y1) считаем что везде z=1, плоская голограмма float D = sqrt( 1.0f + (x0*resize-x1)*(x0*resize-x1) + (y0*resize-y1)*(y0*resize-y1) ); // Фаза волны достигшей этой точки float Phase = D/W; // суммируем свет от очередной точки // (в соответствии с фазой световой волны пришедшей от этой точки) //на выходе имеем - интенсивность света в точке (x1;y1) float val = ptr_src[x0]*cos(Phase); // 3 канала ptr_dst[3*x1] += val; // B - синий ptr_dst[3*x1+1] += val; // G - зелёный ptr_dst[3*x1+2] += val; // R - красный } } ptr_dst[3*x1] = pow(ptr_dst[3*x1], 2); ptr_dst[3*x1+1] = pow(ptr_dst[3*x1+1], 2); ptr_dst[3*x1+2] = pow(ptr_dst[3*x1+2], 2); } } printf("[i] Done.\n"); // находим максимальное значение float maxR = 0, maxG=0, maxB=0; for( int y1=0; y1 height; y1++ ) { float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep); for( int x1=0; x1 width; x1++ ) { if(ptr_dst[3*x1]>maxB){ maxB = ptr_dst[3*x1]; } if(ptr_dst[3*x1+1]>maxG){ maxG = ptr_dst[3*x1+1]; } if(ptr_dst[3*x1+2]>maxR){ maxR = ptr_dst[3*x1+2]; } } } printf("[i] max: %f %f %f \n", maxR, maxG, maxB); IplImage* temp = cvCreateImage(cvSize(src->width*resize, src->height*resize), IPL_DEPTH_8U, 3); // пробегаемся по всем пикселям изображения for( int y1=0; y1 height; y1++ ) { float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep); uchar* ptr_temp = (uchar*) (temp->imageData + y1 * temp->widthStep); for( int x1=0; x1 width; x1++ ) { ptr_temp[3*x1] = ptr_dst[3*x1]*255/maxB; ptr_temp[3*x1+1] = ptr_dst[3*x1+1]*255/maxG; ptr_temp[3*x1+2] = ptr_dst[3*x1+2]*255/maxR; } } cvReleaseImage(&dst); return temp; }
скачать иcходник (hologram.cpp)
Обратите внимание, что сложность алгоритма оценивается как O(n^2), поэтому экспериментировать стоит только на маленьких картинках — 16х16 или 32х32 😉
Очень красивые картинки голограмм получаются при изменении длины падающей волны (код закомментирован) 😉
Примеры работы алгоритма
исходная картинка (32х32) и результат:
увеличенный результат:
исходная картинка (50х50) и результат:
увеличенный результат:
исходная картинка (100х100) и результат:
увеличенный результат:
Результаты получения информации с повреждённой голограммы
голограмма с повреждением:
результат:
голограмма с повреждением:
результат:
а как вам такое?
голограмма с повреждением:
результат:
голограмма с повреждением:
результат:
Красота 🙂
Ссылки
http://ru.wikipedia.org/wiki/Голограмма
Эксперимент с голографическим кодированием и декодированием информации
Эксперимент с голографическим кодированием/декодированием цветных изображений
0 комментариев на «“OpenCV — голографическое кодирование картинки”»
Голограммы можно использовать не только для хранения информации (кстати, есть мнение, что мозг запоминает информацию не напрямую, а Фурье-образами, т.е. голограммами), но и для эффективного распознавания. Это должно быть очень хорошо описано в книге «Распознавание образов с помощью оптической пространственной фильтрации». Автор Ритц или как-то вроде этого. Точно не помню. Когда делал курсовую, не смог найти эту книгу ни в бумажном, ни в электронном виде.