本实验为计算机科学与技术学院计算机专业大四上限选课,2023-2024-1年度课程实验,较以往实验内容发生较大变化
本实验使用vscode,python语言,需要提前安装opencv,具体方法请自行搜索。
实验6:图像统计特征
基于颜色分布的交互图像分割
•
基于由用户交互笔刷标记的前、背景像素(图中黄绿区域),计算前、背景的颜色分布,并用于估计未标记像素属于前景和背景的概率。
•
颜色分布可以用直方图或者高斯混合模型(
GMM
)表示。如果用
GMM
,可以基于
OpenCV
的实现估计
GMM
参数。
import cv2
import numpy as np
from sklearn.mixture import GaussianMixture as GM
img = cv2.imread('1.jpg')
# 使用cv2的resize方法调整图片大小,将图片的长和宽都缩小为原来的0.5倍,然后赋给变量img
img = cv2.resize(img, (0, 0), fx=0.5, fy=0.5)
l = np.zeros(img.shape[:2], dtype=np.uint8)
f, b = 1, 2
d, m = False, f
o = np.zeros_like(img)
fc, bc = (0, 255, 0), (255, 0, 0)
def draw(e, x, y, f, p):
global d, m, o, l
if e == cv2.EVENT_LBUTTONDOWN:#鼠标左键按下
d = True
c, lbl = (fc, f) if m == f else (bc, b)# 如果m等于f,则c为fc,否则为bc;lbl始终为f或b
cv2.circle(o, (x, y), 10, c, -1)
cv2.circle(l, (x, y), 10, lbl, -1)# 在图像l上以(x,y)为圆心画一个半径为10的圆,颜色为lbl
elif e == cv2.EVENT_MOUSEMOVE and d:
c, lbl = (fc, f) if m == f else (bc, b)
cv2.circle(o, (x, y), 10, c, -1)# 在图像o上以(x,y)为圆心画一个半径为10的圆,颜色为c
cv2.circle(l, (x, y), 10, lbl, -1)# 在图像l上以(x,y)为圆心画一个半径为10的圆,颜色为lbl
elif e == cv2.EVENT_LBUTTONUP:
d = False
def perform_gcut(img, l):
fpx, bpx = img[l == f].reshape(-1, 3), img[l == b].reshape(-1, 3)
# 创建两个高斯混合模型(Gaussian Mixture Model,GMM),分别是gmf和gmb。每个模型有5个组件(即高斯分布),并且协方差类型为'full',表示每个组件都有独立的协方差矩阵。
gmf, gmb = GM(n_components=5, covariance_type='full'), GM(n_components=5, covariance_type='full')
gmf.fit(fpx) # 使用fpx训练gmf模型,使用bpx训练gmb模型。
gmb.fit(bpx)
pf, pb = gmf.score_samples(img.reshape(-1, 3)), gmb.score_samples(img.reshape(-1, 3))
pf, pb = np.exp(pf), np.exp(pb)# 计算整个图像img对于gmf和gmb模型的得分,即每个像素属于某个高斯分布的概率。
# 对得分进行指数化,使得概率值在合理的范围内。
pm = pf / (pf + pb)# 计算每个像素属于gmf模型的概率,即前景(标记为f的区域)的概率。
th = 0.5# 定义一个阈值th,用于分割前景和背景。
rm = np.where(pm > th, 1, 0)# 使用条件语句将图像分割为前景(标记为1)和背景(标记为0)。
rm = rm.reshape(img.shape[:2])# 将分割后的二维数组重塑为原始图像的形状,但是只有两个维度(高度和宽度),没有颜色通道。
ri = np.zeros_like(img)# 创建一个与原始图像形状相同但所有像素值为0的新图像ri。
ri[rm == 0], ri[rm == 1] = [0, 0, 0], [255, 255, 255] # 根据分割结果rm,将新图像ri中属于背景的部分设置为黑色(RGB值为[0, 0, 0]),属于前景的部分设置为白色(RGB值为[255, 255, 255])。
se = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
cv2.dilate(ri, se, ri)
cv2.imshow('mask', ri)
cv2.namedWindow('input')
cv2.setMouseCallback('input', draw)
while True:
mi = cv2.addWeighted(img, 1, o, 0.5, 0)
cv2.imshow('input', mi)
k = cv2.waitKey(10) & 0xFF
if k == 27:
break
elif k == ord('f'):
m = f
elif k == ord('b'):
m = b
elif k == ord('n'):
perform_gcut(img, l)
cv2.destroyAllWindows()