自动生成 nemo 鱼的 mask

1. 问题

前段时间正在写一个课程作业,当时遇到一个问题,如何将下面这张图里的鱼抠出来:

如果要求不能使用PS软件抠图,有其它办法吗?
于是脑子里出现了一些奇奇怪怪的想法:


2. 方案

方案一:用 canny 边缘检测一下,在通过得到的边缘图像做膨胀腐蚀,最后选择区域,再膨胀腐蚀得到鱼的 shape

emmm 理论上可以,但效果可能不太行


方案二:查看图片的 RGB 颜色空间分布,选择合适的颜色区域作为 mask:

要不先看看 RGB 空间中的颜色分布吧:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

if __name__ == '__main__':
	# LOAD NEMO IMAGE
	nemo = cv.imread('./resources/313.bmp')
	rgb_nemo = cv.cvtColor(nemo, cv.COLOR_BGR2RGB)

	# 显示 RGB 空间
	fig = plt.figure(1)
	plt.subplot(1, 2, 1)
	plt.imshow(rgb_nemo)
	plt.axis('off')

	r, g, b = cv.split(rgb_nemo)
	rows, cols, d = rgb_nemo.shape
	pixel_colors = (rgb_nemo.reshape(rows*cols, 3)/255).tolist()

	axis = fig.add_subplot(1, 2, 2, projection="3d")
	axis.scatter(r.flatten(), g.flatten(), b.flatten(), 
		facecolors=pixel_colors, marker='.')
	axis.set_xlabel('R')
	axis.set_ylabel('G')
	axis.set_zlabel('B')
	plt.show()

接下来需要选择鱼身体的颜色大致分布范围,鱼的颜色大致为橙色和白色,但 RGB 空间颜色分布不好分割!


方案二:使用 HSV 颜色空间分割图像:

先了解一下 HSV:

— ipad 截图糊了点

动手试试:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

if __name__ == '__main__':
	# LOAD NEMO IMAGE
	nemo = cv.imread('./resources/313.bmp')
	rgb_nemo = cv.cvtColor(nemo, cv.COLOR_BGR2RGB)

	# 显示 HSV 空间
	fig = plt.figure(2)
	plt.subplot(1, 2, 1)
	plt.imshow(rgb_nemo)
	plt.axis('off')

	hsv_nemo = cv.cvtColor(rgb_nemo, cv.COLOR_RGB2HSV)
	h, s, v = cv.split(hsv_nemo)
	rows, cols, d = rgb_nemo.shape
	pixel_colors = (rgb_nemo.reshape(rows*cols, 3)/255).tolist()

	axis = fig.add_subplot(1, 2, 2, projection="3d")
	axis.scatter(h.flatten(), s.flatten(), v.flatten(), 
		facecolors=pixel_colors, marker='.')
	axis.set_xlabel('Hue')
	axis.set_ylabel('Saturation')
	axis.set_zlabel('Value')
	plt.show()

接着选择出红色分量和白色分量的区间,这个我会,HSV 空间中不同颜色和不同饱和度的颜色间隔较大,容易选择区间!

比如选择这样的颜色范围:

light_orange = (5, 160, 120)
dark_orange = (20, 255, 255)
light_white = (35, 0, 165)
dark_light = (255, 120, 255)

其中,light_orange 到 dark_orange 是橙色的 HSV 的范围,H ∈[5, 20], S ∈[166, 255]…

接下来我们使用橙色和白色分别对图像分割(分割前可能需要选择 ROI ),最后将两个分割结果融合:

# 利用 cv.inRange 生成二值化的模板
mask1 = cv.inRange(hsv_nemo, light_orange, dark_orange)
mask2 = cv.inRange(hsv_nemo, light_white, dark_light)
mask12 = np.array((mask1 + mask2) > 0, dtype=mask1.dtype)	# 模板合并

nemo_mask1 = cv.bitwise_and(rgb_nemo, rgb_nemo, mask=mask1)
nemo_mask2 = cv.bitwise_and(rgb_nemo, rgb_nemo, mask=mask2)
nemo_mask12 = cv.bitwise_and(rgb_nemo, rgb_nemo, mask=mask12)

fig = plt.figure(3)
plt.subplot(2, 3, 1), plt.imshow(mask1, cmap='gray'), plt.axis('off'), plt.title('mask1')
plt.subplot(2, 3, 4), plt.imshow(nemo_mask1), plt.axis('off'), plt.title('nemo_mask1')

plt.subplot(2, 3, 2), plt.imshow(mask2, cmap='gray'), plt.axis('off'), plt.title('mask2')
plt.subplot(2, 3, 5), plt.imshow(nemo_mask2), plt.axis('off'), plt.title('nemo_mask2')

plt.subplot(2, 3, 3), plt.imshow(mask12, cmap='gray'), plt.axis('off'), plt.title('mask12')
plt.subplot(2, 3, 6), plt.imshow(nemo_mask12), plt.axis('off'), plt.title('nemo_mask12')
plt.show()

效果很棒 !

接着我们处理一下 mask 中的不连续点和毛刺,之间膨胀腐蚀一下就 ok 了:

# 膨胀腐蚀
plt.figure()
plt.subplot(1, 3, 1), plt.imshow(rgb_nemo)
plt.title('RGB_Nemo'), plt.axis('off')

kernel = np.ones((9, 9), np.uint8)
mask = cv.morphologyEx(mask12, cv.MORPH_CLOSE, kernel)
ROI = np.zeros(mask.shape, np.uint8)
ROI[0:150, 50:200] = 1
mask = mask * ROI
plt.subplot(1, 3, 2), plt.imshow(mask, cmap='gray')
plt.title('Mask after opening'), plt.axis('off')

nemo_ROI = rgb_nemo * cv.merge([mask, mask, mask])
plt.subplot(1, 3, 3), plt.imshow(nemo_ROI)
plt.title('Nemo'), plt.axis('off')
plt.show()

Perfect!


3. 程序代码

为了方便使用,我把程序打包了,在后面需要分割 nemo 鱼的程序中调用即可

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def generate_nemo_mask(img):
	rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
	hsv_img = cv.cvtColor(rgb_img, cv.COLOR_RGB2HSV)

	light_orange = (5, 100, 120)
	dark_orange = (20, 255, 255)
	light_white = (35, 0, 160)
	dark_light = (255, 160, 255)

	mask1 = cv.inRange(hsv_img, light_orange, dark_orange)
	mask2 = cv.inRange(hsv_img, light_white, dark_light)
	mask = np.array((mask1 + mask2) > 0, dtype=mask1.dtype)

	kernel = np.ones((9, 9), np.uint8)
	mask = cv.morphologyEx(mask, cv.MORPH_CLOSE, kernel)

	ROI = np.zeros(mask.shape, np.uint8)
	ROI[0:166, 50:200] = 1
	mask = mask * ROI

	return mask

注意:如果是分割其他图像,则需要重新设置 HSV 参数和 ROI 区域,可以增加 function 的参数~


REFERENCES:

  1. http://www.360doc.com/content/18/1003/15/13328254_791618642.shtml
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值