我正在使用Qt Creator在c++中开发Windows 7。我还使用库OpenCV
我遇到了一个关于函数assert
的奇怪问题。我开发了一个函数,可以对图像进行线性变换,以获得更好的对比度,可以指定我们想要处理的图像的区域(x1,x2,y1,y2),也可以指定容差(Tolmin和Tolmax):我们想要忽略的直方图的百分位数。
我需要确保:
- 图像为16位灰度(Image .type()=CV_16U &&img.channels () = 1)
- 区域坐标正确(正,不超过图像大小…)
- Tolmin,Tolmax正,Tolmin+Tolmax != 1(如果Tolmin+Tolmax=1,将被0除)
我使用了函数assert
void equalizeHist_16U_linear(Mat &img, float Tolmin, float Tolmax, int x1, int x2, int y1, int y2)
{
assert(img.channels() == 1);
assert(img.type() == CV_16U);
assert(x1>=0 && y1>=0 && x2>=x1 && y2>=y1);
assert(y2<img.rows && x2<img.cols);
assert(Tolmin>0 && Tolmax>0);
assert(Tolmin+Tolmax != 1.0);
## code ##
...
}
前5个assert
工作正常,但最后一个assert(Tolmin+Tolmax != 1.0)
不工作。即使Tolmin+Tolmax=1
, assert没有停止执行,程序也会崩溃(除以0)。为了确定,我在调用assert
之前打印了Tolmin+Tolmax
。
void equalizeHist_16U_linear(Mat &img, float Tolmin, float Tolmax, int x1, int x2, int y1, int y2)
{
cout << endl << "Tolmin+Tolmax = " << Tolmin+Tolmax << endl;
assert(img.channels() == 1);
assert(img.type() == CV_16U);
assert(x1>=0 && y1>=0 && x2>=x1 && y2>=y1);
assert(y2<img.rows && x2<img.cols);
assert(Tolmin>0 && Tolmax>0);
assert(Tolmin+Tolmax != 1.0);
## code ##
...
}
显示"Tolmin+Tolmax=1",这次assert
停止了程序的执行!
这怎么可能?为什么显示Tolmin+Tolmax
可以使assert
工作?
我试着添加以下内容:
void equalizeHist_16U_linear(Mat &img, float Tolmin, float Tolmax, int x1, int x2, int y1, int y2)
{
float sum = Tolmin+Tolmax;
assert(img.channels() == 1);
assert(img.type() == CV_16U);
assert(x1>=0 && y1>=0 && x2>=x1 && y2>=y1);
assert(y2<img.rows && x2<img.cols);
assert(Tolmin>0 && Tolmax>0);*/
assert(sum != 1.0);
## code ##
...
}
我的猜测是在你的代码中使用的值(如Tolmin或Tolmax)不能用机器的数字格式表示。我是什么意思?有些数字你可以很容易地想到计算机无法准确地存储它们。计算机必须以有限位数的二进制格式存储数字,因此有些数字不能完全按原样存储。这样想,我们可以考虑(2/3)对吧?但是,当我们必须用有限位数的十进制格式表示这个数字时,我们必须像这样四舍五入:0.66666667,因此我们不能准确地写出这个数字。这种情况也可以发生在机器中,例如,我们不能将数字0.2以二进制格式存储在机器中,因为它的二进制代表数字是无限的,机器只会将这些数字四舍五入到它可以存储的最接近的数字。在您的例子中,您正在将转换为二进制的数与绝对值(1.0)进行比较,即使它们非常接近,在逻辑上它们也是不相同的。所以我的猜测是,Tolmin和Tolmax的总和将非常接近1.0,但不完全是1.0。那么解决方案是什么呢?您应该检查sum是否已经通过了一个任意的区间到1.0。像这样:
assert(abs(sum - 1.0) > 0.0001)
更新:在打印它时,我无法复制您报告的行为,无论是gcc还是MSC++,所以我所能做的就是一个微弱的猜测。当您构建代码时,编译器将在优化阶段对其进行修改。您在两个地方排序计算Tolmin+Tolmax
,首先在cout中,其次在assert中,因此编译器将推断此计算可以完成一次,然后重用它的结果。由于cout对您要显示的数字进行了改革,并且该值非常接近1,因此将对其进行舍入并得到确切的值1,然后结果将在assert中重用。可能是这样,也可能不是,我无法确认,因为我无法复制报告的行为。