ROI是Region Of Interest的简写,字面义是感兴趣的区域,或者说是需要进行某种操作的「目标区域」。
二、如何获取ROI?如果想要对ROI区域进行操作,首先要选中并获取到这个区域。
最简单的方式就是利用Numpy的数组索引切片来获取到一个矩形ROI(当然其他形状的ROI或许更加适合具体情况下的某些Object,但是显然无法通过如此简单的方法就能获取,必须经过有一定复杂度的算法才能得到)。
假设某张image宽高形状是[400, 400],通道数为3,那么如果想要选中中间的一个宽高[40, 40]的物体,可以用下面这个切片索引
三、泛洪填充——floodfill()img[180:220, 180:220, :]
参考:OpenCV: Miscellaneous Image Transformations-floodfill()
泛洪填充的典型应用:填充和目标像素的取值相近的邻近区域;类似Adobe Photoshop中的魔法棒工具所实现的功能。
实现泛洪填充的API是floodfill(),语法如下
cv.floodFill(image, mask, seedPoint, newVal[, loDiff[, upDiff[, flags]]]) ->retval, image, mask, rect
- image是原图像,除非flags的高八位设置成cv.FLOODFILL_MASK_ONLY,否则函数会对image做出改变。image可以是单通道灰度图,也可是多通道彩图。
- mask是掩模,必须是单通道无符号8位整型数组,并且宽和高分别比原图像image的要大2;掩模中的零值区域用来限定图像变化的区域,而非零值区域会屏蔽泛洪填充;mask既是输入也是输出,输出的mask相比原先,泛洪区域全部填充1或者flags里的中八位数值(后续在flags中讲解)。
- seedPoint是泛洪基点,也即“泛洪”从此开始向外扩展,直至碰到掩模边界或者超过loDiff、upDiff边界;当flags不指定为cv.FLOODFILL_FIXED_RANGE时,除了基点附近4或8个像素点是根据它判断的,其他像素点都是根据已经处于泛洪区域上的边界点邻域范围上的点来判断;当flags指定为cv.FLOODFILL_FIXED_RANGE时,所有像素点都基于该点来判断。
- newVal是泛洪填充值,如果原图像是一个单通道图像,newVal是单个数值,而如果原图像是一个三通道图像,newVal应该是一个长度为3的元组。
- loDiff和upDiff分别是下、上边界差值,与newVal的长度一致,只有像素点大于判断基点减去loDiff值、并且小于加上upDiff值时,才会划入泛洪区域。
- flags一共有3个字节,低八位表示邻域范围,默认是4,也可设置成8;中八位表示给mask填充的数值,比如(255 << 8)表示将255左移八位得到的结果;高八位如果不设定,表示会更改image图像,并且是用float_range判断泛洪区域的方式,如果设定为cv2.FLOODFILL_FIXED_RANGE,表示仅凭与泛洪基点的像素差值判断是否属于泛洪区域,如果设定为cv2.FLOODFILL_MASK_ONLY,表示不改变image图像,仅仅改变mask;唯一我有疑惑的就是cv2.FLOODFILL_FIXED_RANGE能否和cv2.FLOODFILL_MASK_ONLY一齐存在呢??逻辑上完全没问题的。
- rect是最后得到的泛洪区域的最小矩形边界的坐标、宽高。
讲点宏参数方面的问题
我们通过floodfill()的官方API文档也可以知道,flags是有低、中、高八位的;而高八位是由宏参数定义的。
在学习C语言的时候,我们讲过用define命令可以定义宏对象,比如把常用到的3.1415926...这个常数π给定义为PI,这样我们就不用时刻把这么长的一串数字打出来了,或者对于记忆不好的同学,也不用记π的小数点后是哪些数字了。
为了让各位人类更方便地进行编程,不用记住那些意义不直观的数字组合,OpenCV的开发人员把高八位的几种选项对应的数值,预定义了几个宏参数,我们程序员只需将宏参数字符串传入API。
最后讲一个错误, 大家执行下面的代码;会发现mask没有什么变化,而image也发生了改变。
import cv2 as cv
import numpy as np
def fill_binary():
image = np.zeros([400, 400, 3], np.uint8)
image[100:300, 100:300, :] = 255
cv.imshow("fill_binary", image)
mask = np.ones([402, 402, 1], np.uint8)
mask[101:301, 101:301] = 0
print(np.sum(mask))
cv.floodFill(image, mask, (200, 200), (0, 0, 255), 4 | (127 << 8) | cv.FLOODFILL_MASK_ONLY)
cv.imshow("filled binary", image)
print(np.sum(mask))
print(np.unique(mask))
cv.imshow("mask", mask)
fill_binary()
cv.waitKey(0)
cv.destroyAllWindows()
这是因为,在flags参数前面还有两个loDiff和upDiff参数;如果不给这两个参数传值,又不以关键词参数的形式给参数flags传值,函数就会误将flags的值传入loDiff,显然是错误的。
修改成下面正确的版本后。
import cv2 as cv
import numpy as np
def fill_binary():
image = np.zeros([400, 400, 3], np.uint8)
image[100:300, 100:300, :] = 255
cv.imshow("fill_binary", image)
mask = np.ones([402, 402, 1], np.uint8)
mask[101:301, 101:301] = 0
print(np.sum(mask))
cv.floodFill(image, mask, (200, 200), (0, 0, 255), flags=4 | (127 << 8) | cv.FLOODFILL_MASK_ONLY)
cv.imshow("filled binary", image)
print(np.sum(mask))
print(np.unique(mask))
cv.imshow("mask", mask)
fill_binary()
cv.waitKey(0)
cv.destroyAllWindows()
这才对了!



