OpenCV自学笔记6. 图像分割

图像分割

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

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

这里写图片描述

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

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

# -*- 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)

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

这里写图片描述

最后的效果为:

这里写图片描述

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

使用GrubCut算法进行图像分割

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

这里写图片描述

# -*- 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算法的参数如下:

这里写图片描述

注意:

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

GrubCut算法的运行结果如下:

这里写图片描述


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

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

grubcut.cpp

#include <opencv2/opencv.hpp>
#include <iostream>

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;
}

测试用图为:
这里写图片描述

背景分割的结果如下:
可以看到,准确分割出了建筑物的主体部分
这里写图片描述

这里写图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值