feat: 切换后端至PaddleOCR-NCNN,切换工程为CMake

1.项目后端整体迁移至PaddleOCR-NCNN算法,已通过基本的兼容性测试
2.工程改为使用CMake组织,后续为了更好地兼容第三方库,不再提供QMake工程
3.重整权利声明文件,重整代码工程,确保最小化侵权风险

Log: 切换后端至PaddleOCR-NCNN,切换工程为CMake
Change-Id: I4d5d2c5d37505a4a24b389b1a4c5d12f17bfa38c
This commit is contained in:
wangzhengyang
2022-05-10 09:54:44 +08:00
parent ecdd171c6f
commit 718c41634f
10018 changed files with 3593797 additions and 186748 deletions

View File

@ -0,0 +1,281 @@
/*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 CV_FINAL : 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) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
process(src, dst);
}
void process(InputArrayOfArrays _src, std::vector<Mat>& dst) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
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) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
Mat img0 = _img0.getMat();
Mat img1 = _img1.getMat();
CV_Assert(img0.channels() == 1 && img0.type() == img1.type());
CV_Assert(img0.size() == img1.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 = (int)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) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
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 CV_OVERRIDE { return max_bits; }
void setMaxBits(int val) CV_OVERRIDE { max_bits = val; }
int getExcludeRange() const CV_OVERRIDE { return exclude_range; }
void setExcludeRange(int val) CV_OVERRIDE { exclude_range = val; }
bool getCut() const CV_OVERRIDE { return cut; }
void setCut(bool val) CV_OVERRIDE { cut = val; }
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << name
<< "max_bits" << max_bits
<< "exclude_range" << exclude_range
<< "cut" << static_cast<int>(cut);
}
void read(const FileNode& fn) CV_OVERRIDE
{
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) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
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 = (int)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);
}
}

View File

@ -0,0 +1,184 @@
/*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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective icvers.
//
// 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 Intel Corporation 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 "opencv2/core/base.hpp"
#ifndef __OPENCV_DENOISING_ARRAYS_HPP__
#define __OPENCV_DENOISING_ARRAYS_HPP__
namespace cv
{
template <class T>
struct Array2d
{
T* a;
int n1,n2;
bool needToDeallocArray;
Array2d(const Array2d& array2d):
a(array2d.a), n1(array2d.n1), n2(array2d.n2), needToDeallocArray(false)
{
if (array2d.needToDeallocArray)
{
CV_Error(Error::BadDataPtr, "Copy constructor for self allocating arrays not supported");
}
}
Array2d(T* _a, int _n1, int _n2):
a(_a), n1(_n1), n2(_n2), needToDeallocArray(false)
{
}
Array2d(int _n1, int _n2):
n1(_n1), n2(_n2), needToDeallocArray(true)
{
a = new T[n1*n2];
}
~Array2d()
{
if (needToDeallocArray)
delete[] a;
}
T* operator [] (int i)
{
return a + i*n2;
}
inline T* row_ptr(int i)
{
return (*this)[i];
}
};
template <class T>
struct Array3d
{
T* a;
int n1,n2,n3;
bool needToDeallocArray;
Array3d(T* _a, int _n1, int _n2, int _n3):
a(_a), n1(_n1), n2(_n2), n3(_n3), needToDeallocArray(false)
{
}
Array3d(int _n1, int _n2, int _n3):
n1(_n1), n2(_n2), n3(_n3), needToDeallocArray(true)
{
a = new T[n1*n2*n3];
}
~Array3d()
{
if (needToDeallocArray)
delete[] a;
}
Array2d<T> operator [] (int i)
{
Array2d<T> array2d(a + i*n2*n3, n2, n3);
return array2d;
}
inline T* row_ptr(int i1, int i2)
{
return a + i1*n2*n3 + i2*n3;
}
};
template <class T>
struct Array4d
{
T* a;
int n1,n2,n3,n4;
bool needToDeallocArray;
int steps[4];
void init_steps()
{
steps[0] = n2*n3*n4;
steps[1] = n3*n4;
steps[2] = n4;
steps[3] = 1;
}
Array4d(T* _a, int _n1, int _n2, int _n3, int _n4) :
a(_a), n1(_n1), n2(_n2), n3(_n3), n4(_n4), needToDeallocArray(false)
{
init_steps();
}
Array4d(int _n1, int _n2, int _n3, int _n4) :
n1(_n1), n2(_n2), n3(_n3), n4(_n4), needToDeallocArray(true)
{
a = new T[n1*n2*n3*n4];
init_steps();
}
~Array4d()
{
if (needToDeallocArray)
delete[] a;
}
Array3d<T> operator [] (int i)
{
Array3d<T> array3d(a + i*n2*n3*n4, n2, n3, n4);
return array3d;
}
inline T* row_ptr(int i1, int i2, int i3)
{
return a + i1*n2*n3*n4 + i2*n3*n4 + i3*n4;
}
inline int step_size(int dimension)
{
return steps[dimension];
}
};
}
#endif

View File

@ -0,0 +1,311 @@
/*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 "hdr_common.hpp"
namespace cv
{
class CalibrateDebevecImpl CV_FINAL : public CalibrateDebevec
{
public:
CalibrateDebevecImpl(int _samples, float _lambda, bool _random) :
name("CalibrateDebevec"),
samples(_samples),
lambda(_lambda),
random(_random),
w(triangleWeights())
{
}
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
// check inputs
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);
CV_Assert(times.type() == CV_32FC1);
// create output
int channels = images[0].channels();
int CV_32FCC = CV_MAKETYPE(CV_32F, channels);
int rows = images[0].rows;
int cols = images[0].cols;
dst.create(LDR_SIZE, 1, CV_32FCC);
Mat result = dst.getMat();
// pick pixel locations (either random or in a rectangular grid)
std::vector<Point> points;
points.reserve(samples);
if(random) {
for(int i = 0; i < samples; i++) {
points.push_back(Point(rand() % cols, rand() % rows));
}
} else {
int x_points = static_cast<int>(sqrt(static_cast<double>(samples) * cols / rows));
CV_Assert(0 < x_points && x_points <= cols);
int y_points = samples / x_points;
CV_Assert(0 < y_points && y_points <= rows);
int step_x = cols / x_points;
int step_y = 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 / 2; j < y_points; j++, y += step_y) {
if( 0 <= x && x < cols && 0 <= y && y < rows ) {
points.push_back(Point(x, y));
}
}
}
// we can have slightly less grid points than specified
//samples = static_cast<int>(points.size());
}
// we need enough equations to ensure a sufficiently overdetermined system
// (maybe only as a warning)
//CV_Assert(points.size() * (images.size() - 1) >= LDR_SIZE);
// solve for imaging system response function, over each channel separately
std::vector<Mat> result_split(channels);
for(int ch = 0; ch < channels; ch++) {
// initialize system of linear equations
Mat A = Mat::zeros((int)points.size() * (int)images.size() + LDR_SIZE + 1,
LDR_SIZE + (int)points.size(), CV_32F);
Mat B = Mat::zeros(A.rows, 1, CV_32F);
// include the data-fitting equations
int k = 0;
for(size_t i = 0; i < points.size(); i++) {
for(size_t j = 0; j < images.size(); j++) {
// val = images[j].at<Vec3b>(points[i].y, points[i].x)[ch]
int val = images[j].ptr()[channels*(points[i].y * cols + points[i].x) + ch];
float wij = w.at<float>(val);
A.at<float>(k, val) = wij;
A.at<float>(k, LDR_SIZE + (int)i) = -wij;
B.at<float>(k, 0) = wij * log(times.at<float>((int)j));
k++;
}
}
// fix the curve by setting its middle value to 0
A.at<float>(k, LDR_SIZE / 2) = 1;
k++;
// include the smoothness equations
for(int i = 0; i < (LDR_SIZE - 2); i++) {
float wi = w.at<float>(i + 1);
A.at<float>(k, i) = lambda * wi;
A.at<float>(k, i + 1) = -2 * lambda * wi;
A.at<float>(k, i + 2) = lambda * wi;
k++;
}
// solve the overdetermined system using SVD (least-squares problem)
Mat solution;
solve(A, B, solution, DECOMP_SVD);
solution.rowRange(0, LDR_SIZE).copyTo(result_split[ch]);
}
// combine log-exposures and take its exponent
merge(result_split, result);
exp(result, result);
}
int getSamples() const CV_OVERRIDE { return samples; }
void setSamples(int val) CV_OVERRIDE { samples = val; }
float getLambda() const CV_OVERRIDE { return lambda; }
void setLambda(float val) CV_OVERRIDE { lambda = val; }
bool getRandom() const CV_OVERRIDE { return random; }
void setRandom(bool val) CV_OVERRIDE { random = val; }
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << name
<< "samples" << samples
<< "lambda" << lambda
<< "random" << static_cast<int>(random);
}
void read(const FileNode& fn) CV_OVERRIDE
{
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; // calibration algorithm identifier
int samples; // number of pixel locations to sample
float lambda; // constant that determines the amount of smoothness
bool random; // whether to sample locations randomly or in a grid shape
Mat w; // weighting function for corresponding pixel values
};
Ptr<CalibrateDebevec> createCalibrateDebevec(int samples, float lambda, bool random)
{
return makePtr<CalibrateDebevecImpl>(samples, lambda, random);
}
class CalibrateRobertsonImpl CV_FINAL : 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) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
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);
CV_Assert(channels >= 1 && channels <= 3);
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>((int)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 CV_OVERRIDE { return max_iter; }
void setMaxIter(int val) CV_OVERRIDE { max_iter = val; }
float getThreshold() const CV_OVERRIDE { return threshold; }
void setThreshold(float val) CV_OVERRIDE { threshold = val; }
Mat getRadiance() const CV_OVERRIDE { return radiance; }
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << name
<< "max_iter" << max_iter
<< "threshold" << threshold;
}
void read(const FileNode& fn) CV_OVERRIDE
{
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);
}
}

View File

@ -0,0 +1,174 @@
/*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 <cmath>
#include <vector>
#include <limits>
#include "contrast_preserve.hpp"
using namespace std;
using namespace cv;
void cv::decolor(InputArray _src, OutputArray _dst, OutputArray _color_boost)
{
CV_INSTRUMENT_REGION();
Mat I = _src.getMat();
_dst.create(I.size(), CV_8UC1);
Mat dst = _dst.getMat();
_color_boost.create(I.size(), CV_8UC3);
Mat color_boost = _color_boost.getMat();
CV_Assert(!I.empty() && (I.channels()==3));
// Parameter Setting
const int maxIter = 15;
const double tol = .0001;
int iterCount = 0;
double E = 0;
double pre_E = std::numeric_limits<double>::infinity();
Mat img;
I.convertTo(img, CV_32FC3, 1.0/255.0);
// Initialization
Decolor obj;
vector <double> Cg;
vector < vector <double> > polyGrad;
vector <Vec3i> comb;
vector <double> alf;
obj.grad_system(img,polyGrad,Cg,comb);
obj.weak_order(img,alf);
// Solver
Mat Mt = Mat(int(polyGrad.size()),int(polyGrad[0].size()), CV_32FC1);
obj.wei_update_matrix(polyGrad,Cg,Mt);
vector <double> wei;
obj.wei_inti(comb,wei);
//////////////////////////////// main loop starting ////////////////////////////////////////
vector <double> G_pos(alf.size());
vector <double> G_neg(alf.size());
vector <double> EXPsum(G_pos.size());
vector <double> EXPterm(G_pos.size());
vector <double> temp(polyGrad[0].size());
vector <double> temp1(polyGrad[0].size());
vector <double> temp2(EXPsum.size());
vector <double> wei1(polyGrad.size());
while(sqrt(pow(E-pre_E,2)) > tol)
{
iterCount +=1;
pre_E = E;
for(size_t i=0; i<polyGrad[0].size(); i++)
{
double val = 0.0;
for(size_t j=0; j<polyGrad.size(); j++)
val = val + (polyGrad[j][i] * wei[j]);
temp[i] = val - Cg[i];
temp1[i] = val + Cg[i];
}
for(size_t i=0; i<alf.size(); i++)
{
const double sqSigma = obj.sigma * obj.sigma;
const double pos = ((1 + alf[i])/2) * exp(-1.0 * 0.5 * (temp[i] * temp[i]) / sqSigma);
const double neg = ((1 - alf[i])/2) * exp(-1.0 * 0.5 * (temp1[i] * temp1[i]) / sqSigma);
G_pos[i] = pos;
G_neg[i] = neg;
}
for(size_t i=0; i<G_pos.size(); i++)
EXPsum[i] = G_pos[i]+G_neg[i];
for(size_t i=0; i<EXPsum.size(); i++)
temp2[i] = (EXPsum[i] == 0) ? 1.0 : 0.0;
for(size_t i=0; i<G_pos.size(); i++)
EXPterm[i] = (G_pos[i] - G_neg[i])/(EXPsum[i] + temp2[i]);
for(int i=0; i<int(polyGrad.size()); i++)
{
double val1 = 0.0;
for(int j=0; j<int(polyGrad[0].size()); j++)
{
val1 = val1 + (Mt.at<float>(i,j) * EXPterm[j]);
}
wei1[i] = val1;
}
for(size_t i=0; i<wei.size(); i++)
wei[i] = wei1[i];
E = obj.energyCalcu(Cg, polyGrad, wei);
if(iterCount > maxIter)
break;
}
Mat Gray = Mat::zeros(img.size(),CV_32FC1);
obj.grayImContruct(wei, img, Gray);
Gray.convertTo(dst,CV_8UC1,255);
/////////////////////////////////// Contrast Boosting /////////////////////////////////
Mat lab;
cvtColor(I,lab,COLOR_BGR2Lab);
vector <Mat> lab_channel;
split(lab,lab_channel);
dst.copyTo(lab_channel[0]);
merge(lab_channel,lab);
cvtColor(lab,color_boost,COLOR_Lab2BGR);
}

View File

@ -0,0 +1,373 @@
/*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 <cmath>
#include <vector>
using namespace std;
using namespace cv;
class Decolor
{
private:
Mat kernelx;
Mat kernely;
int order;
public:
float sigma;
Decolor();
static vector<double> product(const vector <Vec3i> &comb, const double initRGB[3]);
double energyCalcu(const vector <double> &Cg, const vector < vector <double> > &polyGrad, const vector <double> &wei) const;
void singleChannelGradx(const Mat &img, Mat& dest) const;
void singleChannelGrady(const Mat &img, Mat& dest) const;
void gradvector(const Mat &img, vector <double> &grad) const;
void colorGrad(const Mat &img, vector <double> &Cg) const;
static void add_vector(vector <Vec3i> &comb, int &idx, int r,int g,int b);
static void add_to_vector_poly(vector < vector <double> > &polyGrad, const vector <double> &curGrad, int &idx1);
void weak_order(const Mat &img, vector <double> &alf) const;
void grad_system(const Mat &img, vector < vector < double > > &polyGrad,
vector < double > &Cg, vector <Vec3i>& comb) const;
static void wei_update_matrix(const vector < vector <double> > &poly, const vector <double> &Cg, Mat &X);
static void wei_inti(const vector <Vec3i> &comb, vector <double> &wei);
void grayImContruct(vector <double> &wei, const Mat &img, Mat &Gray) const;
};
double Decolor::energyCalcu(const vector <double> &Cg, const vector < vector <double> > &polyGrad, const vector <double> &wei) const
{
const size_t size = polyGrad[0].size();
vector <double> energy(size);
vector <double> temp(size);
vector <double> temp1(size);
for(size_t i=0;i< polyGrad[0].size();i++)
{
double val = 0.0;
for(size_t j =0;j<polyGrad.size();j++)
val = val + (polyGrad[j][i] * wei[j]);
temp[i] = val - Cg[i];
temp1[i] = val + Cg[i];
}
for(size_t i=0;i<polyGrad[0].size();i++)
energy[i] = -1.0*log(exp(-1.0*pow(temp[i],2)/sigma) + exp(-1.0*pow(temp1[i],2)/sigma));
double sum = 0.0;
for(size_t i=0;i<polyGrad[0].size();i++)
sum +=energy[i];
return (sum/polyGrad[0].size());
}
Decolor::Decolor()
{
kernelx = Mat(1,2, CV_32FC1);
kernely = Mat(2,1, CV_32FC1);
kernelx.at<float>(0,0)=1.0;
kernelx.at<float>(0,1)=-1.0;
kernely.at<float>(0,0)=1.0;
kernely.at<float>(1,0)=-1.0;
order = 2;
sigma = 0.02f;
}
vector<double> Decolor::product(const vector <Vec3i> &comb, const double initRGB[3])
{
vector <double> res(comb.size());
for (size_t i=0;i<comb.size();i++)
{
double dp = 0.0;
for(int j=0;j<3;j++)
dp = dp + (comb[i][j] * initRGB[j]);
res[i] = dp;
}
return res;
}
void Decolor::singleChannelGradx(const Mat &img, Mat& dest) const
{
const int w = img.size().width;
const Point anchor(kernelx.cols - kernelx.cols/2 - 1, kernelx.rows - kernelx.rows/2 - 1);
filter2D(img, dest, -1, kernelx, anchor, 0.0, BORDER_CONSTANT);
dest.col(w - 1) = 0.0;
}
void Decolor::singleChannelGrady(const Mat &img, Mat& dest) const
{
const int h = img.size().height;
const Point anchor(kernely.cols - kernely.cols/2 - 1, kernely.rows - kernely.rows/2 - 1);
filter2D(img, dest, -1, kernely, anchor, 0.0, BORDER_CONSTANT);
dest.row(h - 1) = 0.0;
}
void Decolor::gradvector(const Mat &img, vector <double> &grad) const
{
Mat dest;
Mat dest1;
singleChannelGradx(img,dest);
singleChannelGrady(img,dest1);
Mat d_trans=dest.t();
Mat d1_trans=dest1.t();
const int height = d_trans.size().height;
const int width = d_trans.size().width;
grad.resize(width * height * 2);
for(int i=0;i<height;i++)
for(int j=0;j<width;j++)
grad[i*width + j] = d_trans.at<float>(i, j);
const int offset = width * height;
for(int i=0;i<height;i++)
for(int j=0;j<width;j++)
grad[offset + i * width + j] = d1_trans.at<float>(i, j);
}
void Decolor::colorGrad(const Mat &img, vector <double> &Cg) const
{
Mat lab;
cvtColor(img,lab,COLOR_BGR2Lab);
vector <Mat> lab_channel;
split(lab,lab_channel);
vector <double> ImL;
vector <double> Ima;
vector <double> Imb;
gradvector(lab_channel[0],ImL);
gradvector(lab_channel[1],Ima);
gradvector(lab_channel[2],Imb);
Cg.resize(ImL.size());
for(size_t i=0;i<ImL.size();i++)
{
const double res = sqrt(pow(ImL[i],2) + pow(Ima[i],2) + pow(Imb[i],2))/100;
Cg[i] = res;
}
}
void Decolor::add_vector(vector <Vec3i> &comb, int &idx, int r,int g,int b)
{
comb.push_back(Vec3i(r, g, b));
idx++;
}
void Decolor::add_to_vector_poly(vector < vector <double> > &polyGrad, const vector <double> &curGrad, int &idx1)
{
polyGrad.push_back(curGrad);
idx1++;
}
void Decolor::weak_order(const Mat &im, vector <double> &alf) const
{
Mat img;
const int h = im.size().height;
const int w = im.size().width;
if((h + w) > 800)
{
const double sizefactor = double(800)/(h+w);
resize(im, img, Size(cvRound(w*sizefactor), cvRound(h*sizefactor)));
}
else
{
img = im;
}
Mat curIm = Mat(img.size(),CV_32FC1);
vector <Mat> rgb_channel;
split(img,rgb_channel);
vector <double> Rg, Gg, Bg;
gradvector(rgb_channel[2],Rg);
gradvector(rgb_channel[1],Gg);
gradvector(rgb_channel[0],Bg);
vector <double> t1(Rg.size()), t2(Rg.size()), t3(Rg.size());
vector <double> tmp1(Rg.size()), tmp2(Rg.size()), tmp3(Rg.size());
const double level = .05;
for(size_t i=0;i<Rg.size();i++)
{
t1[i] = (Rg[i] > level) ? 1.0 : 0.0;
t2[i] = (Gg[i] > level) ? 1.0 : 0.0;
t3[i] = (Bg[i] > level) ? 1.0 : 0.0;
tmp1[i] = (Rg[i] < -1.0*level) ? 1.0 : 0.0;
tmp2[i] = (Gg[i] < -1.0*level) ? 1.0 : 0.0;
tmp3[i] = (Bg[i] < -1.0*level) ? 1.0 : 0.0;
}
alf.resize(Rg.size());
for(size_t i =0 ;i < Rg.size();i++)
alf[i] = (t1[i] * t2[i] * t3[i]);
for(size_t i =0 ;i < Rg.size();i++)
alf[i] -= tmp1[i] * tmp2[i] * tmp3[i];
}
void Decolor::grad_system(const Mat &im, vector < vector < double > > &polyGrad,
vector < double > &Cg, vector <Vec3i>& comb) const
{
Mat img;
int h = im.size().height;
int w = im.size().width;
if((h + w) > 800)
{
const double sizefactor = double(800)/(h+w);
resize(im, img, Size(cvRound(w*sizefactor), cvRound(h*sizefactor)));
}
else
{
img = im;
}
h = img.size().height;
w = img.size().width;
colorGrad(img,Cg);
Mat curIm = Mat(img.size(),CV_32FC1);
vector <Mat> rgb_channel;
split(img,rgb_channel);
int idx = 0, idx1 = 0;
for(int r=0 ;r <=order; r++)
for(int g=0; g<=order;g++)
for(int b =0; b <=order;b++)
{
if((r+g+b)<=order && (r+g+b) > 0)
{
add_vector(comb,idx,r,g,b);
for(int i = 0;i<h;i++)
for(int j=0;j<w;j++)
curIm.at<float>(i,j)=static_cast<float>(
pow(rgb_channel[2].at<float>(i,j),r)*pow(rgb_channel[1].at<float>(i,j),g)*
pow(rgb_channel[0].at<float>(i,j),b));
vector <double> curGrad;
gradvector(curIm,curGrad);
add_to_vector_poly(polyGrad,curGrad,idx1);
}
}
}
void Decolor::wei_update_matrix(const vector < vector <double> > &poly, const vector <double> &Cg, Mat &X)
{
const int size = static_cast<int>(poly.size());
const int size0 = static_cast<int>(poly[0].size());
Mat P = Mat(size, size0, CV_32FC1);
for (int i = 0; i < size; i++)
for (int j = 0; j < size0;j++)
P.at<float>(i,j) = static_cast<float>(poly[i][j]);
const Mat P_trans = P.t();
Mat B = Mat(size, size0, CV_32FC1);
for(int i =0;i < size;i++)
{
for(int j = 0, end = int(Cg.size()); j < end;j++)
B.at<float>(i,j) = static_cast<float>(poly[i][j] * Cg[j]);
}
Mat A = P*P_trans;
solve(A, B, X, DECOMP_NORMAL);
}
void Decolor::wei_inti(const vector <Vec3i> &comb, vector <double> &wei)
{
double initRGB[3] = { .33, .33, .33 };
wei = product(comb,initRGB);
vector <int> sum(comb.size());
for(size_t i=0;i<comb.size();i++)
sum[i] = (comb[i][0] + comb[i][1] + comb[i][2]);
for(size_t i=0;i<sum.size();i++)
{
if(sum[i] == 1)
wei[i] = wei[i] * double(1);
else
wei[i] = wei[i] * double(0);
}
sum.clear();
}
void Decolor::grayImContruct(vector <double> &wei, const Mat &img, Mat &Gray) const
{
const int h = img.size().height;
const int w = img.size().width;
vector <Mat> rgb_channel;
split(img,rgb_channel);
int kk =0;
for(int r =0;r<=order;r++)
for(int g=0;g<=order;g++)
for(int b=0;b<=order;b++)
if((r + g + b) <=order && (r+g+b) > 0)
{
for(int i = 0;i<h;i++)
for(int j=0;j<w;j++)
Gray.at<float>(i,j)=static_cast<float>(Gray.at<float>(i,j) +
static_cast<float>(wei[kk])*pow(rgb_channel[2].at<float>(i,j),r)*pow(rgb_channel[1].at<float>(i,j),g)*
pow(rgb_channel[0].at<float>(i,j),b));
kk=kk+1;
}
double minval, maxval;
minMaxLoc(Gray, &minval, &maxval);
Gray -= minval;
Gray /= maxval - minval;
}

View File

@ -0,0 +1,564 @@
/*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 "opencv2/core/cuda/common.hpp"
#include "opencv2/core/cuda/vec_traits.hpp"
#include "opencv2/core/cuda/vec_math.hpp"
#include "opencv2/core/cuda/functional.hpp"
#include "opencv2/core/cuda/reduce.hpp"
#include "opencv2/core/cuda/border_interpolate.hpp"
using namespace cv::cuda;
typedef unsigned char uchar;
typedef unsigned short ushort;
//////////////////////////////////////////////////////////////////////////////////
//// Non Local Means Denosing
namespace cv { namespace cuda { namespace device
{
namespace imgproc
{
__device__ __forceinline__ float norm2(const float& v) { return v*v; }
__device__ __forceinline__ float norm2(const float2& v) { return v.x*v.x + v.y*v.y; }
__device__ __forceinline__ float norm2(const float3& v) { return v.x*v.x + v.y*v.y + v.z*v.z; }
__device__ __forceinline__ float norm2(const float4& v) { return v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; }
template<typename T, typename B>
__global__ void nlm_kernel(const PtrStep<T> src, PtrStepSz<T> dst, const B b, int search_radius, int block_radius, float noise_mult)
{
typedef typename TypeVec<float, VecTraits<T>::cn>::vec_type value_type;
const int i = blockDim.y * blockIdx.y + threadIdx.y;
const int j = blockDim.x * blockIdx.x + threadIdx.x;
if (j >= dst.cols || i >= dst.rows)
return;
int bsize = search_radius + block_radius;
int search_window = 2 * search_radius + 1;
float minus_search_window2_inv = -1.f/(search_window * search_window);
value_type sum1 = VecTraits<value_type>::all(0);
float sum2 = 0.f;
if (j - bsize >= 0 && j + bsize < dst.cols && i - bsize >= 0 && i + bsize < dst.rows)
{
for(float y = -search_radius; y <= search_radius; ++y)
for(float x = -search_radius; x <= search_radius; ++x)
{
float dist2 = 0;
for(float ty = -block_radius; ty <= block_radius; ++ty)
for(float tx = -block_radius; tx <= block_radius; ++tx)
{
value_type bv = saturate_cast<value_type>(src(i + y + ty, j + x + tx));
value_type av = saturate_cast<value_type>(src(i + ty, j + tx));
dist2 += norm2(av - bv);
}
float w = __expf(dist2 * noise_mult + (x * x + y * y) * minus_search_window2_inv);
/*if (i == 255 && j == 255)
printf("%f %f\n", w, dist2 * minus_h2_inv + (x * x + y * y) * minus_search_window2_inv);*/
sum1 = sum1 + w * saturate_cast<value_type>(src(i + y, j + x));
sum2 += w;
}
}
else
{
for(float y = -search_radius; y <= search_radius; ++y)
for(float x = -search_radius; x <= search_radius; ++x)
{
float dist2 = 0;
for(float ty = -block_radius; ty <= block_radius; ++ty)
for(float tx = -block_radius; tx <= block_radius; ++tx)
{
value_type bv = saturate_cast<value_type>(b.at(i + y + ty, j + x + tx, src));
value_type av = saturate_cast<value_type>(b.at(i + ty, j + tx, src));
dist2 += norm2(av - bv);
}
float w = __expf(dist2 * noise_mult + (x * x + y * y) * minus_search_window2_inv);
sum1 = sum1 + w * saturate_cast<value_type>(b.at(i + y, j + x, src));
sum2 += w;
}
}
dst(i, j) = saturate_cast<T>(sum1 / sum2);
}
template<typename T, template <typename> class B>
void nlm_caller(const PtrStepSzb src, PtrStepSzb dst, int search_radius, int block_radius, float h, cudaStream_t stream)
{
dim3 block (32, 8);
dim3 grid (divUp (src.cols, block.x), divUp (src.rows, block.y));
B<T> b(src.rows, src.cols);
int block_window = 2 * block_radius + 1;
float minus_h2_inv = -1.f/(h * h * VecTraits<T>::cn);
float noise_mult = minus_h2_inv/(block_window * block_window);
cudaSafeCall( cudaFuncSetCacheConfig (nlm_kernel<T, B<T> >, cudaFuncCachePreferL1) );
nlm_kernel<<<grid, block>>>((PtrStepSz<T>)src, (PtrStepSz<T>)dst, b, search_radius, block_radius, noise_mult);
cudaSafeCall ( cudaGetLastError () );
if (stream == 0)
cudaSafeCall( cudaDeviceSynchronize() );
}
template<typename T>
void nlm_bruteforce_gpu(const PtrStepSzb& src, PtrStepSzb dst, int search_radius, int block_radius, float h, int borderMode, cudaStream_t stream)
{
typedef void (*func_t)(const PtrStepSzb src, PtrStepSzb dst, int search_radius, int block_radius, float h, cudaStream_t stream);
static func_t funcs[] =
{
nlm_caller<T, BrdConstant>,
nlm_caller<T, BrdReplicate>,
nlm_caller<T, BrdReflect>,
nlm_caller<T, BrdWrap>,
nlm_caller<T, BrdReflect101>
};
funcs[borderMode](src, dst, search_radius, block_radius, h, stream);
}
template void nlm_bruteforce_gpu<uchar>(const PtrStepSzb&, PtrStepSzb, int, int, float, int, cudaStream_t);
template void nlm_bruteforce_gpu<uchar2>(const PtrStepSzb&, PtrStepSzb, int, int, float, int, cudaStream_t);
template void nlm_bruteforce_gpu<uchar3>(const PtrStepSzb&, PtrStepSzb, int, int, float, int, cudaStream_t);
}
}}}
//////////////////////////////////////////////////////////////////////////////////
//// Non Local Means Denosing (fast approximate version)
namespace cv { namespace cuda { namespace device
{
namespace imgproc
{
template <int cn> struct Unroll;
template <> struct Unroll<1>
{
template <int BLOCK_SIZE>
static __device__ __forceinline__ thrust::tuple<volatile float*, volatile float*> smem_tuple(float* smem)
{
return cv::cuda::device::smem_tuple(smem, smem + BLOCK_SIZE);
}
static __device__ __forceinline__ thrust::tuple<float&, float&> tie(float& val1, float& val2)
{
return thrust::tie(val1, val2);
}
static __device__ __forceinline__ const thrust::tuple<plus<float>, plus<float> > op()
{
plus<float> op;
return thrust::make_tuple(op, op);
}
};
template <> struct Unroll<2>
{
template <int BLOCK_SIZE>
static __device__ __forceinline__ thrust::tuple<volatile float*, volatile float*, volatile float*> smem_tuple(float* smem)
{
return cv::cuda::device::smem_tuple(smem, smem + BLOCK_SIZE, smem + 2 * BLOCK_SIZE);
}
static __device__ __forceinline__ thrust::tuple<float&, float&, float&> tie(float& val1, float2& val2)
{
return thrust::tie(val1, val2.x, val2.y);
}
static __device__ __forceinline__ const thrust::tuple<plus<float>, plus<float>, plus<float> > op()
{
plus<float> op;
return thrust::make_tuple(op, op, op);
}
};
template <> struct Unroll<3>
{
template <int BLOCK_SIZE>
static __device__ __forceinline__ thrust::tuple<volatile float*, volatile float*, volatile float*, volatile float*> smem_tuple(float* smem)
{
return cv::cuda::device::smem_tuple(smem, smem + BLOCK_SIZE, smem + 2 * BLOCK_SIZE, smem + 3 * BLOCK_SIZE);
}
static __device__ __forceinline__ thrust::tuple<float&, float&, float&, float&> tie(float& val1, float3& val2)
{
return thrust::tie(val1, val2.x, val2.y, val2.z);
}
static __device__ __forceinline__ const thrust::tuple<plus<float>, plus<float>, plus<float>, plus<float> > op()
{
plus<float> op;
return thrust::make_tuple(op, op, op, op);
}
};
template <> struct Unroll<4>
{
template <int BLOCK_SIZE>
static __device__ __forceinline__ thrust::tuple<volatile float*, volatile float*, volatile float*, volatile float*, volatile float*> smem_tuple(float* smem)
{
return cv::cuda::device::smem_tuple(smem, smem + BLOCK_SIZE, smem + 2 * BLOCK_SIZE, smem + 3 * BLOCK_SIZE, smem + 4 * BLOCK_SIZE);
}
static __device__ __forceinline__ thrust::tuple<float&, float&, float&, float&, float&> tie(float& val1, float4& val2)
{
return thrust::tie(val1, val2.x, val2.y, val2.z, val2.w);
}
static __device__ __forceinline__ const thrust::tuple<plus<float>, plus<float>, plus<float>, plus<float>, plus<float> > op()
{
plus<float> op;
return thrust::make_tuple(op, op, op, op, op);
}
};
__device__ __forceinline__ int calcDist(const uchar& a, const uchar& b) { return (a-b)*(a-b); }
__device__ __forceinline__ int calcDist(const uchar2& a, const uchar2& b) { return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y); }
__device__ __forceinline__ int calcDist(const uchar3& a, const uchar3& b) { return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z); }
template <class T> struct FastNonLocalMeans
{
enum
{
CTA_SIZE = 128,
TILE_COLS = 128,
TILE_ROWS = 32,
STRIDE = CTA_SIZE
};
struct plus
{
__device__ __forceinline__ float operator()(float v1, float v2) const { return v1 + v2; }
};
int search_radius;
int block_radius;
int search_window;
int block_window;
float minus_h2_inv;
FastNonLocalMeans(int search_window_, int block_window_, float h) : search_radius(search_window_/2), block_radius(block_window_/2),
search_window(search_window_), block_window(block_window_), minus_h2_inv(-1.f/(h * h * VecTraits<T>::cn)) {}
PtrStep<T> src;
mutable PtrStepi buffer;
__device__ __forceinline__ void initSums_BruteForce(int i, int j, int* dist_sums, PtrStepi& col_sums, PtrStepi& up_col_sums) const
{
for(int index = threadIdx.x; index < search_window * search_window; index += STRIDE)
{
dist_sums[index] = 0;
for(int tx = 0; tx < block_window; ++tx)
col_sums(tx, index) = 0;
int y = index / search_window;
int x = index - y * search_window;
int ay = i;
int ax = j;
int by = i + y - search_radius;
int bx = j + x - search_radius;
#if 1
for (int tx = -block_radius; tx <= block_radius; ++tx)
{
int col_sum = 0;
for (int ty = -block_radius; ty <= block_radius; ++ty)
{
int dist = calcDist(src(ay + ty, ax + tx), src(by + ty, bx + tx));
dist_sums[index] += dist;
col_sum += dist;
}
col_sums(tx + block_radius, index) = col_sum;
}
#else
for (int ty = -block_radius; ty <= block_radius; ++ty)
for (int tx = -block_radius; tx <= block_radius; ++tx)
{
int dist = calcDist(src(ay + ty, ax + tx), src(by + ty, bx + tx));
dist_sums[index] += dist;
col_sums(tx + block_radius, index) += dist;
}
#endif
up_col_sums(j, index) = col_sums(block_window - 1, index);
}
}
__device__ __forceinline__ void shiftRight_FirstRow(int i, int j, int first, int* dist_sums, PtrStepi& col_sums, PtrStepi& up_col_sums) const
{
for(int index = threadIdx.x; index < search_window * search_window; index += STRIDE)
{
int y = index / search_window;
int x = index - y * search_window;
int ay = i;
int ax = j + block_radius;
int by = i + y - search_radius;
int bx = j + x - search_radius + block_radius;
int col_sum = 0;
for (int ty = -block_radius; ty <= block_radius; ++ty)
col_sum += calcDist(src(ay + ty, ax), src(by + ty, bx));
dist_sums[index] += col_sum - col_sums(first, index);
col_sums(first, index) = col_sum;
up_col_sums(j, index) = col_sum;
}
}
__device__ __forceinline__ void shiftRight_UpSums(int i, int j, int first, int* dist_sums, PtrStepi& col_sums, PtrStepi& up_col_sums) const
{
int ay = i;
int ax = j + block_radius;
T a_up = src(ay - block_radius - 1, ax);
T a_down = src(ay + block_radius, ax);
for(int index = threadIdx.x; index < search_window * search_window; index += STRIDE)
{
int y = index / search_window;
int x = index - y * search_window;
int by = i + y - search_radius;
int bx = j + x - search_radius + block_radius;
T b_up = src(by - block_radius - 1, bx);
T b_down = src(by + block_radius, bx);
int col_sum = up_col_sums(j, index) + calcDist(a_down, b_down) - calcDist(a_up, b_up);
dist_sums[index] += col_sum - col_sums(first, index);
col_sums(first, index) = col_sum;
up_col_sums(j, index) = col_sum;
}
}
__device__ __forceinline__ void convolve_window(int i, int j, const int* dist_sums, T& dst) const
{
typedef typename TypeVec<float, VecTraits<T>::cn>::vec_type sum_type;
float weights_sum = 0;
sum_type sum = VecTraits<sum_type>::all(0);
float bw2_inv = 1.f/(block_window * block_window);
int sx = j - search_radius;
int sy = i - search_radius;
for(int index = threadIdx.x; index < search_window * search_window; index += STRIDE)
{
int y = index / search_window;
int x = index - y * search_window;
float avg_dist = dist_sums[index] * bw2_inv;
float weight = __expf(avg_dist * minus_h2_inv);
weights_sum += weight;
sum = sum + weight * saturate_cast<sum_type>(src(sy + y, sx + x));
}
__shared__ float cta_buffer[CTA_SIZE * (VecTraits<T>::cn + 1)];
reduce<CTA_SIZE>(Unroll<VecTraits<T>::cn>::template smem_tuple<CTA_SIZE>(cta_buffer),
Unroll<VecTraits<T>::cn>::tie(weights_sum, sum),
threadIdx.x,
Unroll<VecTraits<T>::cn>::op());
if (threadIdx.x == 0)
dst = saturate_cast<T>(sum / weights_sum);
}
__device__ __forceinline__ void operator()(PtrStepSz<T>& dst) const
{
int tbx = blockIdx.x * TILE_COLS;
int tby = blockIdx.y * TILE_ROWS;
int tex = ::min(tbx + TILE_COLS, dst.cols);
int tey = ::min(tby + TILE_ROWS, dst.rows);
PtrStepi col_sums;
col_sums.data = buffer.ptr(dst.cols + blockIdx.x * block_window) + blockIdx.y * search_window * search_window;
col_sums.step = buffer.step;
PtrStepi up_col_sums;
up_col_sums.data = buffer.data + blockIdx.y * search_window * search_window;
up_col_sums.step = buffer.step;
extern __shared__ int dist_sums[]; //search_window * search_window
int first = 0;
for (int i = tby; i < tey; ++i)
for (int j = tbx; j < tex; ++j)
{
__syncthreads();
if (j == tbx)
{
initSums_BruteForce(i, j, dist_sums, col_sums, up_col_sums);
first = 0;
}
else
{
if (i == tby)
shiftRight_FirstRow(i, j, first, dist_sums, col_sums, up_col_sums);
else
shiftRight_UpSums(i, j, first, dist_sums, col_sums, up_col_sums);
first = (first + 1) % block_window;
}
__syncthreads();
convolve_window(i, j, dist_sums, dst(i, j));
}
}
};
template<typename T>
__global__ void fast_nlm_kernel(const FastNonLocalMeans<T> fnlm, PtrStepSz<T> dst) { fnlm(dst); }
void nln_fast_get_buffer_size(const PtrStepSzb& src, int search_window, int block_window, int& buffer_cols, int& buffer_rows)
{
typedef FastNonLocalMeans<uchar> FNLM;
dim3 grid(divUp(src.cols, FNLM::TILE_COLS), divUp(src.rows, FNLM::TILE_ROWS));
buffer_cols = search_window * search_window * grid.y;
buffer_rows = src.cols + block_window * grid.x;
}
template<typename T>
void nlm_fast_gpu(const PtrStepSzb& src, PtrStepSzb dst, PtrStepi buffer,
int search_window, int block_window, float h, cudaStream_t stream)
{
typedef FastNonLocalMeans<T> FNLM;
FNLM fnlm(search_window, block_window, h);
fnlm.src = (PtrStepSz<T>)src;
fnlm.buffer = buffer;
dim3 block(FNLM::CTA_SIZE, 1);
dim3 grid(divUp(src.cols, FNLM::TILE_COLS), divUp(src.rows, FNLM::TILE_ROWS));
int smem = search_window * search_window * sizeof(int);
fast_nlm_kernel<<<grid, block, smem>>>(fnlm, (PtrStepSz<T>)dst);
cudaSafeCall ( cudaGetLastError () );
if (stream == 0)
cudaSafeCall( cudaDeviceSynchronize() );
}
template void nlm_fast_gpu<uchar>(const PtrStepSzb&, PtrStepSzb, PtrStepi, int, int, float, cudaStream_t);
template void nlm_fast_gpu<uchar2>(const PtrStepSzb&, PtrStepSzb, PtrStepi, int, int, float, cudaStream_t);
template void nlm_fast_gpu<uchar3>(const PtrStepSzb&, PtrStepSzb, PtrStepi, int, int, float, cudaStream_t);
__global__ void fnlm_split_kernel(const PtrStepSz<uchar3> lab, PtrStepb l, PtrStep<uchar2> ab)
{
int x = threadIdx.x + blockIdx.x * blockDim.x;
int y = threadIdx.y + blockIdx.y * blockDim.y;
if (x < lab.cols && y < lab.rows)
{
uchar3 p = lab(y, x);
ab(y,x) = make_uchar2(p.y, p.z);
l(y,x) = p.x;
}
}
void fnlm_split_channels(const PtrStepSz<uchar3>& lab, PtrStepb l, PtrStep<uchar2> ab, cudaStream_t stream)
{
dim3 b(32, 8);
dim3 g(divUp(lab.cols, b.x), divUp(lab.rows, b.y));
fnlm_split_kernel<<<g, b>>>(lab, l, ab);
cudaSafeCall ( cudaGetLastError () );
if (stream == 0)
cudaSafeCall( cudaDeviceSynchronize() );
}
__global__ void fnlm_merge_kernel(const PtrStepb l, const PtrStep<uchar2> ab, PtrStepSz<uchar3> lab)
{
int x = threadIdx.x + blockIdx.x * blockDim.x;
int y = threadIdx.y + blockIdx.y * blockDim.y;
if (x < lab.cols && y < lab.rows)
{
uchar2 p = ab(y, x);
lab(y, x) = make_uchar3(l(y, x), p.x, p.y);
}
}
void fnlm_merge_channels(const PtrStepb& l, const PtrStep<uchar2>& ab, PtrStepSz<uchar3> lab, cudaStream_t stream)
{
dim3 b(32, 8);
dim3 g(divUp(lab.cols, b.x), divUp(lab.rows, b.y));
fnlm_merge_kernel<<<g, b>>>(l, ab, lab);
cudaSafeCall ( cudaGetLastError () );
if (stream == 0)
cudaSafeCall( cudaDeviceSynchronize() );
}
}
}}}

View File

@ -0,0 +1,153 @@
/*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 OpenCV Foundation 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 <vector>
#include <algorithm>
#define ABSCLIP(val,threshold) MIN(MAX((val),-(threshold)),(threshold))
namespace cv{
class AddFloatToCharScaled{
public:
AddFloatToCharScaled(double scale):_scale(scale){}
inline double operator()(double a,uchar b){
return a+_scale*((double)b);
}
private:
double _scale;
};
using std::transform;
void denoise_TVL1(const std::vector<Mat>& observations,Mat& result, double lambda, int niters){
CV_Assert(observations.size()>0 && niters>0 && lambda>0);
const double L2 = 8.0, tau = 0.02, sigma = 1./(L2*tau), theta = 1.0;
double clambda = (double)lambda;
double s=0;
const int workdepth = CV_64F;
int i, x, y, rows=observations[0].rows, cols=observations[0].cols,count;
for(i=1;i<(int)observations.size();i++){
CV_Assert(observations[i].rows==rows && observations[i].cols==cols);
}
Mat X, P = Mat::zeros(rows, cols, CV_MAKETYPE(workdepth, 2));
observations[0].convertTo(X, workdepth, 1./255);
std::vector< Mat_<double> > Rs(observations.size());
for(count=0;count<(int)Rs.size();count++){
Rs[count]=Mat::zeros(rows,cols,workdepth);
}
for( i = 0; i < niters; i++ )
{
double currsigma = i == 0 ? 1 + sigma : sigma;
// P_ = P + sigma*nabla(X)
// P(x,y) = P_(x,y)/max(||P(x,y)||,1)
for( y = 0; y < rows; y++ )
{
const double* x_curr = X.ptr<double>(y);
const double* x_next = X.ptr<double>(std::min(y+1, rows-1));
Point2d* p_curr = P.ptr<Point2d>(y);
double dx, dy, m;
for( x = 0; x < cols-1; x++ )
{
dx = (x_curr[x+1] - x_curr[x])*currsigma + p_curr[x].x;
dy = (x_next[x] - x_curr[x])*currsigma + p_curr[x].y;
m = 1.0/std::max(std::sqrt(dx*dx + dy*dy), 1.0);
p_curr[x].x = dx*m;
p_curr[x].y = dy*m;
}
dy = (x_next[x] - x_curr[x])*currsigma + p_curr[x].y;
m = 1.0/std::max(std::abs(dy), 1.0);
p_curr[x].x = 0.0;
p_curr[x].y = dy*m;
}
//Rs = clip(Rs + sigma*(X-imgs), -clambda, clambda)
for(count=0;count<(int)Rs.size();count++){
transform<MatIterator_<double>,MatConstIterator_<uchar>,MatIterator_<double>,AddFloatToCharScaled>(
Rs[count].begin(),Rs[count].end(),observations[count].begin<uchar>(),
Rs[count].begin(),AddFloatToCharScaled(-sigma/255.0));
Rs[count]+=sigma*X;
min(Rs[count],clambda,Rs[count]);
max(Rs[count],-clambda,Rs[count]);
}
for( y = 0; y < rows; y++ )
{
double* x_curr = X.ptr<double>(y);
const Point2d* p_curr = P.ptr<Point2d>(y);
const Point2d* p_prev = P.ptr<Point2d>(std::max(y - 1, 0));
// X1 = X + tau*(-nablaT(P))
x = 0;
s=0.0;
for(count=0;count<(int)Rs.size();count++){
s=s+Rs[count](y,x);
}
double x_new = x_curr[x] + tau*(p_curr[x].y - p_prev[x].y)-tau*s;
// X = X2 + theta*(X2 - X)
x_curr[x] = x_new + theta*(x_new - x_curr[x]);
for(x = 1; x < cols; x++ )
{
s=0.0;
for(count=0;count<(int)Rs.size();count++){
s+=Rs[count](y,x);
}
// X1 = X + tau*(-nablaT(P))
x_new = x_curr[x] + tau*(p_curr[x].x - p_curr[x-1].x + p_curr[x].y - p_prev[x].y)-tau*s;
// X = X2 + theta*(X2 - X)
x_curr[x] = x_new + theta*(x_new - x_curr[x]);
}
}
}
result.create(X.rows,X.cols,CV_8U);
X.convertTo(result, CV_8U, 255);
}
}

View File

@ -0,0 +1,441 @@
/*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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective icvers.
//
// 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 Intel Corporation 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 "fast_nlmeans_denoising_invoker.hpp"
#include "fast_nlmeans_multi_denoising_invoker.hpp"
#include "fast_nlmeans_denoising_opencl.hpp"
template<typename ST, typename IT, typename UIT, typename D>
static void fastNlMeansDenoising_( const Mat& src, Mat& dst, const std::vector<float>& h,
int templateWindowSize, int searchWindowSize)
{
int hn = (int)h.size();
double granularity = (double)std::max(1., (double)dst.total()/(1 << 17));
switch (CV_MAT_CN(src.type())) {
case 1:
parallel_for_(cv::Range(0, src.rows),
FastNlMeansDenoisingInvoker<ST, IT, UIT, D, int>(
src, dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
break;
case 2:
if (hn == 1)
parallel_for_(cv::Range(0, src.rows),
FastNlMeansDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, int>(
src, dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
else
parallel_for_(cv::Range(0, src.rows),
FastNlMeansDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, Vec2i>(
src, dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
break;
case 3:
if (hn == 1)
parallel_for_(cv::Range(0, src.rows),
FastNlMeansDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, int>(
src, dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
else
parallel_for_(cv::Range(0, src.rows),
FastNlMeansDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, Vec3i>(
src, dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
break;
case 4:
if (hn == 1)
parallel_for_(cv::Range(0, src.rows),
FastNlMeansDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, int>(
src, dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
else
parallel_for_(cv::Range(0, src.rows),
FastNlMeansDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, Vec4i>(
src, dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
break;
default:
CV_Error(Error::StsBadArg,
"Unsupported number of channels! Only 1, 2, 3, and 4 are supported");
}
}
void cv::fastNlMeansDenoising( InputArray _src, OutputArray _dst, float h,
int templateWindowSize, int searchWindowSize)
{
CV_INSTRUMENT_REGION();
fastNlMeansDenoising(_src, _dst, std::vector<float>(1, h),
templateWindowSize, searchWindowSize);
}
void cv::fastNlMeansDenoising( InputArray _src, OutputArray _dst, const std::vector<float>& h,
int templateWindowSize, int searchWindowSize, int normType)
{
CV_INSTRUMENT_REGION();
int hn = (int)h.size(), type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
CV_Assert(!_src.empty());
CV_Assert(hn == 1 || hn == cn);
Size src_size = _src.size();
CV_OCL_RUN(_src.dims() <= 2 && (_src.isUMat() || _dst.isUMat()) &&
src_size.width > 5 && src_size.height > 5, // low accuracy on small sizes
ocl_fastNlMeansDenoising(_src, _dst, &h[0], hn,
templateWindowSize, searchWindowSize, normType))
Mat src = _src.getMat();
_dst.create(src_size, src.type());
Mat dst = _dst.getMat();
switch (normType) {
case NORM_L2:
switch (depth) {
case CV_8U:
fastNlMeansDenoising_<uchar, int, unsigned, DistSquared>(src, dst, h,
templateWindowSize,
searchWindowSize);
break;
default:
CV_Error(Error::StsBadArg,
"Unsupported depth! Only CV_8U is supported for NORM_L2");
}
break;
case NORM_L1:
switch (depth) {
case CV_8U:
fastNlMeansDenoising_<uchar, int, unsigned, DistAbs>(src, dst, h,
templateWindowSize,
searchWindowSize);
break;
case CV_16U:
fastNlMeansDenoising_<ushort, int64, uint64, DistAbs>(src, dst, h,
templateWindowSize,
searchWindowSize);
break;
default:
CV_Error(Error::StsBadArg,
"Unsupported depth! Only CV_8U and CV_16U are supported for NORM_L1");
}
break;
default:
CV_Error(Error::StsBadArg,
"Unsupported norm type! Only NORM_L2 and NORM_L1 are supported");
}
}
void cv::fastNlMeansDenoisingColored( InputArray _src, OutputArray _dst,
float h, float hForColorComponents,
int templateWindowSize, int searchWindowSize)
{
CV_INSTRUMENT_REGION();
int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
Size src_size = _src.size();
if (type != CV_8UC3 && type != CV_8UC4)
{
CV_Error(Error::StsBadArg, "Type of input image should be CV_8UC3 or CV_8UC4!");
return;
}
CV_OCL_RUN(_src.dims() <= 2 && (_dst.isUMat() || _src.isUMat()) &&
src_size.width > 5 && src_size.height > 5, // low accuracy on small sizes
ocl_fastNlMeansDenoisingColored(_src, _dst, h, hForColorComponents,
templateWindowSize, searchWindowSize))
Mat src = _src.getMat();
_dst.create(src_size, type);
Mat dst = _dst.getMat();
Mat src_lab;
cvtColor(src, src_lab, COLOR_LBGR2Lab);
Mat l(src_size, CV_MAKE_TYPE(depth, 1));
Mat ab(src_size, CV_MAKE_TYPE(depth, 2));
Mat l_ab[] = { l, ab };
int from_to[] = { 0,0, 1,1, 2,2 };
mixChannels(&src_lab, 1, l_ab, 2, from_to, 3);
fastNlMeansDenoising(l, l, h, templateWindowSize, searchWindowSize);
fastNlMeansDenoising(ab, ab, hForColorComponents, templateWindowSize, searchWindowSize);
Mat l_ab_denoised[] = { l, ab };
Mat dst_lab(src_size, CV_MAKE_TYPE(depth, 3));
mixChannels(l_ab_denoised, 2, &dst_lab, 1, from_to, 3);
cvtColor(dst_lab, dst, COLOR_Lab2LBGR, cn);
}
static void fastNlMeansDenoisingMultiCheckPreconditions(
const std::vector<Mat>& srcImgs,
int imgToDenoiseIndex, int temporalWindowSize,
int templateWindowSize, int searchWindowSize)
{
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!");
}
if (temporalWindowSize % 2 == 0 ||
searchWindowSize % 2 == 0 ||
templateWindowSize % 2 == 0) {
CV_Error(Error::StsBadArg, "All windows sizes should be odd!");
}
int temporalWindowHalfSize = temporalWindowSize / 2;
if (imgToDenoiseIndex - temporalWindowHalfSize < 0 ||
imgToDenoiseIndex + temporalWindowHalfSize >= src_imgs_size)
{
CV_Error(Error::StsBadArg,
"imgToDenoiseIndex and temporalWindowSize "
"should be chosen corresponding srcImgs size!");
}
for (int i = 1; i < src_imgs_size; i++)
if (srcImgs[0].size() != srcImgs[i].size() || srcImgs[0].type() != srcImgs[i].type())
{
CV_Error(Error::StsBadArg, "Input images should have the same size and type!");
}
}
template<typename ST, typename IT, typename UIT, typename D>
static void fastNlMeansDenoisingMulti_( const std::vector<Mat>& srcImgs, Mat& dst,
int imgToDenoiseIndex, int temporalWindowSize,
const std::vector<float>& h,
int templateWindowSize, int searchWindowSize)
{
int hn = (int)h.size();
double granularity = (double)std::max(1., (double)dst.total()/(1 << 16));
switch (srcImgs[0].type())
{
case CV_8U:
parallel_for_(cv::Range(0, srcImgs[0].rows),
FastNlMeansMultiDenoisingInvoker<uchar, IT, UIT, D, int>(
srcImgs, imgToDenoiseIndex, temporalWindowSize,
dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
break;
case CV_8UC2:
if (hn == 1)
parallel_for_(cv::Range(0, srcImgs[0].rows),
FastNlMeansMultiDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, int>(
srcImgs, imgToDenoiseIndex, temporalWindowSize,
dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
else
parallel_for_(cv::Range(0, srcImgs[0].rows),
FastNlMeansMultiDenoisingInvoker<Vec<ST, 2>, IT, UIT, D, Vec2i>(
srcImgs, imgToDenoiseIndex, temporalWindowSize,
dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
break;
case CV_8UC3:
if (hn == 1)
parallel_for_(cv::Range(0, srcImgs[0].rows),
FastNlMeansMultiDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, int>(
srcImgs, imgToDenoiseIndex, temporalWindowSize,
dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
else
parallel_for_(cv::Range(0, srcImgs[0].rows),
FastNlMeansMultiDenoisingInvoker<Vec<ST, 3>, IT, UIT, D, Vec3i>(
srcImgs, imgToDenoiseIndex, temporalWindowSize,
dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
break;
case CV_8UC4:
if (hn == 1)
parallel_for_(cv::Range(0, srcImgs[0].rows),
FastNlMeansMultiDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, int>(
srcImgs, imgToDenoiseIndex, temporalWindowSize,
dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
else
parallel_for_(cv::Range(0, srcImgs[0].rows),
FastNlMeansMultiDenoisingInvoker<Vec<ST, 4>, IT, UIT, D, Vec4i>(
srcImgs, imgToDenoiseIndex, temporalWindowSize,
dst, templateWindowSize, searchWindowSize, &h[0]),
granularity);
break;
default:
CV_Error(Error::StsBadArg,
"Unsupported image format! Only CV_8U, CV_8UC2, CV_8UC3 and CV_8UC4 are supported");
}
}
void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
int imgToDenoiseIndex, int temporalWindowSize,
float h, int templateWindowSize, int searchWindowSize)
{
CV_INSTRUMENT_REGION();
fastNlMeansDenoisingMulti(_srcImgs, _dst, imgToDenoiseIndex, temporalWindowSize,
std::vector<float>(1, h), templateWindowSize, searchWindowSize);
}
void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
int imgToDenoiseIndex, int temporalWindowSize,
const std::vector<float>& h,
int templateWindowSize, int searchWindowSize, int normType)
{
CV_INSTRUMENT_REGION();
std::vector<Mat> srcImgs;
_srcImgs.getMatVector(srcImgs);
fastNlMeansDenoisingMultiCheckPreconditions(
srcImgs, imgToDenoiseIndex,
temporalWindowSize, templateWindowSize, searchWindowSize);
int hn = (int)h.size();
int type = srcImgs[0].type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
CV_Assert(hn == 1 || hn == cn);
_dst.create(srcImgs[0].size(), srcImgs[0].type());
Mat dst = _dst.getMat();
switch (normType) {
case NORM_L2:
switch (depth) {
case CV_8U:
fastNlMeansDenoisingMulti_<uchar, int, unsigned,
DistSquared>(srcImgs, dst,
imgToDenoiseIndex, temporalWindowSize,
h,
templateWindowSize, searchWindowSize);
break;
default:
CV_Error(Error::StsBadArg,
"Unsupported depth! Only CV_8U is supported for NORM_L2");
}
break;
case NORM_L1:
switch (depth) {
case CV_8U:
fastNlMeansDenoisingMulti_<uchar, int, unsigned,
DistAbs>(srcImgs, dst,
imgToDenoiseIndex, temporalWindowSize,
h,
templateWindowSize, searchWindowSize);
break;
case CV_16U:
fastNlMeansDenoisingMulti_<ushort, int64, uint64,
DistAbs>(srcImgs, dst,
imgToDenoiseIndex, temporalWindowSize,
h,
templateWindowSize, searchWindowSize);
break;
default:
CV_Error(Error::StsBadArg,
"Unsupported depth! Only CV_8U and CV_16U are supported for NORM_L1");
}
break;
default:
CV_Error(Error::StsBadArg,
"Unsupported norm type! Only NORM_L2 and NORM_L1 are supported");
}
}
void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
int imgToDenoiseIndex, int temporalWindowSize,
float h, float hForColorComponents,
int templateWindowSize, int searchWindowSize)
{
CV_INSTRUMENT_REGION();
std::vector<Mat> srcImgs;
_srcImgs.getMatVector(srcImgs);
fastNlMeansDenoisingMultiCheckPreconditions(
srcImgs, imgToDenoiseIndex,
temporalWindowSize, templateWindowSize, searchWindowSize);
_dst.create(srcImgs[0].size(), srcImgs[0].type());
Mat dst = _dst.getMat();
int type = srcImgs[0].type(), depth = CV_MAT_DEPTH(type);
int src_imgs_size = static_cast<int>(srcImgs.size());
if (type != CV_8UC3)
{
CV_Error(Error::StsBadArg, "Type of input images should be CV_8UC3!");
return;
}
int from_to[] = { 0,0, 1,1, 2,2 };
// TODO convert only required images
std::vector<Mat> src_lab(src_imgs_size);
std::vector<Mat> l(src_imgs_size);
std::vector<Mat> ab(src_imgs_size);
for (int i = 0; i < src_imgs_size; i++)
{
src_lab[i] = Mat::zeros(srcImgs[0].size(), type);
l[i] = Mat::zeros(srcImgs[0].size(), CV_MAKE_TYPE(depth, 1));
ab[i] = Mat::zeros(srcImgs[0].size(), CV_MAKE_TYPE(depth, 2));
cvtColor(srcImgs[i], src_lab[i], COLOR_LBGR2Lab);
Mat l_ab[] = { l[i], ab[i] };
mixChannels(&src_lab[i], 1, l_ab, 2, from_to, 3);
}
Mat dst_l;
Mat dst_ab;
fastNlMeansDenoisingMulti(
l, dst_l, imgToDenoiseIndex, temporalWindowSize,
h, templateWindowSize, searchWindowSize);
fastNlMeansDenoisingMulti(
ab, dst_ab, imgToDenoiseIndex, temporalWindowSize,
hForColorComponents, templateWindowSize, searchWindowSize);
Mat l_ab_denoised[] = { dst_l, dst_ab };
Mat dst_lab(srcImgs[0].size(), srcImgs[0].type());
mixChannels(l_ab_denoised, 2, &dst_lab, 1, from_to, 3);
cvtColor(dst_lab, dst, COLOR_Lab2LBGR);
}

View File

@ -0,0 +1,170 @@
/*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/cuda.hpp"
#include "opencv2/core/private.cuda.hpp"
#include "opencv2/opencv_modules.hpp"
#ifdef HAVE_OPENCV_CUDAARITHM
# include "opencv2/cudaarithm.hpp"
#endif
#ifdef HAVE_OPENCV_CUDAIMGPROC
# include "opencv2/cudaimgproc.hpp"
#endif
using namespace cv;
using namespace cv::cuda;
#if !defined (HAVE_CUDA) || !defined(HAVE_OPENCV_CUDAARITHM) || !defined(HAVE_OPENCV_CUDAIMGPROC)
void cv::cuda::nonLocalMeans(InputArray, OutputArray, float, int, int, int, Stream&) { throw_no_cuda(); }
void cv::cuda::fastNlMeansDenoising(InputArray, OutputArray, float, int, int, Stream&) { throw_no_cuda(); }
void cv::cuda::fastNlMeansDenoisingColored(InputArray, OutputArray, float, float, int, int, Stream&) { throw_no_cuda(); }
#else
//////////////////////////////////////////////////////////////////////////////////
//// Non Local Means Denosing (brute force)
namespace cv { namespace cuda { namespace device
{
namespace imgproc
{
template<typename T>
void nlm_bruteforce_gpu(const PtrStepSzb& src, PtrStepSzb dst, int search_radius, int block_radius, float h, int borderMode, cudaStream_t stream);
}
}}}
void cv::cuda::nonLocalMeans(InputArray _src, OutputArray _dst, float h, int search_window, int block_window, int borderMode, Stream& stream)
{
using cv::cuda::device::imgproc::nlm_bruteforce_gpu;
typedef void (*func_t)(const PtrStepSzb& src, PtrStepSzb dst, int search_radius, int block_radius, float h, int borderMode, cudaStream_t stream);
static const func_t funcs[4] = { nlm_bruteforce_gpu<uchar>, nlm_bruteforce_gpu<uchar2>, nlm_bruteforce_gpu<uchar3>, 0/*nlm_bruteforce_gpu<uchar4>,*/ };
const GpuMat src = _src.getGpuMat();
CV_Assert(src.type() == CV_8U || src.type() == CV_8UC2 || src.type() == CV_8UC3);
const func_t func = funcs[src.channels() - 1];
CV_Assert(func != 0);
int b = borderMode;
CV_Assert(b == BORDER_REFLECT101 || b == BORDER_REPLICATE || b == BORDER_CONSTANT || b == BORDER_REFLECT || b == BORDER_WRAP);
_dst.create(src.size(), src.type());
GpuMat dst = _dst.getGpuMat();
func(src, dst, search_window/2, block_window/2, h, borderMode, StreamAccessor::getStream(stream));
}
namespace cv { namespace cuda { namespace device
{
namespace imgproc
{
void nln_fast_get_buffer_size(const PtrStepSzb& src, int search_window, int block_window, int& buffer_cols, int& buffer_rows);
template<typename T>
void nlm_fast_gpu(const PtrStepSzb& src, PtrStepSzb dst, PtrStepi buffer,
int search_window, int block_window, float h, cudaStream_t stream);
void fnlm_split_channels(const PtrStepSz<uchar3>& lab, PtrStepb l, PtrStep<uchar2> ab, cudaStream_t stream);
void fnlm_merge_channels(const PtrStepb& l, const PtrStep<uchar2>& ab, PtrStepSz<uchar3> lab, cudaStream_t stream);
}
}}}
void cv::cuda::fastNlMeansDenoising(InputArray _src, OutputArray _dst, float h, int search_window, int block_window, Stream& stream)
{
const GpuMat src = _src.getGpuMat();
CV_Assert(src.depth() == CV_8U && src.channels() < 4);
int border_size = search_window/2 + block_window/2;
Size esize = src.size() + Size(border_size, border_size) * 2;
BufferPool pool(stream);
GpuMat extended_src = pool.getBuffer(esize, src.type());
cv::cuda::copyMakeBorder(src, extended_src, border_size, border_size, border_size, border_size, cv::BORDER_DEFAULT, Scalar(), stream);
GpuMat src_hdr = extended_src(Rect(Point2i(border_size, border_size), src.size()));
int bcols, brows;
device::imgproc::nln_fast_get_buffer_size(src_hdr, search_window, block_window, bcols, brows);
GpuMat buffer = pool.getBuffer(brows, bcols, CV_32S);
using namespace cv::cuda::device::imgproc;
typedef void (*nlm_fast_t)(const PtrStepSzb&, PtrStepSzb, PtrStepi, int, int, float, cudaStream_t);
static const nlm_fast_t funcs[] = { nlm_fast_gpu<uchar>, nlm_fast_gpu<uchar2>, nlm_fast_gpu<uchar3>, 0};
_dst.create(src.size(), src.type());
GpuMat dst = _dst.getGpuMat();
funcs[src.channels()-1](src_hdr, dst, buffer, search_window, block_window, h, StreamAccessor::getStream(stream));
}
void cv::cuda::fastNlMeansDenoisingColored(InputArray _src, OutputArray _dst, float h_luminance, float h_color, int search_window, int block_window, Stream& stream)
{
const GpuMat src = _src.getGpuMat();
CV_Assert(src.type() == CV_8UC3);
BufferPool pool(stream);
GpuMat lab = pool.getBuffer(src.size(), src.type());
cv::cuda::cvtColor(src, lab, cv::COLOR_BGR2Lab, 0, stream);
GpuMat l = pool.getBuffer(src.size(), CV_8U);
GpuMat ab = pool.getBuffer(src.size(), CV_8UC2);
device::imgproc::fnlm_split_channels(lab, l, ab, StreamAccessor::getStream(stream));
fastNlMeansDenoising(l, l, h_luminance, search_window, block_window, stream);
fastNlMeansDenoising(ab, ab, h_color, search_window, block_window, stream);
device::imgproc::fnlm_merge_channels(l, ab, lab, StreamAccessor::getStream(stream));
cv::cuda::cvtColor(lab, _dst, cv::COLOR_Lab2BGR, 0, stream);
}
#endif

View File

@ -0,0 +1,321 @@
/*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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective icvers.
//
// 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 Intel Corporation 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_FAST_NLMEANS_DENOISING_INVOKER_HPP__
#define __OPENCV_FAST_NLMEANS_DENOISING_INVOKER_HPP__
#include "precomp.hpp"
#include <limits>
#include "fast_nlmeans_denoising_invoker_commons.hpp"
#include "arrays.hpp"
using namespace cv;
template <typename T, typename IT, typename UIT, typename D, typename WT>
struct FastNlMeansDenoisingInvoker :
public ParallelLoopBody
{
public:
FastNlMeansDenoisingInvoker(const Mat& src, Mat& dst,
int template_window_size, int search_window_size, const float *h);
void operator() (const Range& range) const CV_OVERRIDE;
private:
void operator= (const FastNlMeansDenoisingInvoker&);
const Mat& src_;
Mat& dst_;
Mat extended_src_;
int border_size_;
int template_window_size_;
int search_window_size_;
int template_window_half_size_;
int search_window_half_size_;
typename pixelInfo<WT>::sampleType fixed_point_mult_;
int almost_template_window_size_sq_bin_shift_;
std::vector<WT> almost_dist2weight_;
void calcDistSumsForFirstElementInRow(
int i, Array2d<int>& dist_sums,
Array3d<int>& col_dist_sums,
Array3d<int>& up_col_dist_sums) const;
void calcDistSumsForElementInFirstRow(
int i, int j, int first_col_num,
Array2d<int>& dist_sums,
Array3d<int>& col_dist_sums,
Array3d<int>& up_col_dist_sums) const;
};
inline int getNearestPowerOf2(int value)
{
int p = 0;
while( 1 << p < value)
++p;
return p;
}
template <typename T, typename IT, typename UIT, typename D, typename WT>
FastNlMeansDenoisingInvoker<T, IT, UIT, D, WT>::FastNlMeansDenoisingInvoker(
const Mat& src, Mat& dst,
int template_window_size,
int search_window_size,
const float *h) :
src_(src), dst_(dst)
{
CV_Assert(src.channels() == pixelInfo<T>::channels);
template_window_half_size_ = template_window_size / 2;
search_window_half_size_ = search_window_size / 2;
template_window_size_ = template_window_half_size_ * 2 + 1;
search_window_size_ = search_window_half_size_ * 2 + 1;
border_size_ = search_window_half_size_ + template_window_half_size_;
copyMakeBorder(src_, extended_src_, border_size_, border_size_, border_size_, border_size_, BORDER_DEFAULT);
const IT max_estimate_sum_value =
(IT)search_window_size_ * (IT)search_window_size_ * (IT)pixelInfo<T>::sampleMax();
fixed_point_mult_ = (int)std::min<IT>(std::numeric_limits<IT>::max() / max_estimate_sum_value,
pixelInfo<WT>::sampleMax());
// precalc weight for every possible l2 dist between blocks
// additional optimization of precalced weights to replace division(averaging) by binary shift
CV_Assert(template_window_size_ <= 46340); // sqrt(INT_MAX)
int template_window_size_sq = template_window_size_ * template_window_size_;
almost_template_window_size_sq_bin_shift_ = getNearestPowerOf2(template_window_size_sq);
double almost_dist2actual_dist_multiplier = ((double)(1 << almost_template_window_size_sq_bin_shift_)) / template_window_size_sq;
int max_dist = D::template maxDist<T>();
int almost_max_dist = (int)(max_dist / almost_dist2actual_dist_multiplier + 1);
almost_dist2weight_.resize(almost_max_dist);
for (int almost_dist = 0; almost_dist < almost_max_dist; almost_dist++)
{
double dist = almost_dist * almost_dist2actual_dist_multiplier;
almost_dist2weight_[almost_dist] =
D::template calcWeight<T, WT>(dist, h, fixed_point_mult_);
}
// additional optimization init end
if (dst_.empty())
dst_ = Mat::zeros(src_.size(), src_.type());
}
template <typename T, typename IT, typename UIT, typename D, typename WT>
void FastNlMeansDenoisingInvoker<T, IT, UIT, D, WT>::operator() (const Range& range) const
{
int row_from = range.start;
int row_to = range.end - 1;
// sums of cols anf rows for current pixel p
Array2d<int> dist_sums(search_window_size_, search_window_size_);
// for lazy calc optimization (sum of cols for current pixel)
Array3d<int> col_dist_sums(template_window_size_, search_window_size_, search_window_size_);
int first_col_num = -1;
// last elements of column sum (for each element in row)
Array3d<int> up_col_dist_sums(src_.cols, search_window_size_, search_window_size_);
for (int i = row_from; i <= row_to; i++)
{
for (int j = 0; j < src_.cols; j++)
{
int search_window_y = i - search_window_half_size_;
int search_window_x = j - search_window_half_size_;
// calc dist_sums
if (j == 0)
{
calcDistSumsForFirstElementInRow(i, dist_sums, col_dist_sums, up_col_dist_sums);
first_col_num = 0;
}
else
{
// calc cur dist_sums using previous dist_sums
if (i == row_from)
{
calcDistSumsForElementInFirstRow(i, j, first_col_num,
dist_sums, col_dist_sums, up_col_dist_sums);
}
else
{
int ay = border_size_ + i;
int ax = border_size_ + j + template_window_half_size_;
int start_by = border_size_ + i - search_window_half_size_;
int start_bx = border_size_ + j - search_window_half_size_ + template_window_half_size_;
T a_up = extended_src_.at<T>(ay - template_window_half_size_ - 1, ax);
T a_down = extended_src_.at<T>(ay + template_window_half_size_, ax);
// copy class member to local variable for optimization
int search_window_size = search_window_size_;
for (int y = 0; y < search_window_size; y++)
{
int * dist_sums_row = dist_sums.row_ptr(y);
int * col_dist_sums_row = col_dist_sums.row_ptr(first_col_num, y);
int * up_col_dist_sums_row = up_col_dist_sums.row_ptr(j, y);
const T * b_up_ptr = extended_src_.ptr<T>(start_by - template_window_half_size_ - 1 + y);
const T * b_down_ptr = extended_src_.ptr<T>(start_by + template_window_half_size_ + y);
// MSVC 2015 generates unaligned destination for "movaps" instruction for 32-bit builds
#if defined _MSC_VER && _MSC_VER == 1900 && !defined _WIN64
#pragma loop(no_vector)
#endif
for (int x = 0; x < search_window_size; x++)
{
// remove from current pixel sum column sum with index "first_col_num"
dist_sums_row[x] -= col_dist_sums_row[x];
int bx = start_bx + x;
col_dist_sums_row[x] = up_col_dist_sums_row[x] + D::template calcUpDownDist<T>(a_up, a_down, b_up_ptr[bx], b_down_ptr[bx]);
dist_sums_row[x] += col_dist_sums_row[x];
up_col_dist_sums_row[x] = col_dist_sums_row[x];
}
}
}
first_col_num = (first_col_num + 1) % template_window_size_;
}
// calc weights
IT estimation[pixelInfo<T>::channels], weights_sum[pixelInfo<WT>::channels];
for (int channel_num = 0; channel_num < pixelInfo<T>::channels; channel_num++)
estimation[channel_num] = 0;
for (int channel_num = 0; channel_num < pixelInfo<WT>::channels; channel_num++)
weights_sum[channel_num] = 0;
for (int y = 0; y < search_window_size_; y++)
{
const T* cur_row_ptr = extended_src_.ptr<T>(border_size_ + search_window_y + y);
int* dist_sums_row = dist_sums.row_ptr(y);
for (int x = 0; x < search_window_size_; x++)
{
int almostAvgDist = dist_sums_row[x] >> almost_template_window_size_sq_bin_shift_;
WT weight = almost_dist2weight_[almostAvgDist];
T p = cur_row_ptr[border_size_ + search_window_x + x];
incWithWeight<T, IT, WT>(estimation, weights_sum, weight, p);
}
}
divByWeightsSum<IT, UIT, pixelInfo<T>::channels, pixelInfo<WT>::channels>(estimation,
weights_sum);
dst_.at<T>(i,j) = saturateCastFromArray<T, IT>(estimation);
}
}
}
template <typename T, typename IT, typename UIT, typename D, typename WT>
inline void FastNlMeansDenoisingInvoker<T, IT, UIT, D, WT>::calcDistSumsForFirstElementInRow(
int i,
Array2d<int>& dist_sums,
Array3d<int>& col_dist_sums,
Array3d<int>& up_col_dist_sums) const
{
int j = 0;
for (int y = 0; y < search_window_size_; y++)
for (int x = 0; x < search_window_size_; x++)
{
dist_sums[y][x] = 0;
for (int tx = 0; tx < template_window_size_; tx++)
col_dist_sums[tx][y][x] = 0;
int start_y = i + y - search_window_half_size_;
int start_x = j + x - search_window_half_size_;
for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++)
for (int tx = -template_window_half_size_; tx <= template_window_half_size_; tx++)
{
int dist = D::template calcDist<T>(extended_src_,
border_size_ + i + ty, border_size_ + j + tx,
border_size_ + start_y + ty, border_size_ + start_x + tx);
dist_sums[y][x] += dist;
col_dist_sums[tx + template_window_half_size_][y][x] += dist;
}
up_col_dist_sums[j][y][x] = col_dist_sums[template_window_size_ - 1][y][x];
}
}
template <typename T, typename IT, typename UIT, typename D, typename WT>
inline void FastNlMeansDenoisingInvoker<T, IT, UIT, D, WT>::calcDistSumsForElementInFirstRow(
int i, int j, int first_col_num,
Array2d<int>& dist_sums,
Array3d<int>& col_dist_sums,
Array3d<int>& up_col_dist_sums) const
{
int ay = border_size_ + i;
int ax = border_size_ + j + template_window_half_size_;
int start_by = border_size_ + i - search_window_half_size_;
int start_bx = border_size_ + j - search_window_half_size_ + template_window_half_size_;
int new_last_col_num = first_col_num;
for (int y = 0; y < search_window_size_; y++)
for (int x = 0; x < search_window_size_; x++)
{
dist_sums[y][x] -= col_dist_sums[first_col_num][y][x];
col_dist_sums[new_last_col_num][y][x] = 0;
int by = start_by + y;
int bx = start_bx + x;
for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++)
col_dist_sums[new_last_col_num][y][x] += D::template calcDist<T>(extended_src_, ay + ty, ax, by + ty, bx);
dist_sums[y][x] += col_dist_sums[new_last_col_num][y][x];
up_col_dist_sums[j][y][x] = col_dist_sums[new_last_col_num][y][x];
}
}
#endif

View File

@ -0,0 +1,489 @@
/*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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective icvers.
//
// 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 Intel Corporation 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_FAST_NLMEANS_DENOISING_INVOKER_COMMONS_HPP__
#define __OPENCV_FAST_NLMEANS_DENOISING_INVOKER_COMMONS_HPP__
using namespace cv;
// std::isnan is a part of C++11 and it is not supported in MSVS2010/2012
#if defined _MSC_VER && _MSC_VER < 1800 /* MSVC 2013 */
#include <float.h>
namespace std {
template <typename T> bool isnan(T value) { return _isnan(value) != 0; }
}
#endif
template <typename T> struct pixelInfo_
{
static const int channels = 1;
typedef T sampleType;
};
template <typename ET, int n> struct pixelInfo_<Vec<ET, n> >
{
static const int channels = n;
typedef ET sampleType;
};
template <typename T> struct pixelInfo: public pixelInfo_<T>
{
typedef typename pixelInfo_<T>::sampleType sampleType;
static inline sampleType sampleMax()
{
return std::numeric_limits<sampleType>::max();
}
static inline sampleType sampleMin()
{
return std::numeric_limits<sampleType>::min();
}
static inline size_t sampleBytes()
{
return sizeof(sampleType);
}
static inline size_t sampleBits()
{
return 8*sampleBytes();
}
};
class DistAbs
{
template <typename T> struct calcDist_
{
static inline int f(const T a, const T b)
{
return std::abs((int)(a-b));
}
};
template <typename ET> struct calcDist_<Vec<ET, 2> >
{
static inline int f(const Vec<ET, 2> a, const Vec<ET, 2> b)
{
return std::abs((int)(a[0]-b[0])) + std::abs((int)(a[1]-b[1]));
}
};
template <typename ET> struct calcDist_<Vec<ET, 3> >
{
static inline int f(const Vec<ET, 3> a, const Vec<ET, 3> b)
{
return
std::abs((int)(a[0]-b[0])) +
std::abs((int)(a[1]-b[1])) +
std::abs((int)(a[2]-b[2]));
}
};
template <typename ET> struct calcDist_<Vec<ET, 4> >
{
static inline int f(const Vec<ET, 4> a, const Vec<ET, 4> b)
{
return
std::abs((int)(a[0]-b[0])) +
std::abs((int)(a[1]-b[1])) +
std::abs((int)(a[2]-b[2])) +
std::abs((int)(a[3]-b[3]));
}
};
template <typename T, typename WT> struct calcWeight_
{
static inline WT f(double dist, const float *h, WT fixed_point_mult)
{
double w = std::exp(-dist*dist / (h[0]*h[0] * pixelInfo<T>::channels));
if (std::isnan(w)) w = 1.0; // Handle h = 0.0
static const double WEIGHT_THRESHOLD = 0.001;
WT weight = (WT)cvRound(fixed_point_mult * w);
if (weight < WEIGHT_THRESHOLD * fixed_point_mult) weight = 0;
return weight;
}
};
template <typename T, typename ET, int n> struct calcWeight_<T, Vec<ET, n> >
{
static inline Vec<ET, n> f(double dist, const float *h, ET fixed_point_mult)
{
Vec<ET, n> res;
for (int i=0; i<n; i++)
res[i] = calcWeight<T, ET>(dist, &h[i], fixed_point_mult);
return res;
}
};
public:
template <typename T> static inline int calcDist(const T a, const T b)
{
return calcDist_<T>::f(a, b);
}
template <typename T>
static inline int calcDist(const Mat& m, int i1, int j1, int i2, int j2)
{
const T a = m.at<T>(i1, j1);
const T b = m.at<T>(i2, j2);
return calcDist<T>(a,b);
}
template <typename T>
static inline int calcUpDownDist(T a_up, T a_down, T b_up, T b_down)
{
return calcDist<T>(a_down, b_down) - calcDist<T>(a_up, b_up);
};
template <typename T, typename WT>
static inline WT calcWeight(double dist, const float *h,
typename pixelInfo<WT>::sampleType fixed_point_mult)
{
return calcWeight_<T, WT>::f(dist, h, fixed_point_mult);
}
template <typename T>
static inline int maxDist()
{
return (int)pixelInfo<T>::sampleMax() * pixelInfo<T>::channels;
}
};
class DistSquared
{
template <typename T> struct calcDist_
{
static inline int f(const T a, const T b)
{
return (int)(a-b) * (int)(a-b);
}
};
template <typename ET> struct calcDist_<Vec<ET, 2> >
{
static inline int f(const Vec<ET, 2> a, const Vec<ET, 2> b)
{
return (int)(a[0]-b[0])*(int)(a[0]-b[0]) + (int)(a[1]-b[1])*(int)(a[1]-b[1]);
}
};
template <typename ET> struct calcDist_<Vec<ET, 3> >
{
static inline int f(const Vec<ET, 3> a, const Vec<ET, 3> b)
{
return
(int)(a[0]-b[0])*(int)(a[0]-b[0]) +
(int)(a[1]-b[1])*(int)(a[1]-b[1]) +
(int)(a[2]-b[2])*(int)(a[2]-b[2]);
}
};
template <typename ET> struct calcDist_<Vec<ET, 4> >
{
static inline int f(const Vec<ET, 4> a, const Vec<ET, 4> b)
{
return
(int)(a[0]-b[0])*(int)(a[0]-b[0]) +
(int)(a[1]-b[1])*(int)(a[1]-b[1]) +
(int)(a[2]-b[2])*(int)(a[2]-b[2]) +
(int)(a[3]-b[3])*(int)(a[3]-b[3]);
}
};
template <typename T> struct calcUpDownDist_
{
static inline int f(T a_up, T a_down, T b_up, T b_down)
{
int A = a_down - b_down;
int B = a_up - b_up;
return (A-B)*(A+B);
}
};
template <typename ET, int n> struct calcUpDownDist_<Vec<ET, n> >
{
private:
typedef Vec<ET, n> T;
public:
static inline int f(T a_up, T a_down, T b_up, T b_down)
{
return calcDist<T>(a_down, b_down) - calcDist<T>(a_up, b_up);
}
};
template <typename T, typename WT> struct calcWeight_
{
static inline WT f(double dist, const float *h, WT fixed_point_mult)
{
double w = std::exp(-dist / (h[0]*h[0] * pixelInfo<T>::channels));
if (std::isnan(w)) w = 1.0; // Handle h = 0.0
static const double WEIGHT_THRESHOLD = 0.001;
WT weight = (WT)cvRound(fixed_point_mult * w);
if (weight < WEIGHT_THRESHOLD * fixed_point_mult) weight = 0;
return weight;
}
};
template <typename T, typename ET, int n> struct calcWeight_<T, Vec<ET, n> >
{
static inline Vec<ET, n> f(double dist, const float *h, ET fixed_point_mult)
{
Vec<ET, n> res;
for (int i=0; i<n; i++)
res[i] = calcWeight<T, ET>(dist, &h[i], fixed_point_mult);
return res;
}
};
public:
template <typename T> static inline int calcDist(const T a, const T b)
{
return calcDist_<T>::f(a, b);
}
template <typename T>
static inline int calcDist(const Mat& m, int i1, int j1, int i2, int j2)
{
const T a = m.at<T>(i1, j1);
const T b = m.at<T>(i2, j2);
return calcDist<T>(a,b);
}
template <typename T>
static inline int calcUpDownDist(T a_up, T a_down, T b_up, T b_down)
{
return calcUpDownDist_<T>::f(a_up, a_down, b_up, b_down);
};
template <typename T, typename WT>
static inline WT calcWeight(double dist, const float *h,
typename pixelInfo<WT>::sampleType fixed_point_mult)
{
return calcWeight_<T, WT>::f(dist, h, fixed_point_mult);
}
template <typename T>
static inline int maxDist()
{
return (int)pixelInfo<T>::sampleMax() * (int)pixelInfo<T>::sampleMax() *
pixelInfo<T>::channels;
}
};
template <typename T, typename IT, typename WT> struct incWithWeight_
{
static inline void f(IT* estimation, IT* weights_sum, WT weight, T p)
{
estimation[0] += (IT)weight * p;
weights_sum[0] += (IT)weight;
}
};
template <typename ET, typename IT, typename WT> struct incWithWeight_<Vec<ET, 2>, IT, WT>
{
static inline void f(IT* estimation, IT* weights_sum, WT weight, Vec<ET, 2> p)
{
estimation[0] += (IT)weight * p[0];
estimation[1] += (IT)weight * p[1];
weights_sum[0] += (IT)weight;
}
};
template <typename ET, typename IT, typename WT> struct incWithWeight_<Vec<ET, 3>, IT, WT>
{
static inline void f(IT* estimation, IT* weights_sum, WT weight, Vec<ET, 3> p)
{
estimation[0] += (IT)weight * p[0];
estimation[1] += (IT)weight * p[1];
estimation[2] += (IT)weight * p[2];
weights_sum[0] += (IT)weight;
}
};
template <typename ET, typename IT, typename WT> struct incWithWeight_<Vec<ET, 4>, IT, WT>
{
static inline void f(IT* estimation, IT* weights_sum, WT weight, Vec<ET, 4> p)
{
estimation[0] += (IT)weight * p[0];
estimation[1] += (IT)weight * p[1];
estimation[2] += (IT)weight * p[2];
estimation[3] += (IT)weight * p[3];
weights_sum[0] += (IT)weight;
}
};
template <typename ET, typename IT, typename EW> struct incWithWeight_<Vec<ET, 2>, IT, Vec<EW, 2> >
{
static inline void f(IT* estimation, IT* weights_sum, Vec<EW, 2> weight, Vec<ET, 2> p)
{
estimation[0] += (IT)weight[0] * p[0];
estimation[1] += (IT)weight[1] * p[1];
weights_sum[0] += (IT)weight[0];
weights_sum[1] += (IT)weight[1];
}
};
template <typename ET, typename IT, typename EW> struct incWithWeight_<Vec<ET, 3>, IT, Vec<EW, 3> >
{
static inline void f(IT* estimation, IT* weights_sum, Vec<EW, 3> weight, Vec<ET, 3> p)
{
estimation[0] += (IT)weight[0] * p[0];
estimation[1] += (IT)weight[1] * p[1];
estimation[2] += (IT)weight[2] * p[2];
weights_sum[0] += (IT)weight[0];
weights_sum[1] += (IT)weight[1];
weights_sum[2] += (IT)weight[2];
}
};
template <typename ET, typename IT, typename EW> struct incWithWeight_<Vec<ET, 4>, IT, Vec<EW, 4> >
{
static inline void f(IT* estimation, IT* weights_sum, Vec<EW, 4> weight, Vec<ET, 4> p)
{
estimation[0] += (IT)weight[0] * p[0];
estimation[1] += (IT)weight[1] * p[1];
estimation[2] += (IT)weight[2] * p[2];
estimation[3] += (IT)weight[3] * p[3];
weights_sum[0] += (IT)weight[0];
weights_sum[1] += (IT)weight[1];
weights_sum[2] += (IT)weight[2];
weights_sum[3] += (IT)weight[3];
}
};
template <typename T, typename IT, typename WT>
static inline void incWithWeight(IT* estimation, IT* weights_sum, WT weight, T p)
{
incWithWeight_<T, IT, WT>::f(estimation, weights_sum, weight, p);
}
template <typename IT, typename UIT, int nc, int nw> struct divByWeightsSum_
{
static inline void f(IT* estimation, IT* weights_sum);
};
template <typename IT, typename UIT> struct divByWeightsSum_<IT, UIT, 1, 1>
{
static inline void f(IT* estimation, IT* weights_sum)
{
estimation[0] = (static_cast<UIT>(estimation[0]) + weights_sum[0]/2) / weights_sum[0];
}
};
template <typename IT, typename UIT, int n> struct divByWeightsSum_<IT, UIT, n, 1>
{
static inline void f(IT* estimation, IT* weights_sum)
{
for (size_t i = 0; i < n; i++)
estimation[i] = (static_cast<UIT>(estimation[i]) + weights_sum[0]/2) / weights_sum[0];
}
};
template <typename IT, typename UIT, int n> struct divByWeightsSum_<IT, UIT, n, n>
{
static inline void f(IT* estimation, IT* weights_sum)
{
for (size_t i = 0; i < n; i++)
estimation[i] = (static_cast<UIT>(estimation[i]) + weights_sum[i]/2) / weights_sum[i];
}
};
template <typename IT, typename UIT, int nc, int nw>
static inline void divByWeightsSum(IT* estimation, IT* weights_sum)
{
divByWeightsSum_<IT, UIT, nc, nw>::f(estimation, weights_sum);
}
template <typename T, typename IT> struct saturateCastFromArray_
{
static inline T f(IT* estimation)
{
return saturate_cast<T>(estimation[0]);
}
};
template <typename ET, typename IT> struct saturateCastFromArray_<Vec<ET, 2>, IT>
{
static inline Vec<ET, 2> f(IT* estimation)
{
Vec<ET, 2> res;
res[0] = saturate_cast<ET>(estimation[0]);
res[1] = saturate_cast<ET>(estimation[1]);
return res;
}
};
template <typename ET, typename IT> struct saturateCastFromArray_<Vec<ET, 3>, IT>
{
static inline Vec<ET, 3> f(IT* estimation)
{
Vec<ET, 3> res;
res[0] = saturate_cast<ET>(estimation[0]);
res[1] = saturate_cast<ET>(estimation[1]);
res[2] = saturate_cast<ET>(estimation[2]);
return res;
}
};
template <typename ET, typename IT> struct saturateCastFromArray_<Vec<ET, 4>, IT>
{
static inline Vec<ET, 4> f(IT* estimation)
{
Vec<ET, 4> res;
res[0] = saturate_cast<ET>(estimation[0]);
res[1] = saturate_cast<ET>(estimation[1]);
res[2] = saturate_cast<ET>(estimation[2]);
res[3] = saturate_cast<ET>(estimation[3]);
return res;
}
};
template <typename T, typename IT> static inline T saturateCastFromArray(IT* estimation)
{
return saturateCastFromArray_<T, IT>::f(estimation);
}
#endif

View File

@ -0,0 +1,217 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
#include "precomp.hpp"
#ifndef __OPENCV_FAST_NLMEANS_DENOISING_OPENCL_HPP__
#define __OPENCV_FAST_NLMEANS_DENOISING_OPENCL_HPP__
#include "opencl_kernels_photo.hpp"
#ifdef HAVE_OPENCL
namespace cv {
enum
{
BLOCK_ROWS = 32,
BLOCK_COLS = 32,
CTA_SIZE_INTEL = 64,
CTA_SIZE_DEFAULT = 256
};
template <typename FT, typename ST, typename WT>
static bool ocl_calcAlmostDist2Weight(UMat & almostDist2Weight,
int searchWindowSize, int templateWindowSize,
const FT *h, int hn, int cn, int normType,
int & almostTemplateWindowSizeSqBinShift)
{
const WT maxEstimateSumValue = searchWindowSize * searchWindowSize *
std::numeric_limits<ST>::max();
int fixedPointMult = (int)std::min<WT>(std::numeric_limits<WT>::max() / maxEstimateSumValue,
std::numeric_limits<int>::max());
int depth = DataType<FT>::depth;
bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0;
if (depth == CV_64F && !doubleSupport)
return false;
// precalc weight for every possible l2 dist between blocks
// additional optimization of precalced weights to replace division(averaging) by binary shift
CV_Assert(templateWindowSize <= 46340); // sqrt(INT_MAX)
int templateWindowSizeSq = templateWindowSize * templateWindowSize;
almostTemplateWindowSizeSqBinShift = getNearestPowerOf2(templateWindowSizeSq);
FT almostDist2ActualDistMultiplier = (FT)(1 << almostTemplateWindowSizeSqBinShift) / templateWindowSizeSq;
const FT WEIGHT_THRESHOLD = 1e-3f;
WT maxDist = normType == NORM_L1 ? (WT)std::numeric_limits<ST>::max() * cn :
(WT)std::numeric_limits<ST>::max() * (WT)std::numeric_limits<ST>::max() * cn;
int almostMaxDist = (int)(maxDist / almostDist2ActualDistMultiplier + 1);
FT den[4];
CV_Assert(hn > 0 && hn <= 4);
for (int i=0; i<hn; i++)
den[i] = 1.0f / (h[i] * h[i] * cn);
almostDist2Weight.create(1, almostMaxDist, CV_32SC(hn == 3 ? 4 : hn));
char buf[40];
ocl::Kernel k("calcAlmostDist2Weight", ocl::photo::nlmeans_oclsrc,
format("-D OP_CALC_WEIGHTS -D FT=%s -D w_t=%s"
" -D wlut_t=%s -D convert_wlut_t=%s%s%s",
ocl::typeToStr(depth), ocl::typeToStr(CV_MAKE_TYPE(depth, hn)),
ocl::typeToStr(CV_32SC(hn)), ocl::convertTypeStr(depth, CV_32S, hn, buf),
doubleSupport ? " -D DOUBLE_SUPPORT" : "",
normType == NORM_L1 ? " -D ABS" : ""));
if (k.empty())
return false;
k.args(ocl::KernelArg::PtrWriteOnly(almostDist2Weight), almostMaxDist,
almostDist2ActualDistMultiplier, fixedPointMult,
ocl::KernelArg::Constant(den, (hn == 3 ? 4 : hn)*sizeof(FT)), WEIGHT_THRESHOLD);
size_t globalsize[1] = { (size_t)almostMaxDist };
return k.run(1, globalsize, NULL, false);
}
static bool ocl_fastNlMeansDenoising(InputArray _src, OutputArray _dst, const float *h, int hn,
int templateWindowSize, int searchWindowSize, int normType)
{
int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
int ctaSize = ocl::Device::getDefault().isIntel() ? CTA_SIZE_INTEL : CTA_SIZE_DEFAULT;
Size size = _src.size();
if (cn < 1 || cn > 4 || ((normType != NORM_L2 || depth != CV_8U) &&
(normType != NORM_L1 || (depth != CV_8U && depth != CV_16U))))
return false;
int templateWindowHalfWize = templateWindowSize / 2;
int searchWindowHalfSize = searchWindowSize / 2;
templateWindowSize = templateWindowHalfWize * 2 + 1;
searchWindowSize = searchWindowHalfSize * 2 + 1;
int nblocksx = divUp(size.width, BLOCK_COLS), nblocksy = divUp(size.height, BLOCK_ROWS);
int almostTemplateWindowSizeSqBinShift = -1;
char buf[4][40];
const unsigned psz = (depth == CV_8U ? sizeof(uchar) : sizeof(ushort)) * (cn == 3 ? 4 : cn);
String opts = format("-D OP_CALC_FASTNLMEANS -D TEMPLATE_SIZE=%d -D SEARCH_SIZE=%d"
" -D pixel_t=%s -D int_t=%s -D wlut_t=%s"
" -D weight_t=%s -D convert_weight_t=%s -D sum_t=%s -D convert_sum_t=%s"
" -D BLOCK_COLS=%d -D BLOCK_ROWS=%d"
" -D CTA_SIZE=%d -D TEMPLATE_SIZE2=%d -D SEARCH_SIZE2=%d"
" -D convert_int_t=%s -D cn=%d -D psz=%u -D convert_pixel_t=%s%s",
templateWindowSize, searchWindowSize,
ocl::typeToStr(type), ocl::typeToStr(CV_32SC(cn)),
ocl::typeToStr(CV_32SC(hn)),
depth == CV_8U ? ocl::typeToStr(CV_32SC(hn)) :
format("long%s", hn > 1 ? format("%d", hn).c_str() : "").c_str(),
depth == CV_8U ? ocl::convertTypeStr(CV_32S, CV_32S, hn, buf[0]) :
format("convert_long%s", hn > 1 ? format("%d", hn).c_str() : "").c_str(),
depth == CV_8U ? ocl::typeToStr(CV_32SC(cn)) :
format("long%s", cn > 1 ? format("%d", cn).c_str() : "").c_str(),
depth == CV_8U ? ocl::convertTypeStr(depth, CV_32S, cn, buf[1]) :
format("convert_long%s", cn > 1 ? format("%d", cn).c_str() : "").c_str(),
BLOCK_COLS, BLOCK_ROWS,
ctaSize, templateWindowHalfWize, searchWindowHalfSize,
ocl::convertTypeStr(depth, CV_32S, cn, buf[2]), cn,
psz,
ocl::convertTypeStr(CV_32S, depth, cn, buf[3]),
normType == NORM_L1 ? " -D ABS" : "");
ocl::Kernel k("fastNlMeansDenoising", ocl::photo::nlmeans_oclsrc, opts);
if (k.empty())
return false;
UMat almostDist2Weight;
if ((depth == CV_8U &&
!ocl_calcAlmostDist2Weight<float, uchar, int>(almostDist2Weight,
searchWindowSize, templateWindowSize,
h, hn, cn, normType,
almostTemplateWindowSizeSqBinShift)) ||
(depth == CV_16U &&
!ocl_calcAlmostDist2Weight<float, ushort, int64>(almostDist2Weight,
searchWindowSize, templateWindowSize,
h, hn, cn, normType,
almostTemplateWindowSizeSqBinShift)))
return false;
CV_Assert(almostTemplateWindowSizeSqBinShift >= 0);
UMat srcex;
int borderSize = searchWindowHalfSize + templateWindowHalfWize;
if (cn == 3) {
srcex.create(size.height + 2*borderSize, size.width + 2*borderSize, CV_MAKE_TYPE(depth, 4));
UMat src(srcex, Rect(borderSize, borderSize, size.width, size.height));
int from_to[] = { 0,0, 1,1, 2,2 };
mixChannels(std::vector<UMat>(1, _src.getUMat()), std::vector<UMat>(1, src), from_to, 3);
copyMakeBorder(src, srcex, borderSize, borderSize, borderSize, borderSize,
BORDER_DEFAULT|BORDER_ISOLATED); // create borders in place
}
else
copyMakeBorder(_src, srcex, borderSize, borderSize, borderSize, borderSize, BORDER_DEFAULT);
_dst.create(size, type);
UMat dst;
if (cn == 3)
dst.create(size, CV_MAKE_TYPE(depth, 4));
else
dst = _dst.getUMat();
int searchWindowSizeSq = searchWindowSize * searchWindowSize;
Size upColSumSize(size.width, searchWindowSizeSq * nblocksy);
Size colSumSize(nblocksx * templateWindowSize, searchWindowSizeSq * nblocksy);
UMat buffer(upColSumSize + colSumSize, CV_32SC(cn));
srcex = srcex(Rect(Point(borderSize, borderSize), size));
k.args(ocl::KernelArg::ReadOnlyNoSize(srcex), ocl::KernelArg::WriteOnly(dst),
ocl::KernelArg::PtrReadOnly(almostDist2Weight),
ocl::KernelArg::PtrReadOnly(buffer), almostTemplateWindowSizeSqBinShift);
size_t globalsize[2] = { (size_t)nblocksx * ctaSize, (size_t)nblocksy }, localsize[2] = { (size_t)ctaSize, 1 };
if (!k.run(2, globalsize, localsize, false)) return false;
if (cn == 3) {
int from_to[] = { 0,0, 1,1, 2,2 };
mixChannels(std::vector<UMat>(1, dst), std::vector<UMat>(1, _dst.getUMat()), from_to, 3);
}
return true;
}
static bool ocl_fastNlMeansDenoisingColored( InputArray _src, OutputArray _dst,
float h, float hForColorComponents,
int templateWindowSize, int searchWindowSize)
{
UMat src = _src.getUMat();
_dst.create(src.size(), src.type());
UMat dst = _dst.getUMat();
UMat src_lab;
cvtColor(src, src_lab, COLOR_LBGR2Lab);
UMat l(src.size(), CV_8U);
UMat ab(src.size(), CV_8UC2);
std::vector<UMat> l_ab(2), l_ab_denoised(2);
l_ab[0] = l;
l_ab[1] = ab;
l_ab_denoised[0].create(src.size(), CV_8U);
l_ab_denoised[1].create(src.size(), CV_8UC2);
int from_to[] = { 0,0, 1,1, 2,2 };
mixChannels(std::vector<UMat>(1, src_lab), l_ab, from_to, 3);
fastNlMeansDenoising(l_ab[0], l_ab_denoised[0], h, templateWindowSize, searchWindowSize);
fastNlMeansDenoising(l_ab[1], l_ab_denoised[1], hForColorComponents, templateWindowSize, searchWindowSize);
UMat dst_lab(src.size(), CV_8UC3);
mixChannels(l_ab_denoised, std::vector<UMat>(1, dst_lab), from_to, 3);
cvtColor(dst_lab, dst, COLOR_Lab2LBGR, src.channels());
return true;
}
}
#endif
#endif

View File

@ -0,0 +1,360 @@
/*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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective icvers.
//
// 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 Intel Corporation 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_FAST_NLMEANS_MULTI_DENOISING_INVOKER_HPP__
#define __OPENCV_FAST_NLMEANS_MULTI_DENOISING_INVOKER_HPP__
#include "precomp.hpp"
#include <limits>
#include "fast_nlmeans_denoising_invoker_commons.hpp"
#include "arrays.hpp"
using namespace cv;
template <typename T, typename IT, typename UIT, typename D, typename WT>
struct FastNlMeansMultiDenoisingInvoker :
ParallelLoopBody
{
public:
FastNlMeansMultiDenoisingInvoker(const std::vector<Mat>& srcImgs, int imgToDenoiseIndex,
int temporalWindowSize, Mat& dst, int template_window_size,
int search_window_size, const float *h);
void operator() (const Range& range) const CV_OVERRIDE;
private:
void operator= (const FastNlMeansMultiDenoisingInvoker&);
int rows_;
int cols_;
Mat& dst_;
std::vector<Mat> extended_srcs_;
Mat main_extended_src_;
int border_size_;
int template_window_size_;
int search_window_size_;
int temporal_window_size_;
int template_window_half_size_;
int search_window_half_size_;
int temporal_window_half_size_;
typename pixelInfo<WT>::sampleType fixed_point_mult_;
int almost_template_window_size_sq_bin_shift;
std::vector<WT> almost_dist2weight;
void calcDistSumsForFirstElementInRow(int i, Array3d<int>& dist_sums,
Array4d<int>& col_dist_sums,
Array4d<int>& up_col_dist_sums) const;
void calcDistSumsForElementInFirstRow(int i, int j, int first_col_num,
Array3d<int>& dist_sums, Array4d<int>& col_dist_sums,
Array4d<int>& up_col_dist_sums) const;
};
template <typename T, typename IT, typename UIT, typename D, typename WT>
FastNlMeansMultiDenoisingInvoker<T, IT, UIT, D, WT>::FastNlMeansMultiDenoisingInvoker(
const std::vector<Mat>& srcImgs,
int imgToDenoiseIndex,
int temporalWindowSize,
cv::Mat& dst,
int template_window_size,
int search_window_size,
const float *h) :
dst_(dst), extended_srcs_(srcImgs.size())
{
CV_Assert(srcImgs.size() > 0);
CV_Assert(srcImgs[0].channels() == pixelInfo<T>::channels);
rows_ = srcImgs[0].rows;
cols_ = srcImgs[0].cols;
template_window_half_size_ = template_window_size / 2;
search_window_half_size_ = search_window_size / 2;
temporal_window_half_size_ = temporalWindowSize / 2;
template_window_size_ = template_window_half_size_ * 2 + 1;
search_window_size_ = search_window_half_size_ * 2 + 1;
temporal_window_size_ = temporal_window_half_size_ * 2 + 1;
border_size_ = search_window_half_size_ + template_window_half_size_;
for (int i = 0; i < temporal_window_size_; i++)
copyMakeBorder(srcImgs[imgToDenoiseIndex - temporal_window_half_size_ + i], extended_srcs_[i],
border_size_, border_size_, border_size_, border_size_, cv::BORDER_DEFAULT);
main_extended_src_ = extended_srcs_[temporal_window_half_size_];
const IT max_estimate_sum_value =
(IT)temporal_window_size_ * (IT)search_window_size_ * (IT)search_window_size_ * (IT)pixelInfo<T>::sampleMax();
fixed_point_mult_ = (int)std::min<IT>(std::numeric_limits<IT>::max() / max_estimate_sum_value,
pixelInfo<WT>::sampleMax());
// precalc weight for every possible l2 dist between blocks
// additional optimization of precalced weights to replace division(averaging) by binary shift
int template_window_size_sq = template_window_size_ * template_window_size_;
almost_template_window_size_sq_bin_shift = 0;
while (1 << almost_template_window_size_sq_bin_shift < template_window_size_sq)
almost_template_window_size_sq_bin_shift++;
int almost_template_window_size_sq = 1 << almost_template_window_size_sq_bin_shift;
double almost_dist2actual_dist_multiplier = (double) almost_template_window_size_sq / template_window_size_sq;
int max_dist = D::template maxDist<T>();
int almost_max_dist = (int)(max_dist / almost_dist2actual_dist_multiplier + 1);
almost_dist2weight.resize(almost_max_dist);
for (int almost_dist = 0; almost_dist < almost_max_dist; almost_dist++)
{
double dist = almost_dist * almost_dist2actual_dist_multiplier;
almost_dist2weight[almost_dist] =
D::template calcWeight<T, WT>(dist, h, fixed_point_mult_);
}
// additional optimization init end
if (dst_.empty())
dst_ = Mat::zeros(srcImgs[0].size(), srcImgs[0].type());
}
template <typename T, typename IT, typename UIT, typename D, typename WT>
void FastNlMeansMultiDenoisingInvoker<T, IT, UIT, D, WT>::operator() (const Range& range) const
{
int row_from = range.start;
int row_to = range.end - 1;
Array3d<int> dist_sums(temporal_window_size_, search_window_size_, search_window_size_);
// for lazy calc optimization
Array4d<int> col_dist_sums(template_window_size_, temporal_window_size_, search_window_size_, search_window_size_);
int first_col_num = -1;
Array4d<int> up_col_dist_sums(cols_, temporal_window_size_, search_window_size_, search_window_size_);
for (int i = row_from; i <= row_to; i++)
{
for (int j = 0; j < cols_; j++)
{
int search_window_y = i - search_window_half_size_;
int search_window_x = j - search_window_half_size_;
// calc dist_sums
if (j == 0)
{
calcDistSumsForFirstElementInRow(i, dist_sums, col_dist_sums, up_col_dist_sums);
first_col_num = 0;
}
else
{
// calc cur dist_sums using previous dist_sums
if (i == row_from)
{
calcDistSumsForElementInFirstRow(i, j, first_col_num,
dist_sums, col_dist_sums, up_col_dist_sums);
}
else
{
int ay = border_size_ + i;
int ax = border_size_ + j + template_window_half_size_;
int start_by =
border_size_ + i - search_window_half_size_;
int start_bx =
border_size_ + j - search_window_half_size_ + template_window_half_size_;
T a_up = main_extended_src_.at<T>(ay - template_window_half_size_ - 1, ax);
T a_down = main_extended_src_.at<T>(ay + template_window_half_size_, ax);
// copy class member to local variable for optimization
int search_window_size = search_window_size_;
for (int d = 0; d < temporal_window_size_; d++)
{
Mat cur_extended_src = extended_srcs_[d];
Array2d<int> cur_dist_sums = dist_sums[d];
Array2d<int> cur_col_dist_sums = col_dist_sums[first_col_num][d];
Array2d<int> cur_up_col_dist_sums = up_col_dist_sums[j][d];
for (int y = 0; y < search_window_size; y++)
{
int* dist_sums_row = cur_dist_sums.row_ptr(y);
int* col_dist_sums_row = cur_col_dist_sums.row_ptr(y);
int* up_col_dist_sums_row = cur_up_col_dist_sums.row_ptr(y);
const T* b_up_ptr = cur_extended_src.ptr<T>(start_by - template_window_half_size_ - 1 + y);
const T* b_down_ptr = cur_extended_src.ptr<T>(start_by + template_window_half_size_ + y);
for (int x = 0; x < search_window_size; x++)
{
dist_sums_row[x] -= col_dist_sums_row[x];
col_dist_sums_row[x] = up_col_dist_sums_row[x] +
D::template calcUpDownDist<T>(a_up, a_down, b_up_ptr[start_bx + x], b_down_ptr[start_bx + x]);
dist_sums_row[x] += col_dist_sums_row[x];
up_col_dist_sums_row[x] = col_dist_sums_row[x];
}
}
}
}
first_col_num = (first_col_num + 1) % template_window_size_;
}
// calc weights
IT estimation[pixelInfo<T>::channels], weights_sum[pixelInfo<WT>::channels];
for (size_t channel_num = 0; channel_num < pixelInfo<T>::channels; channel_num++)
estimation[channel_num] = 0;
for (size_t channel_num = 0; channel_num < pixelInfo<WT>::channels; channel_num++)
weights_sum[channel_num] = 0;
for (int d = 0; d < temporal_window_size_; d++)
{
const Mat& esrc_d = extended_srcs_[d];
for (int y = 0; y < search_window_size_; y++)
{
const T* cur_row_ptr = esrc_d.ptr<T>(border_size_ + search_window_y + y);
int* dist_sums_row = dist_sums.row_ptr(d, y);
for (int x = 0; x < search_window_size_; x++)
{
int almostAvgDist = dist_sums_row[x] >> almost_template_window_size_sq_bin_shift;
WT weight = almost_dist2weight[almostAvgDist];
T p = cur_row_ptr[border_size_ + search_window_x + x];
incWithWeight<T, IT, WT>(estimation, weights_sum, weight, p);
}
}
}
divByWeightsSum<IT, UIT, pixelInfo<T>::channels, pixelInfo<WT>::channels>(estimation,
weights_sum);
dst_.at<T>(i,j) = saturateCastFromArray<T, IT>(estimation);
}
}
}
template <typename T, typename IT, typename UIT, typename D, typename WT>
inline void FastNlMeansMultiDenoisingInvoker<T, IT, UIT, D, WT>::calcDistSumsForFirstElementInRow(
int i, Array3d<int>& dist_sums, Array4d<int>& col_dist_sums, Array4d<int>& up_col_dist_sums) const
{
int j = 0;
for (int d = 0; d < temporal_window_size_; d++)
{
Mat cur_extended_src = extended_srcs_[d];
for (int y = 0; y < search_window_size_; y++)
for (int x = 0; x < search_window_size_; x++)
{
dist_sums[d][y][x] = 0;
for (int tx = 0; tx < template_window_size_; tx++)
col_dist_sums[tx][d][y][x] = 0;
int start_y = i + y - search_window_half_size_;
int start_x = j + x - search_window_half_size_;
int* dist_sums_ptr = &dist_sums[d][y][x];
int* col_dist_sums_ptr = &col_dist_sums[0][d][y][x];
int col_dist_sums_step = col_dist_sums.step_size(0);
for (int tx = -template_window_half_size_; tx <= template_window_half_size_; tx++)
{
for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++)
{
int dist = D::template calcDist<T>(
main_extended_src_.at<T>(border_size_ + i + ty, border_size_ + j + tx),
cur_extended_src.at<T>(border_size_ + start_y + ty, border_size_ + start_x + tx));
*dist_sums_ptr += dist;
*col_dist_sums_ptr += dist;
}
col_dist_sums_ptr += col_dist_sums_step;
}
up_col_dist_sums[j][d][y][x] = col_dist_sums[template_window_size_ - 1][d][y][x];
}
}
}
template <typename T, typename IT, typename UIT, typename D, typename WT>
inline void FastNlMeansMultiDenoisingInvoker<T, IT, UIT, D, WT>::calcDistSumsForElementInFirstRow(
int i, int j, int first_col_num, Array3d<int>& dist_sums,
Array4d<int>& col_dist_sums, Array4d<int>& up_col_dist_sums) const
{
int ay = border_size_ + i;
int ax = border_size_ + j + template_window_half_size_;
int start_by = border_size_ + i - search_window_half_size_;
int start_bx = border_size_ + j - search_window_half_size_ + template_window_half_size_;
int new_last_col_num = first_col_num;
for (int d = 0; d < temporal_window_size_; d++)
{
Mat cur_extended_src = extended_srcs_[d];
for (int y = 0; y < search_window_size_; y++)
for (int x = 0; x < search_window_size_; x++)
{
dist_sums[d][y][x] -= col_dist_sums[first_col_num][d][y][x];
col_dist_sums[new_last_col_num][d][y][x] = 0;
int by = start_by + y;
int bx = start_bx + x;
int* col_dist_sums_ptr = &col_dist_sums[new_last_col_num][d][y][x];
for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++)
{
*col_dist_sums_ptr += D::template calcDist<T>(
main_extended_src_.at<T>(ay + ty, ax),
cur_extended_src.at<T>(by + ty, bx));
}
dist_sums[d][y][x] += col_dist_sums[new_last_col_num][d][y][x];
up_col_dist_sums[j][d][y][x] = col_dist_sums[new_last_col_num][d][y][x];
}
}
}
#endif

View File

@ -0,0 +1,110 @@
/*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 triangleWeights()
{
// hat function
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;
float e4 = exp(4.f);
float scale = e4/(e4 - 1.f);
float shift = 1 / (1.f - e4);
for(int i = 0; i < LDR_SIZE; i++) {
float value = i / q - 2.0f;
value = scale*exp(-value * value) + shift;
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;
}
}

View 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 triangleWeights();
void mapLuminance(Mat src, Mat dst, Mat lum, Mat new_lum, float saturation);
Mat RobertsonWeights();
Mat linearResponse(int channels);
}
#endif

View File

@ -0,0 +1,863 @@
/*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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective icvers.
//
// 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 Intel Corporation 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*/
/* ////////////////////////////////////////////////////////////////////
//
// Geometrical transforms on images and matrices: rotation, zoom etc.
//
// */
#include "precomp.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/photo/legacy/constants_c.h"
#undef CV_MAT_ELEM_PTR_FAST
#define CV_MAT_ELEM_PTR_FAST( mat, row, col, pix_size ) \
((mat).data.ptr + (size_t)(mat).step*(row) + (pix_size)*(col))
inline float
min4( float a, float b, float c, float d )
{
a = MIN(a,b);
c = MIN(c,d);
return MIN(a,c);
}
#define CV_MAT_3COLOR_ELEM(img,type,y,x,c) CV_MAT_ELEM(img,type,y,(x)*3+(c))
#define KNOWN 0 //known outside narrow band
#define BAND 1 //narrow band (known)
#define INSIDE 2 //unknown
#define CHANGE 3 //servise
typedef struct CvHeapElem
{
float T;
int i,j;
struct CvHeapElem* prev;
struct CvHeapElem* next;
}
CvHeapElem;
class CvPriorityQueueFloat
{
private:
CvPriorityQueueFloat(const CvPriorityQueueFloat & ); // copy disabled
CvPriorityQueueFloat& operator=(const CvPriorityQueueFloat &); // assign disabled
protected:
CvHeapElem *mem,*empty,*head,*tail;
int num,in;
public:
bool Init( const CvMat* f )
{
int i,j;
for( i = num = 0; i < f->rows; i++ )
{
for( j = 0; j < f->cols; j++ )
num += CV_MAT_ELEM(*f,uchar,i,j)!=0;
}
if (num<=0) return false;
mem = (CvHeapElem*)cvAlloc((num+2)*sizeof(CvHeapElem));
if (mem==NULL) return false;
head = mem;
head->i = head->j = -1;
head->prev = NULL;
head->next = mem+1;
head->T = -FLT_MAX;
empty = mem+1;
for (i=1; i<=num; i++) {
mem[i].prev = mem+i-1;
mem[i].next = mem+i+1;
mem[i].i = -1;
mem[i].T = FLT_MAX;
}
tail = mem+i;
tail->i = tail->j = -1;
tail->prev = mem+i-1;
tail->next = NULL;
tail->T = FLT_MAX;
return true;
}
bool Add(const CvMat* f) {
int i,j;
for (i=0; i<f->rows; i++) {
for (j=0; j<f->cols; j++) {
if (CV_MAT_ELEM(*f,uchar,i,j)!=0) {
if (!Push(i,j,0)) return false;
}
}
}
return true;
}
bool Push(int i, int j, float T) {
CvHeapElem *tmp=empty,*add=empty;
if (empty==tail) return false;
while (tmp->prev->T>T) tmp = tmp->prev;
if (tmp!=empty) {
add->prev->next = add->next;
add->next->prev = add->prev;
empty = add->next;
add->prev = tmp->prev;
add->next = tmp;
add->prev->next = add;
add->next->prev = add;
} else {
empty = empty->next;
}
add->i = i;
add->j = j;
add->T = T;
in++;
// printf("push i %3d j %3d T %12.4e in %4d\n",i,j,T,in);
return true;
}
bool Pop(int *i, int *j) {
CvHeapElem *tmp=head->next;
if (empty==tmp) return false;
*i = tmp->i;
*j = tmp->j;
tmp->prev->next = tmp->next;
tmp->next->prev = tmp->prev;
tmp->prev = empty->prev;
tmp->next = empty;
tmp->prev->next = tmp;
tmp->next->prev = tmp;
empty = tmp;
in--;
// printf("pop i %3d j %3d T %12.4e in %4d\n",tmp->i,tmp->j,tmp->T,in);
return true;
}
bool Pop(int *i, int *j, float *T) {
CvHeapElem *tmp=head->next;
if (empty==tmp) return false;
*i = tmp->i;
*j = tmp->j;
*T = tmp->T;
tmp->prev->next = tmp->next;
tmp->next->prev = tmp->prev;
tmp->prev = empty->prev;
tmp->next = empty;
tmp->prev->next = tmp;
tmp->next->prev = tmp;
empty = tmp;
in--;
// printf("pop i %3d j %3d T %12.4e in %4d\n",tmp->i,tmp->j,tmp->T,in);
return true;
}
CvPriorityQueueFloat(void) {
num=in=0;
mem=empty=head=tail=NULL;
}
~CvPriorityQueueFloat(void)
{
cvFree( &mem );
}
};
static inline float VectorScalMult(const cv::Point2f& v1, const cv::Point2f& v2)
{
return v1.x*v2.x+v1.y*v2.y;
}
static inline float VectorLength(const cv::Point2f& v1)
{
return v1.x*v1.x+v1.y*v1.y;
}
///////////////////////////////////////////////////////////////////////////////////////////
//HEAP::iterator Heap_Iterator;
//HEAP Heap;
static float FastMarching_solve(int i1,int j1,int i2,int j2, const CvMat* f, const CvMat* t)
{
double sol, a11, a22, m12;
a11=CV_MAT_ELEM(*t,float,i1,j1);
a22=CV_MAT_ELEM(*t,float,i2,j2);
m12=MIN(a11,a22);
if( CV_MAT_ELEM(*f,uchar,i1,j1) != INSIDE )
if( CV_MAT_ELEM(*f,uchar,i2,j2) != INSIDE )
if( fabs(a11-a22) >= 1.0 )
sol = 1+m12;
else
sol = (a11+a22+sqrt((double)(2-(a11-a22)*(a11-a22))))*0.5;
else
sol = 1+a11;
else if( CV_MAT_ELEM(*f,uchar,i2,j2) != INSIDE )
sol = 1+a22;
else
sol = 1+m12;
return (float)sol;
}
/////////////////////////////////////////////////////////////////////////////////////
static void
icvCalcFMM(const CvMat *f, CvMat *t, CvPriorityQueueFloat *Heap, bool negate) {
int i, j, ii = 0, jj = 0, q;
float dist;
while (Heap->Pop(&ii,&jj)) {
unsigned known=(negate)?CHANGE:KNOWN;
CV_MAT_ELEM(*f,uchar,ii,jj) = (uchar)known;
for (q=0; q<4; q++) {
i=0; j=0;
if (q==0) {i=ii-1; j=jj;}
else if(q==1) {i=ii; j=jj-1;}
else if(q==2) {i=ii+1; j=jj;}
else {i=ii; j=jj+1;}
if ((i<=0)||(j<=0)||(i>f->rows)||(j>f->cols)) continue;
if (CV_MAT_ELEM(*f,uchar,i,j)==INSIDE) {
dist = min4(FastMarching_solve(i-1,j,i,j-1,f,t),
FastMarching_solve(i+1,j,i,j-1,f,t),
FastMarching_solve(i-1,j,i,j+1,f,t),
FastMarching_solve(i+1,j,i,j+1,f,t));
CV_MAT_ELEM(*t,float,i,j) = dist;
CV_MAT_ELEM(*f,uchar,i,j) = BAND;
Heap->Push(i,j,dist);
}
}
}
if (negate) {
for (i=0; i<f->rows; i++) {
for(j=0; j<f->cols; j++) {
if (CV_MAT_ELEM(*f,uchar,i,j) == CHANGE) {
CV_MAT_ELEM(*f,uchar,i,j) = KNOWN;
CV_MAT_ELEM(*t,float,i,j) = -CV_MAT_ELEM(*t,float,i,j);
}
}
}
}
}
template <typename data_type>
static void
icvTeleaInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueueFloat *Heap ) {
int i = 0, j = 0, ii = 0, jj = 0, k, l, q, color = 0;
float dist;
if (CV_MAT_CN(out->type)==3) {
while (Heap->Pop(&ii,&jj)) {
CV_MAT_ELEM(*f,uchar,ii,jj) = KNOWN;
for(q=0; q<4; q++) {
if (q==0) {i=ii-1; j=jj;}
else if(q==1) {i=ii; j=jj-1;}
else if(q==2) {i=ii+1; j=jj;}
else if(q==3) {i=ii; j=jj+1;}
if ((i<=0)||(j<=0)||(i>t->rows-1)||(j>t->cols-1)) continue;
if (CV_MAT_ELEM(*f,uchar,i,j)==INSIDE) {
dist = min4(FastMarching_solve(i-1,j,i,j-1,f,t),
FastMarching_solve(i+1,j,i,j-1,f,t),
FastMarching_solve(i-1,j,i,j+1,f,t),
FastMarching_solve(i+1,j,i,j+1,f,t));
CV_MAT_ELEM(*t,float,i,j) = dist;
cv::Point2f gradT[3];
for (color=0; color<=2; color++) {
if (CV_MAT_ELEM(*f,uchar,i,j+1)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,i,j-1)!=INSIDE) {
gradT[color].x=(float)((CV_MAT_ELEM(*t,float,i,j+1)-CV_MAT_ELEM(*t,float,i,j-1)))*0.5f;
} else {
gradT[color].x=(float)((CV_MAT_ELEM(*t,float,i,j+1)-CV_MAT_ELEM(*t,float,i,j)));
}
} else {
if (CV_MAT_ELEM(*f,uchar,i,j-1)!=INSIDE) {
gradT[color].x=(float)((CV_MAT_ELEM(*t,float,i,j)-CV_MAT_ELEM(*t,float,i,j-1)));
} else {
gradT[color].x=0;
}
}
if (CV_MAT_ELEM(*f,uchar,i+1,j)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,i-1,j)!=INSIDE) {
gradT[color].y=(float)((CV_MAT_ELEM(*t,float,i+1,j)-CV_MAT_ELEM(*t,float,i-1,j)))*0.5f;
} else {
gradT[color].y=(float)((CV_MAT_ELEM(*t,float,i+1,j)-CV_MAT_ELEM(*t,float,i,j)));
}
} else {
if (CV_MAT_ELEM(*f,uchar,i-1,j)!=INSIDE) {
gradT[color].y=(float)((CV_MAT_ELEM(*t,float,i,j)-CV_MAT_ELEM(*t,float,i-1,j)));
} else {
gradT[color].y=0;
}
}
}
cv::Point2f gradI,r;
float Jx[3] = {0,0,0};
float Jy[3] = {0,0,0};
float Ia[3] = {0,0,0};
float s[3] = {1.0e-20f,1.0e-20f,1.0e-20f};
float w,dst,lev,dir,sat;
for (k=i-range; k<=i+range; k++) {
int km=k-1+(k==1),kp=k-1-(k==t->rows-2);
for (l=j-range; l<=j+range; l++) {
int lm=l-1+(l==1),lp=l-1-(l==t->cols-2);
if (k>0&&l>0&&k<t->rows-1&&l<t->cols-1) {
if ((CV_MAT_ELEM(*f,uchar,k,l)!=INSIDE)&&
((l-j)*(l-j)+(k-i)*(k-i)<=range*range)) {
for (color=0; color<=2; color++) {
r.y = (float)(i-k);
r.x = (float)(j-l);
dst = (float)(1./(VectorLength(r)*sqrt((double)VectorLength(r))));
lev = (float)(1./(1+fabs(CV_MAT_ELEM(*t,float,k,l)-CV_MAT_ELEM(*t,float,i,j))));
dir=VectorScalMult(r,gradT[color]);
if (fabs(dir)<=0.01) dir=0.000001f;
w = (float)fabs(dst*lev*dir);
if (CV_MAT_ELEM(*f,uchar,k,l+1)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) {
gradI.x=(float)((CV_MAT_3COLOR_ELEM(*out,uchar,km,lp+1,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km,lm-1,color)))*2.0f;
} else {
gradI.x=(float)((CV_MAT_3COLOR_ELEM(*out,uchar,km,lp+1,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km,lm,color)));
}
} else {
if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) {
gradI.x=(float)((CV_MAT_3COLOR_ELEM(*out,uchar,km,lp,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km,lm-1,color)));
} else {
gradI.x=0;
}
}
if (CV_MAT_ELEM(*f,uchar,k+1,l)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) {
gradI.y=(float)((CV_MAT_3COLOR_ELEM(*out,uchar,kp+1,lm,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km-1,lm,color)))*2.0f;
} else {
gradI.y=(float)((CV_MAT_3COLOR_ELEM(*out,uchar,kp+1,lm,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km,lm,color)));
}
} else {
if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) {
gradI.y=(float)((CV_MAT_3COLOR_ELEM(*out,uchar,kp,lm,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km-1,lm,color)));
} else {
gradI.y=0;
}
}
Ia[color] += (float)w * (float)(CV_MAT_3COLOR_ELEM(*out,uchar,km,lm,color));
Jx[color] -= (float)w * (float)(gradI.x*r.x);
Jy[color] -= (float)w * (float)(gradI.y*r.y);
s[color] += w;
}
}
}
}
}
for (color=0; color<=2; color++) {
sat = (float)((Ia[color]/s[color]+(Jx[color]+Jy[color])/(sqrt(Jx[color]*Jx[color]+Jy[color]*Jy[color])+1.0e-20f)+0.5f));
CV_MAT_3COLOR_ELEM(*out,uchar,i-1,j-1,color) = cv::saturate_cast<uchar>(sat);
}
CV_MAT_ELEM(*f,uchar,i,j) = BAND;
Heap->Push(i,j,dist);
}
}
}
} else if (CV_MAT_CN(out->type)==1) {
while (Heap->Pop(&ii,&jj)) {
CV_MAT_ELEM(*f,uchar,ii,jj) = KNOWN;
for(q=0; q<4; q++) {
if (q==0) {i=ii-1; j=jj;}
else if(q==1) {i=ii; j=jj-1;}
else if(q==2) {i=ii+1; j=jj;}
else if(q==3) {i=ii; j=jj+1;}
if ((i<=0)||(j<=0)||(i>t->rows-1)||(j>t->cols-1)) continue;
if (CV_MAT_ELEM(*f,uchar,i,j)==INSIDE) {
dist = min4(FastMarching_solve(i-1,j,i,j-1,f,t),
FastMarching_solve(i+1,j,i,j-1,f,t),
FastMarching_solve(i-1,j,i,j+1,f,t),
FastMarching_solve(i+1,j,i,j+1,f,t));
CV_MAT_ELEM(*t,float,i,j) = dist;
for (color=0; color<=0; color++) {
cv::Point2f gradI,gradT,r;
float Ia=0,Jx=0,Jy=0,s=1.0e-20f,w,dst,lev,dir,sat;
if (CV_MAT_ELEM(*f,uchar,i,j+1)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,i,j-1)!=INSIDE) {
gradT.x=(float)((CV_MAT_ELEM(*t,float,i,j+1)-CV_MAT_ELEM(*t,float,i,j-1)))*0.5f;
} else {
gradT.x=(float)((CV_MAT_ELEM(*t,float,i,j+1)-CV_MAT_ELEM(*t,float,i,j)));
}
} else {
if (CV_MAT_ELEM(*f,uchar,i,j-1)!=INSIDE) {
gradT.x=(float)((CV_MAT_ELEM(*t,float,i,j)-CV_MAT_ELEM(*t,float,i,j-1)));
} else {
gradT.x=0;
}
}
if (CV_MAT_ELEM(*f,uchar,i+1,j)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,i-1,j)!=INSIDE) {
gradT.y=(float)((CV_MAT_ELEM(*t,float,i+1,j)-CV_MAT_ELEM(*t,float,i-1,j)))*0.5f;
} else {
gradT.y=(float)((CV_MAT_ELEM(*t,float,i+1,j)-CV_MAT_ELEM(*t,float,i,j)));
}
} else {
if (CV_MAT_ELEM(*f,uchar,i-1,j)!=INSIDE) {
gradT.y=(float)((CV_MAT_ELEM(*t,float,i,j)-CV_MAT_ELEM(*t,float,i-1,j)));
} else {
gradT.y=0;
}
}
for (k=i-range; k<=i+range; k++) {
int km=k-1+(k==1),kp=k-1-(k==t->rows-2);
for (l=j-range; l<=j+range; l++) {
int lm=l-1+(l==1),lp=l-1-(l==t->cols-2);
if (k>0&&l>0&&k<t->rows-1&&l<t->cols-1) {
if ((CV_MAT_ELEM(*f,uchar,k,l)!=INSIDE)&&
((l-j)*(l-j)+(k-i)*(k-i)<=range*range)) {
r.y = (float)(i-k);
r.x = (float)(j-l);
dst = (float)(1./(VectorLength(r)*sqrt(VectorLength(r))));
lev = (float)(1./(1+fabs(CV_MAT_ELEM(*t,float,k,l)-CV_MAT_ELEM(*t,float,i,j))));
dir=VectorScalMult(r,gradT);
if (fabs(dir)<=0.01) dir=0.000001f;
w = (float)fabs(dst*lev*dir);
if (CV_MAT_ELEM(*f,uchar,k,l+1)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) {
gradI.x=(float)((CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm-1)))*2.0f;
} else {
gradI.x=(float)((CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm)));
}
} else {
if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) {
gradI.x=(float)((CV_MAT_ELEM(*out,data_type,km,lp)-CV_MAT_ELEM(*out,data_type,km,lm-1)));
} else {
gradI.x=0;
}
}
if (CV_MAT_ELEM(*f,uchar,k+1,l)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) {
gradI.y=(float)((CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm)))*2.0f;
} else {
gradI.y=(float)((CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,km,lm)));
}
} else {
if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) {
gradI.y=(float)((CV_MAT_ELEM(*out,data_type,kp,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm)));
} else {
gradI.y=0;
}
}
Ia += (float)w * (float)(CV_MAT_ELEM(*out,data_type,km,lm));
Jx -= (float)w * (float)(gradI.x*r.x);
Jy -= (float)w * (float)(gradI.y*r.y);
s += w;
}
}
}
}
sat = (float)((Ia/s+(Jx+Jy)/(sqrt(Jx*Jx+Jy*Jy)+1.0e-20f)+0.5f));
{
CV_MAT_ELEM(*out,data_type,i-1,j-1) = cv::saturate_cast<data_type>(sat);
}
}
CV_MAT_ELEM(*f,uchar,i,j) = BAND;
Heap->Push(i,j,dist);
}
}
}
}
}
template <typename data_type>
static void
icvNSInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueueFloat *Heap) {
int i = 0, j = 0, ii = 0, jj = 0, k, l, q, color = 0;
float dist;
if (CV_MAT_CN(out->type)==3) {
while (Heap->Pop(&ii,&jj)) {
CV_MAT_ELEM(*f,uchar,ii,jj) = KNOWN;
for(q=0; q<4; q++) {
if (q==0) {i=ii-1; j=jj;}
else if(q==1) {i=ii; j=jj-1;}
else if(q==2) {i=ii+1; j=jj;}
else if(q==3) {i=ii; j=jj+1;}
if ((i<=0)||(j<=0)||(i>t->rows-1)||(j>t->cols-1)) continue;
if (CV_MAT_ELEM(*f,uchar,i,j)==INSIDE) {
dist = min4(FastMarching_solve(i-1,j,i,j-1,f,t),
FastMarching_solve(i+1,j,i,j-1,f,t),
FastMarching_solve(i-1,j,i,j+1,f,t),
FastMarching_solve(i+1,j,i,j+1,f,t));
CV_MAT_ELEM(*t,float,i,j) = dist;
cv::Point2f gradI,r;
float Ia[3]={0,0,0};
float s[3]={1.0e-20f,1.0e-20f,1.0e-20f};
float w,dst,dir;
for (k=i-range; k<=i+range; k++) {
int km=k-1+(k==1),kp=k-1-(k==f->rows-2);
for (l=j-range; l<=j+range; l++) {
int lm=l-1+(l==1),lp=l-1-(l==f->cols-2);
if (k>0&&l>0&&k<f->rows-1&&l<f->cols-1) {
if ((CV_MAT_ELEM(*f,uchar,k,l)!=INSIDE)&&
((l-j)*(l-j)+(k-i)*(k-i)<=range*range)) {
for (color=0; color<=2; color++) {
r.y=(float)(k-i);
r.x=(float)(l-j);
dst = 1/(VectorLength(r)*VectorLength(r)+1);
if (CV_MAT_ELEM(*f,uchar,k+1,l)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) {
gradI.x=(float)(abs(CV_MAT_3COLOR_ELEM(*out,uchar,kp+1,lm,color)-CV_MAT_3COLOR_ELEM(*out,uchar,kp,lm,color))+
abs(CV_MAT_3COLOR_ELEM(*out,uchar,kp,lm,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km-1,lm,color)));
} else {
gradI.x=(float)(abs(CV_MAT_3COLOR_ELEM(*out,uchar,kp+1,lm,color)-CV_MAT_3COLOR_ELEM(*out,uchar,kp,lm,color)))*2.0f;
}
} else {
if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) {
gradI.x=(float)(abs(CV_MAT_3COLOR_ELEM(*out,uchar,kp,lm,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km-1,lm,color)))*2.0f;
} else {
gradI.x=0;
}
}
if (CV_MAT_ELEM(*f,uchar,k,l+1)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) {
gradI.y=(float)(abs(CV_MAT_3COLOR_ELEM(*out,uchar,km,lp+1,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km,lm,color))+
abs(CV_MAT_3COLOR_ELEM(*out,uchar,km,lm,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km,lm-1,color)));
} else {
gradI.y=(float)(abs(CV_MAT_3COLOR_ELEM(*out,uchar,km,lp+1,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km,lm,color)))*2.0f;
}
} else {
if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) {
gradI.y=(float)(abs(CV_MAT_3COLOR_ELEM(*out,uchar,km,lm,color)-CV_MAT_3COLOR_ELEM(*out,uchar,km,lm-1,color)))*2.0f;
} else {
gradI.y=0;
}
}
gradI.x=-gradI.x;
dir=VectorScalMult(r,gradI);
if (fabs(dir)<=0.01) {
dir=0.000001f;
} else {
dir = (float)fabs(VectorScalMult(r,gradI)/sqrt(VectorLength(r)*VectorLength(gradI)));
}
w = dst*dir;
Ia[color] += (float)w * (float)(CV_MAT_3COLOR_ELEM(*out,uchar,km,lm,color));
s[color] += w;
}
}
}
}
}
for (color=0; color<=2; color++) {
CV_MAT_3COLOR_ELEM(*out,uchar,i-1,j-1,color) = cv::saturate_cast<uchar>((double)Ia[color]/s[color]);
}
CV_MAT_ELEM(*f,uchar,i,j) = BAND;
Heap->Push(i,j,dist);
}
}
}
} else if (CV_MAT_CN(out->type)==1) {
while (Heap->Pop(&ii,&jj)) {
CV_MAT_ELEM(*f,uchar,ii,jj) = KNOWN;
for(q=0; q<4; q++) {
if (q==0) {i=ii-1; j=jj;}
else if(q==1) {i=ii; j=jj-1;}
else if(q==2) {i=ii+1; j=jj;}
else if(q==3) {i=ii; j=jj+1;}
if ((i<=0)||(j<=0)||(i>t->rows-1)||(j>t->cols-1)) continue;
if (CV_MAT_ELEM(*f,uchar,i,j)==INSIDE) {
dist = min4(FastMarching_solve(i-1,j,i,j-1,f,t),
FastMarching_solve(i+1,j,i,j-1,f,t),
FastMarching_solve(i-1,j,i,j+1,f,t),
FastMarching_solve(i+1,j,i,j+1,f,t));
CV_MAT_ELEM(*t,float,i,j) = dist;
{
cv::Point2f gradI,r;
float Ia=0,s=1.0e-20f,w,dst,dir;
for (k=i-range; k<=i+range; k++) {
int km=k-1+(k==1),kp=k-1-(k==t->rows-2);
for (l=j-range; l<=j+range; l++) {
int lm=l-1+(l==1),lp=l-1-(l==t->cols-2);
if (k>0&&l>0&&k<t->rows-1&&l<t->cols-1) {
if ((CV_MAT_ELEM(*f,uchar,k,l)!=INSIDE)&&
((l-j)*(l-j)+(k-i)*(k-i)<=range*range)) {
r.y=(float)(i-k);
r.x=(float)(j-l);
dst = 1/(VectorLength(r)*VectorLength(r)+1);
if (CV_MAT_ELEM(*f,uchar,k+1,l)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) {
gradI.x=(float)(std::abs(CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,kp,lm))+
std::abs(CV_MAT_ELEM(*out,data_type,kp,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm)));
} else {
gradI.x=(float)(std::abs(CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,kp,lm)))*2.0f;
}
} else {
if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) {
gradI.x=(float)(std::abs(CV_MAT_ELEM(*out,data_type,kp,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm)))*2.0f;
} else {
gradI.x=0;
}
}
if (CV_MAT_ELEM(*f,uchar,k,l+1)!=INSIDE) {
if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) {
gradI.y=(float)(std::abs(CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm))+
std::abs(CV_MAT_ELEM(*out,data_type,km,lm)-CV_MAT_ELEM(*out,data_type,km,lm-1)));
} else {
gradI.y=(float)(std::abs(CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm)))*2.0f;
}
} else {
if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) {
gradI.y=(float)(std::abs(CV_MAT_ELEM(*out,data_type,km,lm)-CV_MAT_ELEM(*out,data_type,km,lm-1)))*2.0f;
} else {
gradI.y=0;
}
}
gradI.x=-gradI.x;
dir=VectorScalMult(r,gradI);
if (fabs(dir)<=0.01) {
dir=0.000001f;
} else {
dir = (float)fabs(VectorScalMult(r,gradI)/sqrt(VectorLength(r)*VectorLength(gradI)));
}
w = dst*dir;
Ia += (float)w * (float)(CV_MAT_ELEM(*out,data_type,km,lm));
s += w;
}
}
}
}
CV_MAT_ELEM(*out,data_type,i-1,j-1) = cv::saturate_cast<data_type>((double)Ia/s);
}
CV_MAT_ELEM(*f,uchar,i,j) = BAND;
Heap->Push(i,j,dist);
}
}
}
}
}
#define SET_BORDER1_C1(image,type,value) {\
int i,j;\
for(j=0; j<image->cols; j++) {\
CV_MAT_ELEM(*image,type,0,j) = value;\
}\
for (i=1; i<image->rows-1; i++) {\
CV_MAT_ELEM(*image,type,i,0) = CV_MAT_ELEM(*image,type,i,image->cols-1) = value;\
}\
for(j=0; j<image->cols; j++) {\
CV_MAT_ELEM(*image,type,erows-1,j) = value;\
}\
}
#define COPY_MASK_BORDER1_C1(src,dst,type) {\
int i,j;\
for (i=0; i<src->rows; i++) {\
for(j=0; j<src->cols; j++) {\
if (CV_MAT_ELEM(*src,type,i,j)!=0)\
CV_MAT_ELEM(*dst,type,i+1,j+1) = INSIDE;\
}\
}\
}
namespace cv {
template<> struct DefaultDeleter<IplConvKernel>{ void operator ()(IplConvKernel* obj) const { cvReleaseStructuringElement(&obj); } };
}
static void
icvInpaint( const CvArr* _input_img, const CvArr* _inpaint_mask, CvArr* _output_img,
double inpaintRange, int flags )
{
cv::Ptr<CvMat> mask, band, f, t, out;
cv::Ptr<CvPriorityQueueFloat> Heap, Out;
cv::Ptr<IplConvKernel> el_cross, el_range;
CvMat input_hdr, mask_hdr, output_hdr;
CvMat* input_img, *inpaint_mask, *output_img;
int range=cvRound(inpaintRange);
int erows, ecols;
input_img = cvGetMat( _input_img, &input_hdr );
inpaint_mask = cvGetMat( _inpaint_mask, &mask_hdr );
output_img = cvGetMat( _output_img, &output_hdr );
if( !CV_ARE_SIZES_EQ(input_img,output_img) || !CV_ARE_SIZES_EQ(input_img,inpaint_mask))
CV_Error( CV_StsUnmatchedSizes, "All the input and output images must have the same size" );
if( (CV_MAT_TYPE(input_img->type) != CV_8U &&
CV_MAT_TYPE(input_img->type) != CV_16U &&
CV_MAT_TYPE(input_img->type) != CV_32F &&
CV_MAT_TYPE(input_img->type) != CV_8UC3) ||
!CV_ARE_TYPES_EQ(input_img,output_img) )
CV_Error( CV_StsUnsupportedFormat,
"8-bit, 16-bit unsigned or 32-bit float 1-channel and 8-bit 3-channel input/output images are supported" );
if( CV_MAT_TYPE(inpaint_mask->type) != CV_8UC1 )
CV_Error( CV_StsUnsupportedFormat, "The mask must be 8-bit 1-channel image" );
range = MAX(range,1);
range = MIN(range,100);
ecols = input_img->cols + 2;
erows = input_img->rows + 2;
f.reset(cvCreateMat(erows, ecols, CV_8UC1));
t.reset(cvCreateMat(erows, ecols, CV_32FC1));
band.reset(cvCreateMat(erows, ecols, CV_8UC1));
mask.reset(cvCreateMat(erows, ecols, CV_8UC1));
el_cross.reset(cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_CROSS,NULL));
cvCopy( input_img, output_img );
cvSet(mask,cvScalar(KNOWN,0,0,0));
COPY_MASK_BORDER1_C1(inpaint_mask,mask,uchar);
SET_BORDER1_C1(mask,uchar,0);
cvSet(f,cvScalar(KNOWN,0,0,0));
cvSet(t,cvScalar(1.0e6f,0,0,0));
cvDilate(mask,band,el_cross,1); // image with narrow band
Heap=cv::makePtr<CvPriorityQueueFloat>();
if (!Heap->Init(band))
return;
cvSub(band,mask,band,NULL);
SET_BORDER1_C1(band,uchar,0);
if (!Heap->Add(band))
return;
cvSet(f,cvScalar(BAND,0,0,0),band);
cvSet(f,cvScalar(INSIDE,0,0,0),mask);
cvSet(t,cvScalar(0,0,0,0),band);
if( flags == cv::INPAINT_TELEA )
{
out.reset(cvCreateMat(erows, ecols, CV_8UC1));
el_range.reset(cvCreateStructuringElementEx(2*range+1,2*range+1,
range,range,CV_SHAPE_RECT,NULL));
cvDilate(mask,out,el_range,1);
cvSub(out,mask,out,NULL);
Out=cv::makePtr<CvPriorityQueueFloat>();
if (!Out->Init(out))
return;
if (!Out->Add(band))
return;
cvSub(out,band,out,NULL);
SET_BORDER1_C1(out,uchar,0);
icvCalcFMM(out,t,Out,true);
switch(CV_MAT_DEPTH(output_img->type))
{
case CV_8U:
icvTeleaInpaintFMM<uchar>(mask,t,output_img,range,Heap);
break;
case CV_16U:
icvTeleaInpaintFMM<ushort>(mask,t,output_img,range,Heap);
break;
case CV_32F:
icvTeleaInpaintFMM<float>(mask,t,output_img,range,Heap);
break;
default:
CV_Error( cv::Error::StsBadArg, "Unsupportedformat of the input image" );
}
}
else if (flags == cv::INPAINT_NS) {
switch(CV_MAT_DEPTH(output_img->type))
{
case CV_8U:
icvNSInpaintFMM<uchar>(mask,t,output_img,range,Heap);
break;
case CV_16U:
icvNSInpaintFMM<ushort>(mask,t,output_img,range,Heap);
break;
case CV_32F:
icvNSInpaintFMM<float>(mask,t,output_img,range,Heap);
break;
default:
CV_Error( cv::Error::StsBadArg, "Unsupported format of the input image" );
}
} else {
CV_Error( cv::Error::StsBadArg, "The flags argument must be one of CV_INPAINT_TELEA or CV_INPAINT_NS" );
}
}
void cv::inpaint( InputArray _src, InputArray _mask, OutputArray _dst,
double inpaintRange, int flags )
{
CV_INSTRUMENT_REGION();
Mat src = _src.getMat(), mask = _mask.getMat();
_dst.create( src.size(), src.type() );
Mat dst = _dst.getMat();
CvMat c_src = cvMat(src), c_mask = cvMat(mask), c_dst = cvMat(dst);
icvInpaint( &c_src, &c_mask, &c_dst, inpaintRange, flags );
}

View File

@ -0,0 +1,367 @@
/*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 CV_FINAL : public MergeDebevec
{
public:
MergeDebevecImpl() :
name("MergeDebevec"),
weights(triangleWeights())
{
}
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times, InputArray input_response) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
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);
}
Mat log_response;
log(response, log_response);
CV_Assert(log_response.rows == LDR_SIZE && log_response.cols == 1 &&
log_response.channels() == channels);
Mat exp_values(times.clone());
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], log_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>((int)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) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
process(src, dst, times, Mat());
}
protected:
String name;
Mat weights;
};
Ptr<MergeDebevec> createMergeDebevec()
{
return makePtr<MergeDebevecImpl>();
}
class MergeMertensImpl CV_FINAL : 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) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
process(src, dst);
}
void process(InputArrayOfArrays src, OutputArray dst) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
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 expo = splitted[c] - 0.5f;
pow(expo, 2.0f, expo);
expo = -expo / 0.08f;
exp(expo, expo);
wellexp = wellexp.mul(expo);
}
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) + 1e-12f;
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);
}
float getContrastWeight() const CV_OVERRIDE { return wcon; }
void setContrastWeight(float val) CV_OVERRIDE { wcon = val; }
float getSaturationWeight() const CV_OVERRIDE { return wsat; }
void setSaturationWeight(float val) CV_OVERRIDE { wsat = val; }
float getExposureWeight() const CV_OVERRIDE { return wexp; }
void setExposureWeight(float val) CV_OVERRIDE { wexp = val; }
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << name
<< "contrast_weight" << wcon
<< "saturation_weight" << wsat
<< "exposure_weight" << wexp;
}
void read(const FileNode& fn) CV_OVERRIDE
{
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 CV_FINAL : public MergeRobertson
{
public:
MergeRobertsonImpl() :
name("MergeRobertson"),
weight(RobertsonWeights())
{
}
void process(InputArrayOfArrays src, OutputArray dst, InputArray _times, InputArray input_response) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
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>((int)i) * w.mul(im);
wsum += times.at<float>((int)i) * times.at<float>((int)i) * w;
}
result = result.mul(1 / (wsum + Scalar::all(DBL_EPSILON)));
}
void process(InputArrayOfArrays src, OutputArray dst, InputArray times) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
process(src, dst, times, Mat());
}
protected:
String name;
Mat weight;
};
Ptr<MergeRobertson> createMergeRobertson()
{
return makePtr<MergeRobertsonImpl>();
}
}

View File

@ -0,0 +1,163 @@
/*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 <iostream>
#include <stdlib.h>
#include "npr.hpp"
using namespace std;
using namespace cv;
void cv::edgePreservingFilter(InputArray _src, OutputArray dst, int flags, float sigma_s, float sigma_r)
{
CV_INSTRUMENT_REGION();
Mat I = _src.getMat();
Domain_Filter obj;
Mat img;
I.convertTo(img,CV_32FC3,1.0/255.0);
Mat res;
obj.filter(img, res, sigma_s, sigma_r, flags);
convertScaleAbs(res, dst, 255,0);
}
void cv::detailEnhance(InputArray _src, OutputArray dst, float sigma_s, float sigma_r)
{
CV_INSTRUMENT_REGION();
Mat I = _src.getMat();
float factor = 3.0f;
Mat img;
I.convertTo(img,CV_32FC3,1.0/255.0);
Mat lab;
vector <Mat> lab_channel;
cvtColor(img,lab,COLOR_BGR2Lab);
split(lab,lab_channel);
Mat L;
lab_channel[0].convertTo(L,CV_32FC1,1.0/255.0);
Domain_Filter obj;
Mat res;
obj.filter(L, res, sigma_s, sigma_r, 1);
Mat detail = L - res;
multiply(detail,factor,detail);
L = res + detail;
L.convertTo(lab_channel[0],CV_32FC1,255);
merge(lab_channel,lab);
cvtColor(lab,res,COLOR_Lab2BGR);
res.convertTo(dst,CV_8UC3,255);
}
void cv::pencilSketch(InputArray _src, OutputArray _dst1, OutputArray _dst2, float sigma_s, float sigma_r, float shade_factor)
{
CV_INSTRUMENT_REGION();
Mat I = _src.getMat();
_dst1.create(I.size(), CV_8UC1);
Mat dst1 = _dst1.getMat();
_dst2.create(I.size(), CV_8UC3);
Mat dst2 = _dst2.getMat();
Mat img = Mat(I.size(),CV_32FC3);
I.convertTo(img,CV_32FC3,1.0/255.0);
Domain_Filter obj;
Mat sketch = Mat(I.size(),CV_32FC1);
Mat color_sketch = Mat(I.size(),CV_32FC3);
obj.pencil_sketch(img, sketch, color_sketch, sigma_s, sigma_r, shade_factor);
sketch.convertTo(dst1,CV_8UC1,255);
color_sketch.convertTo(dst2,CV_8UC3,255);
}
void cv::stylization(InputArray _src, OutputArray _dst, float sigma_s, float sigma_r)
{
CV_INSTRUMENT_REGION();
Mat I = _src.getMat();
_dst.create(I.size(), CV_8UC3);
Mat dst = _dst.getMat();
Mat img;
I.convertTo(img,CV_32FC3,1.0/255.0);
int h = img.size().height;
int w = img.size().width;
Mat res;
Mat magnitude = Mat(h,w,CV_32FC1);
Domain_Filter obj;
obj.filter(img, res, sigma_s, sigma_r, NORMCONV_FILTER);
obj.find_magnitude(res,magnitude);
Mat stylized;
vector <Mat> temp;
split(res,temp);
multiply(temp[0],magnitude,temp[0]);
multiply(temp[1],magnitude,temp[1]);
multiply(temp[2],magnitude,temp[2]);
merge(temp,stylized);
stylized.convertTo(dst,CV_8UC3,255);
}

View File

@ -0,0 +1,591 @@
/*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 <iostream>
#include <stdlib.h>
#include <limits>
#include "math.h"
using namespace std;
using namespace cv;
double myinf = std::numeric_limits<double>::infinity();
class Domain_Filter
{
public:
Mat ct_H, ct_V, horiz, vert, O, O_t, lower_idx, upper_idx;
void init(const Mat &img, int flags, float sigma_s, float sigma_r);
void getGradientx( const Mat &img, Mat &gx);
void getGradienty( const Mat &img, Mat &gy);
void diffx(const Mat &img, Mat &temp);
void diffy(const Mat &img, Mat &temp);
void find_magnitude(Mat &img, Mat &mag);
void compute_boxfilter(Mat &output, Mat &hz, Mat &psketch, float radius);
void compute_Rfilter(Mat &O, Mat &horiz, float sigma_h);
void compute_NCfilter(Mat &O, Mat &horiz, Mat &psketch, float radius);
void filter(const Mat &img, Mat &res, float sigma_s, float sigma_r, int flags);
void pencil_sketch(const Mat &img, Mat &sketch, Mat &color_res, float sigma_s, float sigma_r, float shade_factor);
void Depth_of_field(const Mat &img, Mat &img1, float sigma_s, float sigma_r);
};
void Domain_Filter::diffx(const Mat &img, Mat &temp)
{
int channel = img.channels();
for(int i = 0; i < img.size().height; i++)
for(int j = 0; j < img.size().width-1; j++)
{
for(int c =0; c < channel; c++)
{
temp.at<float>(i,j*channel+c) =
img.at<float>(i,(j+1)*channel+c) - img.at<float>(i,j*channel+c);
}
}
}
void Domain_Filter::diffy(const Mat &img, Mat &temp)
{
int channel = img.channels();
for(int i = 0; i < img.size().height-1; i++)
for(int j = 0; j < img.size().width; j++)
{
for(int c =0; c < channel; c++)
{
temp.at<float>(i,j*channel+c) =
img.at<float>((i+1),j*channel+c) - img.at<float>(i,j*channel+c);
}
}
}
void Domain_Filter::getGradientx( const Mat &img, Mat &gx)
{
int w = img.cols;
int h = img.rows;
int channel = img.channels();
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
for(int c=0;c<channel;++c)
{
gx.at<float>(i,j*channel+c) =
img.at<float>(i,(j+1)*channel+c) - img.at<float>(i,j*channel+c);
}
}
void Domain_Filter::getGradienty( const Mat &img, Mat &gy)
{
int w = img.cols;
int h = img.rows;
int channel = img.channels();
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
for(int c=0;c<channel;++c)
{
gy.at<float>(i,j*channel+c) =
img.at<float>(i+1,j*channel+c) - img.at<float>(i,j*channel+c);
}
}
void Domain_Filter::find_magnitude(Mat &img, Mat &mag)
{
int h = img.rows;
int w = img.cols;
vector <Mat> planes;
split(img, planes);
Mat magXR = Mat(h, w, CV_32FC1);
Mat magYR = Mat(h, w, CV_32FC1);
Mat magXG = Mat(h, w, CV_32FC1);
Mat magYG = Mat(h, w, CV_32FC1);
Mat magXB = Mat(h, w, CV_32FC1);
Mat magYB = Mat(h, w, CV_32FC1);
Sobel(planes[0], magXR, CV_32FC1, 1, 0, 3);
Sobel(planes[0], magYR, CV_32FC1, 0, 1, 3);
Sobel(planes[1], magXG, CV_32FC1, 1, 0, 3);
Sobel(planes[1], magYG, CV_32FC1, 0, 1, 3);
Sobel(planes[2], magXB, CV_32FC1, 1, 0, 3);
Sobel(planes[2], magYB, CV_32FC1, 0, 1, 3);
Mat mag1 = Mat(h,w,CV_32FC1);
Mat mag2 = Mat(h,w,CV_32FC1);
Mat mag3 = Mat(h,w,CV_32FC1);
magnitude(magXR,magYR,mag1);
magnitude(magXG,magYG,mag2);
magnitude(magXB,magYB,mag3);
mag = mag1 + mag2 + mag3;
mag = 1.0f - mag;
}
void Domain_Filter::compute_Rfilter(Mat &output, Mat &hz, float sigma_h)
{
int h = output.rows;
int w = output.cols;
int channel = output.channels();
float a = (float) exp((-1.0 * sqrt(2.0)) / sigma_h);
Mat temp = Mat(h,w,CV_32FC3);
output.copyTo(temp);
Mat V = Mat(h,w,CV_32FC1);
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
V.at<float>(i,j) = pow(a,hz.at<float>(i,j));
for(int i=0; i<h; i++)
{
for(int j =1; j < w; j++)
{
for(int c = 0; c<channel; c++)
{
temp.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c) +
(temp.at<float>(i,(j-1)*channel+c) - temp.at<float>(i,j*channel+c)) * V.at<float>(i,j);
}
}
}
for(int i=0; i<h; i++)
{
for(int j =w-2; j >= 0; j--)
{
for(int c = 0; c<channel; c++)
{
temp.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c) +
(temp.at<float>(i,(j+1)*channel+c) - temp.at<float>(i,j*channel+c))*V.at<float>(i,j+1);
}
}
}
temp.copyTo(output);
}
void Domain_Filter::compute_boxfilter(Mat &output, Mat &hz, Mat &psketch, float radius)
{
int h = output.rows;
int w = output.cols;
Mat lower_pos = Mat(h,w,CV_32FC1);
Mat upper_pos = Mat(h,w,CV_32FC1);
lower_pos = hz - radius;
upper_pos = hz + radius;
lower_idx = Mat::zeros(h,w,CV_32FC1);
upper_idx = Mat::zeros(h,w,CV_32FC1);
Mat domain_row = Mat::zeros(1,w+1,CV_32FC1);
for(int i=0;i<h;i++)
{
for(int j=0;j<w;j++)
domain_row.at<float>(0,j) = hz.at<float>(i,j);
domain_row.at<float>(0,w) = (float) myinf;
Mat lower_pos_row = Mat::zeros(1,w,CV_32FC1);
Mat upper_pos_row = Mat::zeros(1,w,CV_32FC1);
for(int j=0;j<w;j++)
{
lower_pos_row.at<float>(0,j) = lower_pos.at<float>(i,j);
upper_pos_row.at<float>(0,j) = upper_pos.at<float>(i,j);
}
Mat temp_lower_idx = Mat::zeros(1,w,CV_32FC1);
Mat temp_upper_idx = Mat::zeros(1,w,CV_32FC1);
for(int j=0;j<w;j++)
{
if(domain_row.at<float>(0,j) > lower_pos_row.at<float>(0,0))
{
temp_lower_idx.at<float>(0,0) = (float) j;
break;
}
}
for(int j=0;j<w;j++)
{
if(domain_row.at<float>(0,j) > upper_pos_row.at<float>(0,0))
{
temp_upper_idx.at<float>(0,0) = (float) j;
break;
}
}
int temp = 0;
for(int j=1;j<w;j++)
{
int count=0;
for(int k=(int) temp_lower_idx.at<float>(0,j-1);k<w+1;k++)
{
if(domain_row.at<float>(0,k) > lower_pos_row.at<float>(0,j))
{
temp = count;
break;
}
count++;
}
temp_lower_idx.at<float>(0,j) = temp_lower_idx.at<float>(0,j-1) + temp;
count = 0;
for(int k=(int) temp_upper_idx.at<float>(0,j-1);k<w+1;k++)
{
if(domain_row.at<float>(0,k) > upper_pos_row.at<float>(0,j))
{
temp = count;
break;
}
count++;
}
temp_upper_idx.at<float>(0,j) = temp_upper_idx.at<float>(0,j-1) + temp;
}
for(int j=0;j<w;j++)
{
lower_idx.at<float>(i,j) = temp_lower_idx.at<float>(0,j) + 1;
upper_idx.at<float>(i,j) = temp_upper_idx.at<float>(0,j) + 1;
}
}
psketch = upper_idx - lower_idx;
}
void Domain_Filter::compute_NCfilter(Mat &output, Mat &hz, Mat &psketch, float radius)
{
int h = output.rows;
int w = output.cols;
int channel = output.channels();
compute_boxfilter(output,hz,psketch,radius);
Mat box_filter = Mat::zeros(h,w+1,CV_32FC3);
for(int i = 0; i < h; i++)
{
box_filter.at<float>(i,1*channel+0) = output.at<float>(i,0*channel+0);
box_filter.at<float>(i,1*channel+1) = output.at<float>(i,0*channel+1);
box_filter.at<float>(i,1*channel+2) = output.at<float>(i,0*channel+2);
for(int j = 2; j < w+1; j++)
{
for(int c=0;c<channel;c++)
box_filter.at<float>(i,j*channel+c) = output.at<float>(i,(j-1)*channel+c) + box_filter.at<float>(i,(j-1)*channel+c);
}
}
Mat indices = Mat::zeros(h,w,CV_32FC1);
Mat final = Mat::zeros(h,w,CV_32FC3);
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
indices.at<float>(i,j) = (float) i+1;
Mat a = Mat::zeros(h,w,CV_32FC1);
Mat b = Mat::zeros(h,w,CV_32FC1);
// Compute the box filter using a summed area table.
for(int c=0;c<channel;c++)
{
Mat flag = Mat::ones(h,w,CV_32FC1);
multiply(flag,c+1,flag);
Mat temp1, temp2;
multiply(flag - 1,h*(w+1),temp1);
multiply(lower_idx - 1,h,temp2);
a = temp1 + temp2 + indices;
multiply(flag - 1,h*(w+1),temp1);
multiply(upper_idx - 1,h,temp2);
b = temp1 + temp2 + indices;
int p,q,r,rem;
int p1,q1,r1,rem1;
// Calculating indices
for(int i=0;i<h;i++)
{
for(int j=0;j<w;j++)
{
r = (int) b.at<float>(i,j)/(h*(w+1));
rem = (int) b.at<float>(i,j) - r*h*(w+1);
q = rem/h;
p = rem - q*h;
if(q==0)
{
p=h;
q=w;
r=r-1;
}
if(p==0)
{
p=h;
q=q-1;
}
r1 = (int) a.at<float>(i,j)/(h*(w+1));
rem1 = (int) a.at<float>(i,j) - r1*h*(w+1);
q1 = rem1/h;
p1 = rem1 - q1*h;
if(p1==0)
{
p1=h;
q1=q1-1;
}
final.at<float>(i,j*channel+2-c) = (box_filter.at<float>(p-1,q*channel+(2-r)) - box_filter.at<float>(p1-1,q1*channel+(2-r1)))
/(upper_idx.at<float>(i,j) - lower_idx.at<float>(i,j));
}
}
}
final.copyTo(output);
}
void Domain_Filter::init(const Mat &img, int flags, float sigma_s, float sigma_r)
{
int h = img.size().height;
int w = img.size().width;
int channel = img.channels();
//////////////////////////////////// horizontal and vertical partial derivatives /////////////////////////////////
Mat derivx = Mat::zeros(h,w-1,CV_32FC3);
Mat derivy = Mat::zeros(h-1,w,CV_32FC3);
diffx(img,derivx);
diffy(img,derivy);
Mat distx = Mat::zeros(h,w,CV_32FC1);
Mat disty = Mat::zeros(h,w,CV_32FC1);
//////////////////////// Compute the l1-norm distance of neighbor pixels ////////////////////////////////////////////////
for(int i = 0; i < h; i++)
for(int j = 0,k=1; j < w-1; j++,k++)
for(int c = 0; c < channel; c++)
{
distx.at<float>(i,k) =
distx.at<float>(i,k) + abs(derivx.at<float>(i,j*channel+c));
}
for(int i = 0,k=1; i < h-1; i++,k++)
for(int j = 0; j < w; j++)
for(int c = 0; c < channel; c++)
{
disty.at<float>(k,j) =
disty.at<float>(k,j) + abs(derivy.at<float>(i,j*channel+c));
}
////////////////////// Compute the derivatives of the horizontal and vertical domain transforms. /////////////////////////////
horiz = Mat(h,w,CV_32FC1);
vert = Mat(h,w,CV_32FC1);
Mat final = Mat(h,w,CV_32FC3);
Mat tempx,tempy;
multiply(distx,sigma_s/sigma_r,tempx);
multiply(disty,sigma_s/sigma_r,tempy);
horiz = 1.0f + tempx;
vert = 1.0f + tempy;
O = Mat(h,w,CV_32FC3);
img.copyTo(O);
O_t = Mat(w,h,CV_32FC3);
if(flags == 2)
{
ct_H = Mat(h,w,CV_32FC1);
ct_V = Mat(h,w,CV_32FC1);
for(int i = 0; i < h; i++)
{
ct_H.at<float>(i,0) = horiz.at<float>(i,0);
for(int j = 1; j < w; j++)
{
ct_H.at<float>(i,j) = horiz.at<float>(i,j) + ct_H.at<float>(i,j-1);
}
}
for(int j = 0; j < w; j++)
{
ct_V.at<float>(0,j) = vert.at<float>(0,j);
for(int i = 1; i < h; i++)
{
ct_V.at<float>(i,j) = vert.at<float>(i,j) + ct_V.at<float>(i-1,j);
}
}
}
}
void Domain_Filter::filter(const Mat &img, Mat &res, float sigma_s = 60, float sigma_r = 0.4, int flags = 1)
{
int no_of_iter = 3;
int h = img.size().height;
int w = img.size().width;
float sigma_h = sigma_s;
init(img,flags,sigma_s,sigma_r);
if(flags == 1)
{
Mat vert_t = vert.t();
for(int i=0;i<no_of_iter;i++)
{
sigma_h = (float) (sigma_s * sqrt(3.0) * pow(2.0,(no_of_iter - (i+1))) / sqrt(pow(4.0,no_of_iter) -1));
compute_Rfilter(O, horiz, sigma_h);
O_t = O.t();
compute_Rfilter(O_t, vert_t, sigma_h);
O = O_t.t();
}
}
else if(flags == 2)
{
Mat vert_t = ct_V.t();
Mat temp = Mat(h,w,CV_32FC1);
Mat temp1 = Mat(w,h,CV_32FC1);
float radius;
for(int i=0;i<no_of_iter;i++)
{
sigma_h = (float) (sigma_s * sqrt(3.0) * pow(2.0,(no_of_iter - (i+1))) / sqrt(pow(4.0,no_of_iter) -1));
radius = (float) sqrt(3.0) * sigma_h;
compute_NCfilter(O, ct_H, temp,radius);
O_t = O.t();
compute_NCfilter(O_t, vert_t, temp1, radius);
O = O_t.t();
}
}
res = O.clone();
}
void Domain_Filter::pencil_sketch(const Mat &img, Mat &sketch, Mat &color_res, float sigma_s, float sigma_r, float shade_factor)
{
int no_of_iter = 3;
init(img,2,sigma_s,sigma_r);
int h = img.size().height;
int w = img.size().width;
/////////////////////// convert to YCBCR model for color pencil drawing //////////////////////////////////////////////////////
Mat color_sketch = Mat(h,w,CV_32FC3);
cvtColor(img,color_sketch,COLOR_BGR2YCrCb);
vector <Mat> YUV_channel;
Mat vert_t = ct_V.t();
float sigma_h = sigma_s;
Mat penx = Mat(h,w,CV_32FC1);
Mat pen_res = Mat::zeros(h,w,CV_32FC1);
Mat peny = Mat(w,h,CV_32FC1);
Mat peny_t;
float radius;
for(int i=0;i<no_of_iter;i++)
{
sigma_h = (float) (sigma_s * sqrt(3.0) * pow(2.0,(no_of_iter - (i+1))) / sqrt(pow(4.0,no_of_iter) -1));
radius = (float) sqrt(3.0) * sigma_h;
compute_boxfilter(O, ct_H, penx, radius);
O_t = O.t();
compute_boxfilter(O_t, vert_t, peny, radius);
O = O_t.t();
peny_t = peny.t();
for(int k=0;k<h;k++)
for(int j=0;j<w;j++)
pen_res.at<float>(k,j) = (shade_factor * (penx.at<float>(k,j) + peny_t.at<float>(k,j)));
if(i==0)
{
sketch = pen_res.clone();
split(color_sketch,YUV_channel);
pen_res.copyTo(YUV_channel[0]);
merge(YUV_channel,color_sketch);
cvtColor(color_sketch,color_res,COLOR_YCrCb2BGR);
}
}
}

View File

@ -0,0 +1,302 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
#ifdef cl_amd_printf
#pragma OPENCL_EXTENSION cl_amd_printf:enable
#endif
#ifdef DOUBLE_SUPPORT
#ifdef cl_amd_fp64
#pragma OPENCL EXTENSION cl_amd_fp64:enable
#elif defined cl_khr_fp64
#pragma OPENCL EXTENSION cl_khr_fp64:enable
#endif
#endif
#ifdef OP_CALC_WEIGHTS
__kernel void calcAlmostDist2Weight(__global wlut_t * almostDist2Weight, int almostMaxDist,
FT almostDist2ActualDistMultiplier, int fixedPointMult,
w_t den, FT WEIGHT_THRESHOLD)
{
int almostDist = get_global_id(0);
if (almostDist < almostMaxDist)
{
FT dist = almostDist * almostDist2ActualDistMultiplier;
#ifdef ABS
w_t w = exp((w_t)(-dist*dist) * den);
#else
w_t w = exp((w_t)(-dist) * den);
#endif
wlut_t weight = convert_wlut_t(fixedPointMult * (isnan(w) ? (w_t)1.0 : w));
almostDist2Weight[almostDist] =
weight < (wlut_t)(WEIGHT_THRESHOLD * fixedPointMult) ? (wlut_t)0 : weight;
}
}
#elif defined OP_CALC_FASTNLMEANS
#define noconvert
#define SEARCH_SIZE_SQ (SEARCH_SIZE * SEARCH_SIZE)
inline int calcDist(pixel_t a, pixel_t b)
{
#ifdef ABS
int_t retval = convert_int_t(abs_diff(a, b));
#else
int_t diff = convert_int_t(a) - convert_int_t(b);
int_t retval = diff * diff;
#endif
#if cn == 1
return retval;
#elif cn == 2
return retval.x + retval.y;
#elif cn == 3
return retval.x + retval.y + retval.z;
#elif cn == 4
return retval.x + retval.y + retval.z + retval.w;
#else
#error "cn should be either 1, 2, 3 or 4"
#endif
}
#ifdef ABS
inline int calcDistUpDown(pixel_t down_value, pixel_t down_value_t, pixel_t up_value, pixel_t up_value_t)
{
return calcDist(down_value, down_value_t) - calcDist(up_value, up_value_t);
}
#else
inline int calcDistUpDown(pixel_t down_value, pixel_t down_value_t, pixel_t up_value, pixel_t up_value_t)
{
int_t A = convert_int_t(down_value) - convert_int_t(down_value_t);
int_t B = convert_int_t(up_value) - convert_int_t(up_value_t);
int_t retval = (A - B) * (A + B);
#if cn == 1
return retval;
#elif cn == 2
return retval.x + retval.y;
#elif cn == 3
return retval.x + retval.y + retval.z;
#elif cn == 4
return retval.x + retval.y + retval.z + retval.w;
#else
#error "cn should be either 1, 2, 3 or 4"
#endif
}
#endif
#define COND if (x == 0 && y == 0)
inline void calcFirstElementInRow(__global const uchar * src, int src_step, int src_offset,
__local int * dists, int y, int x, int id,
__global int * col_dists, __global int * up_col_dists)
{
y -= TEMPLATE_SIZE2;
int sx = x - SEARCH_SIZE2, sy = y - SEARCH_SIZE2;
int col_dists_current_private[TEMPLATE_SIZE];
for (int i = id; i < SEARCH_SIZE_SQ; i += CTA_SIZE)
{
int dist = 0, value;
__global const pixel_t * src_template = (__global const pixel_t *)(src +
mad24(sy + i / SEARCH_SIZE, src_step, mad24(psz, sx + i % SEARCH_SIZE, src_offset)));
__global const pixel_t * src_current = (__global const pixel_t *)(src + mad24(y, src_step, mad24(psz, x, src_offset)));
__global int * col_dists_current = col_dists + i * TEMPLATE_SIZE;
#pragma unroll
for (int j = 0; j < TEMPLATE_SIZE; ++j)
col_dists_current_private[j] = 0;
for (int ty = 0; ty < TEMPLATE_SIZE; ++ty)
{
#pragma unroll
for (int tx = -TEMPLATE_SIZE2; tx <= TEMPLATE_SIZE2; ++tx)
{
value = calcDist(src_template[tx], src_current[tx]);
col_dists_current_private[tx + TEMPLATE_SIZE2] += value;
dist += value;
}
src_current = (__global const pixel_t *)((__global const uchar *)src_current + src_step);
src_template = (__global const pixel_t *)((__global const uchar *)src_template + src_step);
}
#pragma unroll
for (int j = 0; j < TEMPLATE_SIZE; ++j)
col_dists_current[j] = col_dists_current_private[j];
dists[i] = dist;
up_col_dists[0 + i] = col_dists[TEMPLATE_SIZE - 1];
}
}
inline void calcElementInFirstRow(__global const uchar * src, int src_step, int src_offset,
__local int * dists, int y, int x0, int x, int id, int first,
__global int * col_dists, __global int * up_col_dists)
{
x += TEMPLATE_SIZE2;
y -= TEMPLATE_SIZE2;
int sx = x - SEARCH_SIZE2, sy = y - SEARCH_SIZE2;
for (int i = id; i < SEARCH_SIZE_SQ; i += CTA_SIZE)
{
__global const pixel_t * src_current = (__global const pixel_t *)(src + mad24(y, src_step, mad24(psz, x, src_offset)));
__global const pixel_t * src_template = (__global const pixel_t *)(src +
mad24(sy + i / SEARCH_SIZE, src_step, mad24(psz, sx + i % SEARCH_SIZE, src_offset)));
__global int * col_dists_current = col_dists + TEMPLATE_SIZE * i;
int col_dist = 0;
#pragma unroll
for (int ty = 0; ty < TEMPLATE_SIZE; ++ty)
{
col_dist += calcDist(src_current[0], src_template[0]);
src_current = (__global const pixel_t *)((__global const uchar *)src_current + src_step);
src_template = (__global const pixel_t *)((__global const uchar *)src_template + src_step);
}
dists[i] += col_dist - col_dists_current[first];
col_dists_current[first] = col_dist;
up_col_dists[mad24(x0, SEARCH_SIZE_SQ, i)] = col_dist;
}
}
inline void calcElement(__global const uchar * src, int src_step, int src_offset,
__local int * dists, int y, int x0, int x, int id, int first,
__global int * col_dists, __global int * up_col_dists)
{
int sx = x + TEMPLATE_SIZE2;
int sy_up = y - TEMPLATE_SIZE2 - 1;
int sy_down = y + TEMPLATE_SIZE2;
pixel_t up_value = *(__global const pixel_t *)(src + mad24(sy_up, src_step, mad24(psz, sx, src_offset)));
pixel_t down_value = *(__global const pixel_t *)(src + mad24(sy_down, src_step, mad24(psz, sx, src_offset)));
sx -= SEARCH_SIZE2;
sy_up -= SEARCH_SIZE2;
sy_down -= SEARCH_SIZE2;
for (int i = id; i < SEARCH_SIZE_SQ; i += CTA_SIZE)
{
int wx = i % SEARCH_SIZE, wy = i / SEARCH_SIZE;
pixel_t up_value_t = *(__global const pixel_t *)(src + mad24(sy_up + wy, src_step, mad24(psz, sx + wx, src_offset)));
pixel_t down_value_t = *(__global const pixel_t *)(src + mad24(sy_down + wy, src_step, mad24(psz, sx + wx, src_offset)));
__global int * col_dists_current = col_dists + mad24(i, TEMPLATE_SIZE, first);
__global int * up_col_dists_current = up_col_dists + mad24(x0, SEARCH_SIZE_SQ, i);
int col_dist = up_col_dists_current[0] + calcDistUpDown(down_value, down_value_t, up_value, up_value_t);
dists[i] += col_dist - col_dists_current[0];
col_dists_current[0] = col_dist;
up_col_dists_current[0] = col_dist;
}
}
inline void convolveWindow(__global const uchar * src, int src_step, int src_offset,
__local int * dists, __global const wlut_t * almostDist2Weight,
__global uchar * dst, int dst_step, int dst_offset,
int y, int x, int id, __local weight_t * weights_local,
__local sum_t * weighted_sum_local, int almostTemplateWindowSizeSqBinShift)
{
int sx = x - SEARCH_SIZE2, sy = y - SEARCH_SIZE2;
weight_t weights = (weight_t)0;
sum_t weighted_sum = (sum_t)0;
for (int i = id; i < SEARCH_SIZE_SQ; i += CTA_SIZE)
{
int src_index = mad24(sy + i / SEARCH_SIZE, src_step, mad24(i % SEARCH_SIZE + sx, psz, src_offset));
sum_t src_value = convert_sum_t(*(__global const pixel_t *)(src + src_index));
int almostAvgDist = dists[i] >> almostTemplateWindowSizeSqBinShift;
weight_t weight = convert_weight_t(almostDist2Weight[almostAvgDist]);
weights += weight;
weighted_sum += (sum_t)weight * src_value;
}
weights_local[id] = weights;
weighted_sum_local[id] = weighted_sum;
barrier(CLK_LOCAL_MEM_FENCE);
for (int lsize = CTA_SIZE >> 1; lsize > 2; lsize >>= 1)
{
if (id < lsize)
{
int id2 = lsize + id;
weights_local[id] += weights_local[id2];
weighted_sum_local[id] += weighted_sum_local[id2];
}
barrier(CLK_LOCAL_MEM_FENCE);
}
if (id == 0)
{
int dst_index = mad24(y, dst_step, mad24(psz, x, dst_offset));
sum_t weighted_sum_local_0 = weighted_sum_local[0] + weighted_sum_local[1] +
weighted_sum_local[2] + weighted_sum_local[3];
weight_t weights_local_0 = weights_local[0] + weights_local[1] + weights_local[2] + weights_local[3];
*(__global pixel_t *)(dst + dst_index) = convert_pixel_t(weighted_sum_local_0 / (sum_t)weights_local_0);
}
}
__kernel void fastNlMeansDenoising(__global const uchar * src, int src_step, int src_offset,
__global uchar * dst, int dst_step, int dst_offset, int dst_rows, int dst_cols,
__global const wlut_t * almostDist2Weight, __global uchar * buffer,
int almostTemplateWindowSizeSqBinShift)
{
int block_x = get_group_id(0), nblocks_x = get_num_groups(0);
int block_y = get_group_id(1);
int id = get_local_id(0), first;
__local int dists[SEARCH_SIZE_SQ];
__local weight_t weights[CTA_SIZE];
__local sum_t weighted_sum[CTA_SIZE];
int x0 = block_x * BLOCK_COLS, x1 = min(x0 + BLOCK_COLS, dst_cols);
int y0 = block_y * BLOCK_ROWS, y1 = min(y0 + BLOCK_ROWS, dst_rows);
// for each group we need SEARCH_SIZE_SQ * TEMPLATE_SIZE integer buffer for storing part column sum for current element
// and SEARCH_SIZE_SQ * BLOCK_COLS integer buffer for storing last column sum for each element of search window of up row
int block_data_start = SEARCH_SIZE_SQ * (mad24(block_y, dst_cols, x0) + mad24(block_y, nblocks_x, block_x) * TEMPLATE_SIZE);
__global int * col_dists = (__global int *)(buffer + block_data_start * sizeof(int));
__global int * up_col_dists = col_dists + SEARCH_SIZE_SQ * TEMPLATE_SIZE;
for (int y = y0; y < y1; ++y)
for (int x = x0; x < x1; ++x)
{
if (x == x0)
{
calcFirstElementInRow(src, src_step, src_offset, dists, y, x, id, col_dists, up_col_dists);
first = 0;
}
else
{
if (y == y0)
calcElementInFirstRow(src, src_step, src_offset, dists, y, x - x0, x, id, first, col_dists, up_col_dists);
else
calcElement(src, src_step, src_offset, dists, y, x - x0, x, id, first, col_dists, up_col_dists);
first = (first + 1) % TEMPLATE_SIZE;
}
convolveWindow(src, src_step, src_offset, dists, almostDist2Weight, dst, dst_step, dst_offset,
y, x, id, weights, weighted_sum, almostTemplateWindowSizeSqBinShift);
}
}
#endif

View File

@ -0,0 +1,52 @@
/*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 __OPENCV_PRECOMP_H__
#define __OPENCV_PRECOMP_H__
#include "opencv2/core/private.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/photo.hpp"
#include "opencv2/core/ocl.hpp"
#include "opencv2/imgproc.hpp"
#endif

View File

@ -0,0 +1,144 @@
/*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 "seamless_cloning.hpp"
using namespace std;
using namespace cv;
static Mat checkMask(InputArray _mask, Size size)
{
Mat mask = _mask.getMat();
Mat gray;
if (mask.channels() > 1)
cvtColor(mask, gray, COLOR_BGRA2GRAY);
else
{
if (mask.empty())
gray = Mat(size.height, size.width, CV_8UC1, Scalar(255));
else
mask.copyTo(gray);
}
return gray;
}
void cv::seamlessClone(InputArray _src, InputArray _dst, InputArray _mask, Point p, OutputArray _blend, int flags)
{
CV_INSTRUMENT_REGION();
const Mat src = _src.getMat();
const Mat dest = _dst.getMat();
Mat mask = checkMask(_mask, src.size());
dest.copyTo(_blend);
Mat blend = _blend.getMat();
Mat mask_inner = mask(Rect(1, 1, mask.cols - 2, mask.rows - 2));
copyMakeBorder(mask_inner, mask, 1, 1, 1, 1, BORDER_ISOLATED | BORDER_CONSTANT, Scalar(0));
Rect roi_s = boundingRect(mask);
if (roi_s.empty()) return;
Rect roi_d(p.x - roi_s.width / 2, p.y - roi_s.height / 2, roi_s.width, roi_s.height);
Mat destinationROI = dest(roi_d).clone();
Mat sourceROI = Mat::zeros(roi_s.height, roi_s.width, src.type());
src(roi_s).copyTo(sourceROI,mask(roi_s));
Mat maskROI = mask(roi_s);
Mat recoveredROI = blend(roi_d);
Cloning obj;
obj.normalClone(destinationROI,sourceROI,maskROI,recoveredROI,flags);
}
void cv::colorChange(InputArray _src, InputArray _mask, OutputArray _dst, float red, float green, float blue)
{
CV_INSTRUMENT_REGION();
Mat src = _src.getMat();
Mat mask = checkMask(_mask, src.size());
_dst.create(src.size(), src.type());
Mat blend = _dst.getMat();
Mat cs_mask = Mat::zeros(src.size(), src.type());
src.copyTo(cs_mask, mask);
Cloning obj;
obj.localColorChange(src, cs_mask, mask, blend, red, green, blue);
}
void cv::illuminationChange(InputArray _src, InputArray _mask, OutputArray _dst, float alpha, float beta)
{
CV_INSTRUMENT_REGION();
Mat src = _src.getMat();
Mat mask = checkMask(_mask, src.size());
_dst.create(src.size(), src.type());
Mat blend = _dst.getMat();
Mat cs_mask = Mat::zeros(src.size(), src.type());
src.copyTo(cs_mask, mask);
Cloning obj;
obj.illuminationChange(src, cs_mask, mask, blend, alpha, beta);
}
void cv::textureFlattening(InputArray _src, InputArray _mask, OutputArray _dst,
float low_threshold, float high_threshold, int kernel_size)
{
CV_INSTRUMENT_REGION();
Mat src = _src.getMat();
Mat mask = checkMask(_mask, src.size());
_dst.create(src.size(), src.type());
Mat blend = _dst.getMat();
Mat cs_mask = Mat::zeros(src.size(), src.type());
src.copyTo(cs_mask, mask);
Cloning obj;
obj.textureFlatten(src, cs_mask, mask, low_threshold, high_threshold, kernel_size, blend);
}

View 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) 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 CV_SEAMLESS_CLONING_HPP___
#define CV_SEAMLESS_CLONING_HPP___
#include "precomp.hpp"
#include "opencv2/photo.hpp"
#include <vector>
namespace cv
{
class Cloning
{
public:
void normalClone(const cv::Mat& destination, const cv::Mat &mask, const cv::Mat &wmask, cv::Mat &cloned, int flag);
void illuminationChange(cv::Mat &I, cv::Mat &mask, cv::Mat &wmask, cv::Mat &cloned, float alpha, float beta);
void localColorChange(cv::Mat &I, cv::Mat &mask, cv::Mat &wmask, cv::Mat &cloned, float red_mul, float green_mul, float blue_mul);
void textureFlatten(cv::Mat &I, cv::Mat &mask, cv::Mat &wmask, float low_threshold, float high_threhold, int kernel_size, cv::Mat &cloned);
protected:
void initVariables(const cv::Mat &destination, const cv::Mat &binaryMask);
void computeDerivatives(const cv::Mat &destination, const cv::Mat &patch, const cv::Mat &binaryMask);
void scalarProduct(cv::Mat mat, float r, float g, float b);
void poisson(const cv::Mat &destination);
void evaluate(const cv::Mat &I, const cv::Mat &wmask, const cv::Mat &cloned);
void dst(const Mat& src, Mat& dest, bool invert = false);
void solve(const Mat &img, Mat& mod_diff, Mat &result);
void poissonSolver(const cv::Mat &img, cv::Mat &gxx , cv::Mat &gyy, cv::Mat &result);
void arrayProduct(const cv::Mat& lhs, const cv::Mat& rhs, cv::Mat& result) const;
void computeGradientX(const cv::Mat &img, cv::Mat &gx);
void computeGradientY(const cv::Mat &img, cv::Mat &gy);
void computeLaplacianX(const cv::Mat &img, cv::Mat &gxx);
void computeLaplacianY(const cv::Mat &img, cv::Mat &gyy);
private:
std::vector <cv::Mat> rgbx_channel, rgby_channel, output;
cv::Mat destinationGradientX, destinationGradientY;
cv::Mat patchGradientX, patchGradientY;
cv::Mat binaryMaskFloat, binaryMaskFloatInverted;
std::vector<float> filter_X, filter_Y;
};
}
#endif

View File

@ -0,0 +1,453 @@
/*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 "seamless_cloning.hpp"
using namespace cv;
using namespace std;
void Cloning::computeGradientX( const Mat &img, Mat &gx)
{
Mat kernel = Mat::zeros(1, 3, CV_8S);
kernel.at<char>(0,2) = 1;
kernel.at<char>(0,1) = -1;
if(img.channels() == 3)
{
filter2D(img, gx, CV_32F, kernel);
}
else if (img.channels() == 1)
{
filter2D(img, gx, CV_32F, kernel);
cvtColor(gx, gx, COLOR_GRAY2BGR);
}
}
void Cloning::computeGradientY( const Mat &img, Mat &gy)
{
Mat kernel = Mat::zeros(3, 1, CV_8S);
kernel.at<char>(2,0) = 1;
kernel.at<char>(1,0) = -1;
if(img.channels() == 3)
{
filter2D(img, gy, CV_32F, kernel);
}
else if (img.channels() == 1)
{
filter2D(img, gy, CV_32F, kernel);
cvtColor(gy, gy, COLOR_GRAY2BGR);
}
}
void Cloning::computeLaplacianX( const Mat &img, Mat &laplacianX)
{
Mat kernel = Mat::zeros(1, 3, CV_8S);
kernel.at<char>(0,0) = -1;
kernel.at<char>(0,1) = 1;
filter2D(img, laplacianX, CV_32F, kernel);
}
void Cloning::computeLaplacianY( const Mat &img, Mat &laplacianY)
{
Mat kernel = Mat::zeros(3, 1, CV_8S);
kernel.at<char>(0,0) = -1;
kernel.at<char>(1,0) = 1;
filter2D(img, laplacianY, CV_32F, kernel);
}
void Cloning::dst(const Mat& src, Mat& dest, bool invert)
{
Mat temp = Mat::zeros(src.rows, 2 * src.cols + 2, CV_32F);
int flag = invert ? DFT_ROWS + DFT_SCALE + DFT_INVERSE: DFT_ROWS;
src.copyTo(temp(Rect(1,0, src.cols, src.rows)));
for(int j = 0 ; j < src.rows ; ++j)
{
float * tempLinePtr = temp.ptr<float>(j);
const float * srcLinePtr = src.ptr<float>(j);
for(int i = 0 ; i < src.cols ; ++i)
{
tempLinePtr[src.cols + 2 + i] = - srcLinePtr[src.cols - 1 - i];
}
}
Mat planes[] = {temp, Mat::zeros(temp.size(), CV_32F)};
Mat complex;
merge(planes, 2, complex);
dft(complex, complex, flag);
split(complex, planes);
temp = Mat::zeros(src.cols, 2 * src.rows + 2, CV_32F);
for(int j = 0 ; j < src.cols ; ++j)
{
float * tempLinePtr = temp.ptr<float>(j);
for(int i = 0 ; i < src.rows ; ++i)
{
float val = planes[1].ptr<float>(i)[j + 1];
tempLinePtr[i + 1] = val;
tempLinePtr[temp.cols - 1 - i] = - val;
}
}
Mat planes2[] = {temp, Mat::zeros(temp.size(), CV_32F)};
merge(planes2, 2, complex);
dft(complex, complex, flag);
split(complex, planes2);
temp = planes2[1].t();
temp(Rect( 0, 1, src.cols, src.rows)).copyTo(dest);
}
void Cloning::solve(const Mat &img, Mat& mod_diff, Mat &result)
{
const int w = img.cols;
const int h = img.rows;
Mat res;
dst(mod_diff, res);
for(int j = 0 ; j < h-2; j++)
{
float * resLinePtr = res.ptr<float>(j);
for(int i = 0 ; i < w-2; i++)
{
resLinePtr[i] /= (filter_X[i] + filter_Y[j] - 4);
}
}
dst(res, mod_diff, true);
unsigned char * resLinePtr = result.ptr<unsigned char>(0);
const unsigned char * imgLinePtr = img.ptr<unsigned char>(0);
const float * interpLinePtr = NULL;
//first col
for(int i = 0 ; i < w ; ++i)
result.ptr<unsigned char>(0)[i] = img.ptr<unsigned char>(0)[i];
for(int j = 1 ; j < h-1 ; ++j)
{
resLinePtr = result.ptr<unsigned char>(j);
imgLinePtr = img.ptr<unsigned char>(j);
interpLinePtr = mod_diff.ptr<float>(j-1);
//first row
resLinePtr[0] = imgLinePtr[0];
for(int i = 1 ; i < w-1 ; ++i)
{
//saturate cast is not used here, because it behaves differently from the previous implementation
//most notable, saturate_cast rounds before truncating, here it's the opposite.
float value = interpLinePtr[i-1];
if(value < 0.)
resLinePtr[i] = 0;
else if (value > 255.0)
resLinePtr[i] = 255;
else
resLinePtr[i] = static_cast<unsigned char>(value);
}
//last row
resLinePtr[w-1] = imgLinePtr[w-1];
}
//last col
resLinePtr = result.ptr<unsigned char>(h-1);
imgLinePtr = img.ptr<unsigned char>(h-1);
for(int i = 0 ; i < w ; ++i)
resLinePtr[i] = imgLinePtr[i];
}
void Cloning::poissonSolver(const Mat &img, Mat &laplacianX , Mat &laplacianY, Mat &result)
{
const int w = img.cols;
const int h = img.rows;
Mat lap = laplacianX + laplacianY;
Mat bound = img.clone();
rectangle(bound, Point(1, 1), Point(img.cols-2, img.rows-2), Scalar::all(0), -1);
Mat boundary_points;
Laplacian(bound, boundary_points, CV_32F);
boundary_points = lap - boundary_points;
Mat mod_diff = boundary_points(Rect(1, 1, w-2, h-2));
solve(img,mod_diff,result);
}
void Cloning::initVariables(const Mat &destination, const Mat &binaryMask)
{
destinationGradientX = Mat(destination.size(),CV_32FC3);
destinationGradientY = Mat(destination.size(),CV_32FC3);
patchGradientX = Mat(destination.size(),CV_32FC3);
patchGradientY = Mat(destination.size(),CV_32FC3);
binaryMaskFloat = Mat(binaryMask.size(),CV_32FC1);
binaryMaskFloatInverted = Mat(binaryMask.size(),CV_32FC1);
//init of the filters used in the dst
const int w = destination.cols;
filter_X.resize(w - 2);
double scale = CV_PI / (w - 1);
for(int i = 0 ; i < w-2 ; ++i)
filter_X[i] = 2.0f * (float)std::cos(scale * (i + 1));
const int h = destination.rows;
filter_Y.resize(h - 2);
scale = CV_PI / (h - 1);
for(int j = 0 ; j < h - 2 ; ++j)
filter_Y[j] = 2.0f * (float)std::cos(scale * (j + 1));
}
void Cloning::computeDerivatives(const Mat& destination, const Mat &patch, const Mat &binaryMask)
{
initVariables(destination, binaryMask);
computeGradientX(destination, destinationGradientX);
computeGradientY(destination, destinationGradientY);
computeGradientX(patch, patchGradientX);
computeGradientY(patch, patchGradientY);
Mat Kernel(Size(3, 3), CV_8UC1);
Kernel.setTo(Scalar(1));
erode(binaryMask, binaryMask, Kernel, Point(-1,-1), 3);
binaryMask.convertTo(binaryMaskFloat, CV_32FC1, 1.0/255.0);
}
void Cloning::scalarProduct(Mat mat, float r, float g, float b)
{
vector <Mat> channels;
split(mat,channels);
multiply(channels[2],r,channels[2]);
multiply(channels[1],g,channels[1]);
multiply(channels[0],b,channels[0]);
merge(channels,mat);
}
void Cloning::arrayProduct(const cv::Mat& lhs, const cv::Mat& rhs, cv::Mat& result) const
{
vector <Mat> lhs_channels;
vector <Mat> result_channels;
split(lhs,lhs_channels);
split(result,result_channels);
for(int chan = 0 ; chan < 3 ; ++chan)
multiply(lhs_channels[chan],rhs,result_channels[chan]);
merge(result_channels,result);
}
void Cloning::poisson(const Mat &destination)
{
Mat laplacianX = destinationGradientX + patchGradientX;
Mat laplacianY = destinationGradientY + patchGradientY;
computeLaplacianX(laplacianX,laplacianX);
computeLaplacianY(laplacianY,laplacianY);
split(laplacianX,rgbx_channel);
split(laplacianY,rgby_channel);
split(destination,output);
for(int chan = 0 ; chan < 3 ; ++chan)
{
poissonSolver(output[chan], rgbx_channel[chan], rgby_channel[chan], output[chan]);
}
}
void Cloning::evaluate(const Mat &I, const Mat &wmask, const Mat &cloned)
{
bitwise_not(wmask,wmask);
wmask.convertTo(binaryMaskFloatInverted,CV_32FC1,1.0/255.0);
arrayProduct(destinationGradientX, binaryMaskFloatInverted, destinationGradientX);
arrayProduct(destinationGradientY, binaryMaskFloatInverted, destinationGradientY);
poisson(I);
merge(output,cloned);
}
void Cloning::normalClone(const Mat &destination, const Mat &patch, const Mat &binaryMask, Mat &cloned, int flag)
{
const int w = destination.cols;
const int h = destination.rows;
const int channel = destination.channels();
const int n_elem_in_line = w * channel;
computeDerivatives(destination,patch,binaryMask);
switch(flag)
{
case NORMAL_CLONE:
arrayProduct(patchGradientX, binaryMaskFloat, patchGradientX);
arrayProduct(patchGradientY, binaryMaskFloat, patchGradientY);
break;
case MIXED_CLONE:
{
AutoBuffer<int> maskIndices(n_elem_in_line);
for (int i = 0; i < n_elem_in_line; ++i)
maskIndices[i] = i / channel;
for(int i=0;i < h; i++)
{
float * patchXLinePtr = patchGradientX.ptr<float>(i);
float * patchYLinePtr = patchGradientY.ptr<float>(i);
const float * destinationXLinePtr = destinationGradientX.ptr<float>(i);
const float * destinationYLinePtr = destinationGradientY.ptr<float>(i);
const float * binaryMaskLinePtr = binaryMaskFloat.ptr<float>(i);
for(int j=0; j < n_elem_in_line; j++)
{
int maskIndex = maskIndices[j];
if(abs(patchXLinePtr[j] - patchYLinePtr[j]) >
abs(destinationXLinePtr[j] - destinationYLinePtr[j]))
{
patchXLinePtr[j] *= binaryMaskLinePtr[maskIndex];
patchYLinePtr[j] *= binaryMaskLinePtr[maskIndex];
}
else
{
patchXLinePtr[j] = destinationXLinePtr[j]
* binaryMaskLinePtr[maskIndex];
patchYLinePtr[j] = destinationYLinePtr[j]
* binaryMaskLinePtr[maskIndex];
}
}
}
}
break;
case MONOCHROME_TRANSFER:
Mat gray;
cvtColor(patch, gray, COLOR_BGR2GRAY );
computeGradientX(gray,patchGradientX);
computeGradientY(gray,patchGradientY);
arrayProduct(patchGradientX, binaryMaskFloat, patchGradientX);
arrayProduct(patchGradientY, binaryMaskFloat, patchGradientY);
break;
}
evaluate(destination,binaryMask,cloned);
}
void Cloning::localColorChange(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float red_mul=1.0,
float green_mul=1.0, float blue_mul=1.0)
{
computeDerivatives(I,mask,wmask);
arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);
arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);
scalarProduct(patchGradientX,red_mul,green_mul,blue_mul);
scalarProduct(patchGradientY,red_mul,green_mul,blue_mul);
evaluate(I,wmask,cloned);
}
void Cloning::illuminationChange(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float alpha, float beta)
{
CV_INSTRUMENT_REGION();
computeDerivatives(I,mask,wmask);
arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);
arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);
Mat mag;
magnitude(patchGradientX,patchGradientY,mag);
Mat multX, multY, multx_temp, multy_temp;
multiply(patchGradientX,pow(alpha,beta),multX);
pow(mag,-1*beta, multx_temp);
multiply(multX,multx_temp, patchGradientX);
patchNaNs(patchGradientX);
multiply(patchGradientY,pow(alpha,beta),multY);
pow(mag,-1*beta, multy_temp);
multiply(multY,multy_temp,patchGradientY);
patchNaNs(patchGradientY);
Mat zeroMask = (patchGradientX != 0);
patchGradientX.copyTo(patchGradientX, zeroMask);
patchGradientY.copyTo(patchGradientY, zeroMask);
evaluate(I,wmask,cloned);
}
void Cloning::textureFlatten(Mat &I, Mat &mask, Mat &wmask, float low_threshold,
float high_threshold, int kernel_size, Mat &cloned)
{
computeDerivatives(I,mask,wmask);
Mat out;
Canny(mask,out,low_threshold,high_threshold,kernel_size);
Mat zeros = Mat::zeros(patchGradientX.size(), CV_32FC3);
Mat zerosMask = (out != 255);
zeros.copyTo(patchGradientX, zerosMask);
zeros.copyTo(patchGradientY, zerosMask);
arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);
arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);
evaluate(I,wmask,cloned);
}

View File

@ -0,0 +1,472 @@
/*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
{
inline void log_(const Mat& src, Mat& dst)
{
max(src, Scalar::all(1e-4), dst);
log(dst, dst);
}
class TonemapImpl CV_FINAL : public Tonemap
{
public:
TonemapImpl(float _gamma) : name("Tonemap"), gamma(_gamma)
{
}
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
Mat src = _src.getMat();
CV_Assert(!src.empty());
CV_Assert(_src.dims() == 2 && _src.type() == CV_32FC3);
_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 CV_OVERRIDE { return gamma; }
void setGamma(float val) CV_OVERRIDE { gamma = val; }
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << name
<< "gamma" << gamma;
}
void read(const FileNode& fn) CV_OVERRIDE
{
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 CV_FINAL : public TonemapDrago
{
public:
TonemapDragoImpl(float _gamma, float _saturation, float _bias) :
name("TonemapDrago"),
gamma(_gamma),
saturation(_saturation),
bias(_bias)
{
}
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
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);
CV_Assert(max > 0);
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 CV_OVERRIDE { return gamma; }
void setGamma(float val) CV_OVERRIDE { gamma = val; }
float getSaturation() const CV_OVERRIDE { return saturation; }
void setSaturation(float val) CV_OVERRIDE { saturation = val; }
float getBias() const CV_OVERRIDE { return bias; }
void setBias(float val) CV_OVERRIDE { bias = val; }
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << name
<< "gamma" << gamma
<< "bias" << bias
<< "saturation" << saturation;
}
void read(const FileNode& fn) CV_OVERRIDE
{
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 TonemapReinhardImpl CV_FINAL : 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) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
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 CV_OVERRIDE { return gamma; }
void setGamma(float val) CV_OVERRIDE { gamma = val; }
float getIntensity() const CV_OVERRIDE { return intensity; }
void setIntensity(float val) CV_OVERRIDE { intensity = val; }
float getLightAdaptation() const CV_OVERRIDE { return light_adapt; }
void setLightAdaptation(float val) CV_OVERRIDE { light_adapt = val; }
float getColorAdaptation() const CV_OVERRIDE { return color_adapt; }
void setColorAdaptation(float val) CV_OVERRIDE { color_adapt = val; }
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << name
<< "gamma" << gamma
<< "intensity" << intensity
<< "light_adapt" << light_adapt
<< "color_adapt" << color_adapt;
}
void read(const FileNode& fn) CV_OVERRIDE
{
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 CV_FINAL : public TonemapMantiuk
{
public:
TonemapMantiukImpl(float _gamma, float _scale, float _saturation) :
name("TonemapMantiuk"),
gamma(_gamma),
scale(_scale),
saturation(_saturation)
{
}
void process(InputArray _src, OutputArray _dst) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
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);
double dprod = p.dot(product);
CV_Assert(fabs(dprod) > 0);
float alpha = rr / static_cast<float>(dprod);
r -= alpha * product;
x += alpha * p;
float new_rr = static_cast<float>(r.dot(r));
CV_Assert(fabs(rr) > 0);
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 CV_OVERRIDE { return gamma; }
void setGamma(float val) CV_OVERRIDE { gamma = val; }
float getScale() const CV_OVERRIDE { return scale; }
void setScale(float val) CV_OVERRIDE { scale = val; }
float getSaturation() const CV_OVERRIDE { return saturation; }
void setSaturation(float val) CV_OVERRIDE { saturation = val; }
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << name
<< "gamma" << gamma
<< "scale" << scale
<< "saturation" << saturation;
}
void read(const FileNode& fn) CV_OVERRIDE
{
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), 0, 0, INTER_LINEAR);
}
}
void calculateSum(std::vector<Mat>& x_contrast, std::vector<Mat>& y_contrast, Mat& sum)
{
if (x_contrast.empty())
return;
const int last = (int)x_contrast.size() - 1;
sum = Mat::zeros(x_contrast[last].size(), CV_32F);
for(int i = last; 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(), 0, 0, INTER_LINEAR);
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);
}
}