做形态学方法的团队_图像分割实战-分水岭分割方法和GrabCut 算法

7ccf6042bbb685fef07a4b371c2e96dd.png

1. 分水岭分割方法

它是依赖于形态学的,图像的灰度等级不一样,如果图像的灰度等级一样的情况下怎么人为的把它造成不一样?可以通过距离变换实现,这样它们的灰度值就有了阶梯状的变换。风水岭算法常见的有三种方法:(1)基于浸泡理论的分水岭分割方法;(2)基于连通图方法;(3)基于距离变换的方法。OpenCV 中是基于距离变换的分割方法,就相当于我们的小山头(认为造成的)。

基本的步骤:

33a779960cbda5c182f0e2029a7d142c.png

例子1 粘连对象分离和计数。

例子代码:

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
void test()
{Mat srcImg;srcImg = imread("pill_002.png");if (srcImg.empty()){cout << "could not load image...n" << endl;}namedWindow("Original image", CV_WINDOW_AUTOSIZE);imshow("Original image", srcImg);Mat grayImg, binaryImg, shiftedImg;//做滤波,使图像更加平滑,保留边缘,类似于双边滤波pyrMeanShiftFiltering(srcImg, shiftedImg, 21, 51);  namedWindow("shifted", CV_WINDOW_AUTOSIZE);imshow("shifted", shiftedImg);cvtColor(shiftedImg, grayImg, COLOR_BGR2GRAY);  //转为灰度图像//二值化threshold(grayImg, binaryImg, 0, 255, THRESH_BINARY | THRESH_OTSU);  namedWindow("binary", CV_WINDOW_AUTOSIZE);imshow("binary", binaryImg);//距离变换Mat distImg;distanceTransform(binaryImg, distImg, DistanceTypes::DIST_L2, 3, CV_32F);//归一化,因为距离变换后得出来的值都比较小。normalize(distImg, distImg, 0, 1, NORM_MINMAX);  namedWindow("distance", CV_WINDOW_AUTOSIZE);imshow("distance", distImg);//这个二值化的作用是寻找局部最大。threshold(distImg, distImg, 0.4, 1, THRESH_BINARY);namedWindow("distance_binary", CV_WINDOW_AUTOSIZE);imshow("distance_binary", distImg);//生成 markerMat distMaskImg;// distImg 得到的是 0- 1之间的数,转化成8位单通道的。distImg.convertTo(distMaskImg, CV_8U);  vector<vector<Point>>contours;//找到 marker 的轮廓findContours(distMaskImg, contours, RETR_EXTERNAL,CHAIN_APPROX_SIMPLE, Point(0, 0));//create marker 填充 markerMat  markersImg = Mat::zeros(srcImg.size(), CV_32SC1);for (int i = 0; i < contours.size(); i++){drawContours(markersImg, contours, static_cast<int>(i),Scalar::all(static_cast<int>(i)+1), -1); }circle(markersImg, Point(5, 5), 3, Scalar(255), -1);//形态学操作 - 彩色图像,目的是去掉干扰,让结果更好。Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));morphologyEx(srcImg, srcImg, MORPH_ERODE, kernel);//完成分水岭变换watershed(srcImg, markersImg);Mat mark = Mat::zeros(markersImg.size(), CV_8UC1);markersImg.convertTo(mark, CV_8UC1);bitwise_not(mark, mark, Mat());namedWindow("watershed", CV_WINDOW_AUTOSIZE);imshow("watershed", mark);//下面的步骤可以不做,最好做出来让结果显示更美观。//生成随机颜色vector<Vec3b>colors;for (int i = 0; i < contours.size(); i++){int r = theRNG().uniform(0, 255);int g = theRNG().uniform(0, 255);int b = theRNG().uniform(0, 255);colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));}//颜色填充和最终显示Mat dstImg = Mat::zeros(markersImg.size(), CV_8UC3);int index = 0;for (int i = 0; i < markersImg.rows; i++){for (int j = 0; j < markersImg.cols; j++){index = markersImg.at<int>(i, j);if (index > 0 && index <= contours.size()){dstImg.at<Vec3b>(i, j) = colors[index - 1];}else{dstImg.at<Vec3b>(i, j) = Vec3b(0, 0, 0);}}}cout << "number of objects:" << contours.size() << endl;namedWindow("Final Result", CV_WINDOW_AUTOSIZE);imshow("Final Result", dstImg);
}
int main()
{test();waitKey(0);return 0;
}

效果:

7654a9a8dfcd55f9e74e9ee1684e98cc.png

总结:有时候会导致碎片化,过度分割,因为二值化中如果有很多小的黑点或碎片,在分割的时候导致很多 mask ,即小山头太多了,这个时候我们要考虑怎么去合并它,可以通过联通区域的直方图,或者像素值均值相似程度等。

例子2:图像分割

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
//执行分水岭算法函数
Mat watershedCluster(Mat &srcImg, int &numSegments);
//结果显示函数
void DisplaySegments(Mat &markersImg, int numSegments);
void test()
{Mat srcImg;srcImg = imread("toux.jpg");if (srcImg.empty()){cout << "could not load image...n" << endl;}namedWindow("Original image", CV_WINDOW_AUTOSIZE);imshow("Original image", srcImg);int numSegments;Mat markers = watershedCluster(srcImg, numSegments);DisplaySegments(markers, numSegments);
}Mat watershedCluster(Mat &srcImg, int &numSegments)
{//二值化Mat grayImg, binaryImg;cvtColor(srcImg, grayImg, COLOR_BGR2GRAY);threshold(grayImg, binaryImg, 0, 255, THRESH_BINARY | THRESH_OTSU);//形态学和距离变换Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));morphologyEx(binaryImg, binaryImg, MORPH_OPEN, kernel, Point(-1, -1));Mat distImg;distanceTransform(binaryImg, distImg, DistanceTypes::DIST_L2, 3, CV_32F);normalize(distImg, distImg, 0.0, 1.0, NORM_MINMAX);//开始生成标记threshold(distImg, distImg, 0.1, 1.0, THRESH_BINARY);normalize(distImg, distImg, 0, 255, NORM_MINMAX);distImg.convertTo(distImg, CV_8UC1);  //CV_32F 转成 CV_8UC1//标记开始vector<vector<Point>>contours;vector<Vec4i>hireachy;findContours(distImg, contours, hireachy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);if (contours.empty()){return Mat();}Mat markersImg(distImg.size(), CV_32S);markersImg = Scalar::all(0);for (int i = 0; i < contours.size(); i++){drawContours(markersImg, contours, i, Scalar(i + 1), -1, 8, hireachy, INT_MAX);}circle(markersImg, Point(5, 5) ,3, Scalar(255), -1);//分水岭变换watershed(srcImg, markersImg);numSegments = contours.size();return markersImg;
}void DisplaySegments(Mat &markersImg, int numSegments)
{//生成随机颜色vector<Vec3b>colors;for (int i = 0; i < numSegments; i++){int r = theRNG().uniform(0, 255);int g = theRNG().uniform(0, 255);int b = theRNG().uniform(0, 255);colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));}//颜色填充和最终显示Mat dstImg = Mat::zeros(markersImg.size(), CV_8UC3);int index = 0;for (int i = 0; i < markersImg.rows; i++){for (int j = 0; j < markersImg.cols; j++){index = markersImg.at<int>(i, j);if (index > 0 && index <= numSegments){dstImg.at<Vec3b>(i, j) = colors[index - 1];}else{dstImg.at<Vec3b>(i, j) = Vec3b(255, 255, 255);}}}cout << "number of objects:" << numSegments << endl;namedWindow("Final Result", CV_WINDOW_AUTOSIZE);imshow("Final Result", dstImg);
}
int main()
{test();waitKey(0);return 0;
}

效果图:

2d853c4feb51bce22d93cdd4f75488eb.png

2. GrabCut 算法分割图像

GrabCut 算法的原理前面有介绍过,这里就不在介绍了,具体可以看下文章末尾往期推荐中阅读。下面例子实现图像中对象的抠图。

基本步骤:

e057b09ebdd896efa3863092264e3674.png

例子代码:

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int numRun = 0; //算法迭代次数
bool init = false;
Rect rect;
Mat srcImg, MaskImg, bgModel, fgModel;//鼠标回调函数
void onMouse(int event, int x, int y, int flags, void* param);
void showImg();  //显示画的图片
void setRoiMask();  //选择 ROI 的函数
void runGrabCut();  //执行算法函数
static void ShowHelpText();  //提示用户操作函数void test()
{srcImg = imread("toux.jpg");if (srcImg.empty()){cout << "could not load image...n" << endl;}namedWindow("Original image", CV_WINDOW_AUTOSIZE);imshow("Original image", srcImg);//初始化 mask,单通道 8 位MaskImg.create(srcImg.size(), CV_8UC1);  //在不知道它是前景还是背景的情况下,把它全部设为背景。MaskImg.setTo(Scalar::all(GC_BGD));  //结果不是 0 就是 1  GC_BGD为0setMouseCallback("Original image", onMouse, 0);while (true){char c = (char)waitKey(0);if (c == 'n')  // 按下 n 建开始执行算法{runGrabCut();numRun++;showImg();cout << "current iteative times:" << numRun << endl;}if (c == 27){break;}}
}void onMouse(int event, int x, int y, int flags, void* param)
{switch (event){case EVENT_LBUTTONDOWN:rect.x = x;rect.y = y;rect.width = 1;rect.height = 1;break;case EVENT_MOUSEMOVE:if (flags& EVENT_FLAG_LBUTTON){rect = Rect(Point(rect.x, rect.y), Point(x, y));showImg();}break;case EVENT_LBUTTONUP:if (rect.width > 1 && rect.height > 1){showImg();}break;default:break;}
}void showImg()
{Mat result, binMask;binMask.create(MaskImg.size(), CV_8UC1);binMask = MaskImg & 1;if (init){srcImg.copyTo(result,binMask);}else{srcImg.copyTo(result);}rectangle(result, rect, Scalar(0, 0, 255), 2, 8);namedWindow("Original image", CV_WINDOW_AUTOSIZE);imshow("Original image", result);
}void setRoiMask()
{//GC_BGD = 0   明确属于背景的像素//GC_FGD = 1   明确属于前景的像素//GC_PR_BGD = 2  可能属于背景的像素//GC_PR_FGD = 3  可能属于前景的像素MaskImg.setTo(GC_BGD);  //为了避免选择越界rect.x = max(0, rect.x);rect.y = max(0, rect.y);rect.width = min(rect.width, srcImg.cols - rect.x);rect.height = min(rect.height, srcImg.rows - rect.y);//把我们选取的那一块设为前景MaskImg(rect).setTo(Scalar(GC_PR_FGD));
}void runGrabCut()
{if (rect.width < 2 || rect.height < 2){return;}if (init){grabCut(srcImg, MaskImg, rect, bgModel, fgModel, 1);}else{grabCut(srcImg, MaskImg, rect, bgModel, fgModel, 1, GC_INIT_WITH_RECT);init = true;}
}static void ShowHelpText()
{cout << "请先用鼠标在图片窗口中标记出属于前景的区域" << endl;cout << "然后再按按键【n】启动算法" << endl;cout << "按键【ESC】- 退出程序" << endl;
}int main()
{ShowHelpText();test();waitKey(0);return 0;
}

效果图:

471ba07b04f8494dba66941a3672fb59.png

d7c08aa3cff111d9b13324dd9f66ae3f.png

欢迎关注我的微信公众号“OpenCV图像处理算法”,主要是记录自己学习图像处理算法的历程,包括特征提取、目标跟踪、定位、机器学习和深度学习,每一个例子都会提供源码和例子所用的资料,欢迎同行的同学关注我和我一起虚度光阴吧!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/560011.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机gt的使用方法,旗舰级综合效果器 BOSS GT-1000使用宝典(二) | 基础操作

原标题&#xff1a;旗舰级综合效果器 BOSS GT-1000使用宝典(二) | 基础操作在上一期的使用宝典中&#xff0c;B老板为大家介绍了GT-1000内置的前级&#xff0c;箱体及麦克风等&#xff0c;&#x1f449; GT-1000使用宝典(一) | 了解你的神器 想必各位对自己手中的神器有了比较全…

日期格式化为yyyymmdd_你还在用SimpleDateFormat格式化时间嘛

Jdk1.8之时间处理该文章已经同步到Github&#xff1a;https://github.com/stackInk/makerstack1. 传统时间处理的问题1.1 多线程环境下的SimpleDateFormat当多个线程使用同一个时间处理对象进行对日期的格式化的时候&#xff0c;会出现java.lang.NumberFormatException: multip…

存储ic载板_延伸IC领域 崇达技术拟将持有普诺威55%股权

立即加星标每天看好文PCB网城讯崇达技术7月1日公告&#xff0c;6月30日&#xff0c;崇达技术股份有限公司(以下简称“崇达技术”)与朱小红、马洪伟在公司会议室签署了《关于江苏普诺威电子股份有限公司之股份转让协议》(以下简称“协议”)。根据协议&#xff0c;公司拟以自有资…

微云服务器失败原因_梦幻西游:服务器发生异常?游戏出现明显卡顿感,正在排查问题...

就在刚刚&#xff0c;不少梦幻玩家都在讨论一个情况&#xff0c;那就是服务器出现了明显的卡顿感&#xff0c;一些商人也陆续掉线&#xff0c;难道是服务器出现了异常&#xff1f;退出游戏之后&#xff0c;一直无法进入&#xff0c;登录界面总是停留在"正在连接某某服务器…

maven多模块项目部署到服务器,GitHub - baxias/foweb: 一个基于 Spring+SpringMVC+Mybatis 的Maven多模块项目。(实现前后端分离的服务器端)...

Foweb FrameworkA multi-modules maven project base on SpringSpringMVCMybatis.一个基于 SpringSpringMVCMybatis 的Maven多模块项目。使用文档两种使用方式&#xff1a;1. 直接将项目download下来&#xff0c;然后在IDE(Eclipse或者IDEA)中以maven项目导入&#xff0c;注意这…

网站需要数据库服务器吗,网站需要独立的服务器数据库吗

网站需要独立的服务器数据库吗 内容精选换一换文档数据库服务提供使用数据管理服务(Data Admin Service&#xff0c;简称DAS)、内网和公网的连接方式。文档数据库服务默认为您开通了远程主机登录权限&#xff0c;推荐您使用更安全便捷的数据管理服务连接实例&#xff0c;具体请…

只提取单元格中的数字_提取Excel单元格中的数字(4类)及原理

文中总结了4类从包含有中文、字母、数字的单元格数据中只提取数字的公式。(使用的时候把单元格名称改为实际的单元格名称即可)。1.提取文本左侧数据当单元格中数字在左侧&#xff0c;文字内容在右侧时&#xff0c;我们可以使用一下公式来将数字快速提取出来。函数公式&#xff…

delphi 执行长时间存储过程 显示进度_项目管理_十大管理体系之「项目进度管理」知识整理及心得分享...

项目进度管理项目进度管理在项目是项目管理三大要素之一&#xff0c;在项目管理中占有非常重要的地位。往往很多项目要求进度节点是一个固定期限&#xff0c;无法修改&#xff0c;所以进度把控就显得尤为重要。什么是进度管理&#xff0c;在你写project项目计划时就非常清楚的可…

bettertouchtool闪退_BetterTouchTool for Mac 3.238 无闪退 触控板增强工具

BetterTouchTool为魔术鼠标添加了许多新的完全可自定义的手势&#xff0c;Multi-Touch MacBook触控板和Magic Trackpad。这些手势是可定制的&#xff1a;魔术鼠标&#xff1a;捏合/缩小(缩放)单指点击左边单指轻拍右键单指中间单指轻拍单指点击中间双指轻敲双指点击双指轻扫(上…

宝塔php扩展fil_宝塔面板安装Redis给WordPress网站加速优化教程

宝塔面板php安装redis缓存以加快WordPress的开启速度&#xff0c;因为WordPress打开速度慢&#xff0c;所以霜天 seo使用多种方式来提高wordpress的加载速度&#xff0c;其中一个好办法是&#xff1a;使用redis加速缓存。本文的主题是宝塔面板php安装redis缓存加速WordPress&am…

常量缓存与integer比较_Integer缓存范围到底是多少?

本文主要大致思路为&#xff1a;不管从工作中还是面试&#xff0c;这篇文章都应该好好看完&#xff0c;本人认为是非常有用的。案例Integer是基本类型int的封装类。平时不管是入坑多年的小伙伴还在入坑路上的小伙伴&#xff0c;都应该知道的使用频率是相当高。下面模仿订单支付…

supervisor 子进程退出_用supervisor监管你的程序,不用担心程序挂了

1.背景无论多么优秀的程序员都难以避免写出来的程序会有崩溃的一天&#xff0c;特别是c、c这种高技术含量的语言&#xff0c;一不小心就来个段错误&#xff08;segment fault&#xff09;,我们通常会写一个守护进程或者守护脚本&#xff0c;检测对应的进程是否退出&#xff0c;…

pta7-3 统计不及格人数_应用统计专硕院校专业难度分析【青岛大学、苏大、西安交通大学】...

原文链接&#xff1a;应用统计专硕院校专业难度分析【第十二期】​mp.weixin.qq.com应用统计专硕院校专业难度分析 【第十二期】TO&#xff1a;哈喽&#xff0c;小可爱们早上给你好呀&#xff01;今天安安姐给你们更新经济类6个专硕院校专业难度分析专题第十二期&#xff0c;大…

mysql数据库事务有几种特性_面试官:你能说说事务的几个特性是啥?有哪几种隔离级别?...

1、面试题事务的几个特点是什么&#xff1f;数据库事务有哪些隔离级别&#xff1f;MySQL的默认隔离级别&#xff1f;2、面试官心里分析用mysql开发的三个基本面&#xff1a;存储引擎、索引&#xff0c;然后就是事务&#xff0c;你必须得用事务。因为一个业务系统里&#xff0c;…

latex 图片大小_用LaTeX写作业——插入图片(二)

&#xfeff;方法 subfigure可以横向排列一组图片&#xff0c;会自动编号abcd。在一个 subfigure内使用minipage插入图片&#xff0c;通过控制图片大小实现换行效果 # 效果 代码usepackage{subfigure} %所需宏包 usepackage{graphicx}begin{figure}[htbp] centersubfigure[标题…

python中单例模式是什么_python中的单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式&#xff0c;该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中&#xff0c;某个类只能出现一个实例时&#xff0c;单例对象就能派上用场。比如&#xff0c;某个服务器程序的配置信息存放在一个文件中…

java rsa算法_求RSA算法JAVA实现源代码(带界面的)

展开全部import javax.crypto.Cipher;import java.security.*;import java.security.spec.RSAPublicKeySpec;import java.security.spec.RSAPrivateKeySpec;import java.security.spec.InvalidKeySpecException;import java.security.interfaces.RSAPrivateKey;import java.sec…

java string值传递_String是值传递还是引用传递

string中的坑最近看到一道关于string的面试题&#xff0c;差点让我以为string是值传递&#xff0c;就是下面这个例子&#xff0c;体验下&#xff1a;public class demo{public static void main(string[] args) {demo d new demo();string str "bea";d.change(str)…

java切换jdk版本_切换JDK版本quick

最近遇到一个小问题&#xff0c;同时做两个项目&#xff0c;jdk版本一个是5&#xff0c;一个是6&#xff0c;我也去网上找了找方法&#xff0c;但是感觉不是特别好用&#xff0c;最后自己通过一些环境变量设置的技巧和一些批处理命令来使得这件事情只需要双击&#xff0c;输入一…