- 在线时间
- 482 小时
- 最后登录
- 2026-9-11
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7953 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2978
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1183
- 主题
- 1198
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
分水岭算法:模拟地理形态的图像分割
" n* v0 q; F& }/ F分水岭算法通过模拟自然地形来实现图像中物体的分类。在这一过程中,每个像素的灰度值被视作其高度,灰度值较高的像素形成山脊,即分水岭,而二值化阈值则相当于水平面,低于这个水平面的区域会被“淹没”。9 {+ W0 V+ k. B. }/ z" ~* t
测地线距离:地形分析的核心: V+ N8 @; ?* ]6 S, q2 _; V2 D
测地线距离是分水岭算法中的一个关键概念,它代表地球表面两点间的最短路径。这一概念在图论中同样适用,指的是图中两节点间的最短路径,与欧氏距离相比,测地线距离考虑的是实际路径。+ h4 j# H* d: r4 W# e
分水岭算法的执行步骤' S: U8 ~9 ^7 z6 w+ v6 f
梯度图像分类:根据灰度值对梯度图像中的像素进行分类,并设定测地距离阈值。起始点标记:选择灰度值最小的像素点作为起始点,这些点通常是局部最小值。水平面上升:随着阈值的增长,测量周围邻域像素到起始点的测地距离。若小于阈值,则淹没这些像素;若大于阈值,则在这些像素上建立“大坝”。大坝设置与区域分区:随着水平面的上升,建立更多的大坝,直到所有区域在分水岭线上相遇,完成图像的分区。避免过度分割的策略
1 b6 e4 o0 @ R( Z1 j/ H分水岭算法可能会因噪声或干扰导致图像过度分割,形成过多的小区域。解决这一问题的方法包括:
9 ]% f3 P, K! `; L1 B/ ~8 P7 }高斯平滑:通过高斯平滑减少噪声,合并小分区。基于标记的分水岭算法:选择相对较高的灰度值像素作为起始点,手动标记或使用自动方法如距离变换来确定,从而合并小区域。OpenCV 实现 Watershed 算法函数原型:void watershed( InputArray image, InputOutputArray markers );1参数说明:image:输入的图像,必须是8位的单通道灰度图像。这个图像的梯度信息将被用来模拟水流向低洼地区流动的过程。
( p9 S) y! Z: g3 K( V6 Smarkers:输入输出参数,是一个与原图像大小相同的图像,用于存放分割标记。在函数调用前,这个图像应该被初始化,其中包含了用户定义的分割区域的标记。标记是通过正整数索引来表示的,表示用户已知的前景或背景区域。所有未知区域(即算法需要确定的区域)应该被标记为0。函数执行完成后,每个像素点的标记将被更新为“种子”组件的值,或者在区域边界处被设置为-1。
7 [' [+ g2 W5 @' e0 l, |功能说明:watershed 函数会分析 image 的梯度信息,并使用 markers 中定义的已知区域作为分割的起点(种子点)。算法将从这些种子点开始,逐步对图像中的其他像素点进行区域归属的判定,直到所有像素点都被标记。在分割过程中,如果两个相邻的已知区域(种子点)相遇,算法会在它们之间创建一个边界,以避免这些区域合并在一起,从而实现分割。注意事项:markers 中的标记非常重要,它们直接影响分割的结果。因此,用户需要仔细考虑如何标记已知的前景和背景区域。分水岭算法可能会导致过度分割,特别是当图像中存在大量噪声时。在实际应用中,可能需要对图像进行预处理,如使用高斯模糊去除小的局部最小值,以减少过度分割的问题。- #include <opencv2/imgcodecs.hpp>
! q% p( A2 m/ ^! t0 H - #include <opencv2/highgui.hpp>
' X% G% k\" U9 x% f- S8 p; A, u% f - #include <opencv2/imgproc.hpp>7 o7 C$ O$ M\" ?2 F6 y
- % O\" o' f& t2 X# h
- void showImg(const std::string& windowName, const cv::Mat& img){5 A7 w, Z5 G# H6 N) q' V1 F
- cv::imshow(windowName, img);4 w5 S! M2 T$ Z( u0 e
- }/ `- P0 H8 b' G a; s
9 X, T: w/ |: J5 W- void getBackground(const cv::Mat& source, cv::Mat& dst) {
H6 C, O) G$ l$ { k\" w - cv::dilate(source, dst, cv::Mat::ones(3, 3, CV_8U)); // 3x3 核3 J J) N& u! u4 f
- }- U2 {- ?$ D5 l u/ j% Q
( W6 x, [3 s+ S6 l1 e2 F& J- void getForeground(const cv::Mat& source, cv::Mat& dst) {: ?7 A7 U0 ^ F! ^. h8 U$ Y
- cv::distanceTransform(source, dst, cv::DIST_L2, 3, CV_32F);# K% K% h: F2 j
- cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX);+ Q( Q$ [- Z8 o2 V; q: m
- }
; I; K3 Z& J0 k3 s% s( S: D) I - / G8 H! b6 p\" W' g W1 Y* R
- void findMarker(const cv::Mat& sureBg, cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) {6 D$ Q/ k& e4 x+ o5 ^
- cv::findContours(sureBg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); b$ l4 b2 J\" s/ d- e
- // 绘制前景标记0 I# R% a, P+ t- p7 w1 q {1 p: i
- for (size_t i = 0, size = contours.size(); i < size; i++)
' c1 i\" E5 H/ _, Z5 a - drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1);0 l( E# |, ^9 ]! Z# V
- }
. G$ u- ~! v# p% B
. u$ c\" T- q( e2 c8 k, n- void getRandomColor(std::vector<cv::Vec3b>& colors, size_t size) {
, ^8 V- C; S1 _# ^. O - for (int i = 0; i < size ; ++i) {
1 t* i, T% f D2 w - int b = cv::theRNG().uniform(0, 256);
9 p\" @( Y0 ~0 j8 s M8 f% ] - int g = cv::theRNG().uniform(0, 256);
- n& K2 T. i4 S1 `) H6 B8 ? - int r = cv::theRNG().uniform(0, 256);* q7 q% X) ]* R, l
- colors.emplace_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));\" b6 z$ P! K+ Y
- }- D8 o2 }8 q6 m
- }
\" W* T7 i6 i: g: f% v! r\" l5 J
1 H/ D, h3 r4 C6 N% J$ e- int main(int argc, char** argv) {2 r9 C t5 D4 j/ [' ?$ ^! H- m
- if(argc < 2){7 h2 x6 p: C# U\" B
- std::cerr << "Errorn";# o5 }4 {/ i! G, Z
- std::cerr << "Provide Input Image:n n";
3 j4 q! Z) c5 y - return -1;
& L3 b3 r7 x/ n. u - }
: E8 b' D5 _- \# \. U - cv::Mat original_img = cv::imread(argv[1]);2 s- ?) B& ]/ J* U1 ^' L0 ^' n
- if(original_img.empty()){% K: J/ c+ W& ]8 P! v ]
- std::cerr << "Errorn";' A2 |6 y\" n1 ~2 T8 e8 E\" s2 C
- std::cerr << "Cannot Read Imagen"; \- y\" t: P6 n3 o) C3 x
- return -1;: p% q6 U; S% F( G8 x! X/ k0 V1 B
- }5 j7 j, D* c& W3 c3 m: r3 G/ H
- cv::Mat shifted;
4 @9 m9 C [ q, q - cv::pyrMeanShiftFiltering(original_img, shifted, 21, 51);
. S+ M/ X1 ^+ \; N- ?! J3 N8 t - showImg("Mean Shifted", shifted);6 x\" p1 a1 E; F* u4 o: q7 d
- cv::Mat gray_img;0 l! f3 g2 f! l+ H0 G; @' @
- cv::cvtColor(original_img, gray_img, cv::COLOR_BGR2GRAY);
2 r! P, D+ U/ x# j+ z - showImg("GrayIMg", gray_img);
* W0 b, b- d8 `2 w - cv::Mat bin_img;6 U! u7 d& P$ }3 z/ z
- cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
: A! `0 w3 n7 p5 D6 t& A - showImg("thres img", bin_img);
; a' Q2 j( T3 t7 W6 y - cv::Mat sure_bg;
+ W- O( s. A' h: _ - getBackground(bin_img, sure_bg);
+ W# V; I4 S/ e, M - showImg("Sure Background", sure_bg);
/ Y c5 u+ Y( c+ o2 } - cv::Mat sure_fg;
4 {* ]% L( d: G' T - getForeground(bin_img, sure_fg);' Y; Y+ e( X4 ~; J5 s
- showImg("Sure ForeGround", sure_fg);4 W! t4 s# c! d; A. l* O% v* t& B
- cv::Mat markers = cv::Mat::zeros(sure_bg.size(), CV_32S);
. n& p1 T Z& H' @' D - std::vector<std::vector<cv::Point>> contours;
& z X' A0 e. h2 @5 t/ Y& x9 k - findMarker(sure_bg, markers, contours);, @7 N3 n; ?( {) _5 a3 K! v7 b
- cv::circle(markers, cv::Point(5, 5), 3, cv::Scalar(255), -1); // 在标记周围绘制圆圈
5 H5 Y: I) w# T/ q -
$ T1 r$ v- N; l8 C, M - cv::watershed(original_img, markers);
* F' o0 O; G! f, [, K/ K - cv::Mat mark;# @ {! G1 |& r1 R0 n7 q
- markers.convertTo(mark, CV_8U);
% `5 Y( _& |4 W1 [$ ] - cv::bitwise_not(mark, mark); // 将白色转换为黑色,黑色转换为白色
+ y0 M2 E2 |, _8 R+ a - showImg("MARKER", mark);
6 H7 y( U- b. P& K- r x7 ?8 n* x - // 在图像中突出显示标记 / ^6 \2 K0 X+ p2 I0 {) o! f
- std::vector<cv::Vec3b> colors;
6 t( L q$ B\" C - getRandomColor(colors, contours.size()); // 创建结果图像7 m8 }\" G7 c3 J9 J/ `5 w
- cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);
( p6 r w/ L1 A\" B+ \0 w9 C - // 用随机颜色填充标记的对象
: x# D7 J\" B7 y9 q) L9 Y _7 Q$ H - for (int i = 0; i < markers.rows; i++)
3 z7 |9 P- g) n\" P - {
\" X4 j1 q$ z, w - for (int j = 0; j < markers.cols; j++)9 f: u4 P; w/ V
- {
& X) r% R% C; p5 T( b: T% M - int index = markers.at(i,j);8 l3 G. i4 p; Q; M. ^; X5 F
- if (index > 0 && index <= static_cast<int>(contours.size()))
2 b# h, ]5 k! x0 W2 p4 t& R - dst.at<cv::Vec3b>(i,j) = colors[index-1];
, i3 }8 s* }4 z! x1 a7 Q - }8 x: T# V8 Y8 b: t8 s1 p! d
- }
% s, \# R- r9 ` - showImg("Final Result", dst);
\" l5 j* d/ O( R0 ^& _ - cv::waitKey(0);
2 ~) E4 }- m( `/ ] - return 0;
\" b1 c/ x% o8 X - }2 Y w$ ~1 I\" @( r! g0 t9 V+ d$ |5 ?) V
复制代码 ————————————————% X8 ]: E( ]$ C s; }+ ?& V0 Z
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/matt45m/article/details/138211359
7 _9 t/ W& R: X* d& i) l5 n3 }; I' f
|
zan
|