sin*_*ium 1 python parameters opencv
根据opencv 的这个文档,这个链接和这个链接也是:
C++:
void fastNlMeansDenoising(InputArray src, OutputArray dst, float h=3, int templateWindowSize=7, int searchWindowSize=21 )
Run Code Online (Sandbox Code Playgroud)
Python:
cv2.fastNlMeansDenoising(src[, dst[, h[, templateWindowSize[, searchWindowSize]]]]) ? dst
Run Code Online (Sandbox Code Playgroud)
参数(简要)如下:
src – 输入图像。
dst – 与 src 具有相同大小和类型的输出图像。
templateWindowSize – 模板补丁的大小(以像素为单位)。应该是奇葩。
searchWindowSize – 窗口的大小(以像素为单位)。应该是奇葩。
h – 调节过滤强度的参数。
据我所知,在Python中,我们可以采取DST /输出变量的方法的是:dst = cv2.method(input, param1, param2, ..., paramx)。而且我们不需要在方法中放置任何东西(即我们不需要这样做:dst = cv2.method(input, None, param1, param2, ..., paramx)。
虽然这适用于不同的 OpenCV 方法,但它不适用于fastNlMeansDenoising。
以下代码将澄清我的问题:
import cv2
import numpy as np
def thresh(filename):
img = cv2.imread(filename)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#without adding None instead of dst
test_1 = cv2.fastNlMeansDenoising(gray, 31, 7, 21)
cv2.imwrite('test_1.jpg', test_1)
# Adding None instead of dst
test_2 = cv2.fastNlMeansDenoising(gray, None, 31, 7, 21)
cv2.imwrite('test_2.jpg', test_2)
# putting dst inside the method
test_3 = np.empty(gray.shape, np.uint8)
cv2.fastNlMeansDenoising(gray, test_3, 31, 7, 21)
cv2.imwrite('test_3.jpg', test_3)
# Adding the input params
test_4 = cv2.fastNlMeansDenoising(gray, h=31, templateWindowSize=7,
searchWindowSize=21)
cv2.imwrite('test_4.jpg', test_4)
blur = cv2.bilateralFilter(gray, 31, 7, 21)
cv2.imwrite('blur.jpg', blur)
blur_ = cv2.bilateralFilter(gray, 31, 7, 21, None)
cv2.imwrite('blur_.jpg', blur_)
blur__ = np.empty(gray.shape, np.uint8)
cv2.bilateralFilter(gray, 31, 7, 21, blur__)
cv2.imwrite('blur__.jpg', blur__)
thresh('test.png')
Run Code Online (Sandbox Code Playgroud)
这是输入图像:
如果您运行代码,您会注意到,test_2.jpg、test_3.jpg 和 test_4.jpg 是相似的。和 test_1.jpg 一样gray(好像test_1没有收到 的输出fastNlMeansDenoising)。
然而,情况并非如此bilateralFilter:blur.jpg、blur_.jpg 和 blur__.jpg 都是相同的,尽管我重复了与fastNlMeansDenoising.
对此有什么解释吗?我们为什么要添加None到fastNlMeansDenoising参数?
fastNlMeansDenoising我们先来看看Python 函数的签名:
cv2.fastNlMeansDenoising(src[, dst[, h[, templateWindowSize[, searchWindowSize]]]]) ? dst
Run Code Online (Sandbox Code Playgroud)
括号的方式([] ) 的嵌套方式意味着第 2-5 个参数是可选的,但只要它们作为位置参数传入,序列就需要保持不变(即您不能跳过任何一个)。
这意味着仅使用位置参数,有 5 种可能性:
cv2.fastNlMeansDenoising(src) ? dst
cv2.fastNlMeansDenoising(src, dst) ? dst
cv2.fastNlMeansDenoising(src, dst, h) ? dst
cv2.fastNlMeansDenoising(src, dst, h, templateWindowSize) ? dst
cv2.fastNlMeansDenoising(src, dst, h, templateWindowSize, searchWindowSize) ? dst
Run Code Online (Sandbox Code Playgroud)
未提供的任何可选参数将使用默认值。使用的默认值可以从相应的 C++ 函数签名中推导出来。
void fastNlMeansDenoising(InputArray src, OutputArray dst, float h=3, int templateWindowSize=7, int searchWindowSize=21)
Run Code Online (Sandbox Code Playgroud)
在过去的3个参数,这是显而易见的- h=3,templateWindowSize=7和searchWindowSize=21。在 Python 绑定中,OutputArray参数隐式具有None(与 C++ API 不同,Python 变体也返回输出)。
考虑到这一点,您的第一个变体
test_1 = cv2.fastNlMeansDenoising(gray, 31, 7, 21)
Run Code Online (Sandbox Code Playgroud)
方法
test_1 = cv2.fastNlMeansDenoising(src=gray, dst=31, h=7, templateWindowSize=21, searchWindowSize=21)
Run Code Online (Sandbox Code Playgroud)
即h比您预期的要小得多,并且templateWindowSize要大得多。这就是结果不同的原因。
我们将探讨为什么设置dst为 31 不会在后面的答案中引起任何明确的错误。
恕我直言,第四个变体是跳过的最佳方式dst:
test_4 = cv2.fastNlMeansDenoising(gray, h=31, templateWindowSize=7, searchWindowSize=21)
Run Code Online (Sandbox Code Playgroud)
显式使用关键字参数时,您不太可能混淆。
第二个变体(None作为第二个参数传递)是可以的。
第三个变体在循环中很有用,它允许您在后续迭代中重用临时数组并避免重新分配(这可能代价高昂)。但是,有一个问题——数组必须完全具有所需的形状和数据类型。如果不是,它不会被修改(但该函数仍将返回一个新分配的数组,该数组包含您需要捕获的结果)。
当您继续阅读时,我们就会明白这样做的原因。
bilateralFilter你提到是bilateralFilter为了比较,所以让我们也来研究一下。
cv.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]]) ? dst
Run Code Online (Sandbox Code Playgroud)
这意味着仅使用位置参数有 3 种可能性来调用它:
cv.bilateralFilter(src, d, sigmaColor, sigmaSpace) ? dst
cv.bilateralFilter(src, d, sigmaColor, sigmaSpace, dst) ? dst
cv.bilateralFilter(src, d, sigmaColor, sigmaSpace, dst, borderType) ? dst
Run Code Online (Sandbox Code Playgroud)
请注意,由于dst参数在序列中出现得更晚,因此您可能只会犯一个错误——而是传入边框类型。
在您的代码示例中,您只使用了 4 个或 5 个参数,甚至从未使用过borderType,并且在所有情况下都dst获得了有意义的值。
总结一下:函数的行为一致,但后面的可选参数dst越少,让自己陷入困境的机会就越少。
由于需要向 Python 公开的 OpenCV 代码库的大小,C++ 函数的包装器会自动生成。由于 API 的复杂性,除非您详细研究实现,否则某些行为可能不会立即显现。(并且由于实际绑定代码是在构建时自动生成的,最好在本地编译 OpenCV 以检查生成的实现)
让我们看一下为 wrap 生成的一段代码fastNlMeansDenoising:
static PyObject* pyopencv_cv_fastNlMeansDenoising(PyObject* , PyObject* args, PyObject* kw)
{
using namespace cv;
{
PyObject* pyobj_src = NULL;
Mat src;
PyObject* pyobj_dst = NULL;
Mat dst;
float h=3;
int templateWindowSize=7;
int searchWindowSize=21;
const char* keywords[] = { "src", "dst", "h", "templateWindowSize", "searchWindowSize", NULL };
if( PyArg_ParseTupleAndKeywords(args, kw, "O|Ofii:fastNlMeansDenoising", (char**)keywords, &pyobj_src, &pyobj_dst, &h, &templateWindowSize, &searchWindowSize) &&
pyopencv_to(pyobj_src, src, ArgInfo("src", 0)) &&
pyopencv_to(pyobj_dst, dst, ArgInfo("dst", 1)) )
{
ERRWRAP2(cv::fastNlMeansDenoising(src, dst, h, templateWindowSize, searchWindowSize));
return pyopencv_from(dst);
}
}
// Clear Python error, try the same for UMat
// Clear Python error, try overload with Mat
// Clear Python error, try overload with UMat
return NULL;
}
Run Code Online (Sandbox Code Playgroud)
首先,PyArg_ParseTupleAndKeywords用于解析函数参数并将它们的值(如果可选和缺失,则保留预设的默认值)分配给相应的 C++ 变量。
需要注意的是,当相应的 C++ 参数的类型为 时Input/OutputArray,它会被解析为 Python 对象(O格式字符串中的 )——这意味着它在这个阶段可以是任何东西。
解析参数后,pyopencv_to用于将 Python 对象转换为cv::Mat. 由于许多 OpenCV 函数(例如cv::add)允许一些输入参数(以及潜在的输出参数)既是数组又是标量,因此 Python 绑定也支持这一点。
转换cv::Mat工作如下:
None,则留空Mat。Mat4 行 1 列和 64 位浮点值的数据类型。将第一行设置为提供的整数值,其余设置为 0。Mat具有n行和 1 列且数据类型为 64 位浮点值n的元组,其中是元组中的元素数。每一行按顺序保存一个元素。这意味着当您调用 时cv2.fastNlMeansDenoising(gray, 31, 7, 21),整数31变成了Mat具有 64 位浮点元素的 4x1 单通道。因此,可以毫无问题地调用底层 C++ 函数。现在,它为什么不抱怨Mat存储输出的大小和数据类型不正确?
OutputArray工的由于 C++ API 使用输出数组参数来支持返回值,因此它需要能够支持在调用函数之前无法确定结果大小的情况。为了解决这个问题,在给定一个空的Mat,或者Mat不正确的形状或数据类型的情况下,Mat重新创建(分配一个新的缓冲区等)以满足要求。由于Mat它基本上是一个指向底层图像缓冲区的智能指针,因此它可以正常工作,并且在 C++ 中可以完全预测(恕我直言)——即使发生重新分配,Mat您作为输出参数提供的实例也将正确引用新数据。
这解释了为什么31as 没问题dst——它产生Mat了错误的形状和类型,但只是重新分配了,一切都很好。
然而,这个不错的特性在 Python API 中引入了一些障碍。当为Input/OutputArray参数提供 numpy 数组时,将Mat创建一个实例,该实例共享保存值的底层缓冲区。这意味着操作很快(因为没有复制数据),并且 numpy 数组会自动反映对Mat. 但是,如果 OpenCVMat由于形状/类型不正确而重新分配,则会分配一个新缓冲区,而原始 numpy 数组保持不变。
这可以很容易地证明:
>>> a = np.ones((3,3), np.uint8)
>>> b = a + 1
>>> c = np.zeros(a.shape, np.float32)
>>> c
array([[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]], dtype=float32)
>>> cv2.add(a, b, c)
array([[3, 3, 3],
[3, 3, 3],
[3, 3, 3]], dtype=uint8)
>>> c
array([[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]], dtype=float32)
>>> d = np.zeros_like(a)
>>> cv2.add(a, b, d)
array([[3, 3, 3],
[3, 3, 3],
[3, 3, 3]], dtype=uint8)
>>> d
array([[3, 3, 3],
[3, 3, 3],
[3, 3, 3]], dtype=uint8)
Run Code Online (Sandbox Code Playgroud)