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


Недавно на хабре появилось несколько интересных статей про голографическое кодирование и декодирование информации — Эксперимент с голографическим кодированием и декодированием информации и я, разумеется, сразу же захотел реализовать это дело под 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; x1width; x1++ ) {

			// яркость каждой точки искомого изображения -
			// равна сумме света пришедшего ОТО ВСЕХ точек исходного изображения

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

			// пробегаемся по всем пикселям исходного изображения
			for( int y0=0; y0height; y0++ ) {
				uchar* ptr_src = (uchar*) (src->imageData + y0 * src->widthStep);
				for( int x0=0; x0width; 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; y1height; y1++ ) {
		float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep);
		for( int x1=0; x1width; 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; y1height; y1++ ) {
		float* ptr_dst = (float*) (dst->imageData + y1 * dst->widthStep);
		uchar* ptr_temp = (uchar*) (temp->imageData + y1 * temp->widthStep);
		for( int x1=0; x1width; 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 — голографическое кодирование картинки”»

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

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Arduino Bluetooth CraftDuino DIY Google IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение