首页 > 解决方案 > C++ 中的 OpenCV 拉普拉斯输出不包括负值,但在 Python 中不包括

问题描述

C++在中执行相同的拉普拉斯运算Python似乎会产生不同的结果。值得注意的是,该C++变体不会在结果矩阵中报告负斜率。

我正在使用(C++):

Laplacian(img.clone(), img, CV_16S, 1, 1, 0, BORDER_DEFAULT);

和(Python):

img = cv2.Laplacian(img, ddepth=cv2.CV_16S, ksize=1, scale=1, delta=0, borderType=cv2.BORDER_DEFAULT)

通过将 C++ 数据处理成更友好的文本格式:

outfile.open("cpp.csv");
outfile.close();
for (size_t i = 0; i < img.size().width; i++) {
    outfile.open("cpp.csv", std::ios_base::app);
    for (size_t j = 0; j < img.size().height; j++) {
        outfile << (int16_t)img.at<uchar>(j, i);
        if (j < img.size().height - 1) {
            outfile << ",";
        }
    }
    outfile << std::endl;
    outfile.close();
}

然后我可以将这些值与Numpy.

cpp = np.loadtxt("./cpp.csv", delimiter=',')
print("python lap min:", np.min(img))
print("c++    lap min:", np.min(cpp))

此输出显示 CPP 代码未记录负值:

python lap min: -133
c++    lap min: 0.0

那么这里发生了什么?我肯定犯了某种错误,因为如果图像亮度值不是静态的,那么在某个地方的拉普拉斯变换中肯定有负值。

图片来源

每个测试用例的源图像是 1920x1080 全彩 PNG 图像,具有明显的明暗区域。我正在使用月亮的照片。图像在拉普拉斯变换之前转换为 CV_8UC1。

严格打字

我已明确告知C++要使用默认隐式签名的类型存储数据int16_t,而不仅仅是int. 这种类型与拉普拉斯存储矩阵 ( CV_16S) 的输出相匹配。将显式“签名”添加到“int16_t”会产生相同的结果。我的理解是该at功能需要uchar键入类似于此 SO answer这就是我错了!!!

版本说明

Python OpenCV 版本:4.1.1(或 4.4.0,见编辑部分)

C++ OpenCV 版本:3.4.1(或 4.4.0,见编辑部分)

这些版本(3.4.14.1.1)的文档显示两者之间没有明显差异。

类似问题

C++和之间的拉普拉斯变体Python在 SO 上似乎很常见,但对此没有任何用处。我读:

编辑 - 升级 C++ OpenCV 库

OpenCV从 git 构建了 v.4.4,并C++使用pkg-config --cflags --libs opencv4. 同样的错误仍然存​​在。(cv2.getBuildInformation()说 Python 也在运行 4.4)。

Edit2 - Git 问题的上下文:

我在 github repo ( here ) 上发现了一个类似的问题,其中的解决方案是将 Laplacian 结果存储为新的Mat。我的原始代码使用Laplacian(img.clone(), img, ...)应该在适当的位置产生矩阵的“深拷贝” img,避免了 OP 报告的问题。无论如何,我用一个独特的矩阵测试了我的代码,Laplacian(outputimg, img, ...)结果没有看到任何变化。

Edit3 - 概括问题

我处理了原始代码,这次用 Sobel 和自定义filter2D过滤器替换了拉普拉斯过滤器。观察到相同的结果,Python产生良好的值而C++没有。

Edit4 - 回答后清理

由于这是一个简单的打字错误,我已经删除了大部分无关信息。这使得我最初的尝试可读,但很明显它们最终没有用。

标签: pythonc++opencv

解决方案


您的 C++ 实现正在调用Mat::at<uchar>,这意味着“将此位置的数据解释为uchar(ie uint8_t)”,但您的数据当然是short(ie int16),而不是uchar. 从上的文档中Mat::at,请注意它们指定:

如果矩阵是类型,CV_16S则使用Mat.at<short>(y,x).


推荐阅读