本文主要是介绍多尺度视网膜增强算法(MSRCR),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
MSRCR(multi-scale retinex with color restoration)
头文件
/** Copyright (c) 2006, Douglas Gray (dgray@soe.ucsc.edu, dr.de3ug@gmail.com)* All rights reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions are met:* * Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.* * Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the distribution.* * Neither the name of the <organization> nor the* names of its contributors may be used to endorse or promote products* derived from this software without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY Douglas Gray ``AS IS'' AND ANY* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
#pragma once#include "cv.h"extern double* CreateKernel(double sigma);
extern int* CreateFastKernel(double sigma);extern void FilterGaussian(IplImage* img, double sigma);
extern void FastFilter(IplImage *img, double sigma);extern void Retinex
(IplImage *img, double sigma, int gain = 128, int offset = 128);extern void MultiScaleRetinex
(IplImage *img, int scales, double *weights, double *sigmas, int gain = 128, int offset = 128);extern void MultiScaleRetinexCR
(IplImage *img, int scales, double *weights, double *sigmas, int gain = 128, int offset = 128,double restoration_factor = 6, double color_gain = 2);
实现文件
/** Copyright (c) 2006, Douglas Gray (dgray@soe.ucsc.edu, dr.de3ug@gmail.com)* All rights reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions are met:* * Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.* * Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the distribution.* * Neither the name of the <organization> nor the* names of its contributors may be used to endorse or promote products* derived from this software without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY Douglas Gray ``AS IS'' AND ANY* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
#include "retinex.h"
#include<cv.h>
#include <opencv2/opencv.hpp>
#include<omp.h>
#include <math.h>using namespace cv;
using namespace std;
//#define USE_EXACT_SIGMA#define pc(image, x, y, c) image->imageData[(image->widthStep * y) + (image->nChannels * x) + c]#define INT_PREC 1024.0
#define INT_PREC_BITS 10inline double int2double(int x) { return (double)x / INT_PREC; }
inline int double2int(double x) { return (int)(x * INT_PREC + 0.5); }inline int int2smallint(int x) { return (x >> INT_PREC_BITS); }
inline int int2bigint(int x) { return (x << INT_PREC_BITS); }//
// CreateKernel
//
// Summary:
// Creates a normalized 1 dimensional gaussian kernel.
//
// Arguments:
// sigma - the standard deviation of the gaussian kernel.
//
// Returns:
// double* - an array of values of length ((6*sigma)/2) * 2 + 1.
//
// Note:
// Caller is responsable for deleting the kernel.
//
double*
CreateKernel(double sigma)
{int i, x, filter_size;double* filter;double sum;// Reject unreasonable demandsif ( sigma > 200 ) sigma = 200;// get needed filter size (enforce oddness)filter_size = (int)floor(sigma*6) / 2;filter_size = filter_size * 2 + 1;// Allocate kernel spacefilter = new double[filter_size];// Calculate exponentialsum = 0;for (i = 0; i < filter_size; i++) {x = i - (filter_size / 2);filter[i] = exp( -(x*x) / (2*sigma*sigma) );sum += filter[i];}// Normalizefor (i = 0, x; i < filter_size; i++)filter[i] /= sum;return filter;
}//
// CreateFastKernel
//
// Summary:
// Creates a faster gaussian kernal using integers that
// approximate floating point (leftshifted by 8 bits)
//
// Arguments:
// sigma - the standard deviation of the gaussian kernel.
//
// Returns:
// int* - an array of values of length ((6*sigma)/2) * 2 + 1.
//
// Note:
// Caller is responsable for deleting the kernel.
//int*
CreateFastKernel(double sigma)
{double* fp_kernel;int* kernel;int i, filter_size;// Reject unreasonable demandsif ( sigma > 200 ) sigma = 200;// get needed filter size (enforce oddness)filter_size = (int)floor(sigma*6) / 2;filter_size = filter_size * 2 + 1;// Allocate kernel spacekernel = new int[filter_size];fp_kernel = CreateKernel(sigma);for (i = 0; i < filter_size; i++)kernel[i] = double2int(fp_kernel[i]);delete fp_kernel;return kernel;
}//
// FilterGaussian
//
// Summary:
// Performs a gaussian convolution for a value of sigma that is equal
// in both directions.
//
// Arguments:
// img - the image to be filtered in place.
// sigma - the standard deviation of the gaussian kernel to use.
//
void
FilterGaussian(IplImage* img, double sigma)
{int i, j, k, source, filter_size;int* kernel;IplImage* temp;int v1, v2, v3;// Reject unreasonable demandsif ( sigma > 200 ) sigma = 200;// get needed filter size (enforce oddness)filter_size = (int)floor(sigma*6) / 2;filter_size = filter_size * 2 + 1;kernel = CreateFastKernel(sigma);temp = cvCreateImage(cvSize(img->width, img->height), img->depth, img->nChannels);// filter x axisfor (j = 0; j < temp->height; j++)for (i = 0; i < temp->width; i++) {// inner loop has been unrolledv1 = v2 = v3 = 0;for (k = 0; k < filter_size; k++) {source = i + filter_size / 2 - k;if (source < 0) source *= -1;if (source > img->width - 1) source = 2*(img->width - 1) - source;v1 += kernel[k] * (unsigned char)pc(img, source, j, 0);if (img->nChannels == 1) continue;v2 += kernel[k] * (unsigned char)pc(img, source, j, 1);v3 += kernel[k] * (unsigned char)pc(img, source, j, 2);}// set value and move onpc(temp, i, j, 0) = (char)int2smallint(v1);if (img->nChannels == 1) continue;pc(temp, i, j, 1) = (char)int2smallint(v2);pc(temp, i, j, 2) = (char)int2smallint(v3);}// filter y axisfor (j = 0; j < img->height; j++)for (i = 0; i < img->width; i++) {v1 = v2 = v3 = 0;for (k = 0; k < filter_size; k++) {source = j + filter_size / 2 - k;if (source < 0) source *= -1;if (source > temp->height - 1) source = 2*(temp->height - 1) - source;v1 += kernel[k] * (unsigned char)pc(temp, i, source, 0);if (img->nChannels == 1) continue;v2 += kernel[k] * (unsigned char)pc(temp, i, source, 1);v3 += kernel[k] * (unsigned char)pc(temp, i, source, 2);}// set value and move onpc(img, i, j, 0) = (char)int2smallint(v1);if (img->nChannels == 1) continue;pc(img, i, j, 1) = (char)int2smallint(v2);pc(img, i, j, 2) = (char)int2smallint(v3);}cvReleaseImage( &temp );delete kernel;}//
// FastFilter
//
// Summary:
// Performs gaussian convolution of any size sigma very fast by using
// both image pyramids and seperable filters. Recursion is used.
//
// Arguments:
// img - an IplImage to be filtered in place.
//
void
FastFilter(IplImage *img, double sigma)
{int filter_size;// Reject unreasonable demandsif ( sigma > 200 ) sigma = 200;// get needed filter size (enforce oddness)filter_size = (int)floor(sigma*6) / 2;filter_size = filter_size * 2 + 1;// If 3 sigma is less than a pixel, why bother (ie sigma < 2/3)if(filter_size < 3) return;// Filter, or downsample and recurseif (filter_size < 10) {#ifdef USE_EXACT_SIGMAFilterGaussian(img, sigma)
#elsecvSmooth( img, img, CV_GAUSSIAN, filter_size, filter_size );
#endif}else {if (img->width < 2 || img->height < 2) return;IplImage* sub_img = cvCreateImage(cvSize(img->width / 2, img->height / 2), img->depth, img->nChannels);cvPyrDown( img, sub_img );FastFilter( sub_img, sigma / 2.0 );cvResize( sub_img, img, CV_INTER_LINEAR );cvReleaseImage( &sub_img );}}//
// Retinex
//
// Summary:
// Basic retinex restoration. The image and a filtered image are converted
// to the log domain and subtracted.
//
// Arguments:
// img - an IplImage to be enhanced in place.
// sigma - the standard deviation of the gaussian kernal used to filter.
// gain - the factor by which to scale the image back into visable range.
// offset - an offset similar to the gain.
//
void
Retinex(IplImage *img, double sigma, int gain, int offset)
{IplImage *A, *fA, *fB, *fC;// Initialize temp imagesfA = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, img->nChannels);fB = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, img->nChannels);fC = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, img->nChannels);// Compute log imagecvConvert( img, fA );cvLog( fA, fB );// Compute log of blured imageA = cvCloneImage( img );FastFilter( A, sigma );cvConvert( A, fA );cvLog( fA, fC );// Compute differencecvSub( fB, fC, fA );// RestorecvConvertScale( fA, img, gain, offset);// Release temp imagescvReleaseImage( &A );cvReleaseImage( &fA );cvReleaseImage( &fB );cvReleaseImage( &fC );}//
// MultiScaleRetinex
//
// Summary:
// Multiscale retinex restoration. The image and a set of filtered images are
// converted to the log domain and subtracted from the original with some set
// of weights. Typicaly called with three equaly weighted scales of fine,
// medium and wide standard deviations.
//
// Arguments:
// img - an IplImage to be enhanced in place.
// sigma - the standard deviation of the gaussian kernal used to filter.
// gain - the factor by which to scale the image back into visable range.
// offset - an offset similar to the gain.
//
void
MultiScaleRetinex(IplImage *img, int scales, double *weights, double *sigmas, int gain, int offset)
{int i;double weight;IplImage *A, *fA, *fB, *fC;// Initialize temp imagesfA = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, img->nChannels);fB = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, img->nChannels);fC = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, img->nChannels);// Compute log imagecvConvert( img, fA );cvLog( fA, fB );// Normalize according to given weightsfor (i = 0, weight = 0; i < scales; i++)weight += weights[i];if (weight != 1.0) cvScale( fB, fB, weight );// Filter at each scalefor (i = 0; i < scales; i++) {A = cvCloneImage( img );FastFilter( A, sigmas[i] );cvConvert( A, fA );cvLog( fA, fC );cvReleaseImage( &A );// Compute weighted differencecvScale( fC, fC, weights[i] );cvSub( fB, fC, fB );}// RestorecvConvertScale( fB, img, gain, offset);// Release temp imagescvReleaseImage( &fA );cvReleaseImage( &fB );cvReleaseImage( &fC );
}//
// MultiScaleRetinexCR
//
// Summary:
// Multiscale retinex restoration with color restoration. The image and a set of
// filtered images are converted to the log domain and subtracted from the
// original with some set of weights. Typicaly called with three equaly weighted
// scales of fine, medium and wide standard deviations. A color restoration weight
// is then applied to each color channel.
//
// Arguments:
// img - an IplImage to be enhanced in place.
// sigma - the standard deviation of the gaussian kernal used to filter.
// gain - the factor by which to scale the image back into visable range.
// offset - an offset similar to the gain.
// restoration_factor - controls the non-linearaty of the color restoration.
// color_gain - controls the color restoration gain.
////extern void MultiScaleRetinexCR
//(IplImage *img, int scales, double *weights, double *sigmas, int gain = 128, int offset = 128,
// double restoration_factor = 6, double color_gain = 2);void MultiScaleRetinexCR(IplImage *img, int scales, double *weights, double *sigmas,int gain, int offset, double restoration_factor, double color_gain)
{int i;double weight;IplImage *A, *B, *C, *fA, *fB, *fC, *fsA, *fsB, *fsC, *fsD, *fsE, *fsF;// Initialize temp imagesfA = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, img->nChannels);fB = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, img->nChannels);fC = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, img->nChannels);fsA = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, 1);fsB = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, 1);fsC = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, 1);fsD = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, 1);fsE = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, 1);fsF = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_32F, 1);// Compute log imagecvConvert( img, fB );cvLog( fB, fA );=================get image value===================// const int height=26;//const int width=30;//float imageValue[height][width][3];//for(int i=0;i<img->height;i++) //{// for(int j=0;j<img->width;j++)// {// imageValue[i][j][0]=cvGet2D(fA,i,j).val[0];// imageValue[i][j][1]=cvGet2D(fA,i,j).val[1];// imageValue[i][j][2]=cvGet2D(fA,i,j).val[2];// }//}===========================end======================//=============================show image================//IplImage * test = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_8U, img->nChannels);//cvConvert(fB,test);//cvNamedWindow("test",CV_WINDOW_AUTOSIZE);//cvShowImage("test",test);//cvWaitKey(0);==========================end============================// Normalize according to given weightsfor (i = 0, weight = 0; i < scales; i++)weight += weights[i];if (weight != 1.0) cvScale( fA, fA, weight );// Filter at each scale//for (i = 0; i < scales; i++) {A = cvCloneImage( img );FastFilter( A, sigmas[i] );cvConvert( A, fB );cvLog( fB, fC );cvReleaseImage( &A );// Compute weighted differencecvScale( fC, fC, weights[i] );cvSub( fA, fC, fA );}// Color restorationif (img->nChannels > 1) {A = cvCreateImage(cvSize(img->width, img->height), img->depth, 1);B = cvCreateImage(cvSize(img->width, img->height), img->depth, 1);C = cvCreateImage(cvSize(img->width, img->height), img->depth, 1);// Divide image into channels, convert and store sumcvCvtPixToPlane( img, A, B, C, NULL );cvConvert( A, fsA );cvConvert( B, fsB );cvConvert( C, fsC );cvReleaseImage( &A );cvReleaseImage( &B );cvReleaseImage( &C );// Sum componentscvAdd( fsA, fsB, fsD );cvAdd( fsD, fsC, fsD );// Normalize weightscvDiv( fsA, fsD, fsA, restoration_factor);cvDiv( fsB, fsD, fsB, restoration_factor);cvDiv( fsC, fsD, fsC, restoration_factor);cvConvertScale( fsA, fsA, 1, 1 );cvConvertScale( fsB, fsB, 1, 1 );cvConvertScale( fsC, fsC, 1, 1 );// Log weightscvLog( fsA, fsA );cvLog( fsB, fsB );cvLog( fsC, fsC );// Divide retinex image, weight accordingly and recombinecvCvtPixToPlane( fA, fsD, fsE, fsF, NULL );cvMul( fsD, fsA, fsD, color_gain);cvMul( fsE, fsB, fsE, color_gain );cvMul( fsF, fsC, fsF, color_gain );cvCvtPlaneToPix( fsD, fsE, fsF, NULL, fA );}// RestorecvConvertScale( fA, img, gain, offset);// Release temp imagescvReleaseImage( &fA );cvReleaseImage( &fB );cvReleaseImage( &fC );cvReleaseImage( &fsA );cvReleaseImage( &fsB );cvReleaseImage( &fsC );cvReleaseImage( &fsD );cvReleaseImage( &fsE );cvReleaseImage( &fsF );
}
主函数文件
//#include "stdafx.h"
#include<tchar.h>
#include <opencv2/opencv.hpp>
#include<iostream>
#include<sstream>
#include<string>
#include<cv.h>
#include<omp.h>
#include<concrt.h>#include"retinex.h"using namespace std;
using namespace cv;//============================================using for handle single image==================================================
int _tmain(int argc, _TCHAR* argv[])
{//get image const char* imagename = "girl.jpg";const char* imagesavename="girl_retinex.jpg";/*const char* imagename[6] = {"example12.jpg","girl01.jpg","people01.jpg","road01.jpg","tree01.jpg","clock.bmp"};const char* imagesavename[6] = {"example12_retinex.jpg","girl01_retinex.jpg","people01_retinex.jpg","road01_retinex.jpg","tree01_retinex.jpg","clock_retinex.bmp"};*///set retinex parametersint scales=3;double weight[3]={1.0/3.0,1.0/3.0,1.0/3.0};double sigmas[3]={1,50,100}; //- the standard deviation of the gaussian kernal used to filter.int gain=50; //- the factor by which to scale the image back into visable range.int offset=150; //- an offset similar to the gain.double restoration_factor=100; // - controls the non-linearaty of the color restoration.double color_gain=1; // - controls the color restoration gain.char filePath[200]="image\\";char* imagePath=filePath;//get savefilenamechar num[5];char* sigmas_char[3];stringstream ss;for(int j=0;j<3;j++){ sigmas_char[j]=num;ss<<sigmas[j];ss>>sigmas_char[j];imagePath=strcat(imagePath,"sigma=");imagePath=strcat(imagePath,sigmas_char[j]);ss.clear();ss.str("");}//gainss.clear();ss.str("");char* gain_c=num;ss<<gain;ss>>gain_c;imagePath=strcat(imagePath,"_gain=");imagePath=strcat(imagePath,gain_c);//offsetss.clear();ss.str("");char* offset_char=num;ss<<offset;ss>>offset_char;imagePath=strcat(imagePath,"_offset=");imagePath=strcat(imagePath,offset_char);//restoration_factorss.clear();ss.str("");char* restoration_factor_char=num;ss<<restoration_factor;ss>>restoration_factor_char;imagePath=strcat(imagePath,"_factor=");imagePath=strcat(imagePath,restoration_factor_char);//color_gainss.clear();ss.str("");char* color_gain_char=num;ss<<color_gain;ss>>color_gain_char;imagePath=strcat(imagePath,"_color_gain=");imagePath=strcat(imagePath,color_gain_char);ss.clear();ss.str("");const char* imageSavePath=strcat(imagePath,imagesavename);IplImage* img=cvLoadImage(imagename,-1);//read the original channels number from imageMultiScaleRetinexCR(img,scales,weight,sigmas, gain, offset,restoration_factor,color_gain);cvSaveImage(imageSavePath,img);cvReleaseImage(&img);return 0;
}
============================================end=========================================================//==========================================using for handle video==============================
//int _tmain(int argc, _TCHAR* argv[])
//{
// //get core num
// int coreNum=omp_get_num_procs();
//
// IplImage* pFrame=NULL;
// CvCapture* pCapture=NULL;
//
// cvNamedWindow("orginalVideo",1);
// cvNamedWindow("retinexVideo",1);
//
// cvMoveWindow("video",30,0);
// cvMoveWindow("retinexVideo",360,0);
//
// const char* videoName="sister.avi";
// if(!(pCapture=cvCaptureFromFile(videoName)))
// {
// fprintf(stderr, "Can not open video file %s\n", videoName);
// return -1;
// }
//
// /*CvVideoWriter *writer=0;
// int isColor=1;
// int fps=cvGetCaptureProperty(pCapture,CV_CAP_PROP_FPS);
// int frameW=cvGetCaptureProperty(pCapture,CV_CAP_PROP_FRAME_HEIGHT);
// int frameH=cvGetCaptureProperty(pCapture,CV_CAP_PROP_FRAME_WIDTH);
// int numFrames = (int) cvGetCaptureProperty(pCapture, CV_CAP_PROP_FRAME_COUNT);
// writer=cvCreateVideoWriter("sister2retinex.avi",CV_FOURCC('M','J','P','G'),
// fps,cvSize(frameH,frameW),isColor);*/
//
// //set paramaters
// int scales=3;
// double *weights,weight[3]={1.0/3.0,1.0/3.0,1.0/3.0};
// weights=&weight[0];
// double *sigmas, simg[3]={10,50,100};
// sigmas=&simg[0];
//
// int count=0;
//
// while((pFrame=cvQueryFrame(pCapture))!=NULL)
// {
// count++;
// cvShowImage("orginalVideo",pFrame);
// cvWaitKey(1);
// MultiScaleRetinexCR(pFrame,scales,weights,sigmas, 128, 128,6,2);
// /*cvWriteFrame(writer,pFrame);*/
// cvShowImage("retinexVideo",pFrame);
// //cout<<count<<"/"<<numFrames<<endl;
// }
// cvDestroyWindow("orginalVideo");
// cvDestroyWindow("retinexVideo");
//
// cvReleaseImage(&pFrame);
// cvReleaseCapture(&pCapture);
// //cvReleaseVideoWriter(&writer);
// return 0;
//}
//=====================================end========================================================
====================================================================================
改进版 视网膜增强算法
====================================================================================
问题:
上面给出的源码,实现后,在颜色比较鲜艳时,或者颜色为黑色时,会产生变色(比如黑色会变为红色,红色会变为黄色)
产生原因:
RGB值比较大的值(接近255) 或比较低的(接近0 ) 经过上述算法处理后,某一通道的值会超过255或低于0,溢出。
改进:
改进版本可参考reference 3(网站包含论文与demo ,源码(Linux版本))
Reference:
1.原理相关1:http://www.cnblogs.com/Imageshop/archive/2013/04/17/3026881.html
2.源码部分2:http://blog.csdn.net/onezeros/article/details/6342661
---------------------------------------------------------------------------------------------------------------
3.改进版本:http://www.ipol.im/pub/art/2014/107/
这篇关于多尺度视网膜增强算法(MSRCR)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!