- 在线时间
- 479 小时
- 最后登录
- 2026-4-17
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7790 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2923
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1171
- 主题
- 1186
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
分水岭算法:模拟地理形态的图像分割3 i) t5 ~" }- x: F0 h# ?
分水岭算法通过模拟自然地形来实现图像中物体的分类。在这一过程中,每个像素的灰度值被视作其高度,灰度值较高的像素形成山脊,即分水岭,而二值化阈值则相当于水平面,低于这个水平面的区域会被“淹没”。
9 e9 T4 G+ h4 c测地线距离:地形分析的核心! `) [0 s( N8 c! Q: C% Q
测地线距离是分水岭算法中的一个关键概念,它代表地球表面两点间的最短路径。这一概念在图论中同样适用,指的是图中两节点间的最短路径,与欧氏距离相比,测地线距离考虑的是实际路径。
# }: U+ o" |4 F" [分水岭算法的执行步骤
6 }, B/ P- i5 S8 ?梯度图像分类:根据灰度值对梯度图像中的像素进行分类,并设定测地距离阈值。起始点标记:选择灰度值最小的像素点作为起始点,这些点通常是局部最小值。水平面上升:随着阈值的增长,测量周围邻域像素到起始点的测地距离。若小于阈值,则淹没这些像素;若大于阈值,则在这些像素上建立“大坝”。大坝设置与区域分区:随着水平面的上升,建立更多的大坝,直到所有区域在分水岭线上相遇,完成图像的分区。避免过度分割的策略
, G5 H6 s9 f$ p. s( @分水岭算法可能会因噪声或干扰导致图像过度分割,形成过多的小区域。解决这一问题的方法包括:& ?7 r7 b% @7 z/ a5 P1 l+ ~' d0 v
高斯平滑:通过高斯平滑减少噪声,合并小分区。基于标记的分水岭算法:选择相对较高的灰度值像素作为起始点,手动标记或使用自动方法如距离变换来确定,从而合并小区域。OpenCV 实现 Watershed 算法函数原型:void watershed( InputArray image, InputOutputArray markers );1参数说明:image:输入的图像,必须是8位的单通道灰度图像。这个图像的梯度信息将被用来模拟水流向低洼地区流动的过程。
3 Y8 c: I+ s& ]3 P: ?/ G" Wmarkers:输入输出参数,是一个与原图像大小相同的图像,用于存放分割标记。在函数调用前,这个图像应该被初始化,其中包含了用户定义的分割区域的标记。标记是通过正整数索引来表示的,表示用户已知的前景或背景区域。所有未知区域(即算法需要确定的区域)应该被标记为0。函数执行完成后,每个像素点的标记将被更新为“种子”组件的值,或者在区域边界处被设置为-1。% `* _8 |' Y5 J5 f
功能说明:watershed 函数会分析 image 的梯度信息,并使用 markers 中定义的已知区域作为分割的起点(种子点)。算法将从这些种子点开始,逐步对图像中的其他像素点进行区域归属的判定,直到所有像素点都被标记。在分割过程中,如果两个相邻的已知区域(种子点)相遇,算法会在它们之间创建一个边界,以避免这些区域合并在一起,从而实现分割。注意事项:markers 中的标记非常重要,它们直接影响分割的结果。因此,用户需要仔细考虑如何标记已知的前景和背景区域。分水岭算法可能会导致过度分割,特别是当图像中存在大量噪声时。在实际应用中,可能需要对图像进行预处理,如使用高斯模糊去除小的局部最小值,以减少过度分割的问题。- #include <opencv2/imgcodecs.hpp>
4 k5 `% y* j5 d. O3 H - #include <opencv2/highgui.hpp>! E' Q3 x: q( }; `9 \( P4 Y
- #include <opencv2/imgproc.hpp>& ~7 E\" l# `2 Z
, f1 m3 K+ K- N) M- void showImg(const std::string& windowName, const cv::Mat& img){5 q4 K. B0 ?# [* J, F
- cv::imshow(windowName, img);' ^* J$ P) ?1 S# N
- }4 f7 I% g. `( l1 i3 E
3 t; ^, K, ]\" Y# {% I' o( P- void getBackground(const cv::Mat& source, cv::Mat& dst) {
; { q4 \- M# k - cv::dilate(source, dst, cv::Mat::ones(3, 3, CV_8U)); // 3x3 核
4 w+ a( |( r6 O- _+ f - }7 O6 V& d6 o, t- Z( M- Z
( B! U7 O; U/ ~8 V/ S: r- void getForeground(const cv::Mat& source, cv::Mat& dst) {; X\" v0 }( W7 T+ ^9 c0 w: y\" }
- cv::distanceTransform(source, dst, cv::DIST_L2, 3, CV_32F);
P( q; h1 S) C - cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX);$ U7 L& ` u/ {9 b- k8 {' \
- }
% F% W+ t5 F1 R0 b( {( n& |
: |# s( w- s4 G: J, g- void findMarker(const cv::Mat& sureBg, cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) {3 j6 Y# I- L6 d# f
- cv::findContours(sureBg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
9 @* i V& r' T# M% p - // 绘制前景标记
0 N5 E) E# u. H$ K* F3 r& G( y7 y\" r - for (size_t i = 0, size = contours.size(); i < size; i++)
8 P1 }3 O3 {+ a- S0 e* Q! E- ^ - drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1);
/ D* w8 L5 f3 J& ^* }( G/ z - }
+ E* p& r3 A; O- {+ l - 7 t1 | p7 z1 ^! E: _: Q
- void getRandomColor(std::vector<cv::Vec3b>& colors, size_t size) {
4 f4 ~5 p7 p8 b% K\" D _! j4 E - for (int i = 0; i < size ; ++i) { U8 D% a/ r# c
- int b = cv::theRNG().uniform(0, 256);0 ~2 A/ f. I& w/ B7 ]7 w3 n8 j% \
- int g = cv::theRNG().uniform(0, 256);9 P/ M* r# j2 b
- int r = cv::theRNG().uniform(0, 256);
# Z7 C) V b3 n! [: V q' P - colors.emplace_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));: [ S* ]0 A- ]. {+ H
- }
3 H0 v% r8 Y% E( E/ H7 u; y - }
$ H6 p0 ^+ \# g4 O$ B
, o$ b$ W$ G; p) c4 O/ w$ l- int main(int argc, char** argv) {# m+ {; P0 U( Q* s q
- if(argc < 2){
0 O\" ]* f+ x% b( } - std::cerr << "Errorn";
3 ?- h Z: f* r5 l, v* ]* B\" D - std::cerr << "Provide Input Image:n n";
! m% a2 q4 A+ @' g* ] ?) D - return -1;+ F8 M- u' A6 C6 ^3 W2 ^
- }
1 t: v. L$ V% U* S- ]* J\" W+ A - cv::Mat original_img = cv::imread(argv[1]);
: m- {' d1 C i) i+ [$ ^% _ - if(original_img.empty()){
P7 G' Z+ {8 l h - std::cerr << "Errorn";
- R4 ?/ \+ m6 U\" _ - std::cerr << "Cannot Read Imagen";, ~( n5 s' d0 u Z! _ w; k! `$ t/ d1 ^
- return -1;9 k$ K5 g9 W; F( P/ ]
- }
( R% }# U5 i7 |+ z - cv::Mat shifted;2 z* r$ l( S: l2 F6 H# P3 n
- cv::pyrMeanShiftFiltering(original_img, shifted, 21, 51);
& N' e5 T# ?+ l# h - showImg("Mean Shifted", shifted);0 R! y0 U( ^( X\" j w- q# {! Z
- cv::Mat gray_img;
8 D\" E* z# O9 C\" X g - cv::cvtColor(original_img, gray_img, cv::COLOR_BGR2GRAY);
% Z# h0 ?. w2 D) r1 w9 @\" H - showImg("GrayIMg", gray_img);
& \, Z7 Z* J) F; d& f8 ^. n* I - cv::Mat bin_img;
) M) }% J& B, h - cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
\" R* T8 r# `& Y - showImg("thres img", bin_img);5 {$ e$ T8 m, d& e
- cv::Mat sure_bg;
. d9 r/ w- D: S; r\" i2 X. O4 C1 g - getBackground(bin_img, sure_bg);
0 Z3 h\" y5 _# O+ d6 r - showImg("Sure Background", sure_bg);
+ Q9 Q! m3 k$ L6 K3 m+ ~ - cv::Mat sure_fg;9 \5 _6 k: h4 l8 b
- getForeground(bin_img, sure_fg);
; S1 N h9 K& A3 I/ w7 b - showImg("Sure ForeGround", sure_fg);( Q/ }. P0 v/ c8 N
- cv::Mat markers = cv::Mat::zeros(sure_bg.size(), CV_32S);, w0 e) \' r& F\" U( a ^$ R, C
- std::vector<std::vector<cv::Point>> contours;
5 v/ u) p& k# |4 N\" N5 B& ^( l+ g - findMarker(sure_bg, markers, contours);+ S$ n w$ R% F, s. u
- cv::circle(markers, cv::Point(5, 5), 3, cv::Scalar(255), -1); // 在标记周围绘制圆圈
7 i& ?: J3 L9 B6 U -
/ Q5 W+ N. W0 q# ], H, M* S4 y - cv::watershed(original_img, markers);
& z! \7 C# P; G3 f; n - cv::Mat mark;
\" S4 q1 A# Y2 a! ]* F) N3 q7 I - markers.convertTo(mark, CV_8U);: _5 c4 Y% U; H( K\" e0 x
- cv::bitwise_not(mark, mark); // 将白色转换为黑色,黑色转换为白色
, G& n/ V# s h4 w( j - showImg("MARKER", mark);
v# H& V s7 H4 I, w) | - // 在图像中突出显示标记 /
; {1 `4 \/ n8 e5 b( \\" g. s - std::vector<cv::Vec3b> colors;( e; v9 Q+ K\" K: {( M
- getRandomColor(colors, contours.size()); // 创建结果图像
* p; [\" W& C\" _% S - cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);* o7 h& \9 O! ?7 H- y
- // 用随机颜色填充标记的对象
% ]: g! s5 e6 V - for (int i = 0; i < markers.rows; i++)- g- P# n7 J$ {' Y
- {
, T. e& E- M2 R - for (int j = 0; j < markers.cols; j++)9 J) t' r\" y( c7 K' s- ~
- {( b k+ b3 Y4 { e9 c
- int index = markers.at(i,j);0 f/ d4 X# O) b( _
- if (index > 0 && index <= static_cast<int>(contours.size()))* L( O7 t9 \4 E! v2 c# l# f8 @
- dst.at<cv::Vec3b>(i,j) = colors[index-1];
* e9 e4 H: [0 ?: j - }
# ?4 v( \. K! N - }3 @3 C; O. J- B9 R; t- n4 `1 t+ [4 {0 A
- showImg("Final Result", dst);
4 D, a0 c# h( Q - cv::waitKey(0);8 ~+ [* {; D# i5 a2 m( O: {% a9 D
- return 0;
+ y% e, o1 U) W) |8 V9 z) y - }& g\" [: M% `' i( X
复制代码 ————————————————6 R5 _3 u: l: I& s+ b1 O5 R+ g/ i4 k
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/matt45m/article/details/138211359; R0 k( w G" |: d7 _# ^/ w3 l; m
# _7 A2 ?% J- G7 l [$ l, v# S) \ |
zan
|