分割论文 图像处理
2017-06-16 11:39:29 llh_1178 阅读数 18874

我们在处理图像的时候,常常需要将图像的前景和背景做不同的处理,这时需要将前景和背景分割开。关于图像分割的方法我知道的有三种方法:K-means、分水岭和GrabCut算法进行物体分割。不能够肯定的比较出谁优谁劣,各种算法是分各种场合以及设定参数的优化。在此,只是简单介绍,学习之路任重而道远!

K-means方法进行分割:

它是一种最常用的聚类算法。因为,人们不需要手动的为数据集里的每个个体添加标签,能自动的发现集群结构,进行分类。是一种无监督的学习。那么是什么定义了集群的呢?答案是通过中心和形状定义的。然后,通过打分判断依据是:在这个集群中的分数高于在其他聚类中的分数和与本集群中心点比其他集群中心点更相似。具体的步骤是:首先,把观测分配给最近的中心点。然后,把集群中心点修改为被分配给原中心点观测的均值,反复这两步操作,直到全部收敛。

关于OpenCV下的kmean算法,函数为cv2.kmeans()
函数的格式为:kmeans(data, K, bestLabels, criteria, attempts, flags)

其中,K(分类数)和 attempts(Kmeans算法重复次数)是需要根据具体的图像进行优化的参数。像bestLabels预设分类标签可以不需要用None表示,criteria为迭代停止的模式选择,格式为(type,max_iter,epsilon),其中type又有两种选择:cv2.TERM_CRITERIA_EPS :精确度(误差)满足epsilon停止和cv2.TERM_CRITERIA_MAX_ITER:迭代次数超过max_iter停止,也可以两者结合,满意任意一个就结束。而flags(初始类中心选择),有两种方法:cv2.KMEANS_PP_CENTERS ; cv2.KMEANS_RANDOM_CENTERS

下面,就尝试一下修改K(分类数)和 attempts(Kmeans算法重复次数)参数进行测试。

先将K设为默认值,调attempts次数。

# 以灰色导入图像
img = cv2.imread('messi5.jpg',0)#image read be 'gray'
plt.subplot(221),plt.imshow(img,'gray'),plt.title('original')
plt.xticks([]),plt.yticks([])

# 改变图像的维度
img1 = img.reshape((img.shape[0]*img.shape[1],1))
img1 = np.float32(img1)

# 设定一个criteria,
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER,10,1.0)

# 设定一个初始类中心flags
flags = cv2.KMEANS_RANDOM_CENTERS
# 应用K-means
compactness,labels,centers = cv2.kmeans(img1,2,None,criteria,5,flags)
compactness_1,labels_1,centers_1 = cv2.kmeans(img1,2,None,criteria,10,flags)
compactness_2,labels_2,centers_2 = cv2.kmeans(img1,2,None,criteria,15,flags)
img2 = labels.reshape((img.shape[0],img.shape[1]))
img3 = labels_1.reshape((img.shape[0],img.shape[1]))
img4 = labels_2.reshape((img.shape[0],img.shape[1]))
plt.subplot(222),plt.imshow(img2,'gray'),plt.title('kmeans_attempts_5')
plt.xticks([]),plt.yticks([])
plt.subplot(223),plt.imshow(img3,'gray'),plt.title('kmeans_attempts_10')
plt.xticks([]),plt.yticks([])
plt.subplot(224),plt.imshow(img4,'gray'),plt.title('kmeans_attempts_15')
plt.xticks([]),plt.yticks([])
plt.savefig("kmeans_attempts.png")
plt.show()



可以看出attempts次数不同,是会造成图像分割差异的。

再来调K值,这里将attempts次数设为10.得到的图像为:


也可以看出K初始值不同,同样造成图像分割差异。所以,可以说这两个参数的优化是很重要的,但是,也不容易优化。看下一种方法:

分水岭算法

之所以叫分水岭算法,是因为它里面有“水”的概念。把图像中低密度的区域(变化很少)想象成山谷,图像中高密度的区域(变化很多)想象成山峰。开始向山谷中注入水直到不同的山谷中的水开始汇集。为了阻止不同山谷的水汇聚,可以设置一些栅栏,最后得到的栅栏就是图像分割。

img = cv2.imread("water_coins.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 将颜色转变为灰色之后,可为图像设一个阈值,将图像二值化。
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# 下面用morphologyEx变换来除去噪声数据,这是一种对图像进行膨胀之后再进行腐蚀的操作,它可以提取图像特征:
kernel = np.ones((3,3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations= 2)
# 通过对morphologyEx变换之后的图像进行膨胀操作,可以得到大部分都是背景的区域:
sure_bg = cv2.dilate(opening, kernel, iterations=3)
# 接着通过distanceTransform来获取确定前景区域,原理是应用一个阈值来决定哪些区域是前景,越是远离背景区域的边界的点越可能属于前景。
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)
# 考虑前景和背景中有重合的部分,通过sure_fg和sure_bg的集合相减得到。
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)
# 现在有了这些区域,就可以设定“栅栏”来阻止水汇聚了,这通过connectedComponents函数来完成
ret, markers = cv2.connectedComponents(sure_fg)
# 在背景区域上加1, 这会将unknown区域设置为0:
markers = markers + 1
markers[unknown==255] = 0
# 最后打开门,让水漫起来并把栅栏绘成红色
markers = cv2.watershed(img, markers)
img[markers == -1] = [255, 0, 0]
plt.imshow(img), plt.xticks([]),plt.yticks([])
plt.show()



能够看出还是能大多数完整分割。

接下来介绍GrabCut算法进行对图像的分割处理。

使用GrabCut算法的实现步骤为:
1)在图片中定义含有(一个或多个)物体的矩形
2)矩形外的区域被自动认为是背景
3)对于用户定义的矩形区域,可用背景中的数据来区别它里面的前景和背景区域
4)用高斯混合模型(GMM)来对背景和前景建模,并将末定义的像素标记为可能的前景或背景
5)图像中的每一个像素都被看作通过虚拟边与周围像素相连接,而每一条边都有一个属于前景或背景的概率,这基于它与周围像素颜色上的相似性
6)每一个像素会与一个前景或背景节点连接。若节点之间不属于同一个终端(就是两个相邻的节点,一个节点属于前景,一个节点属于背景),则会切断它们之间的边,这就将图像各个部分分割出来了。

import numpy as np
import cv2
from matplotlib import pyplot as plt

# 首先加载图片,然后创建一个与所加载图片同形状的掩模,并用0填充。
img = cv2.imread("messi5.jpg")
mask = np.zeros(img.shape[:2], np.uint8)

# 然后创建以0填充的前景和背景模型:
bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)
# 在实现GrabCut算法前,先用一个标识出想要隔离的对象的矩形来初始化它,这个矩形我们用下面的一行代码定义(x,y,w,h):
rect = (100, 50, 421, 378)
# 接下来用指定的空模型和掩摸来运行GrabCut算法
#mask, bgdModel, fgdModel = cv2.grabCut(img,mask,None,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK)
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT) # 5是指算法的迭代次数。
# 然后,我们再设定一个掩模,用来过滤之前掩模中的值(0-3)。值为0和2的将转为0,值为1和3的将转化为1,这样就可以过滤出所有的0值像素(背景)。
mask2 = np.where((mask==2)|(mask==0), 0, 1).astype("uint8")
img = img * mask2[:, :, np.newaxis]
# 最后可视化展现分割前后的图像
plt.subplot(1, 2, 1)
plt.imshow(img)
plt.title("grabcut"), plt.xticks([]), plt.yticks([])

plt.subplot(1, 2, 2)
plt.imshow(cv2.imread("messi5.jpg"))
plt.title("original"), plt.xticks([]), plt.yticks([])
plt.savefig("grabcut.png")



可以看出来分割的并不完整,而且头发和手都没有被区分到前景中来,这是因为,在设定矩形的时候需要不断优化的,且因每一张图像都有差异,所以矩形的范围也是有差异的。还好在github上找到了一个grabcut算法脚本,能完美的解决这个问题。并用他的代码进行测试。如下图:





参考:

《OpenCV3计算机视觉Python语言实现》

OpenCV帮助文档:

http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_grabcut/py_grabcut.html

https://github.com/opencv/opencv/blob/master/samples/python/grabcut.py


2019-05-08 22:00:20 qq_42016865 阅读数 80
  1. 使用SLIC算法将图像超像素分割。产生一个与原图大小相同的标签矩阵。
  2. 想要操作其中某个超像素,即某个聚类中的所有点,可以使用
    numpy.where(label_mat==label)
    其中,label_mat是超像素的标签矩阵,label是想要操作的超像素的标签值,这 样可以返回两个对应标签值的坐标list,分别对应行坐标和列坐标。进而可以使用  坐标来对原图像相应位置的像素点进行处理。
2017-10-14 05:39:00 weixin_33709590 阅读数 3

 


 

二值图像分割

以下的改进是http://www.imagepy.org/的作者原创,我只是对其理解之后改进和说明,欢迎大家使用这个小软件!

如有朋友需要源工程,请在评论处留邮箱!


 

 

原理:给定最大值处的种子点,然后进行涨水,各个种子点进行碰撞

 

  1 void Binary_Division(InputArray& _src, Mat& mask, vector<Point> Edge_Point)
  2 {
  3     Mat src = _src.getMat();// , mask = _mask.getMat();
  4                 
  5     mask = src.clone();
  6     distanceTransform(src, src, DIST_L2, DIST_MASK_3, 5);
  7     normalize(src, src, 0, 255, NORM_MINMAX);
  8     src.convertTo(src, CV_8UC1);
  9     
 10     vector<vector<Point>> Edge_Data;
 11     for (size_t i = 0; i < Edge_Point.size(); i++)
 12     {
 13         vector<Point> temp;
 14         temp.push_back(Edge_Point[i]);
 15         Edge_Data.push_back(temp);
 16         mask.at<uchar>(Edge_Point[i]) = i + 1;
 17     }
 18     
 19     const int histSize = 255;
 20     float range[] = { 0, 255 };
 21     const float* histRange = { range };
 22     Mat hist;
 23     calcHist(&src, 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false);
 24 
 25     
 26     hist = hist.reshape(1, 1);
 27     normalize(hist, hist, 0, 1000, NORM_MINMAX);
 28     hist.convertTo(hist, CV_32FC1);
 29     for (size_t level = 255; level > 0; level--)
 30     {
 31         if (!hist.at<float>(0, level)) continue;
 32         FindRidge(src, mask, Edge_Data, level);
 33     }
 34 }
 35 
 36 void FindRidge(InputArray& _src, Mat& mask, vector<vector<Point>>& Edge_Point,int level)
 37 {
 38     Mat src = _src.getMat();
 39     for (size_t i = 0; i < Edge_Point.size(); i++)
 40     {
 41         uchar pre_mark = i + 1;
 42         for (int j = 0; j < Edge_Point[i].size(); j++)
 43         {
 44             vector<Point> temp_vector;
 45             temp_vector.push_back(Point(Edge_Point[i][j].x, Edge_Point[i][j].y - 1));
 46             temp_vector.push_back(Point(Edge_Point[i][j].x, Edge_Point[i][j].y + 1));
 47             temp_vector.push_back(Point(Edge_Point[i][j].x - 1, Edge_Point[i][j].y));
 48             temp_vector.push_back(Point(Edge_Point[i][j].x + 1, Edge_Point[i][j].y));
 49             uchar* msk = mask.ptr(Edge_Point[i][j].y);
 50             uchar* img = src.ptr(Edge_Point[i][j].y);
 51             if (img[Edge_Point[i][j].x] < level)    continue;
 52             if (msk[j] == 254)
 53             {
 54                 Edge_Point[i].erase(Edge_Point[i].begin() + j);
 55                 j--;
 56                 continue;
 57             }
 58             bool Flag = true;
 59             for (size_t j = 0; j < temp_vector.size(); j++)
 60             {
 61                 uchar* pre_data = mask.ptr(temp_vector[j].y);
 62                 if (pre_data[temp_vector[j].x] != pre_mark && pre_data[temp_vector[j].x] != 0 
 63                     && pre_data[temp_vector[j].x] != 255 && pre_data[temp_vector[j].x] != (150+i+1)
 64                     && pre_data[temp_vector[j].x] != 254)
 65                 {
 66                     pre_data[temp_vector[j].x] = 254;
 67                     continue;
 68                 }
 69                 else if (pre_data[temp_vector[j].x] == 0 || pre_data[temp_vector[j].x] == pre_mark || pre_data[temp_vector[j].x] == 254) continue;            
 70                 else if (pre_data[temp_vector[j].x] == 255)
 71                 {
 72                     if (src.at<uchar>(temp_vector[j]) <= level)
 73                     {
 74                         pre_data[temp_vector[j].x] = pre_mark;
 75                         Edge_Point[i].push_back(temp_vector[j]);
 76                         //Edge_Point[i].insert(Edge_Point[i].begin() + j + 1, temp_vector[j]);
 77                     }
 78                     else
 79                     {
 80                         FillBlock(src, Edge_Point[i], mask, level, Edge_Point[i][j], i);
 81                         Flag = false;
 82                     }
 83                 }
 84             }
 85             if (Flag)
 86             {
 87                 mask.at<uchar>(Edge_Point[i][j]) = 150 + i + 1;
 88                 Edge_Point[i].erase(Edge_Point[i].begin() + j);
 89                 j--;
 90             }
 91             else
 92             {
 93                 mask.at<uchar>(Edge_Point[i][j]) = i + 1;
 94             }
 95         }
 96     }
 97 }
 98 
 99 void FillBlock(InputArray& _src, vector<Point>& Edge_Point, Mat& mask, int level, Point center,int seed_num)
100 {
101     Mat src = _src.getMat();
102     mask.at<uchar>(center) = seed_num + 151;
103     vector<Point> fill_point;
104     int count = 0, count_mount = 1;//count;
105     fill_point.push_back(center);
106     while (count < count_mount)
107     {
108         vector<uchar*> img;
109         vector<uchar*> msk;
110         for (int i = -1; i < 2; i++)
111         {
112             img.push_back(src.ptr<uchar>(fill_point[count].y + i));
113             msk.push_back(mask.ptr<uchar>(fill_point[count].y + i));
114         }
115         for (size_t i = 0; i < 3; i++)
116         {
117             for (int j = -1; j < 2; j++)
118             {
119                 if (img[i][fill_point[count].x + j] > level && !(j == 0 && i == 1) && msk[i][fill_point[count].x + j] == 255)
120                 {
121                     fill_point.push_back(Point(fill_point[count].x + j, fill_point[count].y + i - 1));
122                     msk[i][fill_point[count].x + j] = seed_num + 151;
123                 }
124                 else if (img[i][fill_point[count].x + j] <= level && msk[i][fill_point[count].x + j] == 255)
125                 {
126                     Edge_Point.push_back(Point(fill_point[count].x + j, fill_point[count].y + i - 1));
127                     msk[i][fill_point[count].x + j] = seed_num + 1;
128                 }
129             }
130         }
131         //msk[1][fill_point[count].x] = 2;
132         count_mount = fill_point.size() - 1;
133         fill_point.erase(fill_point.begin());
134     }
135 }

 

转载于:https://www.cnblogs.com/wjy-lulu/p/7664941.html

2017-10-14 05:39:00 weixin_34184561 阅读数 5

 


 

二值图像分割

以下的改进是http://www.imagepy.org/的作者原创,我只是对其理解之后改进和说明,欢迎大家使用这个小软件!

如有朋友需要源工程,请在评论处留邮箱!


 

 

原理:给定最大值处的种子点,然后进行涨水,各个种子点进行碰撞

 

  1 void Binary_Division(InputArray& _src, Mat& mask, vector<Point> Edge_Point)
  2 {
  3     Mat src = _src.getMat();// , mask = _mask.getMat();
  4                 
  5     mask = src.clone();
  6     distanceTransform(src, src, DIST_L2, DIST_MASK_3, 5);
  7     normalize(src, src, 0, 255, NORM_MINMAX);
  8     src.convertTo(src, CV_8UC1);
  9     
 10     vector<vector<Point>> Edge_Data;
 11     for (size_t i = 0; i < Edge_Point.size(); i++)
 12     {
 13         vector<Point> temp;
 14         temp.push_back(Edge_Point[i]);
 15         Edge_Data.push_back(temp);
 16         mask.at<uchar>(Edge_Point[i]) = i + 1;
 17     }
 18     
 19     const int histSize = 255;
 20     float range[] = { 0, 255 };
 21     const float* histRange = { range };
 22     Mat hist;
 23     calcHist(&src, 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false);
 24 
 25     
 26     hist = hist.reshape(1, 1);
 27     normalize(hist, hist, 0, 1000, NORM_MINMAX);
 28     hist.convertTo(hist, CV_32FC1);
 29     for (size_t level = 255; level > 0; level--)
 30     {
 31         if (!hist.at<float>(0, level)) continue;
 32         FindRidge(src, mask, Edge_Data, level);
 33     }
 34 }
 35 
 36 void FindRidge(InputArray& _src, Mat& mask, vector<vector<Point>>& Edge_Point,int level)
 37 {
 38     Mat src = _src.getMat();
 39     for (size_t i = 0; i < Edge_Point.size(); i++)
 40     {
 41         uchar pre_mark = i + 1;
 42         for (int j = 0; j < Edge_Point[i].size(); j++)
 43         {
 44             vector<Point> temp_vector;
 45             temp_vector.push_back(Point(Edge_Point[i][j].x, Edge_Point[i][j].y - 1));
 46             temp_vector.push_back(Point(Edge_Point[i][j].x, Edge_Point[i][j].y + 1));
 47             temp_vector.push_back(Point(Edge_Point[i][j].x - 1, Edge_Point[i][j].y));
 48             temp_vector.push_back(Point(Edge_Point[i][j].x + 1, Edge_Point[i][j].y));
 49             uchar* msk = mask.ptr(Edge_Point[i][j].y);
 50             uchar* img = src.ptr(Edge_Point[i][j].y);
 51             if (img[Edge_Point[i][j].x] < level)    continue;
 52             if (msk[j] == 254)
 53             {
 54                 Edge_Point[i].erase(Edge_Point[i].begin() + j);
 55                 j--;
 56                 continue;
 57             }
 58             bool Flag = true;
 59             for (size_t j = 0; j < temp_vector.size(); j++)
 60             {
 61                 uchar* pre_data = mask.ptr(temp_vector[j].y);
 62                 if (pre_data[temp_vector[j].x] != pre_mark && pre_data[temp_vector[j].x] != 0 
 63                     && pre_data[temp_vector[j].x] != 255 && pre_data[temp_vector[j].x] != (150+i+1)
 64                     && pre_data[temp_vector[j].x] != 254)
 65                 {
 66                     pre_data[temp_vector[j].x] = 254;
 67                     continue;
 68                 }
 69                 else if (pre_data[temp_vector[j].x] == 0 || pre_data[temp_vector[j].x] == pre_mark || pre_data[temp_vector[j].x] == 254) continue;            
 70                 else if (pre_data[temp_vector[j].x] == 255)
 71                 {
 72                     if (src.at<uchar>(temp_vector[j]) <= level)
 73                     {
 74                         pre_data[temp_vector[j].x] = pre_mark;
 75                         Edge_Point[i].push_back(temp_vector[j]);
 76                         //Edge_Point[i].insert(Edge_Point[i].begin() + j + 1, temp_vector[j]);
 77                     }
 78                     else
 79                     {
 80                         FillBlock(src, Edge_Point[i], mask, level, Edge_Point[i][j], i);
 81                         Flag = false;
 82                     }
 83                 }
 84             }
 85             if (Flag)
 86             {
 87                 mask.at<uchar>(Edge_Point[i][j]) = 150 + i + 1;
 88                 Edge_Point[i].erase(Edge_Point[i].begin() + j);
 89                 j--;
 90             }
 91             else
 92             {
 93                 mask.at<uchar>(Edge_Point[i][j]) = i + 1;
 94             }
 95         }
 96     }
 97 }
 98 
 99 void FillBlock(InputArray& _src, vector<Point>& Edge_Point, Mat& mask, int level, Point center,int seed_num)
100 {
101     Mat src = _src.getMat();
102     mask.at<uchar>(center) = seed_num + 151;
103     vector<Point> fill_point;
104     int count = 0, count_mount = 1;//count;
105     fill_point.push_back(center);
106     while (count < count_mount)
107     {
108         vector<uchar*> img;
109         vector<uchar*> msk;
110         for (int i = -1; i < 2; i++)
111         {
112             img.push_back(src.ptr<uchar>(fill_point[count].y + i));
113             msk.push_back(mask.ptr<uchar>(fill_point[count].y + i));
114         }
115         for (size_t i = 0; i < 3; i++)
116         {
117             for (int j = -1; j < 2; j++)
118             {
119                 if (img[i][fill_point[count].x + j] > level && !(j == 0 && i == 1) && msk[i][fill_point[count].x + j] == 255)
120                 {
121                     fill_point.push_back(Point(fill_point[count].x + j, fill_point[count].y + i - 1));
122                     msk[i][fill_point[count].x + j] = seed_num + 151;
123                 }
124                 else if (img[i][fill_point[count].x + j] <= level && msk[i][fill_point[count].x + j] == 255)
125                 {
126                     Edge_Point.push_back(Point(fill_point[count].x + j, fill_point[count].y + i - 1));
127                     msk[i][fill_point[count].x + j] = seed_num + 1;
128                 }
129             }
130         }
131         //msk[1][fill_point[count].x] = 2;
132         count_mount = fill_point.size() - 1;
133         fill_point.erase(fill_point.begin());
134     }
135 }

 

转载于:https://www.cnblogs.com/wjy-lulu/p/7664941.html

2018-10-09 16:29:46 qq_35054151 阅读数 473

    区域生长方法也是一种常用的区域分割技术,其基本思路是首先定义一个生长准则,然后在每个分割区域内寻找一个种子像素,通过对图像进行扫描,依次在种子点周围邻域内寻找满足生长准则的像素并将其合并到种子所在的区域,然后再检查该区域的全部相邻点,并把满足生长准则的点合并到该区域,不断重复该过程直到找不到满足条件的像素为止。该方法的关键在于种子点的位置、生长准则和生长顺序。分水岭算法[47,48]是以数学形态学作为基础的一种区域分割方法。其基本思想是将梯度图像看成是假想的地形表面,每个像素的梯度值表示该点的海拔高度。原图中的平坦区域梯度较小,构成盆地,边界处梯度较大构成分割盆地的山脊。分水岭算法模拟水的渗入过程,假设水从最低洼的地方渗入,随着水位上升,较小的山脊被淹没,而在较高的山脊上筑起水坝,防止两区域合并。当水位达到最高山脊时,算法结束,每一个孤立的积水盆地构成一个分割区域。由于受到图像噪声和目标区域内部的细节信息等因素影响,使用分水岭算法通常会产生过分割现象,分水岭算法一般是作为一种预分割方法,与其它分割方法结合使用,以提高算法的效率或精度,使用分水岭算法结合基于图论的方法进行图像分割,在计算精度与计算效率方面均取得了很好的效果。

链接:https://blog.csdn.net/nnnnnnnnnnnny/article/details/52862543

没有更多推荐了,返回首页