第十六节:cv2.selectROI函数,图像的背景替换
(一)cv2.selectROI与cv2.selectROIs函数
这个函数可以帮助我们首先上一节的鼠标提取目标区域的功能,非常方便。
上一节传送门
selectROI(windowName, img, showCrosshair, fromCenter):
参数 | 解释 |
---|---|
windowName | 选择的区域被显示在的窗口的名字 |
img | 要在什么图片上选择ROI |
showCrosshair | 是否在矩形框里画十字线(True为画十字线) |
fromCenter | 是否是从矩形框的中心开始画(True为从矩形框的中心开始画) |
这个函数的返回值为 (x_min, y_min, w, h),即矩形框的左上角的坐标和矩形框的宽高。
下面我们将实现用该函数对目标区域进行框选,然后对框选区域进行截取
PS:在你使用该函数对目标区域进行框选时,终端还会打印出该函的使用方法
import cv2
import numpy as np
img = cv2.imread("shapes.png")
r = cv2.selectROI('input', img, True,False) # 返回 (x_min, y_min, w, h)
# roi区域
roi = img[int(r[1]):int(r[1]+r[3]), int(r[0]):int(r[0]+r[2])]
cv2.imshow('roi', roi)
cv2.waitKey(0)
这时候可能就会有小伙伴问了,假如我想同选择多个目标怎么办,那么这时候你就需要用到cv2.selectROIs函数,注意后面有个s
下面将用cv2.selectROIs函数,同时框选3个目标,并将它们裁剪下来(框选方法:当你使用鼠标框选一个一个目标后,你需要点击space或enter键,然后才能框选下一个吗目标。当你框选所有的目标后,你需要esc键)
下面程序会依次显示截取的目标图片
import cv2
import numpy as np
img = cv2.imread("shapes.png")
ROIs = cv2.selectROIs('input', img, True,False) # 返回 (x_min, y_min, w, h)
if len(ROIs) >0:
for ROI in ROIs:
x,y,w,h=ROI
roi = img[int(y):int(y + h), int(x):int(x + w)]
cv2.imshow('roi', roi)
cv2.waitKey(0)
cv2.destroyAllWindows()
这里就不展示结果了,感兴趣的小伙伴自己跑一下吧
(二)图像的背景替换
下面是我写的背景替换,可能比较笨,但是勉强可用
import cv2
import numpy as np
kernel=np.ones((3,3),np.uint8)
src = cv2.imread("lambo.PNG")
r = cv2.selectROI('input', src, True) # 返回 (x_min, y_min, w, h)
# roi区域
roi = src[int(r[1]):int(r[1]+r[3]), int(r[0]):int(r[0]+r[2])]
# 原图mask
mask = np.zeros(src.shape[:2], dtype=np.uint8)
# 矩形roi
rect = (int(r[0]), int(r[1]), int(r[2]), int(r[3])) # 包括前景的矩形,格式为(x,y,w,h)
bgdmodel = np.zeros((1,65),np.float64) # bg模型的临时数组
fgdmodel = np.zeros((1,65),np.float64) # fg模型的临时数组
cv2.grabCut(src,mask,rect,bgdmodel,fgdmodel, 5, mode=cv2.GC_INIT_WITH_RECT)
# 提取前景和可能的前景区域
mask1 = np.where((mask==1) + (mask==3), 255, 0).astype('uint8')
result = cv2.bitwise_and(src,src,mask=mask1)#这里先抠出目标图片
cv2.imshow("result", result)
cv2.waitKey(0)
img1=cv2.imread('background.jpg')#加载背景图片
cv2.imshow('background',img1)
cv2.waitKey(0)
img1=cv2.GaussianBlur(img1,(3,3),15)#虚化背景图片
mask_inv=cv2.bitwise_not(mask1)#这里先生成mask1的反码
cv2.imshow('mask_inv',mask_inv)
cv2.waitKey(0)
out_put = cv2.bitwise_and(img1,img1,mask=mask_inv)#用上面生成的反码,在背景图片上扣一个与目标图片大小的位置
cv2.morphologyEx(out_put,cv2.MORPH_OPEN,kernel)#对上面生成的位置进行光滑处理与降噪
cv2.imshow("out_put",out_put)
cv2.waitKey(0)
cv2.morphologyEx(result.copy(),cv2.MORPH_OPEN,kernel)#对目标图片进行光滑处理与降噪
res=result.copy()+out_put#将目标图片与处理后的背景图片进行相加
cv2.imshow('res',res)
cv2.waitKey(0)
cv2.destroyAllWindows()
这里就不一一展示每幅图片了,小伙伴们自己跑一下(要注意的是,背景图片要与目标图片设置为相同的大小)
下面另一种背景融合的方法,与上面最大的区别就是,最后的图像融合部分
import cv2
import numpy as np
kernel=np.ones((3,3),np.uint8)
src = cv2.imread("lambo.PNG")
r = cv2.selectROI('input', src, True) # 返回 (x_min, y_min, w, h)
# roi区域
roi = src[int(r[1]):int(r[1]+r[3]), int(r[0]):int(r[0]+r[2])]
# 原图mask
mask = np.zeros(src.shape[:2], dtype=np.uint8)
# 矩形roi
rect = (int(r[0]), int(r[1]), int(r[2]), int(r[3])) # 包括前景的矩形,格式为(x,y,w,h)
#bgdmodel = np.zeros((1,65),np.float64) # bg模型的临时数组
#fgdmodel = np.zeros((1,65),np.float64) # fg模型的临时数组
cv2.grabCut(src,mask,rect,None,None, 5, mode=cv2.GC_INIT_WITH_RECT)
# 提取前景和可能的前景区域
mask1 = np.where((mask==1) + (mask==3), 255, 0).astype('uint8')
cv2.erode(mask1,kernel,iterations=3)#腐蚀一下
cv2.dilate(mask1,kernel,iterations=3)#在膨胀回去
cv2.imshow('mask1',mask1)
cv2.waitKey(0)
img1=cv2.imread('background.jpg')#加载背景图片
h,w=img1.shape[:2]
print(h,w)
img1=cv2.GaussianBlur(img1,(3,3),15)#虚化背景图片
cv2.imshow('background',img1)
cv2.waitKey(0)
#遍历替换
pianyi=[0,0]#目标图片相对与在原图中的偏移量,这里设置为0
for i in range(h):
for j in range(w):
if mask1[i,j]==255:#0代表白色的点
img1[pianyi[0]+i,pianyi[1]+j]=src[i,j]#此处替换颜色,为BGR通道
cv2.imshow('out_put',img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
我感觉,这种图像融合的方法,更好。
(三)结语
学习opencv有很多的方法,我的建议是你可以加一些群,可以充分利用B站,CSDN,和百度。
在我的博客中,我不会讲解opencv的算法实现(当然我也不太会),我只会讲解一些函数的调用,不理解就多改一些参数,多尝试尝试,慢慢你就理解来。相信你总有一天可以说opencv不过“Ctrl+C,Crtl+V”
如果有什么错误的地方,还请大家批评指正,最后,希望小伙伴们都能有所收获。