矩阵的掩码操作
矩阵的掩码操作非常简单。其思想是,我们根据掩码矩阵(也称为核)重新计算图像中每个像素的值。此掩码保存的值将调整相邻像素(和当前像素)对新像素值的影响程度。从数学的观点来看,我们用指定的值进行加权平均。
测试用例
让我们考虑图像对比度增强方法的问题。基本上,我们想对图像的每个像素应用下面的公式:
第一种表示法是使用公式,而第二种是第一种表示法的压缩版本,使用掩码。使用掩码时,将掩码矩阵的中心(在上例中标记为 0-0索引) 放在想要计算的像素上。这2种方法是一样的,但是在大矩阵的情况下后一种表示法更容易理解。
代码
from __future__ import print_functionimport sysimport timeimport numpy as npimport cv2 as cvdef is_grayscale(my_image):return len(my_image.shape) < 3def saturated(sum_value):if sum_value > 255:sum_value = 255if sum_value < 0:sum_value = 0return sum_valuedef sharpen(my_image):if is_grayscale(my_image):height, width = my_image.shapeelse:my_image = cv.cvtColor(my_image, cv.CV_8U)height, width, n_channels = my_image.shaperesult = np.zeros(my_image.shape, my_image.dtype)for j in range(1, height - 1):for i in range(1, width - 1):if is_grayscale(my_image):sum_value = 5 * my_image[j, i] - my_image[j + 1, i] - my_image[j - 1, i] \- my_image[j, i + 1] - my_image[j, i - 1]result[j, i] = saturated(sum_value)else:for k in range(0, n_channels):sum_value = 5 * my_image[j, i, k] - my_image[j + 1, i, k] \- my_image[j - 1, i, k] - my_image[j, i + 1, k]\- my_image[j, i - 1, k]result[j, i, k] = saturated(sum_value)return resultdef main(argv):filename = 'lena.jpg'img_codec = cv.IMREAD_COLORif argv:filename = sys.argv[1]if len(argv) >= 2 and sys.argv[2] == "G":img_codec = cv.IMREAD_GRAYSCALEsrc = cv.imread(cv.samples.findFile(filename), img_codec)if src is None:print("无法打开图片 [" + filename + "]")print("使用方法:")print("mat_mask_operations.py [图片地址 -- 默认 lena.jpg] [G -- 灰度]")return -1cv.namedWindow("Input", cv.WINDOW_AUTOSIZE)cv.namedWindow("Output", cv.WINDOW_AUTOSIZE)cv.imshow("Input", src)t = round(time.time())dst0 = sharpen(src)t = (time.time() - t)print("手写函数耗时秒数: %s" % t)cv.imshow("Output", dst0)cv.waitKey()t = time.time()kernel = np.array([[0, -1, 0],[-1, 5, -1],[0, -1, 0]], np.float32) # kernel 应该是浮点类型dst1 = cv.filter2D(src, -1, kernel)t = (time.time() - t)print("内置 filter2D 函数耗时秒数: %s" % t)cv.imshow("Output", dst1)cv.waitKey(0)cv.destroyAllWindows()return 0if __name__ == "__main__":main(sys.argv[1:])
基本方法
现在让我们看看如何通过使用基本的像素访问方法或使用 filter2D() 函数来实现这一点。
下面是一个实现此功能的函数:
def is_grayscale(my_image):return len(my_image.shape) < 3def saturated(sum_value):if sum_value > 255:sum_value = 255if sum_value < 0:sum_value = 0return sum_valuedef sharpen(my_image):if is_grayscale(my_image):height, width = my_image.shapeelse:my_image = cv.cvtColor(my_image, cv.CV_8U)height, width, n_channels = my_image.shaperesult = np.zeros(my_image.shape, my_image.dtype)for j in range(1, height - 1):for i in range(1, width - 1):if is_grayscale(my_image):sum_value = 5 * my_image[j, i] - my_image[j + 1, i] - my_image[j - 1, i] \- my_image[j, i + 1] - my_image[j, i - 1]result[j, i] = saturated(sum_value)else:for k in range(0, n_channels):sum_value = 5 * my_image[j, i, k] - my_image[j + 1, i, k] \- my_image[j - 1, i, k] - my_image[j, i + 1, k]\- my_image[j, i - 1, k]result[j, i, k] = saturated(sum_value)return result
首先,我们确保输入的图像数据为无符号8位格式。
my_image = cv.cvtColor(my_image, cv.CV_8U)
我们创建一个与我们的输入具有相同大小和相同类型的输出图像。根据通道的数量,我们可以有一个或多个子列。
height, width, n_channels = my_image.shaperesult = np.zeros(my_image.shape, my_image.dtype)
我们需要访问多个行和列,这可以通过在当前中心 (i,j) 上加或减 1 来完成。然后我们应用这个和,将新值放入 result 矩阵中。
filter2D 函数
在图像处理中,应用这样的过滤器是如此的常见,以至于在 OpenCV 中有一个函数会负责应用遮罩(在某些地方也称为内核)。为此,你首先需要定义一个保存掩码的对象:
kernel = np.array([[0, -1, 0],[-1, 5, -1],[0, -1, 0]], np.float32)
然后调用 filter2D() 函数指定要使用的输入、输出图像和内核:
dst1 = cv.filter2D(src, -1, kernel)
这个函数更短,更简洁,而且由于进行了一些优化,它通常比手工编码的方法更快。
官方文档:
https://docs.opencv.org/4.x/d7/d37/tutorial_mat_mask_operations.html




