下面的OpenCV源码有没有错误? (没有#else 的#ifdef)
Is there an error in the following OpenCV source code? (#ifdef without #else)
这是来自 OpenCV (v4.1.2, /opencv-4.1.2/modules/imgproc/src/thresh.cpp) 的 Otsu 二值化算法实现的一部分
下面的代码计算图像直方图 h[N]
(其中 N = 256 个灰度值),然后进行一些数学运算。我认为 hist 计算部分有错误。
有两个版本 - 简单的强力循环和展开循环版本。并且强力循环 运行 独立于 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(因为第一个循环不更新它),第二个循环单独处理每个项目。
这是来自 OpenCV (v4.1.2, /opencv-4.1.2/modules/imgproc/src/thresh.cpp) 的 Otsu 二值化算法实现的一部分
下面的代码计算图像直方图 h[N]
(其中 N = 256 个灰度值),然后进行一些数学运算。我认为 hist 计算部分有错误。
有两个版本 - 简单的强力循环和展开循环版本。并且强力循环 运行 独立于 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(因为第一个循环不更新它),第二个循环单独处理每个项目。