一起学opencv-python七(ROI,泛洪填充和颜色替换)

ROI

 

这个ROI是我们自己选择的,ROI就是图像的一个子集,ROI也可以说是我们需要进行操作的一个区域或者说我们选中的区域。我下面是和opencv的滑条结合了一下,就是滑动滑条,ROI的区域会跟着变,我们把ROI区域的图像变为灰度图像,这样可以看出滑块滑动的效果。那么首先来学习这个滑条函数,参考了https://blog.csdn.net/u012005313/article/details/69675803#C4

其实就是一个cv2.createTrackbar函数:

 

 

 

看看例子:

 

里面还用到了一个函数。

 

好,大概了解了这些,我们就可以写代码了。

 

我这个代码的意思就是把99*99(不是100是因为不包含:右边的索引的元素)的ROI范围内的图片显示为灰度值,并且有两个滑条,决定的是这个ROI左上角的位置。一开始的效果:

 

因为我没有动滑条,所以roix和roiy这两个函数都没有执行,所以没有灰色地带。我先移动x试一下。

 

可以看到一个灰色区域。再移动y试一试。

 

灰色的位置下移了。再回到0,0也还会有,因为已经移动过了滑块。

 

关于上面的代码有几个解释和提醒:

第一,numpy的视图(关于什么叫做视图请参考前面的专栏文章),虽然不会改变原数组的形状,但是会改变原数组的内容,这一点和python的列表还不一样。

 

 

 

 

看上去[:]像是一个深拷贝,但是其实不是的。copy.deepcopy才是真正意义上的深拷贝。

 

b=a[:],b是a的一个视图,虽然b形状的改变不会影响a,但是元素值得改变会,而c=a.copy()

这是一个深拷贝,c得任何改变都不会传递到a。如果把b=a.cpoy()都改成b=a[:],那么灰色区域就会累计而不是仅仅是99*99的矩形。

 

第二:用createTrackbar创建滑条时,里面填了一个回调函数对吧,每次当滑条移动的时候,都会调用这个函数,并且把当前位置传递给这个函数,这就是我为什么在roix里直接拿valuex来用,而valuey还需要用getTrackbarPos来获取。

 

第三:灰度转回BGR其实还是灰度。

 

这个找到了一个合理的解释:

 

事实也是如此,

 

如果BGR三个通道的值一样,那么是不会有彩色的,就是灰度图,如果全是0或者靠近就是黑色的,全是255或者靠近就是白的,中间就是灰色的。

泛洪填充

 

有image,mask,seedPoint,newVal,loDiff,upDiff和flags参数,后三个参数是可选的,返回值有retval,image,mask,rect四个。这是基本输入输出信息,更详细的参数用法信息,参考了https://blog.csdn.net/weixin_42296411/article/details/80966724

 

flags是一个32位的,这个是需要注意的,所以下面的填充模式都左移了16位,因为16-31位才是填充模式。

 

 

 

画图软件的标尺和网格线在查看里面,默认是没有勾选的。

 

 

 

这个newVal其实应该是根据要填充的图片的通道数给值,上面给的图像就是个灰度图像,所以newVal只需要给一个值,loDiff,upDiff也是一样,后面会有彩色图像的填充,那个时候是要给三个通道的值。我这里来解释一下flags=4(mask_fill<<8)|cv2.FLOODFILL_FIXED_RANGE,第一个4是4紧邻的意思,关于4紧邻填充,下面会有详细的解释,mask_fill之所以右移8位,是因为flags的8-15位才是mask的填充颜色。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

还参考了https://www.cnblogs.com/FHC1994/p/9033580.html

我们先来复现一下别人的操作:

 

seed所在的点处灰度值为60,也就是说只有(60-20,60+20)之间的会被填充。变一下mask_fill效果立杆见影。这四个条从上到下依次为0,60,120,180。就只有60了。

 

如果我的mask不加2会如何?

 

这里必须要加2了,因为有断言来判断是不是符合+2的条件。改变上下界,现在是[-10,130]。第一,第三条也被加进去了。关于4紧邻还是8紧邻,我觉得我们可以不去了解了,我们可以不关注flags的0-7位,不过知道默认是4近邻。

 

泛洪填充的区域必须是连通的区域。我修改了一下img,其它都没变。

 

 

 

看到240后面有一段45的,完全在范围里面,但是没有被填充,原因就是它们中间有一个240,不在范围内,构不成连通的区域。

 

我下面只修改一个mask,把0-4行的mask改为1,然后就会发现那些地方不被填充了,而是保持1不变。

 

换一个数字呢?

 

 

 

为什么-1变成了255?这是因为-1在存的时候一定是按照补码来存的,-1的补码就是11111111,但是我们定义mask的时候是uint8,所以计算机读的时候就按照uint来读,就是255。或者更直接一点就是256-1=255。

 

-2应该不用解释了。256-2=254。下面试一下另一种填充方式。

 

这里先理解为这种填充方式不影响img,只是影响mask的输出,mask为0的地方会填充为mask_fill的颜色。如果mask_fill=0,那么会填充1,虽然0和1其实区别并不大,都差不多黑。

 

这个时候注意:FLOODFILL_FIXED_RANGE:表示此标志会考虑当前像素与种子像素之间的差,否则就考虑当前像素与相邻像素的差。不过这个区域还是由Seedopint决定。

 

 

 

所以当loDiff,upDiff是70的时候,mask填充的是四段,60的时候也是,说明这个等号是带的,应该是闭区间。20的时候就只有一个段了。

 

 

 

loDiff和upDiff是可选的,默认应该是0。这个flags=(关键字)一定要加,因为它本来不是这个位置的。

 

 

 

flags也是可选的。默认的flags应该是4紧邻,mask填充的是0(其实是1),填充方式是Fixed_Range那一种。

 

MASK_ONLY模式也是一样,mask不等于0的话,这些像素点就保持鸳鸯,不会被填充。

 

小数的话取得是ceil函数,也就是取小于等于的整数。所以0.9被看作0,1.2被看作1(也就是非0)。

 

 

 

上面搞的都是灰度图像,下面对彩色图像搞一下。

 

效果:

 

seedpoint决定了要填充的连通区域,就是那个圆盘,然后loDiff和upDiff决定要被填充的颜色范围,但是区域必须还得是连通的。把mask里的有一些值设为1。

 

这相当于设置了一个禁止填充区。

 

关于MASK_ONLY模式,上面我们已经试过很多次了,确实是只影响mask而不改变原图像。我这里要郑重地纠正一个在网上广为流传的错误认识。先看错误的代码:

 

这个例子唯一可取的就是: mask[101:301, 101:301] = 0 这条语句为什么是101:301而不是100:300呢?我觉得应该是掩膜mask是比原图像左右上下都多了1,所以掩膜mask左右一共比原图像多2,上下也比原图像多2。那么原图像的100就自然对应到掩膜的101,同样原图像的300就自然对应到掩膜的301。这是结果。

 

为什么用的是MASK_ONLY,原图像还会改变?这是因为少了一个关键字flags=,如果没有这个的话,cv2.cv.FLOODFILL_MASK_ONLY(1<<17)不是被作为flags参数,而是loDiff参数,为什么三个通道这里只有一个数不报错呢?我认为应该是广播了。

 

所以上面看似是MASK_ONLY的例子,其实是误人子弟。这个例子要想对,应该这么写:

 

并且用MASK_ONLY是出不来彩色的,因为mask是单通道的,简直是误人子弟。

 

我还是试了一下4紧邻和8紧邻的区别。代码来自https://blog.csdn.net/weixin_42296411/article/details/80966724,,需要按照上面做一些改动。改成250是让效果显示出来。

 

我们分析的时候还是按照博客里面的图来说。这个是原图。

 

以(4,4)为seedpoint,0为loDiff和upDiff,也就是说必须和(4,4)点的像素值一样才会填充。这个其实感觉有点像元胞自动机或者说生命游戏的不完整版(因为没有淘汰规则)。

 

这个4紧邻的过程是这样的,从seedponit开始,在4紧邻范围内,有符合填充条件的(这里就是像素值一样了)就填充,于是上图中灰色的上下左右都填充了,然后以这些点为起点,继续这个过程,就是辐射的那种感觉,只有上面的2的4紧邻区域里面有符合要求的,那么就填充,然后就没有符合要求的了,这个要求是既要在已经填充点的4紧邻区域,又要满足loDiff和upDiff所限制的条件。看懂了4紧邻的,那么8紧邻的其实没有那么难了。

 

下面来做一件事,就是颜色替换。当然这个只是很简单的颜色替换。

颜色替换

我下面要做的是把图中的蓝色换成绿色,并且亮度还要一样,其实很简单,我们只需要调换蓝色和绿色通道的亮度就可以了。

 

这样子有点怪,我们要做的不是这样的,应该是单独把比较蓝色的拿出来换成绿色而不是全部都换。那么我们要用一下inRange函数。

 

 

 

按照标准的蓝色出来的有一些不是我想要的,裙子和衣服,帽子,发带的那种比较深的蓝色我我们不要。

 

我们把Vmin调大一些,调到200大概效果可以了。

 

然后我直接上一个比较综合的代码,稍后会讲解:

 

首先因为inRange出来是一个单通道的,所以先转化了一下,不然后面没有办法和BGR的做逻辑运算,因为不符合广播的规则嘛。c里面我们想要替换的蓝色区域的值是255,我先做了一个非运算,就是0了,为什么要非,后面会看到,然后把c的非和a按位与,因为白色是255,所以白色区域对应的a的像素值再结果里不变,而黑是0,我们要替换的区域再结果输出里面也就是0,

 

然后我们又把c和绿色的图片b与了一下,这个结果是

 

然后把上面两张相加即可,因为黑是0嘛,其实这里或也行,不过为了少打字我就+了。

 

下一讲也许会先做个pyqt结合opencv的个实践项目。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值