OpenCV - голографическое кодирование картинки

Недавно на хабре появилось несколько интересных статей про голографическое кодирование и декодирование информации — Эксперимент с голографическим кодированием и декодированием информации и я, разумеется, сразу же захотел реализовать это дело под OpenCV :)
Алгоритм производит действия, аналогичные процессу получения голограммы:
В определённой области пространства (каждом пикселе изображения) складывают две волны: одна из них идёт непосредственно от источника (опорная волна — постоянное значение: 127.5), а другая отражается от объекта записи (объектная волна — суммарное значение весов всех пикселей исходной картинки). В этой же области размещают фотопластинку (или иной регистрирующий материал — пустая картинка), в результате на этой пластинке возникает сложная картина полос потемнения, которые соответствуют картине интерференции в этой области пространства. Если теперь эту пластинку осветить волной, близкой к опорной, то она преобразует эту волну в волну, близкую к объектной. Таким образом, мы будем видеть (с той или иной степенью точности) такой же свет, какой отражался бы от объекта записи.

Реализацию алгоритма на php смотрите в первой статье, а на паскале во второй(ссылки внизу), вот же что получилось у меня:

//
// голографическое кодирование и декодирование информации
//
// на основе статьи: Эксперимент с голографическим кодированием и декодированием информации
// http://habrahabr.ru/blogs/algorithm/120051/
// http://habrahabr.ru/blogs/algorithm/122318/
//
// Сложность алгоритма оценивается как O(n^2)
// поэтому экспериментировать стоит только на маленьких картинках ;)
//
// http://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; y1<dst->height; y1++ ) {
		float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep);
		for( int x1=0; x1<dst->width; x1++ ) {
		
			// яркость каждой точки искомого изображения - 
			// равна сумме света пришедшего ОТО ВСЕХ точек исходного изображения

			// пробегаемся по всем пикселям исходного изображения
			for( int y0=0; y0<src->height; y0++ ) {
				uchar* ptr_src = (uchar*) (src->imageData + y0 * src->widthStep);
				for( int x0=0; x0<src->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<dst->height; y1++ ) {
		float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep);
		for( int x1=0; x1<dst->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<dst->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<dst->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<dst->height; y1++ ) {
		float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep);
		for( int x1=0; x1<dst->width; x1++ ) {

			// пробегаемся по всем пикселям исходного изображения
			for( int y0=0; y0<src->height; y0++ ) {
				uchar* ptr_src = (uchar*) (src->imageData + y0 * src->widthStep);
				for( int x0=0; x0<src->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<dst->height; y1++ ) {
		float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep);
		for( int x1=0; x1<dst->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<dst->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<dst->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) и результат:

увеличенный результат:


Результаты получения информации с повреждённой голограммы

голограмма с повреждением:

результат:


голограмма с повреждением:

результат:


а как вам такое?

голограмма с повреждением:

результат:


голограмма с повреждением:

результат:


Красота :)

Ссылки:
ru.wikipedia.org/wiki/Голограмма
Эксперимент с голографическим кодированием и декодированием информации
Эксперимент с голографическим кодированием/декодированием цветных изображений
  • +2
  • 25 июня 2011, 08:40
  • noonv

Комментарии (1)

RSS свернуть / развернуть
+
+1
Голограммы можно использовать не только для хранения информации (кстати, есть мнение, что мозг запоминает информацию не напрямую, а Фурье-образами, т.е. голограммами), но и для эффективного распознавания. Это должно быть очень хорошо описано в книге «Распознавание образов с помощью оптической пространственной фильтрации». Автор Ритц или как-то вроде этого. Точно не помню. Когда делал курсовую, не смог найти эту книгу ни в бумажном, ни в электронном виде.
avatar

ProgrammerForever

  • 3 августа 2011, 11:28

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.