Merge remote-tracking branch 'origin/master' into merge-2.4
Conflicts: doc/tutorials/bioinspired/retina_model/retina_model.rst~
This commit is contained in:
@@ -47,26 +47,37 @@
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
typedef double polyfit_type;
|
||||
|
||||
void cv::polyfit(const Mat& src_x, const Mat& src_y, Mat& dst, int order)
|
||||
{
|
||||
CV_Assert((src_x.rows>0)&&(src_y.rows>0)&&(src_x.cols==1)&&(src_y.cols==1)
|
||||
&&(dst.cols==1)&&(dst.rows==(order+1))&&(order>=1));
|
||||
Mat X;
|
||||
X = Mat::zeros(src_x.rows, order+1,CV_32FC1);
|
||||
Mat copy;
|
||||
for(int i = 0; i <=order;i++)
|
||||
const int wdepth = DataType<polyfit_type>::depth;
|
||||
int npoints = src_x.checkVector(1);
|
||||
int nypoints = src_y.checkVector(1);
|
||||
|
||||
CV_Assert(npoints == nypoints && npoints >= order+1);
|
||||
|
||||
Mat srcX = Mat_<polyfit_type>(src_x), srcY = Mat_<polyfit_type>(src_y);
|
||||
|
||||
Mat X = Mat::zeros(order + 1, npoints, wdepth);
|
||||
polyfit_type* pSrcX = (polyfit_type*)srcX.data;
|
||||
polyfit_type* pXData = (polyfit_type*)X.data;
|
||||
int stepX = (int)(X.step/X.elemSize1());
|
||||
for (int y = 0; y < order + 1; ++y)
|
||||
{
|
||||
copy = src_x.clone();
|
||||
pow(copy,i,copy);
|
||||
Mat M1 = X.col(i);
|
||||
copy.col(0).copyTo(M1);
|
||||
for (int x = 0; x < npoints; ++x)
|
||||
{
|
||||
if (y == 0)
|
||||
pXData[x] = 1;
|
||||
else if (y == 1)
|
||||
pXData[x + stepX] = pSrcX[x];
|
||||
else pXData[x + y*stepX] = pSrcX[x]* pXData[x + (y-1)*stepX];
|
||||
}
|
||||
}
|
||||
Mat X_t, X_inv;
|
||||
transpose(X,X_t);
|
||||
Mat temp = X_t*X;
|
||||
Mat temp2;
|
||||
invert (temp,temp2);
|
||||
Mat temp3 = temp2*X_t;
|
||||
Mat W = temp3*src_y;
|
||||
W.copyTo(dst);
|
||||
|
||||
Mat A, b, w;
|
||||
mulTransposed(X, A, false);
|
||||
b = X*srcY;
|
||||
solve(A, b, w, DECOMP_SVD);
|
||||
w.convertTo(dst, std::max(std::max(src_x.depth(), src_y.depth()), CV_32F));
|
||||
}
|
||||
|
@@ -50,6 +50,8 @@ file(GLOB grfmt_hdrs src/grfmt*.hpp)
|
||||
file(GLOB grfmt_srcs src/grfmt*.cpp)
|
||||
list(APPEND grfmt_hdrs src/bitstrm.hpp)
|
||||
list(APPEND grfmt_srcs src/bitstrm.cpp)
|
||||
list(APPEND grfmt_hdrs src/rgbe.hpp)
|
||||
list(APPEND grfmt_srcs src/rgbe.cpp)
|
||||
|
||||
source_group("Src\\grfmts" FILES ${grfmt_hdrs} ${grfmt_srcs})
|
||||
|
||||
|
164
modules/highgui/src/grfmt_hdr.cpp
Normal file
164
modules/highgui/src/grfmt_hdr.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's 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.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "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 the Intel Corporation or contributors 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.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "grfmt_hdr.hpp"
|
||||
#include "rgbe.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
HdrDecoder::HdrDecoder()
|
||||
{
|
||||
m_signature = "#?RGBE";
|
||||
m_signature_alt = "#?RADIANCE";
|
||||
file = NULL;
|
||||
m_type = CV_32FC3;
|
||||
}
|
||||
|
||||
HdrDecoder::~HdrDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
size_t HdrDecoder::signatureLength() const
|
||||
{
|
||||
return m_signature.size() > m_signature_alt.size() ?
|
||||
m_signature.size() : m_signature_alt.size();
|
||||
}
|
||||
|
||||
bool HdrDecoder::readHeader()
|
||||
{
|
||||
file = fopen(m_filename.c_str(), "rb");
|
||||
if(!file) {
|
||||
return false;
|
||||
}
|
||||
RGBE_ReadHeader(file, &m_width, &m_height, NULL);
|
||||
if(m_width <= 0 || m_height <= 0) {
|
||||
fclose(file);
|
||||
file = NULL;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HdrDecoder::readData(Mat& _img)
|
||||
{
|
||||
Mat img(m_height, m_width, CV_32FC3);
|
||||
if(!file) {
|
||||
if(!readHeader()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
RGBE_ReadPixels_RLE(file, const_cast<float*>(img.ptr<float>()), img.cols, img.rows);
|
||||
fclose(file); file = NULL;
|
||||
|
||||
if(_img.depth() == img.depth()) {
|
||||
img.convertTo(_img, _img.type());
|
||||
} else {
|
||||
img.convertTo(_img, _img.type(), 255);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HdrDecoder::checkSignature( const String& signature ) const
|
||||
{
|
||||
if(signature.size() >= m_signature.size() &&
|
||||
(!memcmp(signature.c_str(), m_signature.c_str(), m_signature.size()) ||
|
||||
!memcmp(signature.c_str(), m_signature_alt.c_str(), m_signature_alt.size())))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageDecoder HdrDecoder::newDecoder() const
|
||||
{
|
||||
return makePtr<HdrDecoder>();
|
||||
}
|
||||
|
||||
HdrEncoder::HdrEncoder()
|
||||
{
|
||||
m_description = "Radiance HDR (*.hdr;*.pic)";
|
||||
}
|
||||
|
||||
HdrEncoder::~HdrEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
bool HdrEncoder::write( const Mat& input_img, const std::vector<int>& params )
|
||||
{
|
||||
Mat img;
|
||||
CV_Assert(input_img.channels() == 3 || input_img.channels() == 1);
|
||||
if(input_img.channels() == 1) {
|
||||
std::vector<Mat> splitted(3, input_img);
|
||||
merge(splitted, img);
|
||||
} else {
|
||||
input_img.copyTo(img);
|
||||
}
|
||||
if(img.depth() != CV_32F) {
|
||||
img.convertTo(img, CV_32FC3, 1/255.0f);
|
||||
}
|
||||
CV_Assert(params.empty() || params[0] == HDR_NONE || params[0] == HDR_RLE);
|
||||
FILE *fout = fopen(m_filename.c_str(), "wb");
|
||||
if(!fout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RGBE_WriteHeader(fout, img.cols, img.rows, NULL);
|
||||
if(params.empty() || params[0] == HDR_RLE) {
|
||||
RGBE_WritePixels_RLE(fout, const_cast<float*>(img.ptr<float>()), img.cols, img.rows);
|
||||
} else {
|
||||
RGBE_WritePixels(fout, const_cast<float*>(img.ptr<float>()), img.cols * img.rows);
|
||||
}
|
||||
|
||||
fclose(fout);
|
||||
return true;
|
||||
}
|
||||
|
||||
ImageEncoder HdrEncoder::newEncoder() const
|
||||
{
|
||||
return makePtr<HdrEncoder>();
|
||||
}
|
||||
|
||||
bool HdrEncoder::isFormatSupported( int depth ) const {
|
||||
return depth != CV_64F;
|
||||
}
|
||||
|
||||
}
|
88
modules/highgui/src/grfmt_hdr.hpp
Normal file
88
modules/highgui/src/grfmt_hdr.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's 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.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "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 the Intel Corporation or contributors 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.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef _GRFMT_HDR_H_
|
||||
#define _GRFMT_HDR_H_
|
||||
|
||||
#include "grfmt_base.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
enum HdrCompression
|
||||
{
|
||||
HDR_NONE = 0,
|
||||
HDR_RLE = 1
|
||||
};
|
||||
|
||||
// Radiance rgbe (.hdr) reader
|
||||
class HdrDecoder : public BaseImageDecoder
|
||||
{
|
||||
public:
|
||||
HdrDecoder();
|
||||
~HdrDecoder();
|
||||
bool readHeader();
|
||||
bool readData( Mat& img );
|
||||
bool checkSignature( const String& signature ) const;
|
||||
ImageDecoder newDecoder() const;
|
||||
size_t signatureLength() const;
|
||||
protected:
|
||||
String m_signature_alt;
|
||||
FILE *file;
|
||||
};
|
||||
|
||||
// ... writer
|
||||
class HdrEncoder : public BaseImageEncoder
|
||||
{
|
||||
public:
|
||||
HdrEncoder();
|
||||
~HdrEncoder();
|
||||
bool write( const Mat& img, const std::vector<int>& params );
|
||||
ImageEncoder newEncoder() const;
|
||||
bool isFormatSupported( int depth ) const;
|
||||
protected:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*_GRFMT_HDR_H_*/
|
@@ -47,6 +47,7 @@
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "grfmt_tiff.hpp"
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
@@ -71,6 +72,7 @@ TiffDecoder::TiffDecoder()
|
||||
TIFFSetErrorHandler( GrFmtSilentTIFFErrorHandler );
|
||||
TIFFSetWarningHandler( GrFmtSilentTIFFErrorHandler );
|
||||
}
|
||||
m_hdr = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +135,14 @@ bool TiffDecoder::readHeader()
|
||||
|
||||
m_width = wdth;
|
||||
m_height = hght;
|
||||
if((bpp == 32 && ncn == 3) || photometric == PHOTOMETRIC_LOGLUV)
|
||||
{
|
||||
m_type = CV_32FC3;
|
||||
m_hdr = true;
|
||||
return true;
|
||||
}
|
||||
m_hdr = false;
|
||||
|
||||
if( bpp > 8 &&
|
||||
((photometric != 2 && photometric != 1) ||
|
||||
(ncn != 1 && ncn != 3 && ncn != 4)))
|
||||
@@ -171,6 +181,10 @@ bool TiffDecoder::readHeader()
|
||||
|
||||
bool TiffDecoder::readData( Mat& img )
|
||||
{
|
||||
if(m_hdr && img.type() == CV_32FC3)
|
||||
{
|
||||
return readHdrData(img);
|
||||
}
|
||||
bool result = false;
|
||||
bool color = img.channels() > 1;
|
||||
uchar* data = img.data;
|
||||
@@ -380,6 +394,37 @@ bool TiffDecoder::readData( Mat& img )
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TiffDecoder::readHdrData(Mat& img)
|
||||
{
|
||||
int rows_per_strip = 0, photometric = 0;
|
||||
if(!m_tif)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
TIFF *tif = static_cast<TIFF*>(m_tif);
|
||||
TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rows_per_strip);
|
||||
TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric );
|
||||
TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT);
|
||||
int size = 3 * m_width * m_height * sizeof (float);
|
||||
int strip_size = 3 * m_width * rows_per_strip;
|
||||
float *ptr = img.ptr<float>();
|
||||
for (size_t i = 0; i < TIFFNumberOfStrips(tif); i++, ptr += strip_size)
|
||||
{
|
||||
TIFFReadEncodedStrip(tif, i, ptr, size);
|
||||
size -= strip_size * sizeof(float);
|
||||
}
|
||||
close();
|
||||
if(photometric == PHOTOMETRIC_LOGLUV)
|
||||
{
|
||||
cvtColor(img, img, COLOR_XYZ2BGR);
|
||||
}
|
||||
else
|
||||
{
|
||||
cvtColor(img, img, COLOR_RGB2BGR);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -405,7 +450,11 @@ ImageEncoder TiffEncoder::newEncoder() const
|
||||
|
||||
bool TiffEncoder::isFormatSupported( int depth ) const
|
||||
{
|
||||
#ifdef HAVE_TIFF
|
||||
return depth == CV_8U || depth == CV_16U || depth == CV_32F;
|
||||
#else
|
||||
return depth == CV_8U || depth == CV_16U;
|
||||
#endif
|
||||
}
|
||||
|
||||
void TiffEncoder::writeTag( WLByteStream& strm, TiffTag tag,
|
||||
@@ -557,6 +606,33 @@ bool TiffEncoder::writeLibTiff( const Mat& img, const std::vector<int>& params)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TiffEncoder::writeHdr(const Mat& _img)
|
||||
{
|
||||
Mat img;
|
||||
cvtColor(_img, img, COLOR_BGR2XYZ);
|
||||
TIFF* tif = TIFFOpen(m_filename.c_str(), "w");
|
||||
if (!tif)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, img.cols);
|
||||
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, img.rows);
|
||||
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
|
||||
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_SGILOG);
|
||||
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_LOGLUV);
|
||||
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
||||
TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT);
|
||||
TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, 1);
|
||||
int strip_size = 3 * img.cols;
|
||||
float *ptr = const_cast<float*>(img.ptr<float>());
|
||||
for (int i = 0; i < img.rows; i++, ptr += strip_size)
|
||||
{
|
||||
TIFFWriteEncodedStrip(tif, i, ptr, strip_size * sizeof(float));
|
||||
}
|
||||
TIFFClose(tif);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TIFF
|
||||
@@ -568,6 +644,12 @@ bool TiffEncoder::write( const Mat& img, const std::vector<int>& /*params*/)
|
||||
int channels = img.channels();
|
||||
int width = img.cols, height = img.rows;
|
||||
int depth = img.depth();
|
||||
#ifdef HAVE_TIFF
|
||||
if(img.type() == CV_32FC3)
|
||||
{
|
||||
return writeHdr(img);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (depth != CV_8U && depth != CV_16U)
|
||||
return false;
|
||||
|
@@ -108,6 +108,8 @@ public:
|
||||
protected:
|
||||
void* m_tif;
|
||||
int normalizeChannelsNumber(int channels) const;
|
||||
bool readHdrData(Mat& img);
|
||||
bool m_hdr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -130,6 +132,7 @@ protected:
|
||||
int count, int value );
|
||||
|
||||
bool writeLibTiff( const Mat& img, const std::vector<int>& params );
|
||||
bool writeHdr( const Mat& img );
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -52,5 +52,6 @@
|
||||
#include "grfmt_jpeg2000.hpp"
|
||||
#include "grfmt_exr.hpp"
|
||||
#include "grfmt_webp.hpp"
|
||||
#include "grfmt_hdr.hpp"
|
||||
|
||||
#endif/*_GRFMTS_H_*/
|
||||
|
@@ -47,6 +47,7 @@
|
||||
#include "grfmts.hpp"
|
||||
#undef min
|
||||
#undef max
|
||||
#include <iostream>
|
||||
|
||||
/****************************************************************************************\
|
||||
* Image Codecs *
|
||||
@@ -60,6 +61,8 @@ struct ImageCodecInitializer
|
||||
{
|
||||
decoders.push_back( makePtr<BmpDecoder>() );
|
||||
encoders.push_back( makePtr<BmpEncoder>() );
|
||||
decoders.push_back( makePtr<HdrDecoder>() );
|
||||
encoders.push_back( makePtr<HdrEncoder>() );
|
||||
#ifdef HAVE_JPEG
|
||||
decoders.push_back( makePtr<JpegDecoder>() );
|
||||
encoders.push_back( makePtr<JpegEncoder>() );
|
||||
@@ -203,7 +206,6 @@ imread_( const String& filename, int flags, int hdrtype, Mat* mat=0 )
|
||||
decoder->setSource(filename);
|
||||
if( !decoder->readHeader() )
|
||||
return 0;
|
||||
|
||||
CvSize size;
|
||||
size.width = decoder->width();
|
||||
size.height = decoder->height();
|
||||
@@ -271,7 +273,6 @@ static bool imwrite_( const String& filename, const Mat& image,
|
||||
ImageEncoder encoder = findEncoder( filename );
|
||||
if( !encoder )
|
||||
CV_Error( CV_StsError, "could not find a writer for the specified extension" );
|
||||
|
||||
if( !encoder->isFormatSupported(image.depth()) )
|
||||
{
|
||||
CV_Assert( encoder->isFormatSupported(CV_8U) );
|
||||
|
450
modules/highgui/src/rgbe.cpp
Normal file
450
modules/highgui/src/rgbe.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's 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.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "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 the Intel Corporation or contributors 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.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "rgbe.hpp"
|
||||
#include <math.h>
|
||||
#if !defined(__APPLE__)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
// This file contains code to read and write four byte rgbe file format
|
||||
// developed by Greg Ward. It handles the conversions between rgbe and
|
||||
// pixels consisting of floats. The data is assumed to be an array of floats.
|
||||
// By default there are three floats per pixel in the order red, green, blue.
|
||||
// (RGBE_DATA_??? values control this.) Only the mimimal header reading and
|
||||
// writing is implemented. Each routine does error checking and will return
|
||||
// a status value as defined below. This code is intended as a skeleton so
|
||||
// feel free to modify it to suit your needs.
|
||||
|
||||
// Some opencv specific changes have been added:
|
||||
// inline define specified, error handler uses CV_Error,
|
||||
// defines changed to work in bgr color space.
|
||||
//
|
||||
// posted to http://www.graphics.cornell.edu/~bjw/
|
||||
// written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95
|
||||
// based on code written by Greg Ward
|
||||
|
||||
#define INLINE inline
|
||||
|
||||
/* offsets to red, green, and blue components in a data (float) pixel */
|
||||
#define RGBE_DATA_RED 2
|
||||
#define RGBE_DATA_GREEN 1
|
||||
#define RGBE_DATA_BLUE 0
|
||||
/* number of floats per pixel */
|
||||
#define RGBE_DATA_SIZE 3
|
||||
|
||||
enum rgbe_error_codes {
|
||||
rgbe_read_error,
|
||||
rgbe_write_error,
|
||||
rgbe_format_error,
|
||||
rgbe_memory_error,
|
||||
};
|
||||
|
||||
/* default error routine. change this to change error handling */
|
||||
static int rgbe_error(int rgbe_error_code, const char *msg)
|
||||
{
|
||||
switch (rgbe_error_code) {
|
||||
case rgbe_read_error:
|
||||
CV_Error(cv::Error::StsError, "RGBE read error");
|
||||
break;
|
||||
case rgbe_write_error:
|
||||
CV_Error(cv::Error::StsError, "RGBE write error");
|
||||
break;
|
||||
case rgbe_format_error:
|
||||
CV_Error(cv::Error::StsError, cv::String("RGBE bad file format: ") +
|
||||
cv::String(msg));
|
||||
break;
|
||||
default:
|
||||
case rgbe_memory_error:
|
||||
CV_Error(cv::Error::StsError, cv::String("RGBE error: \n") +
|
||||
cv::String(msg));
|
||||
}
|
||||
return RGBE_RETURN_FAILURE;
|
||||
}
|
||||
|
||||
/* standard conversion from float pixels to rgbe pixels */
|
||||
/* note: you can remove the "inline"s if your compiler complains about it */
|
||||
static INLINE void
|
||||
float2rgbe(unsigned char rgbe[4], float red, float green, float blue)
|
||||
{
|
||||
float v;
|
||||
int e;
|
||||
|
||||
v = red;
|
||||
if (green > v) v = green;
|
||||
if (blue > v) v = blue;
|
||||
if (v < 1e-32) {
|
||||
rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
|
||||
}
|
||||
else {
|
||||
v = static_cast<float>(frexp(v,&e) * 256.0/v);
|
||||
rgbe[0] = (unsigned char) (red * v);
|
||||
rgbe[1] = (unsigned char) (green * v);
|
||||
rgbe[2] = (unsigned char) (blue * v);
|
||||
rgbe[3] = (unsigned char) (e + 128);
|
||||
}
|
||||
}
|
||||
|
||||
/* standard conversion from rgbe to float pixels */
|
||||
/* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */
|
||||
/* in the range [0,1] to map back into the range [0,1]. */
|
||||
static INLINE void
|
||||
rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4])
|
||||
{
|
||||
float f;
|
||||
|
||||
if (rgbe[3]) { /*nonzero pixel*/
|
||||
f = static_cast<float>(ldexp(1.0,rgbe[3]-(int)(128+8)));
|
||||
*red = rgbe[0] * f;
|
||||
*green = rgbe[1] * f;
|
||||
*blue = rgbe[2] * f;
|
||||
}
|
||||
else
|
||||
*red = *green = *blue = 0.0;
|
||||
}
|
||||
|
||||
/* default minimal header. modify if you want more information in header */
|
||||
int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info)
|
||||
{
|
||||
const char *programtype = "RGBE";
|
||||
|
||||
if (info && (info->valid & RGBE_VALID_PROGRAMTYPE))
|
||||
programtype = info->programtype;
|
||||
if (fprintf(fp,"#?%s\n",programtype) < 0)
|
||||
return rgbe_error(rgbe_write_error,NULL);
|
||||
/* The #? is to identify file type, the programtype is optional. */
|
||||
if (info && (info->valid & RGBE_VALID_GAMMA)) {
|
||||
if (fprintf(fp,"GAMMA=%g\n",info->gamma) < 0)
|
||||
return rgbe_error(rgbe_write_error,NULL);
|
||||
}
|
||||
if (info && (info->valid & RGBE_VALID_EXPOSURE)) {
|
||||
if (fprintf(fp,"EXPOSURE=%g\n",info->exposure) < 0)
|
||||
return rgbe_error(rgbe_write_error,NULL);
|
||||
}
|
||||
if (fprintf(fp,"FORMAT=32-bit_rle_rgbe\n\n") < 0)
|
||||
return rgbe_error(rgbe_write_error,NULL);
|
||||
if (fprintf(fp, "-Y %d +X %d\n", height, width) < 0)
|
||||
return rgbe_error(rgbe_write_error,NULL);
|
||||
return RGBE_RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
/* minimal header reading. modify if you want to parse more information */
|
||||
int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info)
|
||||
{
|
||||
char buf[128];
|
||||
float tempf;
|
||||
int i;
|
||||
|
||||
if (info) {
|
||||
info->valid = 0;
|
||||
info->programtype[0] = 0;
|
||||
info->gamma = info->exposure = 1.0;
|
||||
}
|
||||
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == NULL)
|
||||
return rgbe_error(rgbe_read_error,NULL);
|
||||
if ((buf[0] != '#')||(buf[1] != '?')) {
|
||||
/* if you want to require the magic token then uncomment the next line */
|
||||
/*return rgbe_error(rgbe_format_error,"bad initial token"); */
|
||||
}
|
||||
else if (info) {
|
||||
info->valid |= RGBE_VALID_PROGRAMTYPE;
|
||||
for(i=0;i<static_cast<int>(sizeof(info->programtype)-1);i++) {
|
||||
if ((buf[i+2] == 0) || isspace(buf[i+2]))
|
||||
break;
|
||||
info->programtype[i] = buf[i+2];
|
||||
}
|
||||
info->programtype[i] = 0;
|
||||
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
|
||||
return rgbe_error(rgbe_read_error,NULL);
|
||||
}
|
||||
for(;;) {
|
||||
if ((buf[0] == 0)||(buf[0] == '\n'))
|
||||
return rgbe_error(rgbe_format_error,"no FORMAT specifier found");
|
||||
else if (strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0)
|
||||
break; /* format found so break out of loop */
|
||||
else if (info && (sscanf(buf,"GAMMA=%g",&tempf) == 1)) {
|
||||
info->gamma = tempf;
|
||||
info->valid |= RGBE_VALID_GAMMA;
|
||||
}
|
||||
else if (info && (sscanf(buf,"EXPOSURE=%g",&tempf) == 1)) {
|
||||
info->exposure = tempf;
|
||||
info->valid |= RGBE_VALID_EXPOSURE;
|
||||
}
|
||||
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
|
||||
return rgbe_error(rgbe_read_error,NULL);
|
||||
}
|
||||
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
|
||||
return rgbe_error(rgbe_read_error,NULL);
|
||||
if (strcmp(buf,"\n") != 0)
|
||||
return rgbe_error(rgbe_format_error,
|
||||
"missing blank line after FORMAT specifier");
|
||||
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
|
||||
return rgbe_error(rgbe_read_error,NULL);
|
||||
if (sscanf(buf,"-Y %d +X %d",height,width) < 2)
|
||||
return rgbe_error(rgbe_format_error,"missing image size specifier");
|
||||
return RGBE_RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
/* simple write routine that does not use run length encoding */
|
||||
/* These routines can be made faster by allocating a larger buffer and
|
||||
fread-ing and fwrite-ing the data in larger chunks */
|
||||
int RGBE_WritePixels(FILE *fp, float *data, int numpixels)
|
||||
{
|
||||
unsigned char rgbe[4];
|
||||
|
||||
while (numpixels-- > 0) {
|
||||
float2rgbe(rgbe,data[RGBE_DATA_RED],
|
||||
data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
|
||||
data += RGBE_DATA_SIZE;
|
||||
if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1)
|
||||
return rgbe_error(rgbe_write_error,NULL);
|
||||
}
|
||||
return RGBE_RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
/* simple read routine. will not correctly handle run length encoding */
|
||||
int RGBE_ReadPixels(FILE *fp, float *data, int numpixels)
|
||||
{
|
||||
unsigned char rgbe[4];
|
||||
|
||||
while(numpixels-- > 0) {
|
||||
if (fread(rgbe, sizeof(rgbe), 1, fp) < 1)
|
||||
return rgbe_error(rgbe_read_error,NULL);
|
||||
rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
|
||||
&data[RGBE_DATA_BLUE],rgbe);
|
||||
data += RGBE_DATA_SIZE;
|
||||
}
|
||||
return RGBE_RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
/* The code below is only needed for the run-length encoded files. */
|
||||
/* Run length encoding adds considerable complexity but does */
|
||||
/* save some space. For each scanline, each channel (r,g,b,e) is */
|
||||
/* encoded separately for better compression. */
|
||||
|
||||
static int RGBE_WriteBytes_RLE(FILE *fp, unsigned char *data, int numbytes)
|
||||
{
|
||||
#define MINRUNLENGTH 4
|
||||
int cur, beg_run, run_count, old_run_count, nonrun_count;
|
||||
unsigned char buf[2];
|
||||
|
||||
cur = 0;
|
||||
while(cur < numbytes) {
|
||||
beg_run = cur;
|
||||
/* find next run of length at least 4 if one exists */
|
||||
run_count = old_run_count = 0;
|
||||
while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) {
|
||||
beg_run += run_count;
|
||||
old_run_count = run_count;
|
||||
run_count = 1;
|
||||
while( (beg_run + run_count < numbytes) && (run_count < 127)
|
||||
&& (data[beg_run] == data[beg_run + run_count]))
|
||||
run_count++;
|
||||
}
|
||||
/* if data before next big run is a short run then write it as such */
|
||||
if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) {
|
||||
buf[0] = static_cast<unsigned char>(128 + old_run_count); /*write short run*/
|
||||
buf[1] = data[cur];
|
||||
if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
|
||||
return rgbe_error(rgbe_write_error,NULL);
|
||||
cur = beg_run;
|
||||
}
|
||||
/* write out bytes until we reach the start of the next run */
|
||||
while(cur < beg_run) {
|
||||
nonrun_count = beg_run - cur;
|
||||
if (nonrun_count > 128)
|
||||
nonrun_count = 128;
|
||||
buf[0] = static_cast<unsigned char>(nonrun_count);
|
||||
if (fwrite(buf,sizeof(buf[0]),1,fp) < 1)
|
||||
return rgbe_error(rgbe_write_error,NULL);
|
||||
if (fwrite(&data[cur],sizeof(data[0])*nonrun_count,1,fp) < 1)
|
||||
return rgbe_error(rgbe_write_error,NULL);
|
||||
cur += nonrun_count;
|
||||
}
|
||||
/* write out next run if one was found */
|
||||
if (run_count >= MINRUNLENGTH) {
|
||||
buf[0] = static_cast<unsigned char>(128 + run_count);
|
||||
buf[1] = data[beg_run];
|
||||
if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
|
||||
return rgbe_error(rgbe_write_error,NULL);
|
||||
cur += run_count;
|
||||
}
|
||||
}
|
||||
return RGBE_RETURN_SUCCESS;
|
||||
#undef MINRUNLENGTH
|
||||
}
|
||||
|
||||
int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width,
|
||||
int num_scanlines)
|
||||
{
|
||||
unsigned char rgbe[4];
|
||||
unsigned char *buffer;
|
||||
int i, err;
|
||||
|
||||
if ((scanline_width < 8)||(scanline_width > 0x7fff))
|
||||
/* run length encoding is not allowed so write flat*/
|
||||
return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
|
||||
buffer = (unsigned char *)malloc(sizeof(unsigned char)*4*scanline_width);
|
||||
if (buffer == NULL)
|
||||
/* no buffer space so write flat */
|
||||
return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
|
||||
while(num_scanlines-- > 0) {
|
||||
rgbe[0] = 2;
|
||||
rgbe[1] = 2;
|
||||
rgbe[2] = static_cast<unsigned char>(scanline_width >> 8);
|
||||
rgbe[3] = scanline_width & 0xFF;
|
||||
if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) {
|
||||
free(buffer);
|
||||
return rgbe_error(rgbe_write_error,NULL);
|
||||
}
|
||||
for(i=0;i<scanline_width;i++) {
|
||||
float2rgbe(rgbe,data[RGBE_DATA_RED],
|
||||
data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
|
||||
buffer[i] = rgbe[0];
|
||||
buffer[i+scanline_width] = rgbe[1];
|
||||
buffer[i+2*scanline_width] = rgbe[2];
|
||||
buffer[i+3*scanline_width] = rgbe[3];
|
||||
data += RGBE_DATA_SIZE;
|
||||
}
|
||||
/* write out each of the four channels separately run length encoded */
|
||||
/* first red, then green, then blue, then exponent */
|
||||
for(i=0;i<4;i++) {
|
||||
if ((err = RGBE_WriteBytes_RLE(fp,&buffer[i*scanline_width],
|
||||
scanline_width)) != RGBE_RETURN_SUCCESS) {
|
||||
free(buffer);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
return RGBE_RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width,
|
||||
int num_scanlines)
|
||||
{
|
||||
unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
|
||||
int i, count;
|
||||
unsigned char buf[2];
|
||||
|
||||
if ((scanline_width < 8)||(scanline_width > 0x7fff))
|
||||
/* run length encoding is not allowed so read flat*/
|
||||
return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines);
|
||||
scanline_buffer = NULL;
|
||||
/* read in each successive scanline */
|
||||
while(num_scanlines > 0) {
|
||||
if (fread(rgbe,sizeof(rgbe),1,fp) < 1) {
|
||||
free(scanline_buffer);
|
||||
return rgbe_error(rgbe_read_error,NULL);
|
||||
}
|
||||
if ((rgbe[0] != 2)||(rgbe[1] != 2)||(rgbe[2] & 0x80)) {
|
||||
/* this file is not run length encoded */
|
||||
rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],&data[RGBE_DATA_BLUE],rgbe);
|
||||
data += RGBE_DATA_SIZE;
|
||||
free(scanline_buffer);
|
||||
return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines-1);
|
||||
}
|
||||
if ((((int)rgbe[2])<<8 | rgbe[3]) != scanline_width) {
|
||||
free(scanline_buffer);
|
||||
return rgbe_error(rgbe_format_error,"wrong scanline width");
|
||||
}
|
||||
if (scanline_buffer == NULL)
|
||||
scanline_buffer = (unsigned char *)
|
||||
malloc(sizeof(unsigned char)*4*scanline_width);
|
||||
if (scanline_buffer == NULL)
|
||||
return rgbe_error(rgbe_memory_error,"unable to allocate buffer space");
|
||||
|
||||
ptr = &scanline_buffer[0];
|
||||
/* read each of the four channels for the scanline into the buffer */
|
||||
for(i=0;i<4;i++) {
|
||||
ptr_end = &scanline_buffer[(i+1)*scanline_width];
|
||||
while(ptr < ptr_end) {
|
||||
if (fread(buf,sizeof(buf[0])*2,1,fp) < 1) {
|
||||
free(scanline_buffer);
|
||||
return rgbe_error(rgbe_read_error,NULL);
|
||||
}
|
||||
if (buf[0] > 128) {
|
||||
/* a run of the same value */
|
||||
count = buf[0]-128;
|
||||
if ((count == 0)||(count > ptr_end - ptr)) {
|
||||
free(scanline_buffer);
|
||||
return rgbe_error(rgbe_format_error,"bad scanline data");
|
||||
}
|
||||
while(count-- > 0)
|
||||
*ptr++ = buf[1];
|
||||
}
|
||||
else {
|
||||
/* a non-run */
|
||||
count = buf[0];
|
||||
if ((count == 0)||(count > ptr_end - ptr)) {
|
||||
free(scanline_buffer);
|
||||
return rgbe_error(rgbe_format_error,"bad scanline data");
|
||||
}
|
||||
*ptr++ = buf[1];
|
||||
if (--count > 0) {
|
||||
if (fread(ptr,sizeof(*ptr)*count,1,fp) < 1) {
|
||||
free(scanline_buffer);
|
||||
return rgbe_error(rgbe_read_error,NULL);
|
||||
}
|
||||
ptr += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* now convert data from buffer into floats */
|
||||
for(i=0;i<scanline_width;i++) {
|
||||
rgbe[0] = scanline_buffer[i];
|
||||
rgbe[1] = scanline_buffer[i+scanline_width];
|
||||
rgbe[2] = scanline_buffer[i+2*scanline_width];
|
||||
rgbe[3] = scanline_buffer[i+3*scanline_width];
|
||||
rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
|
||||
&data[RGBE_DATA_BLUE],rgbe);
|
||||
data += RGBE_DATA_SIZE;
|
||||
}
|
||||
num_scanlines--;
|
||||
}
|
||||
free(scanline_buffer);
|
||||
return RGBE_RETURN_SUCCESS;
|
||||
}
|
89
modules/highgui/src/rgbe.hpp
Normal file
89
modules/highgui/src/rgbe.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's 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.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "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 the Intel Corporation or contributors 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.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef _RGBE_HDR_H_
|
||||
#define _RGBE_HDR_H_
|
||||
|
||||
// posted to http://www.graphics.cornell.edu/~bjw/
|
||||
// written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95
|
||||
// based on code written by Greg Ward
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
int valid; /* indicate which fields are valid */
|
||||
char programtype[16]; /* listed at beginning of file to identify it
|
||||
* after "#?". defaults to "RGBE" */
|
||||
float gamma; /* image has already been gamma corrected with
|
||||
* given gamma. defaults to 1.0 (no correction) */
|
||||
float exposure; /* a value of 1.0 in an image corresponds to
|
||||
* <exposure> watts/steradian/m^2.
|
||||
* defaults to 1.0 */
|
||||
} rgbe_header_info;
|
||||
|
||||
/* flags indicating which fields in an rgbe_header_info are valid */
|
||||
#define RGBE_VALID_PROGRAMTYPE 0x01
|
||||
#define RGBE_VALID_GAMMA 0x02
|
||||
#define RGBE_VALID_EXPOSURE 0x04
|
||||
|
||||
/* return codes for rgbe routines */
|
||||
#define RGBE_RETURN_SUCCESS 0
|
||||
#define RGBE_RETURN_FAILURE -1
|
||||
|
||||
/* read or write headers */
|
||||
/* you may set rgbe_header_info to null if you want to */
|
||||
int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info);
|
||||
int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info);
|
||||
|
||||
/* read or write pixels */
|
||||
/* can read or write pixels in chunks of any size including single pixels*/
|
||||
int RGBE_WritePixels(FILE *fp, float *data, int numpixels);
|
||||
int RGBE_ReadPixels(FILE *fp, float *data, int numpixels);
|
||||
|
||||
/* read or write run length encoded files */
|
||||
/* must be called to read or write whole scanlines */
|
||||
int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width,
|
||||
int num_scanlines);
|
||||
int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width,
|
||||
int num_scanlines);
|
||||
|
||||
#endif/*_RGBE_HDR_H_*/
|
@@ -430,11 +430,11 @@ TEST(Highgui_Tiff, decode_tile16384x16384)
|
||||
TEST(Highgui_WebP, encode_decode_lossless_webp)
|
||||
{
|
||||
cvtest::TS& ts = *cvtest::TS::ptr();
|
||||
std::string input = std::string(ts.get_data_path()) + "../cv/shared/lena.png";
|
||||
string input = string(ts.get_data_path()) + "../cv/shared/lena.png";
|
||||
cv::Mat img = cv::imread(input);
|
||||
ASSERT_FALSE(img.empty());
|
||||
|
||||
std::string output = cv::tempfile(".webp");
|
||||
string output = cv::tempfile(".webp");
|
||||
EXPECT_NO_THROW(cv::imwrite(output, img)); // lossless
|
||||
|
||||
cv::Mat img_webp = cv::imread(output);
|
||||
@@ -525,3 +525,28 @@ TEST(Highgui_WebP, encode_decode_with_alpha_webp)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST(Highgui_Hdr, regression)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "/readwrite/";
|
||||
string name_rle = folder + "rle.hdr";
|
||||
string name_no_rle = folder + "no_rle.hdr";
|
||||
Mat img_rle = imread(name_rle, -1);
|
||||
ASSERT_FALSE(img_rle.empty()) << "Could not open " << name_rle;
|
||||
Mat img_no_rle = imread(name_no_rle, -1);
|
||||
ASSERT_FALSE(img_no_rle.empty()) << "Could not open " << name_no_rle;
|
||||
|
||||
double min = 0.0, max = 1.0;
|
||||
minMaxLoc(abs(img_rle - img_no_rle), &min, &max);
|
||||
ASSERT_FALSE(max > DBL_EPSILON);
|
||||
string tmp_file_name = tempfile(".hdr");
|
||||
vector<int>param(1);
|
||||
for(int i = 0; i < 2; i++) {
|
||||
param[0] = i;
|
||||
imwrite(tmp_file_name, img_rle, param);
|
||||
Mat written_img = imread(tmp_file_name, -1);
|
||||
ASSERT_FALSE(written_img.empty()) << "Could not open " << tmp_file_name;
|
||||
minMaxLoc(abs(img_rle - written_img), &min, &max);
|
||||
ASSERT_FALSE(max > DBL_EPSILON);
|
||||
}
|
||||
}
|
||||
|
@@ -509,11 +509,11 @@ Line segment detector class, following the algorithm described at [Rafael12]_.
|
||||
.. ocv:class:: LineSegmentDetector : public Algorithm
|
||||
|
||||
|
||||
createLineSegmentDetectorPtr
|
||||
----------------------------
|
||||
createLineSegmentDetector
|
||||
-------------------------
|
||||
Creates a smart pointer to a LineSegmentDetector object and initializes it.
|
||||
|
||||
.. ocv:function:: Ptr<LineSegmentDetector> createLineSegmentDetectorPtr(int _refine = LSD_REFINE_STD, double _scale = 0.8, double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5, double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024)
|
||||
.. ocv:function:: Ptr<LineSegmentDetector> createLineSegmentDetector(int _refine = LSD_REFINE_STD, double _scale = 0.8, double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5, double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024)
|
||||
|
||||
:param _refine: The way found lines will be refined:
|
||||
|
||||
|
@@ -904,7 +904,7 @@ protected:
|
||||
Point2f bottomRight;
|
||||
};
|
||||
|
||||
class LineSegmentDetector : public Algorithm
|
||||
class CV_EXPORTS_W LineSegmentDetector : public Algorithm
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@@ -926,7 +926,7 @@ public:
|
||||
* * 1 corresponds to 0.1 mean false alarms
|
||||
* This vector will be calculated _only_ when the objects type is REFINE_ADV
|
||||
*/
|
||||
virtual void detect(InputArray _image, OutputArray _lines,
|
||||
CV_WRAP virtual void detect(InputArray _image, OutputArray _lines,
|
||||
OutputArray width = noArray(), OutputArray prec = noArray(),
|
||||
OutputArray nfa = noArray()) = 0;
|
||||
|
||||
@@ -937,7 +937,7 @@ public:
|
||||
* Should have the size of the image, where the lines were found
|
||||
* @param lines The lines that need to be drawn
|
||||
*/
|
||||
virtual void drawSegments(InputOutputArray _image, InputArray lines) = 0;
|
||||
CV_WRAP virtual void drawSegments(InputOutputArray _image, InputArray lines) = 0;
|
||||
|
||||
/**
|
||||
* Draw both vectors on the image canvas. Uses blue for lines 1 and red for lines 2.
|
||||
@@ -949,13 +949,13 @@ public:
|
||||
* Should have the size of the image, where the lines were found
|
||||
* @return The number of mismatching pixels between lines1 and lines2.
|
||||
*/
|
||||
virtual int compareSegments(const Size& size, InputArray lines1, InputArray lines2, InputOutputArray _image = noArray()) = 0;
|
||||
CV_WRAP virtual int compareSegments(const Size& size, InputArray lines1, InputArray lines2, InputOutputArray _image = noArray()) = 0;
|
||||
|
||||
virtual ~LineSegmentDetector() {};
|
||||
};
|
||||
|
||||
//! Returns a pointer to a LineSegmentDetector class.
|
||||
CV_EXPORTS Ptr<LineSegmentDetector> createLineSegmentDetectorPtr(
|
||||
CV_EXPORTS_W Ptr<LineSegmentDetector> createLineSegmentDetector(
|
||||
int _refine = LSD_REFINE_STD, double _scale = 0.8,
|
||||
double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5,
|
||||
double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024);
|
||||
|
@@ -388,7 +388,7 @@ private:
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CV_EXPORTS Ptr<LineSegmentDetector> createLineSegmentDetectorPtr(
|
||||
CV_EXPORTS Ptr<LineSegmentDetector> createLineSegmentDetector(
|
||||
int _refine, double _scale, double _sigma_scale, double _quant, double _ang_th,
|
||||
double _log_eps, double _density_th, int _n_bins)
|
||||
{
|
||||
|
@@ -110,7 +110,7 @@ TEST_F(Imgproc_LSD_ADV, whiteNoise)
|
||||
for (int i = 0; i < EPOCHS; ++i)
|
||||
{
|
||||
GenerateWhiteNoise(test_image);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_ADV);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_ADV);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(40u >= lines.size()) ++passedtests;
|
||||
@@ -123,7 +123,7 @@ TEST_F(Imgproc_LSD_ADV, constColor)
|
||||
for (int i = 0; i < EPOCHS; ++i)
|
||||
{
|
||||
GenerateConstColor(test_image);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_ADV);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_ADV);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(0u == lines.size()) ++passedtests;
|
||||
@@ -137,7 +137,7 @@ TEST_F(Imgproc_LSD_ADV, lines)
|
||||
{
|
||||
const unsigned int numOfLines = 1;
|
||||
GenerateLines(test_image, numOfLines);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_ADV);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_ADV);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(numOfLines * 2 == lines.size()) ++passedtests; // * 2 because of Gibbs effect
|
||||
@@ -150,7 +150,7 @@ TEST_F(Imgproc_LSD_ADV, rotatedRect)
|
||||
for (int i = 0; i < EPOCHS; ++i)
|
||||
{
|
||||
GenerateRotatedRect(test_image);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_ADV);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_ADV);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(2u <= lines.size()) ++passedtests;
|
||||
@@ -163,7 +163,7 @@ TEST_F(Imgproc_LSD_STD, whiteNoise)
|
||||
for (int i = 0; i < EPOCHS; ++i)
|
||||
{
|
||||
GenerateWhiteNoise(test_image);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_STD);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_STD);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(50u >= lines.size()) ++passedtests;
|
||||
@@ -176,7 +176,7 @@ TEST_F(Imgproc_LSD_STD, constColor)
|
||||
for (int i = 0; i < EPOCHS; ++i)
|
||||
{
|
||||
GenerateConstColor(test_image);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_STD);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_STD);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(0u == lines.size()) ++passedtests;
|
||||
@@ -190,7 +190,7 @@ TEST_F(Imgproc_LSD_STD, lines)
|
||||
{
|
||||
const unsigned int numOfLines = 1;
|
||||
GenerateLines(test_image, numOfLines);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_STD);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_STD);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(numOfLines * 2 == lines.size()) ++passedtests; // * 2 because of Gibbs effect
|
||||
@@ -203,7 +203,7 @@ TEST_F(Imgproc_LSD_STD, rotatedRect)
|
||||
for (int i = 0; i < EPOCHS; ++i)
|
||||
{
|
||||
GenerateRotatedRect(test_image);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_STD);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_STD);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(4u <= lines.size()) ++passedtests;
|
||||
@@ -216,7 +216,7 @@ TEST_F(Imgproc_LSD_NONE, whiteNoise)
|
||||
for (int i = 0; i < EPOCHS; ++i)
|
||||
{
|
||||
GenerateWhiteNoise(test_image);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_STD);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_STD);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(50u >= lines.size()) ++passedtests;
|
||||
@@ -229,7 +229,7 @@ TEST_F(Imgproc_LSD_NONE, constColor)
|
||||
for (int i = 0; i < EPOCHS; ++i)
|
||||
{
|
||||
GenerateConstColor(test_image);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_NONE);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_NONE);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(0u == lines.size()) ++passedtests;
|
||||
@@ -243,7 +243,7 @@ TEST_F(Imgproc_LSD_NONE, lines)
|
||||
{
|
||||
const unsigned int numOfLines = 1;
|
||||
GenerateLines(test_image, numOfLines);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_NONE);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_NONE);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(numOfLines * 2 == lines.size()) ++passedtests; // * 2 because of Gibbs effect
|
||||
@@ -256,7 +256,7 @@ TEST_F(Imgproc_LSD_NONE, rotatedRect)
|
||||
for (int i = 0; i < EPOCHS; ++i)
|
||||
{
|
||||
GenerateRotatedRect(test_image);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetectorPtr(LSD_REFINE_NONE);
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_NONE);
|
||||
detector->detect(test_image, lines);
|
||||
|
||||
if(8u <= lines.size()) ++passedtests;
|
||||
|
377
modules/photo/doc/hdr_imaging.rst
Normal file
377
modules/photo/doc/hdr_imaging.rst
Normal file
@@ -0,0 +1,377 @@
|
||||
HDR imaging
|
||||
=============
|
||||
|
||||
.. highlight:: cpp
|
||||
|
||||
This section describes high dynamic range imaging algorithms namely tonemapping, exposure alignment, camera calibration with multiple exposures and exposure fusion.
|
||||
|
||||
Tonemap
|
||||
---------------------------
|
||||
.. ocv:class:: Tonemap : public Algorithm
|
||||
|
||||
Base class for tonemapping algorithms - tools that are used to map HDR image to 8-bit range.
|
||||
|
||||
Tonemap::process
|
||||
---------------------------
|
||||
Tonemaps image
|
||||
|
||||
.. ocv:function:: void Tonemap::process(InputArray src, OutputArray dst)
|
||||
|
||||
:param src: source image - 32-bit 3-channel Mat
|
||||
:param dst: destination image - 32-bit 3-channel Mat with values in [0, 1] range
|
||||
|
||||
createTonemap
|
||||
---------------------------
|
||||
Creates simple linear mapper with gamma correction
|
||||
|
||||
.. ocv:function:: Ptr<Tonemap> createTonemap(float gamma = 1.0f)
|
||||
|
||||
:param gamma: positive value for gamma correction. Gamma value of 1.0 implies no correction, gamma equal to 2.2f is suitable for most displays.
|
||||
|
||||
Generally gamma > 1 brightens the image and gamma < 1 darkens it.
|
||||
|
||||
TonemapDrago
|
||||
---------------------------
|
||||
.. ocv:class:: TonemapDrago : public Tonemap
|
||||
|
||||
Adaptive logarithmic mapping is a fast global tonemapping algorithm that scales the image in logarithmic domain.
|
||||
|
||||
Since it's a global operator the same function is applied to all the pixels, it is controlled by the bias parameter.
|
||||
|
||||
Optional saturation enhancement is possible as described in [FL02]_.
|
||||
|
||||
For more information see [DM03]_.
|
||||
|
||||
createTonemapDrago
|
||||
---------------------------
|
||||
Creates TonemapDrago object
|
||||
|
||||
.. ocv:function:: Ptr<TonemapDrago> createTonemapDrago(float gamma = 1.0f, float saturation = 1.0f, float bias = 0.85f)
|
||||
|
||||
:param gamma: gamma value for gamma correction. See :ocv:func:`createTonemap`
|
||||
|
||||
:param saturation: positive saturation enhancement value. 1.0 preserves saturation, values greater than 1 increase saturation and values less than 1 decrease it.
|
||||
|
||||
:param bias: value for bias function in [0, 1] range. Values from 0.7 to 0.9 usually give best results, default value is 0.85.
|
||||
|
||||
TonemapDurand
|
||||
---------------------------
|
||||
.. ocv:class:: TonemapDurand : public Tonemap
|
||||
|
||||
This algorithm decomposes image into two layers: base layer and detail layer using bilateral filter and compresses contrast of the base layer thus preserving all the details.
|
||||
|
||||
This implementation uses regular bilateral filter from opencv.
|
||||
|
||||
Saturation enhancement is possible as in ocv:class:`TonemapDrago`.
|
||||
|
||||
For more information see [DD02]_.
|
||||
|
||||
createTonemapDurand
|
||||
---------------------------
|
||||
Creates TonemapDurand object
|
||||
|
||||
.. ocv:function:: Ptr<TonemapDurand> createTonemapDurand(float gamma = 1.0f, float contrast = 4.0f, float saturation = 1.0f, float sigma_space = 2.0f, float sigma_color = 2.0f)
|
||||
|
||||
:param gamma: gamma value for gamma correction. See :ocv:func:`createTonemap`
|
||||
|
||||
:param contrast: resulting contrast on logarithmic scale, i. e. log(max / min), where max and min are maximum and minimum luminance values of the resulting image.
|
||||
|
||||
:param saturation: saturation enhancement value. See :ocv:func:`createTonemapDrago`
|
||||
|
||||
:param sigma_space: bilateral filter sigma in color space
|
||||
|
||||
:param sigma_color: bilateral filter sigma in coordinate space
|
||||
|
||||
TonemapReinhard
|
||||
---------------------------
|
||||
.. ocv:class:: TonemapReinhard : public Tonemap
|
||||
|
||||
This is a global tonemapping operator that models human visual system.
|
||||
|
||||
Mapping function is controlled by adaptation parameter, that is computed using light adaptation and color adaptation.
|
||||
|
||||
For more information see [RD05]_.
|
||||
|
||||
createTonemapReinhard
|
||||
---------------------------
|
||||
Creates TonemapReinhard object
|
||||
|
||||
.. ocv:function:: Ptr<TonemapReinhard> createTonemapReinhard(float gamma = 1.0f, float intensity = 0.0f, float light_adapt = 1.0f, float color_adapt = 0.0f)
|
||||
|
||||
:param gamma: gamma value for gamma correction. See :ocv:func:`createTonemap`
|
||||
|
||||
:param intensity: result intensity in [-8, 8] range. Greater intensity produces brighter results.
|
||||
|
||||
:param light_adapt: light adaptation in [0, 1] range. If 1 adaptation is based only on pixel value, if 0 it's global, otherwise it's a weighted mean of this two cases.
|
||||
|
||||
:param color_adapt: chromatic adaptation in [0, 1] range. If 1 channels are treated independently, if 0 adaptation level is the same for each channel.
|
||||
|
||||
TonemapMantiuk
|
||||
---------------------------
|
||||
.. ocv:class:: TonemapMantiuk : public Tonemap
|
||||
|
||||
This algorithm transforms image to contrast using gradients on all levels of gaussian pyramid, transforms contrast values to HVS response and scales the response.
|
||||
After this the image is reconstructed from new contrast values.
|
||||
|
||||
For more information see [MM06]_.
|
||||
|
||||
createTonemapMantiuk
|
||||
---------------------------
|
||||
Creates TonemapMantiuk object
|
||||
|
||||
.. ocv:function:: Ptr<TonemapMantiuk> createTonemapMantiuk(float gamma = 1.0f, float scale = 0.7f, float saturation = 1.0f)
|
||||
|
||||
:param gamma: gamma value for gamma correction. See :ocv:func:`createTonemap`
|
||||
|
||||
:param scale: contrast scale factor. HVS response is multiplied by this parameter, thus compressing dynamic range. Values from 0.6 to 0.9 produce best results.
|
||||
|
||||
:param saturation: saturation enhancement value. See :ocv:func:`createTonemapDrago`
|
||||
|
||||
AlignExposures
|
||||
---------------------------
|
||||
.. ocv:class:: AlignExposures : public Algorithm
|
||||
|
||||
The base class for algorithms that align images of the same scene with different exposures
|
||||
|
||||
AlignExposures::process
|
||||
---------------------------
|
||||
Aligns images
|
||||
|
||||
.. ocv:function:: void AlignExposures::process(InputArrayOfArrays src, std::vector<Mat>& dst, InputArray times, InputArray response)
|
||||
|
||||
:param src: vector of input images
|
||||
|
||||
:param dst: vector of aligned images
|
||||
|
||||
:param times: vector of exposure time values for each image
|
||||
|
||||
:param response: 256x1 matrix with inverse camera response function for each pixel value, it should have the same number of channels as images.
|
||||
|
||||
AlignMTB
|
||||
---------------------------
|
||||
.. ocv:class:: AlignMTB : public AlignExposures
|
||||
|
||||
This algorithm converts images to median threshold bitmaps (1 for pixels brighter than median luminance and 0 otherwise) and than aligns the resulting bitmaps using bit operations.
|
||||
|
||||
It is invariant to exposure, so exposure values and camera response are not necessary.
|
||||
|
||||
In this implementation new image regions are filled with zeros.
|
||||
|
||||
For more information see [GW03]_.
|
||||
|
||||
AlignMTB::process
|
||||
---------------------------
|
||||
Short version of process, that doesn't take extra arguments.
|
||||
|
||||
.. ocv:function:: void AlignMTB::process(InputArrayOfArrays src, std::vector<Mat>& dst)
|
||||
|
||||
:param src: vector of input images
|
||||
|
||||
:param dst: vector of aligned images
|
||||
|
||||
AlignMTB::calculateShift
|
||||
---------------------------
|
||||
Calculates shift between two images, i. e. how to shift the second image to correspond it with the first.
|
||||
|
||||
.. ocv:function:: Point AlignMTB::calculateShift(InputArray img0, InputArray img1)
|
||||
|
||||
:param img0: first image
|
||||
|
||||
:param img1: second image
|
||||
|
||||
AlignMTB::shiftMat
|
||||
---------------------------
|
||||
Helper function, that shift Mat filling new regions with zeros.
|
||||
|
||||
.. ocv:function:: void AlignMTB::shiftMat(InputArray src, OutputArray dst, const Point shift)
|
||||
|
||||
:param src: input image
|
||||
|
||||
:param dst: result image
|
||||
|
||||
:param shift: shift value
|
||||
|
||||
AlignMTB::computeBitmaps
|
||||
---------------------------
|
||||
Computes median threshold and exclude bitmaps of given image.
|
||||
|
||||
.. ocv:function:: void AlignMTB::computeBitmaps(InputArray img, OutputArray tb, OutputArray eb)
|
||||
|
||||
:param img: input image
|
||||
|
||||
:param tb: median threshold bitmap
|
||||
|
||||
:param eb: exclude bitmap
|
||||
|
||||
createAlignMTB
|
||||
---------------------------
|
||||
Creates AlignMTB object
|
||||
|
||||
.. ocv:function:: Ptr<AlignMTB> createAlignMTB(int max_bits = 6, int exclude_range = 4, bool cut = true)
|
||||
|
||||
:param max_bits: logarithm to the base 2 of maximal shift in each dimension. Values of 5 and 6 are usually good enough (31 and 63 pixels shift respectively).
|
||||
|
||||
:param exclude_range: range for exclusion bitmap that is constructed to suppress noise around the median value.
|
||||
|
||||
:param cut: if true cuts images, otherwise fills the new regions with zeros.
|
||||
|
||||
CalibrateCRF
|
||||
---------------------------
|
||||
.. ocv:class:: CalibrateCRF : public Algorithm
|
||||
|
||||
The base class for camera response calibration algorithms.
|
||||
|
||||
CalibrateCRF::process
|
||||
---------------------------
|
||||
Recovers inverse camera response.
|
||||
|
||||
.. ocv:function:: void CalibrateCRF::process(InputArrayOfArrays src, OutputArray dst, InputArray times)
|
||||
|
||||
:param src: vector of input images
|
||||
|
||||
:param dst: 256x1 matrix with inverse camera response function
|
||||
|
||||
:param times: vector of exposure time values for each image
|
||||
|
||||
CalibrateDebevec
|
||||
---------------------------
|
||||
.. ocv:class:: CalibrateDebevec : public CalibrateCRF
|
||||
|
||||
Inverse camera response function is extracted for each brightness value by minimizing an objective function as linear system.
|
||||
Objective function is constructed using pixel values on the same position in all images, extra term is added to make the result smoother.
|
||||
|
||||
For more information see [DM97]_.
|
||||
|
||||
createCalibrateDebevec
|
||||
---------------------------
|
||||
Creates CalibrateDebevec object
|
||||
|
||||
.. ocv:function:: createCalibrateDebevec(int samples = 70, float lambda = 10.0f, bool random = false)
|
||||
|
||||
:param samples: number of pixel locations to use
|
||||
|
||||
:param lambda: smoothness term weight. Greater values produce smoother results, but can alter the response.
|
||||
|
||||
:param random: if true sample pixel locations are chosen at random, otherwise the form a rectangular grid.
|
||||
|
||||
CalibrateRobertson
|
||||
---------------------------
|
||||
.. ocv:class:: CalibrateRobertson : public CalibrateCRF
|
||||
|
||||
Inverse camera response function is extracted for each brightness value by minimizing an objective function as linear system.
|
||||
This algorithm uses all image pixels.
|
||||
|
||||
For more information see [RB99]_.
|
||||
|
||||
createCalibrateRobertson
|
||||
---------------------------
|
||||
Creates CalibrateRobertson object
|
||||
|
||||
.. ocv:function:: createCalibrateRobertson(int max_iter = 30, float threshold = 0.01f)
|
||||
|
||||
:param max_iter: maximal number of Gauss-Seidel solver iterations.
|
||||
|
||||
:param threshold: target difference between results of two successive steps of the minimization.
|
||||
|
||||
MergeExposures
|
||||
---------------------------
|
||||
.. ocv:class:: MergeExposures : public Algorithm
|
||||
|
||||
The base class algorithms that can merge exposure sequence to a single image.
|
||||
|
||||
MergeExposures::process
|
||||
---------------------------
|
||||
Merges images.
|
||||
|
||||
.. ocv:function:: void MergeExposures::process(InputArrayOfArrays src, OutputArray dst, InputArray times, InputArray response)
|
||||
|
||||
:param src: vector of input images
|
||||
|
||||
:param dst: result image
|
||||
|
||||
:param times: vector of exposure time values for each image
|
||||
|
||||
:param response: 256x1 matrix with inverse camera response function for each pixel value, it should have the same number of channels as images.
|
||||
|
||||
MergeDebevec
|
||||
---------------------------
|
||||
.. ocv:class:: MergeDebevec : public MergeExposures
|
||||
|
||||
The resulting HDR image is calculated as weighted average of the exposures considering exposure values and camera response.
|
||||
|
||||
For more information see [DM97]_.
|
||||
|
||||
createMergeDebevec
|
||||
---------------------------
|
||||
Creates MergeDebevec object
|
||||
|
||||
.. ocv:function:: Ptr<MergeDebevec> createMergeDebevec()
|
||||
|
||||
MergeMertens
|
||||
---------------------------
|
||||
.. ocv:class:: MergeMertens : public MergeExposures
|
||||
|
||||
Pixels are weighted using contrast, saturation and well-exposedness measures, than images are combined using laplacian pyramids.
|
||||
|
||||
The resulting image weight is constructed as weighted average of contrast, saturation and well-exposedness measures.
|
||||
|
||||
The resulting image doesn't require tonemapping and can be converted to 8-bit image by multiplying by 255, but it's recommended to apply gamma correction and/or linear tonemapping.
|
||||
|
||||
For more information see [MK07]_.
|
||||
|
||||
MergeMertens::process
|
||||
---------------------------
|
||||
Short version of process, that doesn't take extra arguments.
|
||||
|
||||
.. ocv:function:: void MergeMertens::process(InputArrayOfArrays src, OutputArray dst)
|
||||
|
||||
:param src: vector of input images
|
||||
|
||||
:param dst: result image
|
||||
|
||||
createMergeMertens
|
||||
---------------------------
|
||||
Creates MergeMertens object
|
||||
|
||||
.. ocv:function:: Ptr<MergeMertens> createMergeMertens(float contrast_weight = 1.0f, float saturation_weight = 1.0f, float exposure_weight = 0.0f)
|
||||
|
||||
:param contrast_weight: contrast measure weight. See :ocv:class:`MergeMertens`.
|
||||
|
||||
:param saturation_weight: saturation measure weight
|
||||
|
||||
:param exposure_weight: well-exposedness measure weight
|
||||
|
||||
MergeRobertson
|
||||
---------------------------
|
||||
.. ocv:class:: MergeRobertson : public MergeExposures
|
||||
|
||||
The resulting HDR image is calculated as weighted average of the exposures considering exposure values and camera response.
|
||||
|
||||
For more information see [RB99]_.
|
||||
|
||||
createMergeRobertson
|
||||
---------------------------
|
||||
Creates MergeRobertson object
|
||||
|
||||
.. ocv:function:: Ptr<MergeRobertson> createMergeRobertson()
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [DM03] F. Drago, K. Myszkowski, T. Annen, N. Chiba, "Adaptive Logarithmic Mapping For Displaying High Contrast Scenes", Computer Graphics Forum, 2003, 22, 419 - 426.
|
||||
|
||||
.. [FL02] R. Fattal, D. Lischinski, M. Werman, "Gradient Domain High Dynamic Range Compression", Proceedings OF ACM SIGGRAPH, 2002, 249 - 256.
|
||||
|
||||
.. [DD02] F. Durand and Julie Dorsey, "Fast Bilateral Filtering for the Display of High-Dynamic-Range Images", ACM Transactions on Graphics, 2002, 21, 3, 257 - 266.
|
||||
|
||||
.. [RD05] E. Reinhard, K. Devlin, "Dynamic Range Reduction Inspired by Photoreceptor Physiology", IEEE Transactions on Visualization and Computer Graphics, 2005, 11, 13 - 24.
|
||||
|
||||
.. [MM06] R. Mantiuk, K. Myszkowski, H.-P. Seidel, "Perceptual Framework for Contrast Processing of High Dynamic Range Images", ACM Transactions on Applied Perception, 2006, 3, 3, 286 - 308.
|
||||
|
||||
.. [GW03] G. Ward, "Fast, Robust Image Registration for Compositing High Dynamic Range Photographs from Handheld Exposures", Journal of Graphics Tools, 2003, 8, 17 - 30.
|
||||
|
||||
.. [DM97] P. Debevec, J. Malik, "Recovering High Dynamic Range Radiance Maps from Photographs", Proceedings OF ACM SIGGRAPH, 1997, 369 - 378.
|
||||
|
||||
.. [MK07] T. Mertens, J. Kautz, F. Van Reeth, "Exposure Fusion", Proceedings of the 15th Pacific Conference on Computer Graphics and Applications, 2007, 382 - 390.
|
||||
|
||||
.. [RB99] M. Robertson , S. Borman , R. Stevenson , "Dynamic range improvement through multiple exposures ", Proceedings of the Int. Conf. on Image Processing , 1999, 159 - 163.
|
@@ -9,3 +9,4 @@ photo. Computational Photography
|
||||
|
||||
inpainting
|
||||
denoising
|
||||
hdr_imaging
|
@@ -80,6 +80,214 @@ CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs,
|
||||
float h = 3, float hColor = 3,
|
||||
int templateWindowSize = 7, int searchWindowSize = 21);
|
||||
|
||||
enum { LDR_SIZE = 256 };
|
||||
|
||||
class CV_EXPORTS_W Tonemap : public Algorithm
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArray src, OutputArray dst) = 0;
|
||||
|
||||
CV_WRAP virtual float getGamma() const = 0;
|
||||
CV_WRAP virtual void setGamma(float gamma) = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<Tonemap> createTonemap(float gamma = 1.0f);
|
||||
|
||||
// "Adaptive Logarithmic Mapping For Displaying HighContrast Scenes", Drago et al., 2003
|
||||
|
||||
class CV_EXPORTS_W TonemapDrago : public Tonemap
|
||||
{
|
||||
public:
|
||||
|
||||
CV_WRAP virtual float getSaturation() const = 0;
|
||||
CV_WRAP virtual void setSaturation(float saturation) = 0;
|
||||
|
||||
CV_WRAP virtual float getBias() const = 0;
|
||||
CV_WRAP virtual void setBias(float bias) = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<TonemapDrago> createTonemapDrago(float gamma = 1.0f, float saturation = 1.0f, float bias = 0.85f);
|
||||
|
||||
// "Fast Bilateral Filtering for the Display of High-Dynamic-Range Images", Durand, Dorsey, 2002
|
||||
|
||||
class CV_EXPORTS_W TonemapDurand : public Tonemap
|
||||
{
|
||||
public:
|
||||
|
||||
CV_WRAP virtual float getSaturation() const = 0;
|
||||
CV_WRAP virtual void setSaturation(float saturation) = 0;
|
||||
|
||||
CV_WRAP virtual float getContrast() const = 0;
|
||||
CV_WRAP virtual void setContrast(float contrast) = 0;
|
||||
|
||||
CV_WRAP virtual float getSigmaSpace() const = 0;
|
||||
CV_WRAP virtual void setSigmaSpace(float sigma_space) = 0;
|
||||
|
||||
CV_WRAP virtual float getSigmaColor() const = 0;
|
||||
CV_WRAP virtual void setSigmaColor(float sigma_color) = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<TonemapDurand>
|
||||
createTonemapDurand(float gamma = 1.0f, float contrast = 4.0f, float saturation = 1.0f, float sigma_space = 2.0f, float sigma_color = 2.0f);
|
||||
|
||||
// "Dynamic Range Reduction Inspired by Photoreceptor Physiology", Reinhard, Devlin, 2005
|
||||
|
||||
class CV_EXPORTS_W TonemapReinhard : public Tonemap
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual float getIntensity() const = 0;
|
||||
CV_WRAP virtual void setIntensity(float intensity) = 0;
|
||||
|
||||
CV_WRAP virtual float getLightAdaptation() const = 0;
|
||||
CV_WRAP virtual void setLightAdaptation(float light_adapt) = 0;
|
||||
|
||||
CV_WRAP virtual float getColorAdaptation() const = 0;
|
||||
CV_WRAP virtual void setColorAdaptation(float color_adapt) = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<TonemapReinhard>
|
||||
createTonemapReinhard(float gamma = 1.0f, float intensity = 0.0f, float light_adapt = 1.0f, float color_adapt = 0.0f);
|
||||
|
||||
// "Perceptual Framework for Contrast Processing of High Dynamic Range Images", Mantiuk et al., 2006
|
||||
|
||||
class CV_EXPORTS_W TonemapMantiuk : public Tonemap
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual float getScale() const = 0;
|
||||
CV_WRAP virtual void setScale(float scale) = 0;
|
||||
|
||||
CV_WRAP virtual float getSaturation() const = 0;
|
||||
CV_WRAP virtual void setSaturation(float saturation) = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<TonemapMantiuk>
|
||||
createTonemapMantiuk(float gamma = 1.0f, float scale = 0.7f, float saturation = 1.0f);
|
||||
|
||||
class CV_EXPORTS_W AlignExposures : public Algorithm
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, std::vector<Mat>& dst,
|
||||
InputArray times, InputArray response) = 0;
|
||||
};
|
||||
|
||||
// "Fast, Robust Image Registration for Compositing High Dynamic Range Photographs from Handheld Exposures", Ward, 2003
|
||||
|
||||
class CV_EXPORTS_W AlignMTB : public AlignExposures
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, std::vector<Mat>& dst,
|
||||
InputArray times, InputArray response) = 0;
|
||||
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, std::vector<Mat>& dst) = 0;
|
||||
|
||||
CV_WRAP virtual Point calculateShift(InputArray img0, InputArray img1) = 0;
|
||||
CV_WRAP virtual void shiftMat(InputArray src, OutputArray dst, const Point shift) = 0;
|
||||
CV_WRAP virtual void computeBitmaps(InputArray img, OutputArray tb, OutputArray eb) = 0;
|
||||
|
||||
CV_WRAP virtual int getMaxBits() const = 0;
|
||||
CV_WRAP virtual void setMaxBits(int max_bits) = 0;
|
||||
|
||||
CV_WRAP virtual int getExcludeRange() const = 0;
|
||||
CV_WRAP virtual void setExcludeRange(int exclude_range) = 0;
|
||||
|
||||
CV_WRAP virtual bool getCut() const = 0;
|
||||
CV_WRAP virtual void setCut(bool value) = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<AlignMTB> createAlignMTB(int max_bits = 6, int exclude_range = 4, bool cut = true);
|
||||
|
||||
class CV_EXPORTS_W CalibrateCRF : public Algorithm
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, InputArray times) = 0;
|
||||
};
|
||||
|
||||
// "Recovering High Dynamic Range Radiance Maps from Photographs", Debevec, Malik, 1997
|
||||
|
||||
class CV_EXPORTS_W CalibrateDebevec : public CalibrateCRF
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual float getLambda() const = 0;
|
||||
CV_WRAP virtual void setLambda(float lambda) = 0;
|
||||
|
||||
CV_WRAP virtual int getSamples() const = 0;
|
||||
CV_WRAP virtual void setSamples(int samples) = 0;
|
||||
|
||||
CV_WRAP virtual bool getRandom() const = 0;
|
||||
CV_WRAP virtual void setRandom(bool random) = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<CalibrateDebevec> createCalibrateDebevec(int samples = 70, float lambda = 10.0f, bool random = false);
|
||||
|
||||
// "Dynamic range improvement through multiple exposures", Robertson et al., 1999
|
||||
|
||||
class CV_EXPORTS_W CalibrateRobertson : public CalibrateCRF
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual int getMaxIter() const = 0;
|
||||
CV_WRAP virtual void setMaxIter(int max_iter) = 0;
|
||||
|
||||
CV_WRAP virtual float getThreshold() const = 0;
|
||||
CV_WRAP virtual void setThreshold(float threshold) = 0;
|
||||
|
||||
CV_WRAP virtual Mat getRadiance() const = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<CalibrateRobertson> createCalibrateRobertson(int max_iter = 30, float threshold = 0.01f);
|
||||
|
||||
class CV_EXPORTS_W MergeExposures : public Algorithm
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
|
||||
InputArray times, InputArray response) = 0;
|
||||
};
|
||||
|
||||
// "Recovering High Dynamic Range Radiance Maps from Photographs", Debevec, Malik, 1997
|
||||
|
||||
class CV_EXPORTS_W MergeDebevec : public MergeExposures
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
|
||||
InputArray times, InputArray response) = 0;
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, InputArray times) = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<MergeDebevec> createMergeDebevec();
|
||||
|
||||
// "Exposure Fusion", Mertens et al., 2007
|
||||
|
||||
class CV_EXPORTS_W MergeMertens : public MergeExposures
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
|
||||
InputArray times, InputArray response) = 0;
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst) = 0;
|
||||
|
||||
CV_WRAP virtual float getContrastWeight() const = 0;
|
||||
CV_WRAP virtual void setContrastWeight(float contrast_weiht) = 0;
|
||||
|
||||
CV_WRAP virtual float getSaturationWeight() const = 0;
|
||||
CV_WRAP virtual void setSaturationWeight(float saturation_weight) = 0;
|
||||
|
||||
CV_WRAP virtual float getExposureWeight() const = 0;
|
||||
CV_WRAP virtual void setExposureWeight(float exposure_weight) = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<MergeMertens>
|
||||
createMergeMertens(float contrast_weight = 1.0f, float saturation_weight = 1.0f, float exposure_weight = 0.0f);
|
||||
|
||||
// "Dynamic range improvement through multiple exposures", Robertson et al., 1999
|
||||
|
||||
class CV_EXPORTS_W MergeRobertson : public MergeExposures
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst,
|
||||
InputArray times, InputArray response) = 0;
|
||||
CV_WRAP virtual void process(InputArrayOfArrays src, OutputArray dst, InputArray times) = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<MergeRobertson> createMergeRobertson();
|
||||
|
||||
} // cv
|
||||
|
||||
#endif
|
||||
|
270
modules/photo/src/align.cpp
Normal file
270
modules/photo/src/align.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's 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.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "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 the Intel Corporation or contributors 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.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "hdr_common.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
class AlignMTBImpl : public AlignMTB
|
||||
{
|
||||
public:
|
||||
AlignMTBImpl(int _max_bits, int _exclude_range, bool _cut) :
|
||||
name("AlignMTB"),
|
||||
max_bits(_max_bits),
|
||||
exclude_range(_exclude_range),
|
||||
cut(_cut)
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, std::vector<Mat>& dst,
|
||||
InputArray, InputArray)
|
||||
{
|
||||
process(src, dst);
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays _src, std::vector<Mat>& dst)
|
||||
{
|
||||
std::vector<Mat> src;
|
||||
_src.getMatVector(src);
|
||||
|
||||
checkImageDimensions(src);
|
||||
dst.resize(src.size());
|
||||
|
||||
size_t pivot = src.size() / 2;
|
||||
dst[pivot] = src[pivot];
|
||||
Mat gray_base;
|
||||
cvtColor(src[pivot], gray_base, COLOR_RGB2GRAY);
|
||||
std::vector<Point> shifts;
|
||||
|
||||
for(size_t i = 0; i < src.size(); i++) {
|
||||
if(i == pivot) {
|
||||
shifts.push_back(Point(0, 0));
|
||||
continue;
|
||||
}
|
||||
Mat gray;
|
||||
cvtColor(src[i], gray, COLOR_RGB2GRAY);
|
||||
Point shift = calculateShift(gray_base, gray);
|
||||
shifts.push_back(shift);
|
||||
shiftMat(src[i], dst[i], shift);
|
||||
}
|
||||
if(cut) {
|
||||
Point max(0, 0), min(0, 0);
|
||||
for(size_t i = 0; i < shifts.size(); i++) {
|
||||
if(shifts[i].x > max.x) {
|
||||
max.x = shifts[i].x;
|
||||
}
|
||||
if(shifts[i].y > max.y) {
|
||||
max.y = shifts[i].y;
|
||||
}
|
||||
if(shifts[i].x < min.x) {
|
||||
min.x = shifts[i].x;
|
||||
}
|
||||
if(shifts[i].y < min.y) {
|
||||
min.y = shifts[i].y;
|
||||
}
|
||||
}
|
||||
Point size = dst[0].size();
|
||||
for(size_t i = 0; i < dst.size(); i++) {
|
||||
dst[i] = dst[i](Rect(max, min + size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Point calculateShift(InputArray _img0, InputArray _img1)
|
||||
{
|
||||
Mat img0 = _img0.getMat();
|
||||
Mat img1 = _img1.getMat();
|
||||
CV_Assert(img0.channels() == 1 && img0.type() == img1.type());
|
||||
CV_Assert(img0.size() == img0.size());
|
||||
|
||||
int maxlevel = static_cast<int>(log((double)max(img0.rows, img0.cols)) / log(2.0)) - 1;
|
||||
maxlevel = min(maxlevel, max_bits - 1);
|
||||
|
||||
std::vector<Mat> pyr0;
|
||||
std::vector<Mat> pyr1;
|
||||
buildPyr(img0, pyr0, maxlevel);
|
||||
buildPyr(img1, pyr1, maxlevel);
|
||||
|
||||
Point shift(0, 0);
|
||||
for(int level = maxlevel; level >= 0; level--) {
|
||||
|
||||
shift *= 2;
|
||||
Mat tb1, tb2, eb1, eb2;
|
||||
computeBitmaps(pyr0[level], tb1, eb1);
|
||||
computeBitmaps(pyr1[level], tb2, eb2);
|
||||
|
||||
int min_err = pyr0[level].total();
|
||||
Point new_shift(shift);
|
||||
for(int i = -1; i <= 1; i++) {
|
||||
for(int j = -1; j <= 1; j++) {
|
||||
Point test_shift = shift + Point(i, j);
|
||||
Mat shifted_tb2, shifted_eb2, diff;
|
||||
shiftMat(tb2, shifted_tb2, test_shift);
|
||||
shiftMat(eb2, shifted_eb2, test_shift);
|
||||
bitwise_xor(tb1, shifted_tb2, diff);
|
||||
bitwise_and(diff, eb1, diff);
|
||||
bitwise_and(diff, shifted_eb2, diff);
|
||||
int err = countNonZero(diff);
|
||||
if(err < min_err) {
|
||||
new_shift = test_shift;
|
||||
min_err = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
shift = new_shift;
|
||||
}
|
||||
return shift;
|
||||
}
|
||||
|
||||
void shiftMat(InputArray _src, OutputArray _dst, const Point shift)
|
||||
{
|
||||
Mat src = _src.getMat();
|
||||
_dst.create(src.size(), src.type());
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
Mat res = Mat::zeros(src.size(), src.type());
|
||||
int width = src.cols - abs(shift.x);
|
||||
int height = src.rows - abs(shift.y);
|
||||
Rect dst_rect(max(shift.x, 0), max(shift.y, 0), width, height);
|
||||
Rect src_rect(max(-shift.x, 0), max(-shift.y, 0), width, height);
|
||||
src(src_rect).copyTo(res(dst_rect));
|
||||
res.copyTo(dst);
|
||||
}
|
||||
|
||||
int getMaxBits() const { return max_bits; }
|
||||
void setMaxBits(int val) { max_bits = val; }
|
||||
|
||||
int getExcludeRange() const { return exclude_range; }
|
||||
void setExcludeRange(int val) { exclude_range = val; }
|
||||
|
||||
bool getCut() const { return cut; }
|
||||
void setCut(bool val) { cut = val; }
|
||||
|
||||
void write(FileStorage& fs) const
|
||||
{
|
||||
fs << "name" << name
|
||||
<< "max_bits" << max_bits
|
||||
<< "exclude_range" << exclude_range
|
||||
<< "cut" << static_cast<int>(cut);
|
||||
}
|
||||
|
||||
void read(const FileNode& fn)
|
||||
{
|
||||
FileNode n = fn["name"];
|
||||
CV_Assert(n.isString() && String(n) == name);
|
||||
max_bits = fn["max_bits"];
|
||||
exclude_range = fn["exclude_range"];
|
||||
int cut_val = fn["cut"];
|
||||
cut = (cut_val != 0);
|
||||
}
|
||||
|
||||
void computeBitmaps(InputArray _img, OutputArray _tb, OutputArray _eb)
|
||||
{
|
||||
Mat img = _img.getMat();
|
||||
_tb.create(img.size(), CV_8U);
|
||||
_eb.create(img.size(), CV_8U);
|
||||
Mat tb = _tb.getMat(), eb = _eb.getMat();
|
||||
int median = getMedian(img);
|
||||
compare(img, median, tb, CMP_GT);
|
||||
compare(abs(img - median), exclude_range, eb, CMP_GT);
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
int max_bits, exclude_range;
|
||||
bool cut;
|
||||
|
||||
void downsample(Mat& src, Mat& dst)
|
||||
{
|
||||
dst = Mat(src.rows / 2, src.cols / 2, CV_8UC1);
|
||||
|
||||
int offset = src.cols * 2;
|
||||
uchar *src_ptr = src.ptr();
|
||||
uchar *dst_ptr = dst.ptr();
|
||||
for(int y = 0; y < dst.rows; y ++) {
|
||||
uchar *ptr = src_ptr;
|
||||
for(int x = 0; x < dst.cols; x++) {
|
||||
dst_ptr[0] = ptr[0];
|
||||
dst_ptr++;
|
||||
ptr += 2;
|
||||
}
|
||||
src_ptr += offset;
|
||||
}
|
||||
}
|
||||
|
||||
void buildPyr(Mat& img, std::vector<Mat>& pyr, int maxlevel)
|
||||
{
|
||||
pyr.resize(maxlevel + 1);
|
||||
pyr[0] = img.clone();
|
||||
for(int level = 0; level < maxlevel; level++) {
|
||||
downsample(pyr[level], pyr[level + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
int getMedian(Mat& img)
|
||||
{
|
||||
int channels = 0;
|
||||
Mat hist;
|
||||
int hist_size = LDR_SIZE;
|
||||
float range[] = {0, LDR_SIZE} ;
|
||||
const float* ranges[] = {range};
|
||||
calcHist(&img, 1, &channels, Mat(), hist, 1, &hist_size, ranges);
|
||||
float *ptr = hist.ptr<float>();
|
||||
int median = 0, sum = 0;
|
||||
int thresh = img.total() / 2;
|
||||
while(sum < thresh && median < LDR_SIZE) {
|
||||
sum += static_cast<int>(ptr[median]);
|
||||
median++;
|
||||
}
|
||||
return median;
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<AlignMTB> createAlignMTB(int max_bits, int exclude_range, bool cut)
|
||||
{
|
||||
return makePtr<AlignMTBImpl>(max_bits, exclude_range, cut);
|
||||
}
|
||||
|
||||
}
|
276
modules/photo/src/calibrate.cpp
Normal file
276
modules/photo/src/calibrate.cpp
Normal file
@@ -0,0 +1,276 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's 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.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "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 the Intel Corporation or contributors 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.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
//#include "opencv2/highgui.hpp"
|
||||
#include "hdr_common.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
class CalibrateDebevecImpl : public CalibrateDebevec
|
||||
{
|
||||
public:
|
||||
CalibrateDebevecImpl(int _samples, float _lambda, bool _random) :
|
||||
name("CalibrateDebevec"),
|
||||
samples(_samples),
|
||||
lambda(_lambda),
|
||||
random(_random),
|
||||
w(tringleWeights())
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times)
|
||||
{
|
||||
std::vector<Mat> images;
|
||||
src.getMatVector(images);
|
||||
Mat times = _times.getMat();
|
||||
|
||||
CV_Assert(images.size() == times.total());
|
||||
checkImageDimensions(images);
|
||||
CV_Assert(images[0].depth() == CV_8U);
|
||||
|
||||
int channels = images[0].channels();
|
||||
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
|
||||
|
||||
dst.create(LDR_SIZE, 1, CV_32FCC);
|
||||
Mat result = dst.getMat();
|
||||
|
||||
std::vector<Point> sample_points;
|
||||
if(random) {
|
||||
for(int i = 0; i < samples; i++) {
|
||||
sample_points.push_back(Point(rand() % images[0].cols, rand() % images[0].rows));
|
||||
}
|
||||
} else {
|
||||
int x_points = static_cast<int>(sqrt(static_cast<double>(samples) * images[0].cols / images[0].rows));
|
||||
int y_points = samples / x_points;
|
||||
int step_x = images[0].cols / x_points;
|
||||
int step_y = images[0].rows / y_points;
|
||||
|
||||
for(int i = 0, x = step_x / 2; i < x_points; i++, x += step_x) {
|
||||
for(int j = 0, y = step_y; j < y_points; j++, y += step_y) {
|
||||
sample_points.push_back(Point(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Mat> result_split(channels);
|
||||
for(int channel = 0; channel < channels; channel++) {
|
||||
Mat A = Mat::zeros(sample_points.size() * images.size() + LDR_SIZE + 1, LDR_SIZE + sample_points.size(), CV_32F);
|
||||
Mat B = Mat::zeros(A.rows, 1, CV_32F);
|
||||
|
||||
int eq = 0;
|
||||
for(size_t i = 0; i < sample_points.size(); i++) {
|
||||
for(size_t j = 0; j < images.size(); j++) {
|
||||
|
||||
int val = images[j].ptr()[3*(sample_points[i].y * images[j].cols + sample_points[j].x) + channel];
|
||||
A.at<float>(eq, val) = w.at<float>(val);
|
||||
A.at<float>(eq, LDR_SIZE + i) = -w.at<float>(val);
|
||||
B.at<float>(eq, 0) = w.at<float>(val) * log(times.at<float>(j));
|
||||
eq++;
|
||||
}
|
||||
}
|
||||
A.at<float>(eq, LDR_SIZE / 2) = 1;
|
||||
eq++;
|
||||
|
||||
for(int i = 0; i < 254; i++) {
|
||||
A.at<float>(eq, i) = lambda * w.at<float>(i + 1);
|
||||
A.at<float>(eq, i + 1) = -2 * lambda * w.at<float>(i + 1);
|
||||
A.at<float>(eq, i + 2) = lambda * w.at<float>(i + 1);
|
||||
eq++;
|
||||
}
|
||||
Mat solution;
|
||||
solve(A, B, solution, DECOMP_SVD);
|
||||
solution.rowRange(0, LDR_SIZE).copyTo(result_split[channel]);
|
||||
}
|
||||
merge(result_split, result);
|
||||
exp(result, result);
|
||||
}
|
||||
|
||||
int getSamples() const { return samples; }
|
||||
void setSamples(int val) { samples = val; }
|
||||
|
||||
float getLambda() const { return lambda; }
|
||||
void setLambda(float val) { lambda = val; }
|
||||
|
||||
bool getRandom() const { return random; }
|
||||
void setRandom(bool val) { random = val; }
|
||||
|
||||
void write(FileStorage& fs) const
|
||||
{
|
||||
fs << "name" << name
|
||||
<< "samples" << samples
|
||||
<< "lambda" << lambda
|
||||
<< "random" << static_cast<int>(random);
|
||||
}
|
||||
|
||||
void read(const FileNode& fn)
|
||||
{
|
||||
FileNode n = fn["name"];
|
||||
CV_Assert(n.isString() && String(n) == name);
|
||||
samples = fn["samples"];
|
||||
lambda = fn["lambda"];
|
||||
int random_val = fn["random"];
|
||||
random = (random_val != 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
int samples;
|
||||
float lambda;
|
||||
bool random;
|
||||
Mat w;
|
||||
};
|
||||
|
||||
Ptr<CalibrateDebevec> createCalibrateDebevec(int samples, float lambda, bool random)
|
||||
{
|
||||
return makePtr<CalibrateDebevecImpl>(samples, lambda, random);
|
||||
}
|
||||
|
||||
class CalibrateRobertsonImpl : public CalibrateRobertson
|
||||
{
|
||||
public:
|
||||
CalibrateRobertsonImpl(int _max_iter, float _threshold) :
|
||||
name("CalibrateRobertson"),
|
||||
max_iter(_max_iter),
|
||||
threshold(_threshold),
|
||||
weight(RobertsonWeights())
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times)
|
||||
{
|
||||
std::vector<Mat> images;
|
||||
src.getMatVector(images);
|
||||
Mat times = _times.getMat();
|
||||
|
||||
CV_Assert(images.size() == times.total());
|
||||
checkImageDimensions(images);
|
||||
CV_Assert(images[0].depth() == CV_8U);
|
||||
|
||||
int channels = images[0].channels();
|
||||
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
|
||||
|
||||
dst.create(LDR_SIZE, 1, CV_32FCC);
|
||||
Mat response = dst.getMat();
|
||||
response = linearResponse(3) / (LDR_SIZE / 2.0f);
|
||||
|
||||
Mat card = Mat::zeros(LDR_SIZE, 1, CV_32FCC);
|
||||
for(size_t i = 0; i < images.size(); i++) {
|
||||
uchar *ptr = images[i].ptr();
|
||||
for(size_t pos = 0; pos < images[i].total(); pos++) {
|
||||
for(int c = 0; c < channels; c++, ptr++) {
|
||||
card.at<Vec3f>(*ptr)[c] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
card = 1.0 / card;
|
||||
|
||||
Ptr<MergeRobertson> merge = createMergeRobertson();
|
||||
for(int iter = 0; iter < max_iter; iter++) {
|
||||
|
||||
radiance = Mat::zeros(images[0].size(), CV_32FCC);
|
||||
merge->process(images, radiance, times, response);
|
||||
|
||||
Mat new_response = Mat::zeros(LDR_SIZE, 1, CV_32FC3);
|
||||
for(size_t i = 0; i < images.size(); i++) {
|
||||
uchar *ptr = images[i].ptr();
|
||||
float* rad_ptr = radiance.ptr<float>();
|
||||
for(size_t pos = 0; pos < images[i].total(); pos++) {
|
||||
for(int c = 0; c < channels; c++, ptr++, rad_ptr++) {
|
||||
new_response.at<Vec3f>(*ptr)[c] += times.at<float>(i) * *rad_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
new_response = new_response.mul(card);
|
||||
for(int c = 0; c < 3; c++) {
|
||||
float middle = new_response.at<Vec3f>(LDR_SIZE / 2)[c];
|
||||
for(int i = 0; i < LDR_SIZE; i++) {
|
||||
new_response.at<Vec3f>(i)[c] /= middle;
|
||||
}
|
||||
}
|
||||
float diff = static_cast<float>(sum(sum(abs(new_response - response)))[0] / channels);
|
||||
new_response.copyTo(response);
|
||||
if(diff < threshold) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getMaxIter() const { return max_iter; }
|
||||
void setMaxIter(int val) { max_iter = val; }
|
||||
|
||||
float getThreshold() const { return threshold; }
|
||||
void setThreshold(float val) { threshold = val; }
|
||||
|
||||
Mat getRadiance() const { return radiance; }
|
||||
|
||||
void write(FileStorage& fs) const
|
||||
{
|
||||
fs << "name" << name
|
||||
<< "max_iter" << max_iter
|
||||
<< "threshold" << threshold;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn)
|
||||
{
|
||||
FileNode n = fn["name"];
|
||||
CV_Assert(n.isString() && String(n) == name);
|
||||
max_iter = fn["max_iter"];
|
||||
threshold = fn["threshold"];
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
int max_iter;
|
||||
float threshold;
|
||||
Mat weight, radiance;
|
||||
};
|
||||
|
||||
Ptr<CalibrateRobertson> createCalibrateRobertson(int max_iter, float threshold)
|
||||
{
|
||||
return makePtr<CalibrateRobertsonImpl>(max_iter, threshold);
|
||||
}
|
||||
|
||||
}
|
@@ -116,7 +116,7 @@ static void fastNlMeansDenoisingMultiCheckPreconditions(
|
||||
int imgToDenoiseIndex, int temporalWindowSize,
|
||||
int templateWindowSize, int searchWindowSize)
|
||||
{
|
||||
int src_imgs_size = (int)srcImgs.size();
|
||||
int src_imgs_size = static_cast<int>(srcImgs.size());
|
||||
if (src_imgs_size == 0) {
|
||||
CV_Error(Error::StsBadArg, "Input images vector should not be empty!");
|
||||
}
|
||||
@@ -198,7 +198,7 @@ void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs, OutputAr
|
||||
_dst.create(srcImgs[0].size(), srcImgs[0].type());
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
int src_imgs_size = (int)srcImgs.size();
|
||||
int src_imgs_size = static_cast<int>(srcImgs.size());
|
||||
|
||||
if (srcImgs[0].type() != CV_8UC3) {
|
||||
CV_Error(Error::StsBadArg, "Type of input images should be CV_8UC3!");
|
||||
|
105
modules/photo/src/hdr_common.cpp
Normal file
105
modules/photo/src/hdr_common.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's 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.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "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 the Intel Corporation or contributors 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.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
#include "hdr_common.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
void checkImageDimensions(const std::vector<Mat>& images)
|
||||
{
|
||||
CV_Assert(!images.empty());
|
||||
int width = images[0].cols;
|
||||
int height = images[0].rows;
|
||||
int type = images[0].type();
|
||||
|
||||
for(size_t i = 0; i < images.size(); i++) {
|
||||
CV_Assert(images[i].cols == width && images[i].rows == height);
|
||||
CV_Assert(images[i].type() == type);
|
||||
}
|
||||
}
|
||||
|
||||
Mat tringleWeights()
|
||||
{
|
||||
Mat w(LDR_SIZE, 1, CV_32F);
|
||||
int half = LDR_SIZE / 2;
|
||||
for(int i = 0; i < LDR_SIZE; i++) {
|
||||
w.at<float>(i) = i < half ? i + 1.0f : LDR_SIZE - i;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
Mat RobertsonWeights()
|
||||
{
|
||||
Mat weight(LDR_SIZE, 1, CV_32FC3);
|
||||
float q = (LDR_SIZE - 1) / 4.0f;
|
||||
for(int i = 0; i < LDR_SIZE; i++) {
|
||||
float value = i / q - 2.0f;
|
||||
value = exp(-value * value);
|
||||
weight.at<Vec3f>(i) = Vec3f::all(value);
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation)
|
||||
{
|
||||
std::vector<Mat> channels(3);
|
||||
split(src, channels);
|
||||
for(int i = 0; i < 3; i++) {
|
||||
channels[i] = channels[i].mul(1.0f / lum);
|
||||
pow(channels[i], saturation, channels[i]);
|
||||
channels[i] = channels[i].mul(new_lum);
|
||||
}
|
||||
merge(channels, dst);
|
||||
}
|
||||
|
||||
Mat linearResponse(int channels)
|
||||
{
|
||||
Mat response = Mat(LDR_SIZE, 1, CV_MAKETYPE(CV_32F, channels));
|
||||
for(int i = 0; i < LDR_SIZE; i++) {
|
||||
response.at<Vec3f>(i) = Vec3f::all(static_cast<float>(i));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
};
|
62
modules/photo/src/hdr_common.hpp
Normal file
62
modules/photo/src/hdr_common.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's 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.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "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 the Intel Corporation or contributors 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.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_HDR_COMMON_HPP__
|
||||
#define __OPENCV_HDR_COMMON_HPP__
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
void checkImageDimensions(const std::vector<Mat>& images);
|
||||
|
||||
Mat tringleWeights();
|
||||
|
||||
void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation);
|
||||
|
||||
Mat RobertsonWeights();
|
||||
|
||||
Mat linearResponse(int channels);
|
||||
};
|
||||
|
||||
#endif
|
351
modules/photo/src/merge.cpp
Normal file
351
modules/photo/src/merge.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's 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.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "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 the Intel Corporation or contributors 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.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "hdr_common.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
class MergeDebevecImpl : public MergeDebevec
|
||||
{
|
||||
public:
|
||||
MergeDebevecImpl() :
|
||||
name("MergeDebevec"),
|
||||
weights(tringleWeights())
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times, InputArray input_response)
|
||||
{
|
||||
std::vector<Mat> images;
|
||||
src.getMatVector(images);
|
||||
Mat times = _times.getMat();
|
||||
|
||||
CV_Assert(images.size() == times.total());
|
||||
checkImageDimensions(images);
|
||||
CV_Assert(images[0].depth() == CV_8U);
|
||||
|
||||
int channels = images[0].channels();
|
||||
Size size = images[0].size();
|
||||
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
|
||||
|
||||
dst.create(images[0].size(), CV_32FCC);
|
||||
Mat result = dst.getMat();
|
||||
|
||||
Mat response = input_response.getMat();
|
||||
|
||||
if(response.empty()) {
|
||||
response = linearResponse(channels);
|
||||
response.at<Vec3f>(0) = response.at<Vec3f>(1);
|
||||
}
|
||||
log(response, response);
|
||||
CV_Assert(response.rows == LDR_SIZE && response.cols == 1 &&
|
||||
response.channels() == channels);
|
||||
|
||||
Mat exp_values(times);
|
||||
log(exp_values, exp_values);
|
||||
|
||||
result = Mat::zeros(size, CV_32FCC);
|
||||
std::vector<Mat> result_split;
|
||||
split(result, result_split);
|
||||
Mat weight_sum = Mat::zeros(size, CV_32F);
|
||||
|
||||
for(size_t i = 0; i < images.size(); i++) {
|
||||
std::vector<Mat> splitted;
|
||||
split(images[i], splitted);
|
||||
|
||||
Mat w = Mat::zeros(size, CV_32F);
|
||||
for(int c = 0; c < channels; c++) {
|
||||
LUT(splitted[c], weights, splitted[c]);
|
||||
w += splitted[c];
|
||||
}
|
||||
w /= channels;
|
||||
|
||||
Mat response_img;
|
||||
LUT(images[i], response, response_img);
|
||||
split(response_img, splitted);
|
||||
for(int c = 0; c < channels; c++) {
|
||||
result_split[c] += w.mul(splitted[c] - exp_values.at<float>(i));
|
||||
}
|
||||
weight_sum += w;
|
||||
}
|
||||
weight_sum = 1.0f / weight_sum;
|
||||
for(int c = 0; c < channels; c++) {
|
||||
result_split[c] = result_split[c].mul(weight_sum);
|
||||
}
|
||||
merge(result_split, result);
|
||||
exp(result, result);
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst, InputArray times)
|
||||
{
|
||||
process(src, dst, times, Mat());
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
Mat weights;
|
||||
};
|
||||
|
||||
Ptr<MergeDebevec> createMergeDebevec()
|
||||
{
|
||||
return makePtr<MergeDebevecImpl>();
|
||||
}
|
||||
|
||||
class MergeMertensImpl : public MergeMertens
|
||||
{
|
||||
public:
|
||||
MergeMertensImpl(float _wcon, float _wsat, float _wexp) :
|
||||
name("MergeMertens"),
|
||||
wcon(_wcon),
|
||||
wsat(_wsat),
|
||||
wexp(_wexp)
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArrayOfArrays dst, InputArray, InputArray)
|
||||
{
|
||||
process(src, dst);
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst)
|
||||
{
|
||||
std::vector<Mat> images;
|
||||
src.getMatVector(images);
|
||||
checkImageDimensions(images);
|
||||
|
||||
int channels = images[0].channels();
|
||||
CV_Assert(channels == 1 || channels == 3);
|
||||
Size size = images[0].size();
|
||||
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
|
||||
|
||||
std::vector<Mat> weights(images.size());
|
||||
Mat weight_sum = Mat::zeros(size, CV_32F);
|
||||
|
||||
for(size_t i = 0; i < images.size(); i++) {
|
||||
Mat img, gray, contrast, saturation, wellexp;
|
||||
std::vector<Mat> splitted(channels);
|
||||
|
||||
images[i].convertTo(img, CV_32F, 1.0f/255.0f);
|
||||
if(channels == 3) {
|
||||
cvtColor(img, gray, COLOR_RGB2GRAY);
|
||||
} else {
|
||||
img.copyTo(gray);
|
||||
}
|
||||
split(img, splitted);
|
||||
|
||||
Laplacian(gray, contrast, CV_32F);
|
||||
contrast = abs(contrast);
|
||||
|
||||
Mat mean = Mat::zeros(size, CV_32F);
|
||||
for(int c = 0; c < channels; c++) {
|
||||
mean += splitted[c];
|
||||
}
|
||||
mean /= channels;
|
||||
|
||||
saturation = Mat::zeros(size, CV_32F);
|
||||
for(int c = 0; c < channels; c++) {
|
||||
Mat deviation = splitted[c] - mean;
|
||||
pow(deviation, 2.0f, deviation);
|
||||
saturation += deviation;
|
||||
}
|
||||
sqrt(saturation, saturation);
|
||||
|
||||
wellexp = Mat::ones(size, CV_32F);
|
||||
for(int c = 0; c < channels; c++) {
|
||||
Mat exp = splitted[c] - 0.5f;
|
||||
pow(exp, 2.0f, exp);
|
||||
exp = -exp / 0.08f;
|
||||
wellexp = wellexp.mul(exp);
|
||||
}
|
||||
|
||||
pow(contrast, wcon, contrast);
|
||||
pow(saturation, wsat, saturation);
|
||||
pow(wellexp, wexp, wellexp);
|
||||
|
||||
weights[i] = contrast;
|
||||
if(channels == 3) {
|
||||
weights[i] = weights[i].mul(saturation);
|
||||
}
|
||||
weights[i] = weights[i].mul(wellexp);
|
||||
weight_sum += weights[i];
|
||||
}
|
||||
int maxlevel = static_cast<int>(logf(static_cast<float>(min(size.width, size.height))) / logf(2.0f));
|
||||
std::vector<Mat> res_pyr(maxlevel + 1);
|
||||
|
||||
for(size_t i = 0; i < images.size(); i++) {
|
||||
weights[i] /= weight_sum;
|
||||
Mat img;
|
||||
images[i].convertTo(img, CV_32F, 1.0f/255.0f);
|
||||
|
||||
std::vector<Mat> img_pyr, weight_pyr;
|
||||
buildPyramid(img, img_pyr, maxlevel);
|
||||
buildPyramid(weights[i], weight_pyr, maxlevel);
|
||||
|
||||
for(int lvl = 0; lvl < maxlevel; lvl++) {
|
||||
Mat up;
|
||||
pyrUp(img_pyr[lvl + 1], up, img_pyr[lvl].size());
|
||||
img_pyr[lvl] -= up;
|
||||
}
|
||||
for(int lvl = 0; lvl <= maxlevel; lvl++) {
|
||||
std::vector<Mat> splitted(channels);
|
||||
split(img_pyr[lvl], splitted);
|
||||
for(int c = 0; c < channels; c++) {
|
||||
splitted[c] = splitted[c].mul(weight_pyr[lvl]);
|
||||
}
|
||||
merge(splitted, img_pyr[lvl]);
|
||||
if(res_pyr[lvl].empty()) {
|
||||
res_pyr[lvl] = img_pyr[lvl];
|
||||
} else {
|
||||
res_pyr[lvl] += img_pyr[lvl];
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int lvl = maxlevel; lvl > 0; lvl--) {
|
||||
Mat up;
|
||||
pyrUp(res_pyr[lvl], up, res_pyr[lvl - 1].size());
|
||||
res_pyr[lvl - 1] += up;
|
||||
}
|
||||
dst.create(size, CV_32FCC);
|
||||
res_pyr[0].copyTo(dst.getMat());
|
||||
}
|
||||
|
||||
float getContrastWeight() const { return wcon; }
|
||||
void setContrastWeight(float val) { wcon = val; }
|
||||
|
||||
float getSaturationWeight() const { return wsat; }
|
||||
void setSaturationWeight(float val) { wsat = val; }
|
||||
|
||||
float getExposureWeight() const { return wexp; }
|
||||
void setExposureWeight(float val) { wexp = val; }
|
||||
|
||||
void write(FileStorage& fs) const
|
||||
{
|
||||
fs << "name" << name
|
||||
<< "contrast_weight" << wcon
|
||||
<< "saturation_weight" << wsat
|
||||
<< "exposure_weight" << wexp;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn)
|
||||
{
|
||||
FileNode n = fn["name"];
|
||||
CV_Assert(n.isString() && String(n) == name);
|
||||
wcon = fn["contrast_weight"];
|
||||
wsat = fn["saturation_weight"];
|
||||
wexp = fn["exposure_weight"];
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
float wcon, wsat, wexp;
|
||||
};
|
||||
|
||||
Ptr<MergeMertens> createMergeMertens(float wcon, float wsat, float wexp)
|
||||
{
|
||||
return makePtr<MergeMertensImpl>(wcon, wsat, wexp);
|
||||
}
|
||||
|
||||
class MergeRobertsonImpl : public MergeRobertson
|
||||
{
|
||||
public:
|
||||
MergeRobertsonImpl() :
|
||||
name("MergeRobertson"),
|
||||
weight(RobertsonWeights())
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times, InputArray input_response)
|
||||
{
|
||||
std::vector<Mat> images;
|
||||
src.getMatVector(images);
|
||||
Mat times = _times.getMat();
|
||||
|
||||
CV_Assert(images.size() == times.total());
|
||||
checkImageDimensions(images);
|
||||
CV_Assert(images[0].depth() == CV_8U);
|
||||
|
||||
int channels = images[0].channels();
|
||||
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
|
||||
|
||||
dst.create(images[0].size(), CV_32FCC);
|
||||
Mat result = dst.getMat();
|
||||
|
||||
Mat response = input_response.getMat();
|
||||
if(response.empty()) {
|
||||
float middle = LDR_SIZE / 2.0f;
|
||||
response = linearResponse(channels) / middle;
|
||||
}
|
||||
CV_Assert(response.rows == LDR_SIZE && response.cols == 1 &&
|
||||
response.channels() == channels);
|
||||
|
||||
result = Mat::zeros(images[0].size(), CV_32FCC);
|
||||
Mat wsum = Mat::zeros(images[0].size(), CV_32FCC);
|
||||
for(size_t i = 0; i < images.size(); i++) {
|
||||
Mat im, w;
|
||||
LUT(images[i], weight, w);
|
||||
LUT(images[i], response, im);
|
||||
|
||||
result += times.at<float>(i) * w.mul(im);
|
||||
wsum += times.at<float>(i) * times.at<float>(i) * w;
|
||||
}
|
||||
result = result.mul(1 / wsum);
|
||||
}
|
||||
|
||||
void process(InputArrayOfArrays src, OutputArray dst, InputArray times)
|
||||
{
|
||||
process(src, dst, times, Mat());
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
Mat weight;
|
||||
};
|
||||
|
||||
Ptr<MergeRobertson> createMergeRobertson()
|
||||
{
|
||||
return makePtr<MergeRobertsonImpl>();
|
||||
}
|
||||
|
||||
}
|
531
modules/photo/src/tonemap.cpp
Normal file
531
modules/photo/src/tonemap.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's 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.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "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 the Intel Corporation or contributors 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.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "hdr_common.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
class TonemapImpl : public Tonemap
|
||||
{
|
||||
public:
|
||||
TonemapImpl(float _gamma) : name("Tonemap"), gamma(_gamma)
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArray _src, OutputArray _dst)
|
||||
{
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(!src.empty());
|
||||
_dst.create(src.size(), CV_32FC3);
|
||||
Mat dst = _dst.getMat();
|
||||
|
||||
double min, max;
|
||||
minMaxLoc(src, &min, &max);
|
||||
if(max - min > DBL_EPSILON) {
|
||||
dst = (src - min) / (max - min);
|
||||
} else {
|
||||
src.copyTo(dst);
|
||||
}
|
||||
|
||||
pow(dst, 1.0f / gamma, dst);
|
||||
}
|
||||
|
||||
float getGamma() const { return gamma; }
|
||||
void setGamma(float val) { gamma = val; }
|
||||
|
||||
void write(FileStorage& fs) const
|
||||
{
|
||||
fs << "name" << name
|
||||
<< "gamma" << gamma;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn)
|
||||
{
|
||||
FileNode n = fn["name"];
|
||||
CV_Assert(n.isString() && String(n) == name);
|
||||
gamma = fn["gamma"];
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
float gamma;
|
||||
};
|
||||
|
||||
Ptr<Tonemap> createTonemap(float gamma)
|
||||
{
|
||||
return makePtr<TonemapImpl>(gamma);
|
||||
}
|
||||
|
||||
class TonemapDragoImpl : public TonemapDrago
|
||||
{
|
||||
public:
|
||||
TonemapDragoImpl(float _gamma, float _saturation, float _bias) :
|
||||
name("TonemapDrago"),
|
||||
gamma(_gamma),
|
||||
saturation(_saturation),
|
||||
bias(_bias)
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArray _src, OutputArray _dst)
|
||||
{
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(!src.empty());
|
||||
_dst.create(src.size(), CV_32FC3);
|
||||
Mat img = _dst.getMat();
|
||||
|
||||
Ptr<Tonemap> linear = createTonemap(1.0f);
|
||||
linear->process(src, img);
|
||||
|
||||
Mat gray_img;
|
||||
cvtColor(img, gray_img, COLOR_RGB2GRAY);
|
||||
Mat log_img;
|
||||
log(gray_img, log_img);
|
||||
float mean = expf(static_cast<float>(sum(log_img)[0]) / log_img.total());
|
||||
gray_img /= mean;
|
||||
log_img.release();
|
||||
|
||||
double max;
|
||||
minMaxLoc(gray_img, NULL, &max);
|
||||
|
||||
Mat map;
|
||||
log(gray_img + 1.0f, map);
|
||||
Mat div;
|
||||
pow(gray_img / static_cast<float>(max), logf(bias) / logf(0.5f), div);
|
||||
log(2.0f + 8.0f * div, div);
|
||||
map = map.mul(1.0f / div);
|
||||
div.release();
|
||||
|
||||
mapLuminance(img, img, gray_img, map, saturation);
|
||||
|
||||
linear->setGamma(gamma);
|
||||
linear->process(img, img);
|
||||
}
|
||||
|
||||
float getGamma() const { return gamma; }
|
||||
void setGamma(float val) { gamma = val; }
|
||||
|
||||
float getSaturation() const { return saturation; }
|
||||
void setSaturation(float val) { saturation = val; }
|
||||
|
||||
float getBias() const { return bias; }
|
||||
void setBias(float val) { bias = val; }
|
||||
|
||||
void write(FileStorage& fs) const
|
||||
{
|
||||
fs << "name" << name
|
||||
<< "gamma" << gamma
|
||||
<< "bias" << bias
|
||||
<< "saturation" << saturation;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn)
|
||||
{
|
||||
FileNode n = fn["name"];
|
||||
CV_Assert(n.isString() && String(n) == name);
|
||||
gamma = fn["gamma"];
|
||||
bias = fn["bias"];
|
||||
saturation = fn["saturation"];
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
float gamma, saturation, bias;
|
||||
};
|
||||
|
||||
Ptr<TonemapDrago> createTonemapDrago(float gamma, float saturation, float bias)
|
||||
{
|
||||
return makePtr<TonemapDragoImpl>(gamma, saturation, bias);
|
||||
}
|
||||
|
||||
class TonemapDurandImpl : public TonemapDurand
|
||||
{
|
||||
public:
|
||||
TonemapDurandImpl(float _gamma, float _contrast, float _saturation, float _sigma_color, float _sigma_space) :
|
||||
name("TonemapDurand"),
|
||||
gamma(_gamma),
|
||||
contrast(_contrast),
|
||||
saturation(_saturation),
|
||||
sigma_color(_sigma_color),
|
||||
sigma_space(_sigma_space)
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArray _src, OutputArray _dst)
|
||||
{
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(!src.empty());
|
||||
_dst.create(src.size(), CV_32FC3);
|
||||
Mat img = _dst.getMat();
|
||||
Ptr<Tonemap> linear = createTonemap(1.0f);
|
||||
linear->process(src, img);
|
||||
|
||||
Mat gray_img;
|
||||
cvtColor(img, gray_img, COLOR_RGB2GRAY);
|
||||
Mat log_img;
|
||||
log(gray_img, log_img);
|
||||
Mat map_img;
|
||||
bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space);
|
||||
|
||||
double min, max;
|
||||
minMaxLoc(map_img, &min, &max);
|
||||
float scale = contrast / static_cast<float>(max - min);
|
||||
exp(map_img * (scale - 1.0f) + log_img, map_img);
|
||||
log_img.release();
|
||||
|
||||
mapLuminance(img, img, gray_img, map_img, saturation);
|
||||
pow(img, 1.0f / gamma, img);
|
||||
}
|
||||
|
||||
float getGamma() const { return gamma; }
|
||||
void setGamma(float val) { gamma = val; }
|
||||
|
||||
float getSaturation() const { return saturation; }
|
||||
void setSaturation(float val) { saturation = val; }
|
||||
|
||||
float getContrast() const { return contrast; }
|
||||
void setContrast(float val) { contrast = val; }
|
||||
|
||||
float getSigmaColor() const { return sigma_color; }
|
||||
void setSigmaColor(float val) { sigma_color = val; }
|
||||
|
||||
float getSigmaSpace() const { return sigma_space; }
|
||||
void setSigmaSpace(float val) { sigma_space = val; }
|
||||
|
||||
void write(FileStorage& fs) const
|
||||
{
|
||||
fs << "name" << name
|
||||
<< "gamma" << gamma
|
||||
<< "contrast" << contrast
|
||||
<< "sigma_color" << sigma_color
|
||||
<< "sigma_space" << sigma_space
|
||||
<< "saturation" << saturation;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn)
|
||||
{
|
||||
FileNode n = fn["name"];
|
||||
CV_Assert(n.isString() && String(n) == name);
|
||||
gamma = fn["gamma"];
|
||||
contrast = fn["contrast"];
|
||||
sigma_color = fn["sigma_color"];
|
||||
sigma_space = fn["sigma_space"];
|
||||
saturation = fn["saturation"];
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
float gamma, contrast, saturation, sigma_color, sigma_space;
|
||||
};
|
||||
|
||||
Ptr<TonemapDurand> createTonemapDurand(float gamma, float contrast, float saturation, float sigma_color, float sigma_space)
|
||||
{
|
||||
return makePtr<TonemapDurandImpl>(gamma, contrast, saturation, sigma_color, sigma_space);
|
||||
}
|
||||
|
||||
class TonemapReinhardImpl : public TonemapReinhard
|
||||
{
|
||||
public:
|
||||
TonemapReinhardImpl(float _gamma, float _intensity, float _light_adapt, float _color_adapt) :
|
||||
name("TonemapReinhard"),
|
||||
gamma(_gamma),
|
||||
intensity(_intensity),
|
||||
light_adapt(_light_adapt),
|
||||
color_adapt(_color_adapt)
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArray _src, OutputArray _dst)
|
||||
{
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(!src.empty());
|
||||
_dst.create(src.size(), CV_32FC3);
|
||||
Mat img = _dst.getMat();
|
||||
Ptr<Tonemap> linear = createTonemap(1.0f);
|
||||
linear->process(src, img);
|
||||
|
||||
Mat gray_img;
|
||||
cvtColor(img, gray_img, COLOR_RGB2GRAY);
|
||||
Mat log_img;
|
||||
log(gray_img, log_img);
|
||||
|
||||
float log_mean = static_cast<float>(sum(log_img)[0] / log_img.total());
|
||||
double log_min, log_max;
|
||||
minMaxLoc(log_img, &log_min, &log_max);
|
||||
log_img.release();
|
||||
|
||||
double key = static_cast<float>((log_max - log_mean) / (log_max - log_min));
|
||||
float map_key = 0.3f + 0.7f * pow(static_cast<float>(key), 1.4f);
|
||||
intensity = exp(-intensity);
|
||||
Scalar chan_mean = mean(img);
|
||||
float gray_mean = static_cast<float>(mean(gray_img)[0]);
|
||||
|
||||
std::vector<Mat> channels(3);
|
||||
split(img, channels);
|
||||
|
||||
for(int i = 0; i < 3; i++) {
|
||||
float global = color_adapt * static_cast<float>(chan_mean[i]) + (1.0f - color_adapt) * gray_mean;
|
||||
Mat adapt = color_adapt * channels[i] + (1.0f - color_adapt) * gray_img;
|
||||
adapt = light_adapt * adapt + (1.0f - light_adapt) * global;
|
||||
pow(intensity * adapt, map_key, adapt);
|
||||
channels[i] = channels[i].mul(1.0f / (adapt + channels[i]));
|
||||
}
|
||||
gray_img.release();
|
||||
merge(channels, img);
|
||||
|
||||
linear->setGamma(gamma);
|
||||
linear->process(img, img);
|
||||
}
|
||||
|
||||
float getGamma() const { return gamma; }
|
||||
void setGamma(float val) { gamma = val; }
|
||||
|
||||
float getIntensity() const { return intensity; }
|
||||
void setIntensity(float val) { intensity = val; }
|
||||
|
||||
float getLightAdaptation() const { return light_adapt; }
|
||||
void setLightAdaptation(float val) { light_adapt = val; }
|
||||
|
||||
float getColorAdaptation() const { return color_adapt; }
|
||||
void setColorAdaptation(float val) { color_adapt = val; }
|
||||
|
||||
void write(FileStorage& fs) const
|
||||
{
|
||||
fs << "name" << name
|
||||
<< "gamma" << gamma
|
||||
<< "intensity" << intensity
|
||||
<< "light_adapt" << light_adapt
|
||||
<< "color_adapt" << color_adapt;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn)
|
||||
{
|
||||
FileNode n = fn["name"];
|
||||
CV_Assert(n.isString() && String(n) == name);
|
||||
gamma = fn["gamma"];
|
||||
intensity = fn["intensity"];
|
||||
light_adapt = fn["light_adapt"];
|
||||
color_adapt = fn["color_adapt"];
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
float gamma, intensity, light_adapt, color_adapt;
|
||||
};
|
||||
|
||||
Ptr<TonemapReinhard> createTonemapReinhard(float gamma, float contrast, float sigma_color, float sigma_space)
|
||||
{
|
||||
return makePtr<TonemapReinhardImpl>(gamma, contrast, sigma_color, sigma_space);
|
||||
}
|
||||
|
||||
class TonemapMantiukImpl : public TonemapMantiuk
|
||||
{
|
||||
public:
|
||||
TonemapMantiukImpl(float _gamma, float _scale, float _saturation) :
|
||||
name("TonemapMantiuk"),
|
||||
gamma(_gamma),
|
||||
scale(_scale),
|
||||
saturation(_saturation)
|
||||
{
|
||||
}
|
||||
|
||||
void process(InputArray _src, OutputArray _dst)
|
||||
{
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(!src.empty());
|
||||
_dst.create(src.size(), CV_32FC3);
|
||||
Mat img = _dst.getMat();
|
||||
Ptr<Tonemap> linear = createTonemap(1.0f);
|
||||
linear->process(src, img);
|
||||
|
||||
Mat gray_img;
|
||||
cvtColor(img, gray_img, COLOR_RGB2GRAY);
|
||||
Mat log_img;
|
||||
log(gray_img, log_img);
|
||||
|
||||
std::vector<Mat> x_contrast, y_contrast;
|
||||
getContrast(log_img, x_contrast, y_contrast);
|
||||
|
||||
for(size_t i = 0; i < x_contrast.size(); i++) {
|
||||
mapContrast(x_contrast[i]);
|
||||
mapContrast(y_contrast[i]);
|
||||
}
|
||||
|
||||
Mat right(src.size(), CV_32F);
|
||||
calculateSum(x_contrast, y_contrast, right);
|
||||
|
||||
Mat p, r, product, x = log_img;
|
||||
calculateProduct(x, r);
|
||||
r = right - r;
|
||||
r.copyTo(p);
|
||||
|
||||
const float target_error = 1e-3f;
|
||||
float target_norm = static_cast<float>(right.dot(right)) * powf(target_error, 2.0f);
|
||||
int max_iterations = 100;
|
||||
float rr = static_cast<float>(r.dot(r));
|
||||
|
||||
for(int i = 0; i < max_iterations; i++)
|
||||
{
|
||||
calculateProduct(p, product);
|
||||
float alpha = rr / static_cast<float>(p.dot(product));
|
||||
|
||||
r -= alpha * product;
|
||||
x += alpha * p;
|
||||
|
||||
float new_rr = static_cast<float>(r.dot(r));
|
||||
p = r + (new_rr / rr) * p;
|
||||
rr = new_rr;
|
||||
|
||||
if(rr < target_norm) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
exp(x, x);
|
||||
mapLuminance(img, img, gray_img, x, saturation);
|
||||
|
||||
linear = createTonemap(gamma);
|
||||
linear->process(img, img);
|
||||
}
|
||||
|
||||
float getGamma() const { return gamma; }
|
||||
void setGamma(float val) { gamma = val; }
|
||||
|
||||
float getScale() const { return scale; }
|
||||
void setScale(float val) { scale = val; }
|
||||
|
||||
float getSaturation() const { return saturation; }
|
||||
void setSaturation(float val) { saturation = val; }
|
||||
|
||||
void write(FileStorage& fs) const
|
||||
{
|
||||
fs << "name" << name
|
||||
<< "gamma" << gamma
|
||||
<< "scale" << scale
|
||||
<< "saturation" << saturation;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn)
|
||||
{
|
||||
FileNode n = fn["name"];
|
||||
CV_Assert(n.isString() && String(n) == name);
|
||||
gamma = fn["gamma"];
|
||||
scale = fn["scale"];
|
||||
saturation = fn["saturation"];
|
||||
}
|
||||
|
||||
protected:
|
||||
String name;
|
||||
float gamma, scale, saturation;
|
||||
|
||||
void signedPow(Mat src, float power, Mat& dst)
|
||||
{
|
||||
Mat sign = (src > 0);
|
||||
sign.convertTo(sign, CV_32F, 1.0f/255.0f);
|
||||
sign = sign * 2.0f - 1.0f;
|
||||
pow(abs(src), power, dst);
|
||||
dst = dst.mul(sign);
|
||||
}
|
||||
|
||||
void mapContrast(Mat& contrast)
|
||||
{
|
||||
const float response_power = 0.4185f;
|
||||
signedPow(contrast, response_power, contrast);
|
||||
contrast *= scale;
|
||||
signedPow(contrast, 1.0f / response_power, contrast);
|
||||
}
|
||||
|
||||
void getGradient(Mat src, Mat& dst, int pos)
|
||||
{
|
||||
dst = Mat::zeros(src.size(), CV_32F);
|
||||
Mat a, b;
|
||||
Mat grad = src.colRange(1, src.cols) - src.colRange(0, src.cols - 1);
|
||||
grad.copyTo(dst.colRange(pos, src.cols + pos - 1));
|
||||
if(pos == 1) {
|
||||
src.col(0).copyTo(dst.col(0));
|
||||
}
|
||||
}
|
||||
|
||||
void getContrast(Mat src, std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast)
|
||||
{
|
||||
int levels = static_cast<int>(logf(static_cast<float>(min(src.rows, src.cols))) / logf(2.0f));
|
||||
x_contrast.resize(levels);
|
||||
y_contrast.resize(levels);
|
||||
|
||||
Mat layer;
|
||||
src.copyTo(layer);
|
||||
for(int i = 0; i < levels; i++) {
|
||||
getGradient(layer, x_contrast[i], 0);
|
||||
getGradient(layer.t(), y_contrast[i], 0);
|
||||
resize(layer, layer, Size(layer.cols / 2, layer.rows / 2));
|
||||
}
|
||||
}
|
||||
|
||||
void calculateSum(std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast, Mat& sum)
|
||||
{
|
||||
sum = Mat::zeros(x_contrast[x_contrast.size() - 1].size(), CV_32F);
|
||||
for(int i = x_contrast.size() - 1; i >= 0; i--)
|
||||
{
|
||||
Mat grad_x, grad_y;
|
||||
getGradient(x_contrast[i], grad_x, 1);
|
||||
getGradient(y_contrast[i], grad_y, 1);
|
||||
resize(sum, sum, x_contrast[i].size());
|
||||
sum += grad_x + grad_y.t();
|
||||
}
|
||||
}
|
||||
|
||||
void calculateProduct(Mat src, Mat& dst)
|
||||
{
|
||||
std::vector<Mat> x_contrast, y_contrast;
|
||||
getContrast(src, x_contrast, y_contrast);
|
||||
calculateSum(x_contrast, y_contrast, dst);
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<TonemapMantiuk> createTonemapMantiuk(float gamma, float scale, float saturation)
|
||||
{
|
||||
return makePtr<TonemapMantiukImpl>(gamma, scale, saturation);
|
||||
}
|
||||
|
||||
}
|
249
modules/photo/test/test_hdr.cpp
Normal file
249
modules/photo/test/test_hdr.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's 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.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "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 the Intel Corporation or contributors 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.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
void loadImage(string path, Mat &img)
|
||||
{
|
||||
img = imread(path, -1);
|
||||
ASSERT_FALSE(img.empty()) << "Could not load input image " << path;
|
||||
}
|
||||
|
||||
void checkEqual(Mat img0, Mat img1, double threshold)
|
||||
{
|
||||
double max = 1.0;
|
||||
minMaxLoc(abs(img0 - img1), NULL, &max);
|
||||
ASSERT_FALSE(max > threshold) << max;
|
||||
}
|
||||
|
||||
static vector<float> DEFAULT_VECTOR;
|
||||
void loadExposureSeq(String path, vector<Mat>& images, vector<float>& times = DEFAULT_VECTOR)
|
||||
{
|
||||
ifstream list_file((path + "list.txt").c_str());
|
||||
ASSERT_TRUE(list_file.is_open());
|
||||
string name;
|
||||
float val;
|
||||
while(list_file >> name >> val) {
|
||||
Mat img = imread(path + name);
|
||||
ASSERT_FALSE(img.empty()) << "Could not load input image " << path + name;
|
||||
images.push_back(img);
|
||||
times.push_back(1 / val);
|
||||
}
|
||||
list_file.close();
|
||||
}
|
||||
|
||||
void loadResponseCSV(String path, Mat& response)
|
||||
{
|
||||
response = Mat(256, 1, CV_32FC3);
|
||||
ifstream resp_file(path.c_str());
|
||||
for(int i = 0; i < 256; i++) {
|
||||
for(int c = 0; c < 3; c++) {
|
||||
resp_file >> response.at<Vec3f>(i)[c];
|
||||
resp_file.ignore(1);
|
||||
}
|
||||
}
|
||||
resp_file.close();
|
||||
}
|
||||
|
||||
TEST(Photo_Tonemap, regression)
|
||||
{
|
||||
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/tonemap/";
|
||||
|
||||
Mat img, expected, result;
|
||||
loadImage(test_path + "image.hdr", img);
|
||||
float gamma = 2.2f;
|
||||
|
||||
Ptr<Tonemap> linear = createTonemap(gamma);
|
||||
linear->process(img, result);
|
||||
loadImage(test_path + "linear.png", expected);
|
||||
result.convertTo(result, CV_8UC3, 255);
|
||||
checkEqual(result, expected, 3);
|
||||
|
||||
Ptr<TonemapDrago> drago = createTonemapDrago(gamma);
|
||||
drago->process(img, result);
|
||||
loadImage(test_path + "drago.png", expected);
|
||||
result.convertTo(result, CV_8UC3, 255);
|
||||
checkEqual(result, expected, 3);
|
||||
|
||||
Ptr<TonemapDurand> durand = createTonemapDurand(gamma);
|
||||
durand->process(img, result);
|
||||
loadImage(test_path + "durand.png", expected);
|
||||
result.convertTo(result, CV_8UC3, 255);
|
||||
checkEqual(result, expected, 3);
|
||||
|
||||
Ptr<TonemapReinhard> reinhard = createTonemapReinhard(gamma);
|
||||
reinhard->process(img, result);
|
||||
loadImage(test_path + "reinhard.png", expected);
|
||||
result.convertTo(result, CV_8UC3, 255);
|
||||
checkEqual(result, expected, 3);
|
||||
|
||||
Ptr<TonemapMantiuk> mantiuk = createTonemapMantiuk(gamma);
|
||||
mantiuk->process(img, result);
|
||||
loadImage(test_path + "mantiuk.png", expected);
|
||||
result.convertTo(result, CV_8UC3, 255);
|
||||
checkEqual(result, expected, 3);
|
||||
}
|
||||
|
||||
TEST(Photo_AlignMTB, regression)
|
||||
{
|
||||
const int TESTS_COUNT = 100;
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "shared/";
|
||||
|
||||
string file_name = folder + "lena.png";
|
||||
Mat img;
|
||||
loadImage(file_name, img);
|
||||
cvtColor(img, img, COLOR_RGB2GRAY);
|
||||
|
||||
int max_bits = 5;
|
||||
int max_shift = 32;
|
||||
srand(static_cast<unsigned>(time(0)));
|
||||
int errors = 0;
|
||||
|
||||
Ptr<AlignMTB> align = createAlignMTB(max_bits);
|
||||
|
||||
for(int i = 0; i < TESTS_COUNT; i++) {
|
||||
Point shift(rand() % max_shift, rand() % max_shift);
|
||||
Mat res;
|
||||
align->shiftMat(img, res, shift);
|
||||
Point calc = align->calculateShift(img, res);
|
||||
errors += (calc != -shift);
|
||||
}
|
||||
ASSERT_TRUE(errors < 5) << errors << " errors";
|
||||
}
|
||||
|
||||
TEST(Photo_MergeMertens, regression)
|
||||
{
|
||||
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
|
||||
|
||||
vector<Mat> images;
|
||||
loadExposureSeq((test_path + "exposures/").c_str() , images);
|
||||
|
||||
Ptr<MergeMertens> merge = createMergeMertens();
|
||||
|
||||
Mat result, expected;
|
||||
loadImage(test_path + "merge/mertens.png", expected);
|
||||
merge->process(images, result);
|
||||
result.convertTo(result, CV_8UC3, 255);
|
||||
checkEqual(expected, result, 3);
|
||||
}
|
||||
|
||||
TEST(Photo_MergeDebevec, regression)
|
||||
{
|
||||
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
|
||||
|
||||
vector<Mat> images;
|
||||
vector<float> times;
|
||||
Mat response;
|
||||
loadExposureSeq(test_path + "exposures/", images, times);
|
||||
loadResponseCSV(test_path + "exposures/response.csv", response);
|
||||
|
||||
Ptr<MergeDebevec> merge = createMergeDebevec();
|
||||
|
||||
Mat result, expected;
|
||||
loadImage(test_path + "merge/debevec.hdr", expected);
|
||||
merge->process(images, result, times, response);
|
||||
|
||||
Ptr<Tonemap> map = createTonemap();
|
||||
map->process(result, result);
|
||||
map->process(expected, expected);
|
||||
|
||||
checkEqual(expected, result, 1e-2f);
|
||||
}
|
||||
|
||||
TEST(Photo_MergeRobertson, regression)
|
||||
{
|
||||
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
|
||||
|
||||
vector<Mat> images;
|
||||
vector<float> times;
|
||||
loadExposureSeq(test_path + "exposures/", images, times);
|
||||
|
||||
Ptr<MergeRobertson> merge = createMergeRobertson();
|
||||
|
||||
Mat result, expected;
|
||||
loadImage(test_path + "merge/robertson.hdr", expected);
|
||||
merge->process(images, result, times);
|
||||
Ptr<Tonemap> map = createTonemap();
|
||||
map->process(result, result);
|
||||
map->process(expected, expected);
|
||||
|
||||
checkEqual(expected, result, 1e-2f);
|
||||
}
|
||||
|
||||
TEST(Photo_CalibrateDebevec, regression)
|
||||
{
|
||||
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
|
||||
|
||||
vector<Mat> images;
|
||||
vector<float> times;
|
||||
Mat response, expected;
|
||||
loadExposureSeq(test_path + "exposures/", images, times);
|
||||
loadResponseCSV(test_path + "calibrate/debevec.csv", expected);
|
||||
Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec();
|
||||
|
||||
calibrate->process(images, response, times);
|
||||
Mat diff = abs(response - expected);
|
||||
diff = diff.mul(1.0f / response);
|
||||
double max;
|
||||
minMaxLoc(diff, NULL, &max);
|
||||
ASSERT_FALSE(max > 0.1);
|
||||
}
|
||||
|
||||
TEST(Photo_CalibrateRobertson, regression)
|
||||
{
|
||||
string test_path = string(cvtest::TS::ptr()->get_data_path()) + "hdr/";
|
||||
|
||||
vector<Mat> images;
|
||||
vector<float> times;
|
||||
Mat response, expected;
|
||||
loadExposureSeq(test_path + "exposures/", images, times);
|
||||
loadResponseCSV(test_path + "calibrate/robertson.csv", expected);
|
||||
|
||||
Ptr<CalibrateRobertson> calibrate = createCalibrateRobertson();
|
||||
calibrate->process(images, response, times);
|
||||
checkEqual(expected, response, 1e-3f);
|
||||
}
|
@@ -135,8 +135,22 @@ typedef Ptr<StereoMatcher> Ptr_StereoMatcher;
|
||||
typedef Ptr<StereoBM> Ptr_StereoBM;
|
||||
typedef Ptr<StereoSGBM> Ptr_StereoSGBM;
|
||||
|
||||
typedef Ptr<Tonemap> Ptr_Tonemap;
|
||||
typedef Ptr<TonemapDrago> Ptr_TonemapDrago;
|
||||
typedef Ptr<TonemapReinhard> Ptr_TonemapReinhard;
|
||||
typedef Ptr<TonemapDurand> Ptr_TonemapDurand;
|
||||
typedef Ptr<TonemapMantiuk> Ptr_TonemapMantiuk;
|
||||
typedef Ptr<AlignMTB> Ptr_AlignMTB;
|
||||
typedef Ptr<CalibrateDebevec> Ptr_CalibrateDebevec;
|
||||
typedef Ptr<CalibrateRobertson> Ptr_CalibrateRobertson;
|
||||
typedef Ptr<MergeDebevec> Ptr_MergeDebevec;
|
||||
typedef Ptr<MergeRobertson> Ptr_MergeRobertson;
|
||||
typedef Ptr<MergeMertens> Ptr_MergeMertens;
|
||||
typedef Ptr<MergeRobertson> Ptr_MergeRobertson;
|
||||
|
||||
typedef Ptr<cv::softcascade::ChannelFeatureBuilder> Ptr_ChannelFeatureBuilder;
|
||||
typedef Ptr<CLAHE> Ptr_CLAHE;
|
||||
typedef Ptr<LineSegmentDetector > Ptr_LineSegmentDetector;
|
||||
|
||||
typedef SimpleBlobDetector::Params SimpleBlobDetector_Params;
|
||||
|
||||
|
Reference in New Issue
Block a user