- 在线时间
- 482 小时
- 最后登录
- 2026-9-11
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7951 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2977
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1183
- 主题
- 1198
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
分水岭算法:模拟地理形态的图像分割) ^7 P9 c/ w- y! ?: |! b; s6 y& ], L0 y
分水岭算法通过模拟自然地形来实现图像中物体的分类。在这一过程中,每个像素的灰度值被视作其高度,灰度值较高的像素形成山脊,即分水岭,而二值化阈值则相当于水平面,低于这个水平面的区域会被“淹没”。
/ n+ C) N8 ]; M7 T8 @! A测地线距离:地形分析的核心6 Q5 A1 `5 Q/ Q' t; _& L2 `
测地线距离是分水岭算法中的一个关键概念,它代表地球表面两点间的最短路径。这一概念在图论中同样适用,指的是图中两节点间的最短路径,与欧氏距离相比,测地线距离考虑的是实际路径。
9 S; y; u( Y9 s/ `分水岭算法的执行步骤0 x, U6 U( m o* a- Y4 k
梯度图像分类:根据灰度值对梯度图像中的像素进行分类,并设定测地距离阈值。起始点标记:选择灰度值最小的像素点作为起始点,这些点通常是局部最小值。水平面上升:随着阈值的增长,测量周围邻域像素到起始点的测地距离。若小于阈值,则淹没这些像素;若大于阈值,则在这些像素上建立“大坝”。大坝设置与区域分区:随着水平面的上升,建立更多的大坝,直到所有区域在分水岭线上相遇,完成图像的分区。避免过度分割的策略( ?; {( n4 {$ @
分水岭算法可能会因噪声或干扰导致图像过度分割,形成过多的小区域。解决这一问题的方法包括:
6 y, H1 l0 ~: U高斯平滑:通过高斯平滑减少噪声,合并小分区。基于标记的分水岭算法:选择相对较高的灰度值像素作为起始点,手动标记或使用自动方法如距离变换来确定,从而合并小区域。OpenCV 实现 Watershed 算法函数原型:void watershed( InputArray image, InputOutputArray markers );1参数说明:image:输入的图像,必须是8位的单通道灰度图像。这个图像的梯度信息将被用来模拟水流向低洼地区流动的过程。' X" @) j9 ?2 y7 p4 B
markers:输入输出参数,是一个与原图像大小相同的图像,用于存放分割标记。在函数调用前,这个图像应该被初始化,其中包含了用户定义的分割区域的标记。标记是通过正整数索引来表示的,表示用户已知的前景或背景区域。所有未知区域(即算法需要确定的区域)应该被标记为0。函数执行完成后,每个像素点的标记将被更新为“种子”组件的值,或者在区域边界处被设置为-1。6 R& ]5 u5 A% p: u3 j* C) C+ z' X
功能说明:watershed 函数会分析 image 的梯度信息,并使用 markers 中定义的已知区域作为分割的起点(种子点)。算法将从这些种子点开始,逐步对图像中的其他像素点进行区域归属的判定,直到所有像素点都被标记。在分割过程中,如果两个相邻的已知区域(种子点)相遇,算法会在它们之间创建一个边界,以避免这些区域合并在一起,从而实现分割。注意事项:markers 中的标记非常重要,它们直接影响分割的结果。因此,用户需要仔细考虑如何标记已知的前景和背景区域。分水岭算法可能会导致过度分割,特别是当图像中存在大量噪声时。在实际应用中,可能需要对图像进行预处理,如使用高斯模糊去除小的局部最小值,以减少过度分割的问题。- #include <opencv2/imgcodecs.hpp>
?' j, R3 Z9 z2 \1 u& ?9 I w3 X- } - #include <opencv2/highgui.hpp>, s r1 i# d% b# x\" O) k( d8 j
- #include <opencv2/imgproc.hpp>
0 f' E* f! \- `4 Y( c& Q
% j% J; _: Q# \, m- void showImg(const std::string& windowName, const cv::Mat& img){
' c3 I( _) N7 {3 q, t7 O& D f - cv::imshow(windowName, img);$ Z% _8 K1 W2 {% x4 B
- }
/ u$ V\" e% F( n' `/ f - * @3 |) e. f/ [1 t% M1 K
- void getBackground(const cv::Mat& source, cv::Mat& dst) {2 x! z\" m\" ^3 |( l5 M) P+ ~% O- Q0 g
- cv::dilate(source, dst, cv::Mat::ones(3, 3, CV_8U)); // 3x3 核
; g9 j! U# G$ }0 I - }
% L8 c3 ^1 m' V/ e5 e+ P - , ?9 X1 @6 a ~+ U) P) N* l
- void getForeground(const cv::Mat& source, cv::Mat& dst) {
* K; Y$ a2 h( ~4 P5 H/ ?5 H - cv::distanceTransform(source, dst, cv::DIST_L2, 3, CV_32F);
) Z, d7 u. @1 D$ M\" L# H# [ - cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX);9 K& N$ a7 Z7 B0 [
- }$ ?: n: J3 n& A
- * h: H1 }9 W. f+ w1 G
- void findMarker(const cv::Mat& sureBg, cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) {+ _6 n5 f0 n& F. m; q/ H( N
- cv::findContours(sureBg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
+ T) R& h8 j; t3 K - // 绘制前景标记! j$ R5 h; V& C+ I# i1 F7 }
- for (size_t i = 0, size = contours.size(); i < size; i++)+ L\" X' X/ P% |) C9 v+ F
- drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1);- U! w6 ^1 V$ Y4 v, x2 x1 x; P- e
- }
4 a) \) c- j6 `
3 e! e: @# B4 d: U- void getRandomColor(std::vector<cv::Vec3b>& colors, size_t size) {
5 ~ f I3 m0 k6 W# ?) ? - for (int i = 0; i < size ; ++i) {; J' i* a b8 k
- int b = cv::theRNG().uniform(0, 256);
- }, w3 `, _$ n( M7 r - int g = cv::theRNG().uniform(0, 256);' C$ X! m4 g- @ ~
- int r = cv::theRNG().uniform(0, 256);4 Q* A: }0 c/ K! ?# B: n
- colors.emplace_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));( u8 j3 _: D8 m8 {/ I
- }& ?4 i5 B4 T/ x* e\" t- b7 M
- }, r/ i/ C, P/ |\" V6 }: }
- % d- o2 n\" p5 D; X+ W3 w5 U
- int main(int argc, char** argv) {
5 q/ l/ m$ n0 Q f! G# A3 n3 Y - if(argc < 2){6 P- @/ R A- ^; f
- std::cerr << "Errorn";6 e; w, a5 m1 x$ q/ b9 |
- std::cerr << "Provide Input Image:n n";/ H, x) n2 j H1 S
- return -1;( J5 h% `0 n2 |# M# U( t& A2 T
- }, E2 w; F/ J( l: |8 z% ?
- cv::Mat original_img = cv::imread(argv[1]);' L) s @\" A. P% Y$ f+ I( M- A1 a
- if(original_img.empty()){& j- u3 c; E. ~4 E$ {7 J# q2 o\" U
- std::cerr << "Errorn";
4 }# i5 j$ r+ |8 K/ A- j) z* l6 j - std::cerr << "Cannot Read Imagen";! M/ A$ J1 L1 s0 ]6 [* n
- return -1;
! }4 Z* G4 m9 G* N3 l) Y - }$ v- k% B8 ^* p2 s, V0 z0 w! X6 ?
- cv::Mat shifted;! K) d2 Y' |9 B' M8 B
- cv::pyrMeanShiftFiltering(original_img, shifted, 21, 51);
7 d9 |; K! s% ^- \* o - showImg("Mean Shifted", shifted);
9 ^* X8 q+ ~: d5 u4 H- A g - cv::Mat gray_img;
% B. I X- j6 e. u0 L: D% y. a: J\" T - cv::cvtColor(original_img, gray_img, cv::COLOR_BGR2GRAY);
; W+ O, P: `6 L! U - showImg("GrayIMg", gray_img);
( u, @0 ^) v& e! Q; q- ?) }\" L! | - cv::Mat bin_img;1 W5 P& j2 B7 E( d
- cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
4 i0 o3 e7 m% ^2 ~0 ~7 } ~ - showImg("thres img", bin_img);
; u1 T; O' n/ G C. K \* t - cv::Mat sure_bg;3 H$ ]' P& \7 D- E7 Y: g
- getBackground(bin_img, sure_bg);+ A: f# r- f2 p2 `
- showImg("Sure Background", sure_bg);+ N# c2 Z: G+ G0 r
- cv::Mat sure_fg;7 ~, ]1 L3 t& X; F6 X! \0 [1 ^
- getForeground(bin_img, sure_fg);- t: U& a4 j, p* O. r# s
- showImg("Sure ForeGround", sure_fg);3 l2 D% v6 G# l' I& V0 ]7 X% W2 Q
- cv::Mat markers = cv::Mat::zeros(sure_bg.size(), CV_32S);
: t/ p$ F' n7 _\" Y& B) k - std::vector<std::vector<cv::Point>> contours;
4 d! P2 w+ b0 f! q* \- }9 G\" i/ [ - findMarker(sure_bg, markers, contours);/ F4 Q- L. Q1 V5 e7 Y\" i
- cv::circle(markers, cv::Point(5, 5), 3, cv::Scalar(255), -1); // 在标记周围绘制圆圈2 ~& _# Q; L, D$ s- g
- q! E% w# |. [
- cv::watershed(original_img, markers);3 o- t6 V3 E: i/ t/ j& a8 B
- cv::Mat mark;' c* q% e8 b& j6 |/ _6 R+ ~
- markers.convertTo(mark, CV_8U);3 ^\" n7 m3 Q4 z' c3 z$ h$ x6 N
- cv::bitwise_not(mark, mark); // 将白色转换为黑色,黑色转换为白色
: C! T7 h+ e3 i ^8 } - showImg("MARKER", mark);
2 U+ H3 v- H& f& l - // 在图像中突出显示标记 /
& I! w4 @5 [. [$ j - std::vector<cv::Vec3b> colors;
2 \6 T& `! s. q! j6 ^* x# ~( Q( f - getRandomColor(colors, contours.size()); // 创建结果图像
% N+ S) d2 a2 G# G\" z - cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);
1 a! Q: V- y6 m% ?, i A - // 用随机颜色填充标记的对象+ t8 S( ?3 `% D) Z
- for (int i = 0; i < markers.rows; i++)$ j/ w, d- I\" y' i& A! |3 v
- {
2 a# x8 v$ c\" j* y. V+ c - for (int j = 0; j < markers.cols; j++)
4 U% f! N& ^7 k5 a( T1 h; Z - { o3 ?5 o q0 t3 u7 v5 a+ \( J6 m j: q+ Y
- int index = markers.at(i,j);
6 ?0 f& u G2 n - if (index > 0 && index <= static_cast<int>(contours.size()))
, ^4 i4 Z. z h - dst.at<cv::Vec3b>(i,j) = colors[index-1];0 ]! ?9 P: g% c\" o& a
- }' _) a% R; t _: b3 Z# T
- }7 @; g+ Z: `\" o2 E; S
- showImg("Final Result", dst);
- i5 t* V6 ` r- q) @+ I - cv::waitKey(0);
4 V1 p$ e\" f0 H' {7 _* f8 |) p - return 0;
. ?3 h' z6 k7 j - }8 z& q+ f |( C\" r7 Q- {) `# X
复制代码 ————————————————
- F7 \7 H! X5 Q! M, @! B" r! Q 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/matt45m/article/details/1382113598 o- w" R4 `1 F; u/ v
8 d1 w. p* A/ F8 K
|
zan
|