Commit 6ee1f6f6 authored by Alexandre Benoit's avatar Alexandre Benoit

removing some mistakes

parent eef900e4
//============================================================================ //============================================================================
// Name : HighDynamicRange_RetinaCompression.cpp // Name : OpenEXRimages_HighDynamicRange_Retina_toneMapping_video.cpp
// Author : Alexandre Benoit (benoit.alexandre.vision@gmail.com) // Author : Alexandre Benoit (benoit.alexandre.vision@gmail.com)
// Version : 0.1 // Version : 0.2
// Copyright : Alexandre Benoit, LISTIC Lab, december 2011 // Copyright : Alexandre Benoit, LISTIC Lab, december 2011
// Description : HighDynamicRange compression (tone mapping) with the help of the Gipsa/Listic's retina in C++, Ansi-style // Description : HighDynamicRange compression (tone mapping) for image sequences with the help of the Gipsa/Listic's retina in C++, Ansi-style
// Known issues: the input OpenEXR sequences can have bad computed pixels that should be removed
// => a simple method consists of cutting histogram edges (a slider for this on the UI is provided)
// => however, in image sequences, this histogramm cut must be done in an elegant way from frame to frame... still not done...
//============================================================================ //============================================================================
#include <iostream> #include <iostream>
...@@ -22,8 +25,10 @@ void help(std::string errorMessage) ...@@ -22,8 +25,10 @@ void help(std::string errorMessage)
std::cout<<"\t[start frame] : the starting frame tat should be considered"<<std::endl; std::cout<<"\t[start frame] : the starting frame tat should be considered"<<std::endl;
std::cout<<"\t[end frame] : the ending frame tat should be considered"<<std::endl; std::cout<<"\t[end frame] : the ending frame tat should be considered"<<std::endl;
std::cout<<"\nExamples:"<<std::endl; std::cout<<"\nExamples:"<<std::endl;
std::cout<<"\t-Image processing : ./OpenEXRimages_HighDynamicRange_Retina_toneMapping memorial%3d.exr 20 45"<<std::endl; std::cout<<"\t-Image processing : ./OpenEXRimages_HighDynamicRange_Retina_toneMapping_video memorial%3d.exr 20 45"<<std::endl;
std::cout<<"\t-Image processing : ./OpenEXRimages_HighDynamicRange_Retina_toneMapping memorial%3d.exr 20 45 log"<<std::endl; std::cout<<"\t-Image processing : ./OpenEXRimages_HighDynamicRange_Retina_toneMapping_video memorial%3d.exr 20 45 log"<<std::endl;
std::cout<<"\t ==> to process images from memorial020d.exr to memorial045d.exr"<<std::endl;
} }
// simple procedure for 1D curve tracing // simple procedure for 1D curve tracing
...@@ -51,28 +56,38 @@ void drawPlot(const cv::Mat curve, const std::string figureTitle, const int lowe ...@@ -51,28 +56,38 @@ void drawPlot(const cv::Mat curve, const std::string figureTitle, const int lowe
cv::imshow(figureTitle, displayedCurveImage); cv::imshow(figureTitle, displayedCurveImage);
} }
/* /*
* objective : get the gray level map of the input image and rescale it to the range [0-255] if rescale0_255=TRUE, simply trunks else * objective : get the gray level map of the input image and rescale it to the range [0-255] if rescale0_255=TRUE, simply trunks else
*/void rescaleGrayLevelMat(const cv::Mat &inputMat, cv::Mat &outputMat, const float histogramClippingLimit, const bool rescale0_255) */
void rescaleGrayLevelMat(const cv::Mat &inputMat, cv::Mat &outputMat, const float histogramClippingLimit, const bool rescale0_255)
{ {
// adjust output matrix wrt the input size but single channel // adjust output matrix wrt the input size but single channel
std::cout<<"Input image rescaling with histogram edges cutting (in order to eliminate bad pixels created during the HDR image creation) :"<<std::endl; std::cout<<"Input image rescaling with histogram edges cutting (in order to eliminate bad pixels created during the HDR image creation) :"<<std::endl;
//std::cout<<"=> image size (h,w,channels) = "<<inputMat.size().height<<", "<<inputMat.size().width<<", "<<inputMat.channels()<<std::endl; //std::cout<<"=> image size (h,w,channels) = "<<inputMat.size().height<<", "<<inputMat.size().width<<", "<<inputMat.channels()<<std::endl;
//std::cout<<"=> pixel coding (nbchannel, bytes per channel) = "<<inputMat.elemSize()/inputMat.elemSize1()<<", "<<inputMat.elemSize1()<<std::endl; //std::cout<<"=> pixel coding (nbchannel, bytes per channel) = "<<inputMat.elemSize()/inputMat.elemSize1()<<", "<<inputMat.elemSize1()<<std::endl;
// get min and max values to use afterwards if no 0-255 rescaling is used
double maxInput, minInput, histNormRescalefactor=1.f;
double histNormOffset=0.f;
minMaxLoc(inputMat, &minInput, &maxInput);
histNormRescalefactor=255.f/(maxInput-minInput);
histNormOffset=minInput;
std::cout<<"Hist max,min = "<<maxInput<<", "<<minInput<<" => scale, offset = "<<histNormRescalefactor<<", "<<histNormOffset<<std::endl;
// rescale between 0-255, keeping floating point values // rescale between 0-255, keeping floating point values
cv::normalize(inputMat, outputMat, 0.0, 255.0, cv::NORM_MINMAX); cv::Mat normalisedImage;
cv::normalize(inputMat, normalisedImage, 0.f, 255.f, cv::NORM_MINMAX);
if (rescale0_255)
normalisedImage.copyTo(outputMat);
// extract a 8bit image that will be used for histogram edge cut // extract a 8bit image that will be used for histogram edge cut
cv::Mat intGrayImage; cv::Mat intGrayImage;
if (inputMat.channels()==1) if (inputMat.channels()==1)
{ {
outputMat.convertTo(intGrayImage, CV_8U); normalisedImage.convertTo(intGrayImage, CV_8U);
}else }else
{ {
cv::Mat rgbIntImg; cv::Mat rgbIntImg;
outputMat.convertTo(rgbIntImg, CV_8UC3); normalisedImage.convertTo(rgbIntImg, CV_8UC3);
cvtColor(rgbIntImg, intGrayImage, CV_BGR2GRAY); cvtColor(rgbIntImg, intGrayImage, CV_BGR2GRAY);
} }
...@@ -81,12 +96,8 @@ void drawPlot(const cv::Mat curve, const std::string figureTitle, const int lowe ...@@ -81,12 +96,8 @@ void drawPlot(const cv::Mat curve, const std::string figureTitle, const int lowe
int histSize = 256; int histSize = 256;
calcHist(&intGrayImage, 1, 0, cv::Mat(), hist, 1, &histSize, 0); calcHist(&intGrayImage, 1, 0, cv::Mat(), hist, 1, &histSize, 0);
cv::Mat normalizedHist; cv::Mat normalizedHist;
normalize(hist, normalizedHist, 1, 0, cv::NORM_L1, CV_32F); // normalize histogram so that its sum equals 1
normalize(hist, normalizedHist, 1.f, 0.f, cv::NORM_L1, CV_32F); // normalize histogram so that its sum equals 1
double min_val, max_val;
CvMat histArr(normalizedHist);
cvMinMaxLoc(&histArr, &min_val, &max_val);
//std::cout<<"Hist max,min = "<<max_val<<", "<<min_val<<std::endl;
// compute density probability // compute density probability
cv::Mat denseProb=cv::Mat::zeros(normalizedHist.size(), CV_32F); cv::Mat denseProb=cv::Mat::zeros(normalizedHist.size(), CV_32F);
...@@ -98,42 +109,54 @@ void drawPlot(const cv::Mat curve, const std::string figureTitle, const int lowe ...@@ -98,42 +109,54 @@ void drawPlot(const cv::Mat curve, const std::string figureTitle, const int lowe
//std::cout<<normalizedHist.at<float>(i)<<", "<<denseProb.at<float>(i)<<std::endl; //std::cout<<normalizedHist.at<float>(i)<<", "<<denseProb.at<float>(i)<<std::endl;
if ( denseProb.at<float>(i)<histogramClippingLimit) if ( denseProb.at<float>(i)<histogramClippingLimit)
histLowerLimit=i; histLowerLimit=i;
if ( denseProb.at<float>(i)<1-histogramClippingLimit) if ( denseProb.at<float>(i)<1.f-histogramClippingLimit)
histUpperLimit=i; histUpperLimit=i;
} }
// deduce min and max admitted gray levels // deduce min and max admitted gray levels
float minInputValue = (float)histLowerLimit/histSize*255; float minInputValue = (float)histLowerLimit/histSize*255.f;
float maxInputValue = (float)histUpperLimit/histSize*255; float maxInputValue = (float)histUpperLimit/histSize*255.f;
std::cout<<"=> Histogram limits " std::cout<<"=> Histogram limits "
<<"\n\t"<<histogramClippingLimit*100<<"% index = "<<histLowerLimit<<" => normalizedHist value = "<<denseProb.at<float>(histLowerLimit)<<" => input gray level = "<<minInputValue <<"\n\t"<<histogramClippingLimit*100.f<<"% index = "<<histLowerLimit<<" => normalizedHist value = "<<denseProb.at<float>(histLowerLimit)<<" => input gray level = "<<minInputValue
<<"\n\t"<<(1-histogramClippingLimit)*100<<"% index = "<<histUpperLimit<<" => normalizedHist value = "<<denseProb.at<float>(histUpperLimit)<<" => input gray level = "<<maxInputValue <<"\n\t"<<(1.f-histogramClippingLimit)*100.f<<"% index = "<<histUpperLimit<<" => normalizedHist value = "<<denseProb.at<float>(histUpperLimit)<<" => input gray level = "<<maxInputValue
<<std::endl; <<std::endl;
//drawPlot(denseProb, "input histogram density probability", histLowerLimit, histUpperLimit); //drawPlot(denseProb, "input histogram density probability", histLowerLimit, histUpperLimit);
drawPlot(normalizedHist, "input histogram", histLowerLimit, histUpperLimit); drawPlot(normalizedHist, "input histogram", histLowerLimit, histUpperLimit);
if(rescale0_255) // rescale between 0-255 if asked to if(rescale0_255) // rescale between 0-255 if asked to
{ {
// rescale image range [minInputValue-maxInputValue] to [0-255] cv::threshold( outputMat, outputMat, maxInputValue, maxInputValue, 2 ); //THRESH_TRUNC, clips values above maxInputValue
outputMat-=minInputValue; cv::threshold( outputMat, outputMat, minInputValue, minInputValue, 3 ); //THRESH_TOZERO, clips values under minInputValue
outputMat*=255.0/(maxInputValue-minInputValue); // rescale image range [minInputValue-maxInputValue] to [0-255]
// cut original histogram and back project to original image outputMat-=minInputValue;
minInputValue=0.0; outputMat*=255.f/(maxInputValue-minInputValue);
maxInputValue=255.0; }else
{
inputMat.copyTo(outputMat);
// update threshold in the initial input image range
maxInputValue=(maxInputValue-255.f)/histNormRescalefactor+maxInput;
minInputValue=minInputValue/histNormRescalefactor+minInput;
std::cout<<"===> Input Hist clipping values (max,min) = "<<maxInputValue<<", "<<minInputValue<<std::endl;
cv::threshold( outputMat, outputMat, maxInputValue, maxInputValue, 2 ); //THRESH_TRUNC, clips values above maxInputValue
cv::threshold( outputMat, outputMat, minInputValue, minInputValue, 3 ); //
} }
cv::threshold( outputMat, outputMat, maxInputValue, maxInputValue, 2 ); //THRESH_TRUNC, clips values above 255
cv::threshold( outputMat, outputMat, minInputValue, minInputValue, 3 ); //THRESH_TOZERO, clips values under 0
} }
// basic callback method for interface management // basic callback method for interface management
cv::Mat inputImage; cv::Mat inputImage;
cv::Mat imageInputRescaled; cv::Mat imageInputRescaled;
float globalRescalefactor=1;
cv::Scalar globalOffset=0;
int histogramClippingValue; int histogramClippingValue;
void callBack_rescaleGrayLevelMat(int, void*) void callBack_rescaleGrayLevelMat(int, void*)
{ {
std::cout<<"Histogram clipping value changed, current value = "<<histogramClippingValue<<std::endl; std::cout<<"Histogram clipping value changed, current value = "<<histogramClippingValue<<std::endl;
rescaleGrayLevelMat(inputImage, imageInputRescaled, (float)(histogramClippingValue/100.0), false); // rescale and process
normalize(imageInputRescaled, imageInputRescaled, 0.0, 255.0, cv::NORM_MINMAX); inputImage+=globalOffset;
inputImage*=globalRescalefactor;
inputImage+=cv::Scalar(50, 50, 50, 50); // WARNING value linked to the hardcoded value (200.0) used in the globalRescalefactor in order to center on the 128 mean value... experimental but... basic compromise
rescaleGrayLevelMat(inputImage, imageInputRescaled, (float)histogramClippingValue/100.f, true);
} }
cv::Ptr<cv::Retina> retina; cv::Ptr<cv::Retina> retina;
...@@ -152,7 +175,7 @@ void drawPlot(const cv::Mat curve, const std::string figureTitle, const int lowe ...@@ -152,7 +175,7 @@ void drawPlot(const cv::Mat curve, const std::string figureTitle, const int lowe
} }
// loadNewFrame : loads a n image wrt filename parameters. it also manages image rescaling/histogram edges cutting (acts differently at first image i.e. if firstTimeread=true) // loadNewFrame : loads a n image wrt filename parameters. it also manages image rescaling/histogram edges cutting (acts differently at first image i.e. if firstTimeread=true)
cv::Mat loadNewFrame(const std::string filenamePrototype, const int currentFileIndex, const bool firstTimeread) void loadNewFrame(const std::string filenamePrototype, const int currentFileIndex, const bool firstTimeread)
{ {
char *currentImageName=NULL; char *currentImageName=NULL;
currentImageName = (char*)malloc(sizeof(char)*filenamePrototype.size()+10); currentImageName = (char*)malloc(sizeof(char)*filenamePrototype.size()+10);
...@@ -170,17 +193,30 @@ cv::Mat loadNewFrame(const std::string filenamePrototype, const int currentFileI ...@@ -170,17 +193,30 @@ cv::Mat loadNewFrame(const std::string filenamePrototype, const int currentFileI
if (inputImage.empty()) if (inputImage.empty())
{ {
help("could not load image, program end"); help("could not load image, program end");
return cv::Mat(); return;;
} }
// rescaling/histogram clipping stage // rescaling/histogram clipping stage
// rescale between 0 and 1 // rescale between 0 and 1
// TODO : take care of this step !!! maybe disable of do this in a nicer way ... each successive image should get the same transformation... but it depends on the initial image format // TODO : take care of this step !!! maybe disable of do this in a nicer way ... each successive image should get the same transformation... but it depends on the initial image format
//normalize(inputImage, inputImage, 0.0, 1.0, cv::NORM_MINMAX); double maxInput, minInput;
//inputImage/=255.0; minMaxLoc(inputImage, &minInput, &maxInput);
rescaleGrayLevelMat(inputImage, imageInputRescaled, (float)histogramClippingValue/100, false); std::cout<<"ORIGINAL IMAGE pixels values range (max,min) : "<<maxInput<<", "<<minInput<<std::endl
// return rescaled image ;if (firstTimeread)
return imageInputRescaled; {
/* the first time, get the pixel values range and rougthly update scaling value
in order to center values around 128 and getting a range close to [0-255],
=> actually using a little less in order to let some more flexibility in range evolves...
*/
double maxInput, minInput;
minMaxLoc(inputImage, &minInput, &maxInput);
std::cout<<"FIRST IMAGE pixels values range (max,min) : "<<maxInput<<", "<<minInput<<std::endl;
globalRescalefactor=50.0/(maxInput-minInput); // less than 255 for flexibility... experimental value to be carefull about
float channelOffset = -1.5*minInput;
globalOffset= cv::Scalar(channelOffset, channelOffset, channelOffset, channelOffset);
}
// call the generic input image rescaling callback
callBack_rescaleGrayLevelMat(1,NULL);
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
...@@ -226,7 +262,7 @@ cv::Mat loadNewFrame(const std::string filenamePrototype, const int currentFileI ...@@ -226,7 +262,7 @@ cv::Mat loadNewFrame(const std::string filenamePrototype, const int currentFileI
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// checking input media type (still image, video file, live video acquisition) // checking input media type (still image, video file, live video acquisition)
std::cout<<"RetinaDemo: setting up system with first image..."<<std::endl; std::cout<<"RetinaDemo: setting up system with first image..."<<std::endl;
inputImage = loadNewFrame(inputImageNamePrototype, startFrameIndex, true); loadNewFrame(inputImageNamePrototype, startFrameIndex, true);
if (inputImage.empty()) if (inputImage.empty())
{ {
...@@ -289,23 +325,23 @@ cv::Mat loadNewFrame(const std::string filenamePrototype, const int currentFileI ...@@ -289,23 +325,23 @@ cv::Mat loadNewFrame(const std::string filenamePrototype, const int currentFileI
currentFrameIndex=startFrameIndex; currentFrameIndex=startFrameIndex;
while(currentFrameIndex <= endFrameIndex) while(currentFrameIndex <= endFrameIndex)
{ {
cv::Mat currentFrame = loadNewFrame(inputImageNamePrototype, currentFrameIndex, false); loadNewFrame(inputImageNamePrototype, currentFrameIndex, false);
if (currentFrame.empty()) if (inputImage.empty())
{ {
std::cout<<"Could not load new image (index = "<<currentFrameIndex<<"), program end"<<std::endl; std::cout<<"Could not load new image (index = "<<currentFrameIndex<<"), program end"<<std::endl;
return -1; return -1;
} }
// display input & process standard power transformation // display input & process standard power transformation
imshow("EXR image original image, 16bits=>8bits linear rescaling ", currentFrame); imshow("EXR image original image, 16bits=>8bits linear rescaling ", imageInputRescaled);
cv::Mat gammaTransformedImage; cv::Mat gammaTransformedImage;
cv::pow(currentFrame, 1./5, gammaTransformedImage); // apply gamma curve: img = img ** (1./5) cv::pow(imageInputRescaled, 1./5, gammaTransformedImage); // apply gamma curve: img = img ** (1./5)
imshow(powerTransformedInput, gammaTransformedImage); imshow(powerTransformedInput, gammaTransformedImage);
// run retina filter // run retina filter
retina->run(imageInputRescaled); retina->run(imageInputRescaled);
// Retrieve and display retina output // Retrieve and display retina output
retina->getParvo(retinaOutput_parvo); retina->getParvo(retinaOutput_parvo);
cv::imshow(retinaInputCorrected, imageInputRescaled/255.0); cv::imshow(retinaInputCorrected, imageInputRescaled/255.f);
cv::imshow(RetinaParvoWindow, retinaOutput_parvo); cv::imshow(RetinaParvoWindow, retinaOutput_parvo);
cv::waitKey(4); cv::waitKey(4);
// jump to next frame // jump to next frame
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment