Commit ba0b4023 authored by Seon-Wook Park's avatar Seon-Wook Park

Apply fixes suggested by Vadim

parent 508092ac
......@@ -47,7 +47,6 @@
*/
#include "xphoto/inpainting.hpp"
#include "xphoto/simple_color_balance.hpp"
#include "xphoto/white_balance.hpp"
#include "xphoto/dct_image_denoising.hpp"
#include "xphoto/grayworld_white_balance.hpp"
#endif
/*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-2011, 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_GRAYWORLD_WHITE_BALANCE_HPP__
#define __OPENCV_GRAYWORLD_WHITE_BALANCE_HPP__
/** @file
@date Jun 3, 2015
@author Seon-Wook Park
*/
#include <opencv2/core.hpp>
namespace cv { namespace xphoto {
//! @addtogroup xphoto
//! @{
/** @brief Implements a simple grayworld white balance algorithm.
The function autowbGrayworld scales the values of pixels based on a
gray-world assumption which states that the average of all channels
should result in a gray image.
This function adds a modification which thresholds pixels based on their
saturation value and only uses pixels below the provided threshold in
finding average pixel values.
Saturation is calculated using the following for a 3-channel RGB image per
pixel I and is in the range [0, 1]:
\f[ \texttt{Saturation} [I] = \frac{\textrm{max}(R,G,B) - \textrm{min}(R,G,B)
}{\textrm{max}(R,G,B)} \f]
A threshold of 1 means that all pixels are used to white-balance, while a
threshold of 0 means no pixels are used. Lower thresholds are useful in
white-balancing saturated images.
Currently only works on images of type @ref CV_8UC3.
@param src Input array.
@param dst Output array of the same size and type as src.
@param thresh Maximum saturation for a pixel to be included in the
gray-world assumption.
@sa balanceWhite
*/
CV_EXPORTS_W void autowbGrayworld(InputArray src, OutputArray dst,
const float thresh = 0.5f);
//! @}
}}
#endif // __OPENCV_GRAYWORLD_WHITE_BALANCE_HPP__
......@@ -85,6 +85,38 @@ namespace xphoto
const float inputMin = 0.0f, const float inputMax = 255.0f,
const float outputMin = 0.0f, const float outputMax = 255.0f);
/** @brief Implements a simple grayworld white balance algorithm.
The function autowbGrayworld scales the values of pixels based on a
gray-world assumption which states that the average of all channels
should result in a gray image.
This function adds a modification which thresholds pixels based on their
saturation value and only uses pixels below the provided threshold in
finding average pixel values.
Saturation is calculated using the following for a 3-channel RGB image per
pixel I and is in the range [0, 1]:
\f[ \texttt{Saturation} [I] = \frac{\textrm{max}(R,G,B) - \textrm{min}(R,G,B)
}{\textrm{max}(R,G,B)} \f]
A threshold of 1 means that all pixels are used to white-balance, while a
threshold of 0 means no pixels are used. Lower thresholds are useful in
white-balancing saturated images.
Currently only works on images of type @ref CV_8UC3.
@param src Input array.
@param dst Output array of the same size and type as src.
@param thresh Maximum saturation for a pixel to be included in the
gray-world assumption.
@sa balanceWhite
*/
CV_EXPORTS_W void autowbGrayworld(InputArray src, OutputArray dst,
float thresh = 0.5f);
//! @}
}
......
......@@ -9,7 +9,7 @@ typedef perf::TestBaseWithParam<Size_WBThresh_t> Size_WBThresh;
PERF_TEST_P( Size_WBThresh, autowbGrayworld,
testing::Combine(
testing::Values( TYPICAL_MAT_SIZES ),
SZ_ALL_HD,
testing::Values( 0.1, 0.5, 1.0 )
)
)
......
......@@ -5,6 +5,9 @@
#include "opencv2/core/utility.hpp"
using namespace cv;
using namespace std;
const char* keys =
{
"{i || input image name}"
......@@ -14,8 +17,8 @@ const char* keys =
int main( int argc, const char** argv )
{
bool printHelp = ( argc == 1 );
printHelp = printHelp || ( argc == 2 && std::string(argv[1]) == "--help" );
printHelp = printHelp || ( argc == 2 && std::string(argv[1]) == "-h" );
printHelp = printHelp || ( argc == 2 && string(argv[1]) == "--help" );
printHelp = printHelp || ( argc == 2 && string(argv[1]) == "-h" );
if ( printHelp )
{
......@@ -25,35 +28,35 @@ int main( int argc, const char** argv )
return 0;
}
cv::CommandLineParser parser(argc, argv, keys);
CommandLineParser parser(argc, argv, keys);
if ( !parser.check() )
{
parser.printErrors();
return -1;
}
std::string inFilename = parser.get<std::string>("i");
std::string outFilename = parser.get<std::string>("o");
string inFilename = parser.get<string>("i");
string outFilename = parser.get<string>("o");
cv::Mat src = cv::imread(inFilename, 1);
Mat src = imread(inFilename, 1);
if ( src.empty() )
{
printf("Cannot read image file: %s\n", inFilename.c_str());
return -1;
}
cv::Mat res(src.size(), src.type());
cv::xphoto::autowbGrayworld(src, res);
Mat res(src.size(), src.type());
xphoto::autowbGrayworld(src, res);
if ( outFilename == "" )
{
cv::namedWindow("after white balance", 1);
cv::imshow("after white balance", res);
namedWindow("after white balance", 1);
imshow("after white balance", res);
cv::waitKey(0);
waitKey(0);
}
else
cv::imwrite(outFilename, res);
imwrite(outFilename, res);
return 0;
}
......@@ -44,7 +44,7 @@
namespace cv { namespace xphoto {
void autowbGrayworld(InputArray _src, OutputArray _dst, const float thresh)
void autowbGrayworld(InputArray _src, OutputArray _dst, float thresh)
{
Mat src = _src.getMat();
......@@ -57,6 +57,7 @@ namespace cv { namespace xphoto {
_dst.create(src.size(), src.type());
Mat dst = _dst.getMat();
CV_Assert(dst.isContinuous());
int width = src.cols,
height = src.rows,
......@@ -66,6 +67,7 @@ namespace cv { namespace xphoto {
// Calculate sum of pixel values of each channel
const uchar* src_data = src.ptr<uchar>(0);
unsigned long sum1 = 0, sum2 = 0, sum3 = 0;
unsigned int thresh255 = round(thresh * 255);
int i = 0;
#if CV_SIMD128
v_uint8x16 v_inB, v_inG, v_inR;
......@@ -73,14 +75,14 @@ namespace cv { namespace xphoto {
v_uint32x4 v_iB1, v_iB2, v_iB3, v_iB4,
v_iG1, v_iG2, v_iG3, v_iG4,
v_iR1, v_iR2, v_iR3, v_iR4,
v_255 = v_setall_u32(255),
v_thresh = v_setall_u32(thresh255),
v_min1, v_min2, v_min3, v_min4,
v_max1, v_max2, v_max3, v_max4,
v_m1, v_m2, v_m3, v_m4,
v_SB = v_setzero_u32(),
v_SG = v_setzero_u32(),
v_SR = v_setzero_u32(),
v_m1, v_m2, v_m3, v_m4;
v_float32x4 v_thresh = v_setall_f32(thresh),
v_min1, v_min2, v_min3, v_min4,
v_max1, v_max2, v_max3, v_max4,
v_sat1, v_sat2, v_sat3, v_sat4;
v_SR = v_setzero_u32();
for ( ; i < N3 - 47; i += 48 )
{
......@@ -102,27 +104,22 @@ namespace cv { namespace xphoto {
v_expand(v_s1, v_iR1, v_iR2);
v_expand(v_s2, v_iR3, v_iR4);
// Get saturation
v_min1 = v_cvt_f32(v_reinterpret_as_s32(v_min(v_iB1, v_min(v_iG1, v_iR1))));
v_min2 = v_cvt_f32(v_reinterpret_as_s32(v_min(v_iB2, v_min(v_iG2, v_iR2))));
v_min3 = v_cvt_f32(v_reinterpret_as_s32(v_min(v_iB3, v_min(v_iG3, v_iR3))));
v_min4 = v_cvt_f32(v_reinterpret_as_s32(v_min(v_iB4, v_min(v_iG4, v_iR4))));
// Get mins and maxs
v_min1 = v_min(v_iB1, v_min(v_iG1, v_iR1));
v_min2 = v_min(v_iB2, v_min(v_iG2, v_iR2));
v_min3 = v_min(v_iB3, v_min(v_iG3, v_iR3));
v_min4 = v_min(v_iB4, v_min(v_iG4, v_iR4));
v_max1 = v_cvt_f32(v_reinterpret_as_s32(v_max(v_iB1, v_max(v_iG1, v_iR1))));
v_max2 = v_cvt_f32(v_reinterpret_as_s32(v_max(v_iB2, v_max(v_iG2, v_iR2))));
v_max3 = v_cvt_f32(v_reinterpret_as_s32(v_max(v_iB3, v_max(v_iG3, v_iR3))));
v_max4 = v_cvt_f32(v_reinterpret_as_s32(v_max(v_iB4, v_max(v_iG4, v_iR4))));
v_sat1 = (v_max1 - v_min1) / v_max1;
v_sat2 = (v_max2 - v_min2) / v_max2;
v_sat3 = (v_max3 - v_min3) / v_max3;
v_sat4 = (v_max4 - v_min4) / v_max4;
v_max1 = v_max(v_iB1, v_max(v_iG1, v_iR1));
v_max2 = v_max(v_iB2, v_max(v_iG2, v_iR2));
v_max3 = v_max(v_iB3, v_max(v_iG3, v_iR3));
v_max4 = v_max(v_iB4, v_max(v_iG4, v_iR4));
// Calculate masks
v_m1 = v_reinterpret_as_u32(v_sat1 <= v_thresh);
v_m2 = v_reinterpret_as_u32(v_sat2 <= v_thresh);
v_m3 = v_reinterpret_as_u32(v_sat3 <= v_thresh);
v_m4 = v_reinterpret_as_u32(v_sat4 <= v_thresh);
v_m1 = ~((v_max1 - v_min1) * v_255 > v_thresh * v_max1);
v_m2 = ~((v_max2 - v_min2) * v_255 > v_thresh * v_max2);
v_m3 = ~((v_max3 - v_min3) * v_255 > v_thresh * v_max3);
v_m4 = ~((v_max4 - v_min4) * v_255 > v_thresh * v_max4);
// Apply mask
v_SB += (v_iB1 & v_m1) + (v_iB2 & v_m2) + (v_iB3 & v_m3) + (v_iB4 & v_m4);
......@@ -135,13 +132,12 @@ namespace cv { namespace xphoto {
sum2 = v_reduce_sum(v_SG);
sum3 = v_reduce_sum(v_SR);
#endif
double minRGB, maxRGB, satur;
unsigned int minRGB, maxRGB;
for ( ; i < N3; i += 3 )
{
minRGB = min(src_data[i], min(src_data[i + 1], src_data[i + 2]));
maxRGB = max(src_data[i], max(src_data[i + 1], src_data[i + 2]));
satur = (maxRGB - minRGB) / maxRGB;
if ( satur > thresh ) continue;
if ( (maxRGB - minRGB) * 255 > thresh255 * maxRGB ) continue;
sum1 += src_data[i];
sum2 += src_data[i + 1];
sum3 += src_data[i + 2];
......@@ -168,64 +164,49 @@ namespace cv { namespace xphoto {
inv3 = (float)((double)inv3 / inv_max);
}
// Fixed point arithmetic, mul by 2^8 then shift back 8 bits
unsigned int i_inv1 = inv1 * (1 << 8),
i_inv2 = inv2 * (1 << 8),
i_inv3 = inv3 * (1 << 8);
// Scale input pixel values
uchar* dst_data = dst.ptr<uchar>(0);
i = 0;
#if CV_SIMD128
v_uint8x16 v_in, v_out;
v_uint32x4 v_i1, v_i2, v_i3, v_i4;
v_float32x4 v_f1, v_f2, v_f3, v_f4,
scal1(inv1, inv2, inv3, inv1),
scal2(inv2, inv3, inv1, inv2),
scal3(inv3, inv1, inv2, inv3),
scal4(inv1, inv2, inv3, 0.f);
for ( ; i < N3 - 14; i += 15 )
v_uint8x16 v_outB, v_outG, v_outR;
v_uint16x8 v_sB1, v_sB2, v_sG1, v_sG2, v_sR1, v_sR2,
v_invB = v_setall_u16(i_inv1),
v_invG = v_setall_u16(i_inv2),
v_invR = v_setall_u16(i_inv3);
for ( ; i < N3 - 47; i += 48 )
{
// Load 16 x 8bit uchars
v_in = v_load(&src_data[i]);
// Split into two vectors of 8 ushorts
v_expand(v_in, v_s1, v_s2);
// Split into four vectors of 4 uints
v_expand(v_s1, v_i1, v_i2);
v_expand(v_s2, v_i3, v_i4);
v_load_deinterleave(&src_data[i], v_inB, v_inG, v_inR);
// Convert into four vectors of 4 floats
v_f1 = v_cvt_f32(v_reinterpret_as_s32(v_i1));
v_f2 = v_cvt_f32(v_reinterpret_as_s32(v_i2));
v_f3 = v_cvt_f32(v_reinterpret_as_s32(v_i3));
v_f4 = v_cvt_f32(v_reinterpret_as_s32(v_i4));
// Split into four int vectors per channel
v_expand(v_inB, v_sB1, v_sB2);
v_expand(v_inG, v_sG1, v_sG2);
v_expand(v_inR, v_sR1, v_sR2);
// Multiply by scaling factors
v_f1 *= scal1;
v_f2 *= scal2;
v_f3 *= scal3;
v_f4 *= scal4;
// Convert back into four vectors of 4 uints
v_i1 = v_reinterpret_as_u32(v_round(v_f1));
v_i2 = v_reinterpret_as_u32(v_round(v_f2));
v_i3 = v_reinterpret_as_u32(v_round(v_f3));
v_i4 = v_reinterpret_as_u32(v_round(v_f4));
// Pack into two vectors of 8 ushorts
v_s1 = v_pack(v_i1, v_i2);
v_s2 = v_pack(v_i3, v_i4);
// Pack into vector of 16 uchars
v_out = v_pack(v_s1, v_s2);
// Store
v_store(&dst_data[i], v_out);
v_sB1 = (v_sB1 * v_invB) >> 8;
v_sB2 = (v_sB2 * v_invB) >> 8;
v_sG1 = (v_sG1 * v_invG) >> 8;
v_sG2 = (v_sG2 * v_invG) >> 8;
v_sR1 = (v_sR1 * v_invR) >> 8;
v_sR2 = (v_sR2 * v_invR) >> 8;
// Pack into vectors of v_uint8x16
v_store_interleave(&dst_data[i], v_pack(v_sB1, v_sB2),
v_pack(v_sG1, v_sG2), v_pack(v_sR1, v_sR2));
}
#endif
for ( ; i < N3; i += 3 )
{
dst_data[i + 0] = (uchar)(src_data[i + 0] * inv1);
dst_data[i + 1] = (uchar)(src_data[i + 1] * inv2);
dst_data[i + 2] = (uchar)(src_data[i + 2] * inv3);
dst_data[i] = (uchar)((src_data[i] * i_inv1) >> 8);
dst_data[i + 1] = (uchar)((src_data[i + 1] * i_inv2) >> 8);
dst_data[i + 2] = (uchar)((src_data[i + 2] * i_inv3) >> 8);
}
}
......
......@@ -4,7 +4,7 @@ namespace cvtest {
using namespace cv;
void ref_autowbGrayworld(InputArray _src, OutputArray _dst, const float thresh)
void ref_autowbGrayworld(InputArray _src, OutputArray _dst, float thresh)
{
Mat src = _src.getMat();
......@@ -20,13 +20,12 @@ namespace cvtest {
const uchar* src_data = src.ptr<uchar>(0);
unsigned long sum1 = 0, sum2 = 0, sum3 = 0;
int i = 0;
double minRGB, maxRGB, satur;
unsigned int minRGB, maxRGB, thresh255 = round(thresh * 255);
for ( ; i < N3; i += 3 )
{
minRGB = std::min(src_data[i], std::min(src_data[i + 1], src_data[i + 2]));
maxRGB = std::max(src_data[i], std::max(src_data[i + 1], src_data[i + 2]));
satur = (maxRGB - minRGB) / maxRGB;
if ( satur > thresh ) continue;
if ( (maxRGB - minRGB) * 255 > thresh255 * maxRGB ) continue;
sum1 += src_data[i];
sum2 += src_data[i + 1];
sum3 += src_data[i + 2];
......@@ -48,14 +47,19 @@ namespace cvtest {
inv3 = (double) inv3 / inv_max;
}
// Fixed point arithmetic, mul by 2^8 then shift back 8 bits
unsigned int i_inv1 = inv1 * (1 << 8),
i_inv2 = inv2 * (1 << 8),
i_inv3 = inv3 * (1 << 8);
// Scale input pixel values
uchar* dst_data = dst.ptr<uchar>(0);
i = 0;
for ( ; i < N3; i += 3 )
{
dst_data[i] = (uchar)(src_data[i] * inv1);
dst_data[i + 1] = (uchar)(src_data[i + 1] * inv2);
dst_data[i + 2] = (uchar)(src_data[i + 2] * inv3);
dst_data[i] = (uchar)((src_data[i] * i_inv1) >> 8);
dst_data[i + 1] = (uchar)((src_data[i + 1] * i_inv2) >> 8);
dst_data[i + 2] = (uchar)((src_data[i + 2] * i_inv3) >> 8);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment