这是OpenCV(v4.1.2,/ opencv-4.1.2 / modules / imgproc / src / thresh.cpp)Otsu二值化算法实现的一部分下面的代码计算图像直方图h[N]
(其中N = 256个灰度值),然后进行一些数学运算。我认为历史计算部分存在错误。有两个版本-简单的暴力循环和展开循环版本。并且暴力循环独立于CV_ENABLE_UNROLLED标志运行。
static double
getThreshVal_Otsu_8u( const Mat& _src )
{
Size size = _src.size();
int step = (int) _src.step;
if( _src.isContinuous() )
{
size.width *= size.height;
size.height = 1;
step = size.width;
}
#ifdef HAVE_IPP
unsigned char thresh = 0;
CV_IPP_RUN_FAST(ipp_getThreshVal_Otsu_8u(_src.ptr(), step, size, thresh), thresh);
#endif
const int N = 256;
int i, j, h[N] = {0};
#if CV_ENABLE_UNROLLED
int h_unrolled[3][N] = {};
#endif
for( i = 0; i < size.height; i++ )
{
const uchar* src = _src.ptr() + step*i;
j = 0;
#if CV_ENABLE_UNROLLED
for( ; j <= size.width - 4; j += 4 )
{
int v0 = src[j], v1 = src[j+1];
h[v0]++; h_unrolled[0][v1]++;
v0 = src[j+2]; v1 = src[j+3];
h_unrolled[1][v0]++; h_unrolled[2][v1]++;
}
#endif
for( ; j < size.width; j++ ) <-------------------------------- !!!here
h[src[j]]++;
}
double mu = 0, scale = 1./(size.width*size.height);
for( i = 0; i < N; i++ )
{
#if CV_ENABLE_UNROLLED
h[i] += h_unrolled[0][i] + h_unrolled[1][i] + h_unrolled[2][i];
#endif
mu += i*(double)h[i];
}
mu *= scale;
double mu1 = 0, q1 = 0;
double max_sigma = 0, max_val = 0;
for( i = 0; i < N; i++ )
{
double p_i, q2, mu2, sigma;
p_i = h[i]*scale;
mu1 *= q1;
q1 += p_i;
q2 = 1. - q1;
if( std::min(q1,q2) < FLT_EPSILON || std::max(q1,q2) > 1. - FLT_EPSILON )
continue;
mu1 = (mu1 + i*p_i)/q1;
mu2 = (mu - q1*mu1)/q2;
sigma = q1*q2*(mu1 - mu2)*(mu1 - mu2);
if( sigma > max_sigma )
{
max_sigma = sigma;
max_val = i;
}
}
return max_val;
}
必须有
for( i = 0; i < size.height; i++ )
{
const uchar* src = _src.ptr() + step*i;
j = 0;
#if CV_ENABLE_UNROLLED
for( ; j <= size.width - 4; j += 4 )
{
int v0 = src[j], v1 = src[j+1];
h[v0]++; h_unrolled[0][v1]++;
v0 = src[j+2]; v1 = src[j+3];
h_unrolled[1][v0]++; h_unrolled[2][v1]++;
}
#else
for( ; j < size.width; j++ ) <-------------------------------- !!!here
h[src[j]]++;
#endif
}
不,这不是错误。请注意,j
变量不会在每个循环开始时重置。第一个循环处理4个项目的块(无论它们是什么),直到剩下不到4个为止。此时,j是需要处理的下一项的索引。第二个循环分别处理其余项目。
如果未定义CV_ENABLE_UNROLLED
,则j
为0(因为第一个循环没有更新它,而第二个循环分别处理每个项目。