Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何避免在使用opencv Filter2D函数时包含零?_Python_Opencv - Fatal编程技术网

Python 如何避免在使用opencv Filter2D函数时包含零?

Python 如何避免在使用opencv Filter2D函数时包含零?,python,opencv,Python,Opencv,我在一张卫星图像上使用opencv python中的Filter2D函数,该图像的边缘有几个黑色填充值零。在这里,你可以找到我所说的一个例子: 在该图像上使用Filter2D时,来自黑色填充区域的像素被视为有效值,从而创建边缘瑕疵。我怎么能在计算中不包括零呢?例如,在IDL中,我可以使用以下缺少的和无效的字段: output = CONVOL(input, kernel, /EDGE_TRUNCATE, MISSING=0.0, INVALID=0.0, /NAN, /NORMALIZE) 避

我在一张卫星图像上使用opencv python中的Filter2D函数,该图像的边缘有几个黑色填充值零。在这里,你可以找到我所说的一个例子:

在该图像上使用Filter2D时,来自黑色填充区域的像素被视为有效值,从而创建边缘瑕疵。我怎么能在计算中不包括零呢?例如,在IDL中,我可以使用以下缺少的和无效的字段:

output = CONVOL(input, kernel, /EDGE_TRUNCATE, MISSING=0.0, INVALID=0.0, /NAN, /NORMALIZE)

避免边缘问题,但我在opencv中找不到类似的功能。如何解决这个问题?

OpenCV函数中没有掩码或忽略参数。然而,唯一的伪影将在图像外部,即在黑色区域中。默认情况下,只要过滤器的定位点位于边缘但位于黑色像素上方,它就会将过滤结果添加到该像素。但是当锚定位于图像顶部时,黑色值不会向过滤器添加任何内容。所以,一个简单的解决方案是创建一个带有黑色值的遮罩,并将其从过滤后的图像中移除

编辑:确定,因此,从:

提示:使用无效关键字相当于在计算卷积和时将这些值视为0.0。可以使用NORMALIZE关键字完全排除这些点

提示:如果设置了NORMALIZE,并且您的输入数组缺少数据,则设置了INVALID或NAN关键字,然后对于每个结果值,仅使用对该结果值起作用的那些内核值计算比例因子和偏差。这可确保所有结果值在量级上具有可比性,而不考虑任何缺失数据

因此,从这里我们可以看到,通过将无效像素视为0来排除点,然后通过除以不同于内核大小的像素数(即有效像素数)来偏移和

这在OpenCV中是不可能的,至少在内置过滤方法中是不可能的,因为OpenCV不会规范化过滤结果。看,这个方程只是简单的关联,没有除法

现在,您可以做的是手动规范化。这并不难。如果创建了一个掩码,其中值在正常图像内为1,在图像外为0,那么与过滤器2D具有相同内核大小的将在每个位置生成内核窗口内的像素计数。这将是一种标准化图像的方法。然后,您可以简单地屏蔽此boxFilter结果,以便忽略图像边界之外的值,最后,将filter2D结果除以屏蔽的boxFilter结果忽略,其中boxFilter结果为0,这样您就不会被零除。这正是你想要的

编辑2:这里有一个具体的例子。首先,我将定义一个简单的7x7图像,其中5x5的内部正方形为1:

import cv2
import numpy as np

img = np.array([
    [0, 0, 0, 0, 0, 0, 0],
    [0, 1, 1, 1, 1, 1, 0],
    [0, 1, 1, 1, 1, 1, 0],
    [0, 1, 1, 1, 1, 1, 0],
    [0, 1, 1, 1, 1, 1, 0],
    [0, 1, 1, 1, 1, 1, 0],
    [0, 0, 0, 0, 0, 0, 0]], dtype=np.float32)
我们将继续使用高斯核的简单过滤示例:

gauss_kernel = np.array([
    [1/16, 1/8, 1/16],
    [1/8, 1/4, 1/8],
    [1/16, 1/8, 1/16]], dtype=np.float32)
现在,首先,过滤图像

filtered = cv2.filter2D(img, -1, gauss_kernel)
print(filtered)

[[ 0.25    0.375   0.5     0.5     0.5     0.375   0.25  ]
 [ 0.375   0.5625  0.75    0.75    0.75    0.5625  0.375 ]
 [ 0.5     0.75    1.      1.      1.      0.75    0.5   ]
 [ 0.5     0.75    1.      1.      1.      0.75    0.5   ]
 [ 0.5     0.75    1.      1.      1.      0.75    0.5   ]
 [ 0.375   0.5625  0.75    0.75    0.75    0.5625  0.375 ]
 [ 0.25    0.375   0.5     0.5     0.5     0.375   0.25  ]]
这就是我们所期望的…一堆1的高斯模糊应该是,1。然后我们在边缘有一些衰减,在图像内部和零区域外部。因此,我们将创建一个面具;在这种情况下,它与图像完全相同。然后我们将在遮罩上执行一个长方体过滤器,以获得正确的缩放值:

mask = img.copy()  # in this case, the mask is identical
scaling_vals = cv2.boxFilter(mask, -1, gauss_kernel.shape, borderType=cv2.BORDER_CONSTANT)
print(scaling_vals)

[[ 0.111  0.222  0.333  0.333  0.333  0.222   0.111]
 [ 0.222  0.444  0.666  0.666  0.666  0.444   0.222]
 [ 0.333  0.666  1.     1.     1.     0.666   0.333]
 [ 0.333  0.666  1.     1.     1.     0.666   0.333]
 [ 0.333  0.666  1.     1.     1.     0.666   0.333]
 [ 0.222  0.444  0.666  0.666  0.666  0.444   0.222]
 [ 0.111  0.222  0.333  0.333  0.333  0.222   0.111]]
请注意,如果将其乘以内核中VAL的数量9,那么我们将得到像素位置周围非零像素的确切数量。这就是我们的标准化比例因子。现在所要做的就是…规范化并删除图像边界之外的内容

mask = mask.astype(bool)  # turn mask bool for indexing
normalized_filter = filtered.copy()
normalized_filter[mask] /= scaling_vals[mask]
normalized_filter[~mask] = 0
print(normalized_filter)

[[ 0.  0.     0.     0.     0.     0.     0. ]
 [ 0.  1.265  1.125  1.125  1.125  1.265  0. ]
 [ 0.  1.125  1.     1.     1.     1.125  0. ]
 [ 0.  1.125  1.     1.     1.     1.125  0. ]
 [ 0.  1.125  1.     1.     1.     1.125  0. ]
 [ 0.  1.265  1.125  1.125  1.125  1.265  0. ]
 [ 0.  0.     0.     0.     0.     0.     0. ]]
现在这些值并不完美,但有偏和也不完美。注意,即使在IDL文档中,它们也声明:

在分析这些值时应该小心,因为内核中的点较少可能会导致结果有偏差

所以像这样缩放不会得到完美的结果。但是,我们可以做得更好!我们使用的比例因子仅使用点的数量,而不使用与每个点关联的实际权重。为此,我们可以使用相关的权重过滤遮罩。换句话说,只需在遮罩上而不是图像上运行filter2D。显然,将图像除以这个值,所有值都会变成1;然后我们戴上面具。我们完了!不要被这个例子搞糊涂了,因为遮罩和图像是相同的——在本例中,将过滤后的图像除以过滤后的遮罩得到1,但一般来说,这只是一个比长方体过滤器更好的缩放

mask = img.copy()
scaling_vals = cv2.filter2D(mask, -1, gauss_kernel)

mask = mask.astype(bool)  # turn mask bool for indexing
normalized_filter = filtered.copy()
normalized_filter[mask] /= scaling_vals[mask]
normalized_filter[~mask] = 0
print(normalized_filter)

[[ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  1.  1.  1.  1.  1.  0.]
 [ 0.  1.  1.  1.  1.  1.  0.]
 [ 0.  1.  1.  1.  1.  1.  0.]
 [ 0.  1.  1.  1.  1.  1.  0.]
 [ 0.  1.  1.  1.  1.  1.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]]
亚历山大

首先,非常感谢您的详细解释和测试代码。我相信我理解您使用的基本原理,并且我已经推导出了这段代码,它似乎正是我所需要的:

import numpy as np
import cv2


a = np.array([[0.0,0.0,0.0,0.0,0.0],
              [0.0,0.0,0.0,0.0,0.0],
              [10.0,0.0,0.0,0.0,0.0],
              [20.0,20.0,20.0,0.0,0.0],
              [30.0,30.0,30.0,30.0,30.0]], dtype=np.float32)

kernel = np.ones((3,3), dtype=np.float32)

filtered_a = cv2.filter2D(a, -1, kernel)
mask = (a > 0)
if np.any(~mask):
    scaling_vals = cv2.filter2D(mask.astype(np.float32), -1, kernel)
    filtered_a[mask] /= scaling_vals[mask]
    filtered_a[~mask] = 0
    scaling_vals = None
    mask = None

print a
print filtered_a
产生:

[[  0.           0.           0.           0.           0.        ]
 [ 17.5          0.           0.           0.           0.        ]
 [ 22.8571434   22.8571434   26.           0.           0.        ]
 [ 23.33333397  23.33333397  24.2857151   26.          30.        ]]

这是一个进行均值筛选的示例,但可能适用于您的问题:

im[maskInvalid] = 0
maskValid = np.logical_not(maskInvalid)

# mean filter, but without dividing by the number of elements
imFilt = cv2.boxFilter(im,-1,(51,51),normalize=False)
# count the number of elements that you need to devide by
maskValidTmp = cv2.boxFilter(maskValid.astype('int'),-1,(51,51),normalize=False)
imFilt[maskValidTmp!=0] /= maskValidTmp[maskValidTmp!=0]
imFilt[maskValidTmp==0] = 0

Alexander,问题是靠近黑边的值会因为Conva而减小
粘连。所以,一个简单的掩蔽是不起作用的。的确,但还有什么其他选择呢?如果忽略这些像素,那是一样的——这些部分不会添加到卷积中,因此值会更小。你想让卷积核永远不会与黑色区域重叠吗?即,接近黑色的边缘像素没有卷积结果?换句话说,通常进行卷积时,除非您另有指定,否则图像的边缘将填充黑色。您是否会使用不同的标志,如border reflect或border repeat?是的,正确。在IDL中,这些零值被忽略,这样靠近边缘的像素就不会减少;IDL所做的是通过缺失区域中较小数量的像素进行规范化。那么不,我认为在OpenCV中没有可比的干净方法,因为OpenCV不会在卷积中标准化像素。@Knulph我编辑了我的文章,并提出了一个解决方案,可以为您提供相同的值。这看起来有点复杂,但实际上很简单。我花了一段时间来举一个例子,因为我没有得到一个完美的掩码,因为提供的示例图像是jpg而不是png。这看起来很棒,但我会有点谨慎地添加偏差,这样你可以除以非常小的数字…可能会产生一些奇怪的精度错误;除以1e-10等于乘以1000000000,所以要小心。手动屏蔽可能更好。我修改了代码以反映您的评论。再次感谢!