OpenCV自学笔记6. 图像分割

图像分割

引言:
图像分割是图像预处理的重要步骤之一,它的主要目标是将图像划分为不同的区域,这些区域与真实世界中的物体具有一定的关联成分。图像分割的方法大体分为以下三种:基于阈值的分割、基于边缘的分割和基于区域的分割。其中基于区域的分割较为常用。OpenCV提供了 分水岭算法 和 GrabCut算法,可以快速实现图像的分割。

本小节使用的测试图像为:

OpenCV自学笔记6. 图像分割_第1张图片

————————————————————————–

使用分水岭算法进行图像分割

# -*- coding:utf-8 -*-

import cv2
import numpy as np

# Step1. 加载图像
img = cv2.imread('images/fruits.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Step2.阈值分割,将图像分为黑白两部分
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
cv2.imshow("thresh", thresh)

# Step3. 对图像进行“开运算”,先腐蚀再膨胀
kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
cv2.imshow("opening", opening)

# Step4. 对“开运算”的结果进行膨胀,得到大部分都是背景的区域
sure_bg = cv2.dilate(opening, kernel, iterations=3)
cv2.imshow("sure_bg", sure_bg)

# Step5.通过distanceTransform获取前景区域
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, sure_fg = cv2.threshold(dist_transform, 0.6 * dist_transform.max(), 255, 0)
cv2.imshow("sure_fg", sure_fg)

# Step6. sure_bg与sure_fg相减,得到既有前景又有背景的重合区域
sure_fg = np.uint8(sure_fg)
unknow = cv2.subtract(sure_bg, sure_fg)

# Step7. 连通区域处理
ret, markers = cv2.connectedComponents(sure_fg)
markers = markers + 1
markers[unknow==255] = 0

# Step8.分水岭算法
markers = cv2.watershed(img, markers)
img[markers == -1] = [0, 255, 0]

cv2.imshow("dst", img)
cv2.waitKey(0)

各个步骤的运行结果如图:

OpenCV自学笔记6. 图像分割_第2张图片

最后的效果为:

OpenCV自学笔记6. 图像分割_第3张图片

————————————————————————–

使用GrubCut算法进行图像分割

本小节使用的测试图像为:

OpenCV自学笔记6. 图像分割_第4张图片

# -*- coding:utf-8 -*-

import cv2
import numpy as np

# Step1. 加载图像
img = cv2.imread('images/messi5.jpg')

# Step2. 创建掩模、背景图和前景图
mask = np.zeros(img.shape[:2], np.uint8) # 创建大小相同的掩模
bgdModel = np.zeros((1,65), np.float64) # 创建背景图像
fgdModel = np.zeros((1,65), np.float64) # 创建前景图像

# Step3. 初始化矩形区域
# 这个矩形必须完全包含前景(相当于这里的梅西)
rect = (50,50,450,290)

# Step4. GrubCut算法,迭代5次
# mask的取值为0,1,2,3
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT) # 迭代5次

# Step5. mask中,值为2和0的统一转化为0, 1和3转化为1 
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
img = img * mask2[:,:,np.newaxis] # np.newaxis 插入一个新维度,相当于将二维矩阵扩充为三维

cv2.imshow("dst", img)
cv2.waitKey(0)

GrubCut算法的参数如下:

OpenCV自学笔记6. 图像分割_第5张图片

注意:

Initially user draws a rectangle around the foreground region (foreground region shoule be completely inside the rectangle)
也就是说,前景图必须完全包含在矩形内,即:初始化的矩形必须完全包含梅西~

GrubCut算法的运行结果如下:

OpenCV自学笔记6. 图像分割_第6张图片


附录:用C++写的GrubCut算法小例子

参考:http://www.cnblogs.com/mikewolf2002/p/3330390.html

grubcut.cpp

#include 
#include 

using namespace cv;
using namespace std;

int main() 
{
    string path = "images/test4.jpg";

    Mat img = imread(path); // 读入图像
    Mat bgModel, fgModel, mask;
    Rect rect;
    rect.x = 20;
    rect.y = 30;
    rect.width = img.cols - (rect.x << 1);
    rect.height = img.rows - (rect.y << 1);
    //rectangle(img, rect, Scalar(0, 0, 255), 3, 8, 0);//用矩形画矩形窗  

    //循环执行3次,这个可以自己设置
    grabCut(img, mask, rect, bgModel, fgModel, 3, GC_INIT_WITH_RECT);
    compare(mask, GC_PR_FGD, mask, CMP_EQ);
    Mat foreground(img.size(), CV_8UC3, Scalar(255, 255, 255));
    img.copyTo(foreground, mask);
    imshow("foreground", foreground);

    waitKey(0);
    return 0;
}

测试用图为:
OpenCV自学笔记6. 图像分割_第7张图片

背景分割的结果如下:
可以看到,准确分割出了建筑物的主体部分
OpenCV自学笔记6. 图像分割_第8张图片

OpenCV自学笔记6. 图像分割_第9张图片

你可能感兴趣的:(OpenCV,OpenCV学习笔记)