- 在线时间
- 482 小时
- 最后登录
- 2026-9-11
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7953 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2978
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1183
- 主题
- 1198
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
分水岭算法:模拟地理形态的图像分割
9 H( o7 m2 E* a+ a% c. s# {分水岭算法通过模拟自然地形来实现图像中物体的分类。在这一过程中,每个像素的灰度值被视作其高度,灰度值较高的像素形成山脊,即分水岭,而二值化阈值则相当于水平面,低于这个水平面的区域会被“淹没”。" z/ S: c l% }# N' u& Y' i
测地线距离:地形分析的核心& i" R1 `3 @! @" B( B
测地线距离是分水岭算法中的一个关键概念,它代表地球表面两点间的最短路径。这一概念在图论中同样适用,指的是图中两节点间的最短路径,与欧氏距离相比,测地线距离考虑的是实际路径。9 p9 K$ q" r( M- {$ U+ q
分水岭算法的执行步骤 V' d0 e1 e, {# j2 x
梯度图像分类:根据灰度值对梯度图像中的像素进行分类,并设定测地距离阈值。起始点标记:选择灰度值最小的像素点作为起始点,这些点通常是局部最小值。水平面上升:随着阈值的增长,测量周围邻域像素到起始点的测地距离。若小于阈值,则淹没这些像素;若大于阈值,则在这些像素上建立“大坝”。大坝设置与区域分区:随着水平面的上升,建立更多的大坝,直到所有区域在分水岭线上相遇,完成图像的分区。避免过度分割的策略9 ]- o! A# l' N: \- n8 ~
分水岭算法可能会因噪声或干扰导致图像过度分割,形成过多的小区域。解决这一问题的方法包括:
: c: e5 A+ s! c高斯平滑:通过高斯平滑减少噪声,合并小分区。基于标记的分水岭算法:选择相对较高的灰度值像素作为起始点,手动标记或使用自动方法如距离变换来确定,从而合并小区域。OpenCV 实现 Watershed 算法函数原型:void watershed( InputArray image, InputOutputArray markers );1参数说明:image:输入的图像,必须是8位的单通道灰度图像。这个图像的梯度信息将被用来模拟水流向低洼地区流动的过程。- J* q& O D$ b; T, _4 R
markers:输入输出参数,是一个与原图像大小相同的图像,用于存放分割标记。在函数调用前,这个图像应该被初始化,其中包含了用户定义的分割区域的标记。标记是通过正整数索引来表示的,表示用户已知的前景或背景区域。所有未知区域(即算法需要确定的区域)应该被标记为0。函数执行完成后,每个像素点的标记将被更新为“种子”组件的值,或者在区域边界处被设置为-1。
6 Q4 G* N+ m" H# V功能说明:watershed 函数会分析 image 的梯度信息,并使用 markers 中定义的已知区域作为分割的起点(种子点)。算法将从这些种子点开始,逐步对图像中的其他像素点进行区域归属的判定,直到所有像素点都被标记。在分割过程中,如果两个相邻的已知区域(种子点)相遇,算法会在它们之间创建一个边界,以避免这些区域合并在一起,从而实现分割。注意事项:markers 中的标记非常重要,它们直接影响分割的结果。因此,用户需要仔细考虑如何标记已知的前景和背景区域。分水岭算法可能会导致过度分割,特别是当图像中存在大量噪声时。在实际应用中,可能需要对图像进行预处理,如使用高斯模糊去除小的局部最小值,以减少过度分割的问题。- #include <opencv2/imgcodecs.hpp>) m\" c\" h5 o9 C! ]
- #include <opencv2/highgui.hpp>4 A! B& T- @; W- X: W# L
- #include <opencv2/imgproc.hpp>) [/ \9 P* x5 X# S
- ; S. p8 L1 a* c% N1 p- }- `
- void showImg(const std::string& windowName, const cv::Mat& img){$ U& |5 {. \6 S' K4 n/ a; H
- cv::imshow(windowName, img);
& _; R* a& ^6 L8 Z! g7 U5 q; p - }
# C: i3 `3 @\" |- z8 ]/ G\" n/ F+ g! y
% V- h! r5 e+ D+ u/ v8 b- void getBackground(const cv::Mat& source, cv::Mat& dst) {6 [. d; S! S5 c9 p$ U {# y Z: u& v
- cv::dilate(source, dst, cv::Mat::ones(3, 3, CV_8U)); // 3x3 核$ p2 A; u$ d9 N; }
- }
0 ~( M5 J/ D: V1 {2 F - 9 z6 F! v; Y# I; @9 s
- void getForeground(const cv::Mat& source, cv::Mat& dst) {: j' W7 M- P7 Q# {4 \
- cv::distanceTransform(source, dst, cv::DIST_L2, 3, CV_32F);
4 L\" D- r' `+ a2 j0 z - cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX);
1 }4 A1 n\" T' q - }
+ u/ P9 V) N2 d5 O7 ]0 { - 7 E: g5 S+ o! V% D
- void findMarker(const cv::Mat& sureBg, cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) {2 p2 m2 F% W. ~
- cv::findContours(sureBg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
$ D d+ x9 ^, N7 J! k3 P& v5 R - // 绘制前景标记
# b, z1 j& I+ t4 e' ]2 R9 h7 M - for (size_t i = 0, size = contours.size(); i < size; i++)
# v! N$ X( k+ J$ w: f - drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1);
$ T1 g1 R/ j4 Q$ j - }) g8 J) _9 x8 H, r% K
6 n. v) b8 {0 H5 J3 o) ~# U+ s: ?- void getRandomColor(std::vector<cv::Vec3b>& colors, size_t size) {
5 G: o% L- L) d( ^8 W - for (int i = 0; i < size ; ++i) {
/ [- Q5 e6 M\" j) {3 m# l7 B5 P - int b = cv::theRNG().uniform(0, 256);
7 P: n7 ]$ K$ H9 V( \ - int g = cv::theRNG().uniform(0, 256);
. u J( X' w3 c0 u* \ - int r = cv::theRNG().uniform(0, 256);- N\" @# k1 I( t' X9 D3 a' [) C\" B0 y
- colors.emplace_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));: i/ \2 Q' c# H$ e. v
- }
( y, H+ @' c6 Y1 `( A - }1 J* p% e! [& f6 [ G
2 T0 u' d\" r1 B( N+ h$ o3 s- int main(int argc, char** argv) {& V, _$ r2 O& ^2 Q, P) C5 V! p
- if(argc < 2){& a: S8 F2 m% t6 }
- std::cerr << "Errorn";
7 D6 D- a% Z\" W, J% Y - std::cerr << "Provide Input Image:n n";) v. e# k* M1 S3 N
- return -1;, i+ J U8 X# o+ ~6 I+ O( Q
- }
\" b' e4 u0 T% X4 g - cv::Mat original_img = cv::imread(argv[1]);
3 j) G' ?\" E$ [' p7 e5 N$ v - if(original_img.empty()){
7 g& Z* N+ U+ @\" ?; I - std::cerr << "Errorn";
/ T- B) n( _# ] - std::cerr << "Cannot Read Imagen";+ P$ l6 D& _; @
- return -1;
% q2 T9 m) v4 v; [0 E& b - }
& Y- c# \( v- P3 |$ d6 L& Q- F7 y& K - cv::Mat shifted;
# {* q/ s5 d: e L7 x# |6 m1 V - cv::pyrMeanShiftFiltering(original_img, shifted, 21, 51);' Y* s6 ~& y8 Q
- showImg("Mean Shifted", shifted);, }3 Z4 q+ ^/ F- _1 [
- cv::Mat gray_img;$ j! m% ?) r$ u) s0 z @, B: L
- cv::cvtColor(original_img, gray_img, cv::COLOR_BGR2GRAY);, x( c* u# n' s; B7 c
- showImg("GrayIMg", gray_img);6 v1 G. U+ S, e/ C\" H1 T8 h3 V
- cv::Mat bin_img;
. \: ^; w3 P/ J. J/ r - cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);0 q( i! h4 y2 p8 J( t$ v7 W% F8 i
- showImg("thres img", bin_img);8 T7 p# q# ^9 G3 f$ W
- cv::Mat sure_bg;# s5 w3 f) N' f, U/ ]: z) y& V2 H) S
- getBackground(bin_img, sure_bg);5 X! J- ]( [2 t4 V: _- j* B
- showImg("Sure Background", sure_bg);0 u/ T7 g* M9 Q8 F. a$ {
- cv::Mat sure_fg;: S5 O# I, t; x+ q2 @, L7 C) Y$ O
- getForeground(bin_img, sure_fg);
. d+ S, m& F' u! Z' v - showImg("Sure ForeGround", sure_fg);
: p7 O( U; ?3 Q8 a( B( I5 z - cv::Mat markers = cv::Mat::zeros(sure_bg.size(), CV_32S);
$ r' \; S$ E6 T/ c - std::vector<std::vector<cv::Point>> contours;\" T) m/ | M' f+ p( W\" C
- findMarker(sure_bg, markers, contours);. R% ]7 \' }1 [( R5 `, s+ C
- cv::circle(markers, cv::Point(5, 5), 3, cv::Scalar(255), -1); // 在标记周围绘制圆圈3 R# i& k\" ~/ s0 M& G! N! a- ^
- 3 O3 D2 a a8 y4 L, _2 i5 l
- cv::watershed(original_img, markers);
2 n\" C3 f D' {' [# ~0 G8 p0 {; G D - cv::Mat mark;% z) V6 U+ Z$ v/ C4 J& X
- markers.convertTo(mark, CV_8U);
. J! @6 V6 @7 V9 K6 G( M - cv::bitwise_not(mark, mark); // 将白色转换为黑色,黑色转换为白色
5 }1 p5 u\" B3 S- B7 L+ `$ e\" X - showImg("MARKER", mark);3 F# `( m3 j+ l- }; F% T
- // 在图像中突出显示标记 /1 [; B1 o, ~4 C; d3 x+ q. E' K
- std::vector<cv::Vec3b> colors;
0 `, l; `* b9 m% }% ?, v3 S - getRandomColor(colors, contours.size()); // 创建结果图像$ G\" t6 ?. N/ h) H
- cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);
% A' `1 [$ ?& b8 m - // 用随机颜色填充标记的对象\" A* j. A F/ n z1 ]$ F
- for (int i = 0; i < markers.rows; i++)( t1 g; R8 c9 t
- {
; ~' J9 B8 S( R2 M8 D0 d# W8 ] - for (int j = 0; j < markers.cols; j++)1 `; X+ U$ p+ a& K0 ?* e c T
- {% G4 l0 f7 J9 T( J' m
- int index = markers.at(i,j);
6 z) {. N7 ^\" g5 e3 S5 [$ c2 p7 y- n - if (index > 0 && index <= static_cast<int>(contours.size()))
+ v8 l5 D- n! O( A5 i: l5 u( d) u! J5 C3 \ - dst.at<cv::Vec3b>(i,j) = colors[index-1];
\" P j9 o; D' q6 ^ - }
; ?' E2 f9 T( u0 p! d - }$ _! S' P& H1 e/ w
- showImg("Final Result", dst);5 a } _; o' o' c
- cv::waitKey(0);& F3 j2 m6 h\" k% |7 e9 R% s' _- ~
- return 0;( T! H4 K/ Y1 I5 o* \1 o# z
- }
: j Z( H& z4 t/ P _
复制代码 ————————————————
5 U9 ^' a" @8 f& ]/ W4 w 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/matt45m/article/details/138211359' Z2 K6 f( ?3 u0 S. [' T2 \0 s
+ K4 b" `" @7 t$ A X- o' j2 V
|
zan
|