在线时间 482 小时 最后登录 2026-9-11 注册时间 2023-7-11 听众数 4 收听数 0 能力 0 分 体力 7951 点 威望 0 点 阅读权限 255 积分 2977 相册 0 日志 0 记录 0 帖子 1183 主题 1198 精华 0 分享 0 好友 1
该用户从未签到
分水岭算法:模拟地理形态的图像分割
^4 G' N5 A( c 分水岭算法通过模拟自然地形来实现图像中物体的分类。在这一过程中,每个像素的灰度值被视作其高度,灰度值较高的像素形成山脊,即分水岭,而二值化阈值则相当于水平面,低于这个水平面的区域会被“淹没”。 0 J4 U. b( Y( H q0 d" @/ \
测地线距离:地形分析的核心
6 H+ ~+ J1 }2 I$ Z 测地线距离是分水岭算法中的一个关键概念,它代表地球表面两点间的最短路径。这一概念在图论中同样适用,指的是图中两节点间的最短路径,与欧氏距离相比,测地线距离考虑的是实际路径。
. Y/ O* p# U, n6 h 分水岭算法的执行步骤
) _ H( Z% v3 X. y1 A& Z 梯度图像分类:根据灰度值对梯度图像中的像素进行分类,并设定测地距离阈值。 起始点标记:选择灰度值最小的像素点作为起始点,这些点通常是局部最小值。 水平面上升:随着阈值的增长,测量周围邻域像素到起始点的测地距离。若小于阈值,则淹没这些像素;若大于阈值,则在这些像素上建立“大坝”。 大坝设置与区域分区:随着水平面的上升,建立更多的大坝,直到所有区域在分水岭线上相遇,完成图像的分区。 避免过度分割的策略 ! a+ {2 \" Y% `8 A0 `% t' V
分水岭算法可能会因噪声或干扰导致图像过度分割,形成过多的小区域。解决这一问题的方法包括:
! A# u3 [; m7 @8 z) I+ M6 k 高斯平滑:通过高斯平滑减少噪声,合并小分区。 基于标记的分水岭算法:选择相对较高的灰度值像素作为起始点,手动标记或使用自动方法如距离变换来确定,从而合并小区域。 OpenCV 实现 Watershed 算法 函数原型: void watershed( InputArray image, InputOutputArray markers ); 1 参数说明: image:输入的图像,必须是8位的单通道灰度图像。这个图像的梯度信息将被用来模拟水流向低洼地区流动的过程。 / _7 m& W% p8 b( M" }
markers:输入输出参数,是一个与原图像大小相同的图像,用于存放分割标记。在函数调用前,这个图像应该被初始化,其中包含了用户定义的分割区域的标记。标记是通过正整数索引来表示的,表示用户已知的前景或背景区域。所有未知区域(即算法需要确定的区域)应该被标记为0。函数执行完成后,每个像素点的标记将被更新为“种子”组件的值,或者在区域边界处被设置为-1。
o; m3 c% H. Q& J# ^7 i+ V 功能说明: watershed 函数会分析 image 的梯度信息,并使用 markers 中定义的已知区域作为分割的起点(种子点)。 算法将从这些种子点开始,逐步对图像中的其他像素点进行区域归属的判定,直到所有像素点都被标记。 在分割过程中,如果两个相邻的已知区域(种子点)相遇,算法会在它们之间创建一个边界,以避免这些区域合并在一起,从而实现分割。 注意事项: markers 中的标记非常重要,它们直接影响分割的结果。因此,用户需要仔细考虑如何标记已知的前景和背景区域。 分水岭算法可能会导致过度分割,特别是当图像中存在大量噪声时。在实际应用中,可能需要对图像进行预处理,如使用高斯模糊去除小的局部最小值,以减少过度分割的问题。 #include <opencv2/imgcodecs.hpp>) M1 ~' c) Y9 @! }! ]/ X+ w( W
#include <opencv2/highgui.hpp>: P* j/ c/ e. N, Q: n8 p
#include <opencv2/imgproc.hpp>
2 a% S! Q' k6 w5 p( v' b4 x: {# b
# z5 T& `1 t0 k* B) l; H void showImg(const std::string& windowName, const cv::Mat& img){. p+ F- x+ B\" r/ L
cv::imshow(windowName, img);% W. G2 M/ ~' n1 m3 y6 M
}
/ Q) n) t5 g8 R( H& Q+ g: } }2 p\" X
! h, |! x* E5 V' u9 A void getBackground(const cv::Mat& source, cv::Mat& dst) {
3 ^4 h( ?\" h( n( j cv::dilate(source, dst, cv::Mat::ones(3, 3, CV_8U)); // 3x3 核
( B v' ^3 x, i- z2 ] }
. W& F. D. H; f0 a3 A
8 o/ d4 o4 h+ ~6 R+ E6 @ void getForeground(const cv::Mat& source, cv::Mat& dst) {
& }' B d, N' x z2 M' I cv::distanceTransform(source, dst, cv::DIST_L2, 3, CV_32F);; I9 Q' _+ W9 c
cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX);, y8 _$ u; |5 ~; ?
}
' m, ?! d5 J\" }; c( N, Q# n1 ^
$ A( b- Y; p3 r! u- L$ J! \: R void findMarker(const cv::Mat& sureBg, cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) {
) U. v0 S8 G8 T t4 Z cv::findContours(sureBg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);8 C$ {& f- E# g! @- }, d* P0 {1 T
// 绘制前景标记2 p( L7 Q4 n8 Z
for (size_t i = 0, size = contours.size(); i < size; i++)
) Z! |9 F1 [+ s: Y$ g3 o, O drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1);) v: O1 Q4 ~, P, g* c& {
}& x/ H9 I% f8 W. p& G
( i$ p/ @5 |: B- a P\" N( q
void getRandomColor(std::vector<cv::Vec3b>& colors, size_t size) {3 g$ S- `6 t* v) a1 f5 R' P! y, a5 t' [
for (int i = 0; i < size ; ++i) {/ { y& X8 {, q/ h' E4 X- W% t, d
int b = cv::theRNG().uniform(0, 256);
3 e6 L5 U/ @& I0 ^\" w4 S int g = cv::theRNG().uniform(0, 256);
0 }) a( H/ }% k* G int r = cv::theRNG().uniform(0, 256);
! g9 j% I) i& o+ f; G colors.emplace_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));5 [2 C1 n( j8 E) F! p; F( I2 F
}( s- C, B+ o, b8 j% r5 Q
}+ W! G0 v7 p3 l% L. B. A* y
3 i! W\" s3 d4 f S3 e# x
int main(int argc, char** argv) {
' ^* L6 D4 N- `8 x2 o if(argc < 2){( t6 n9 [( U3 v
std::cerr << "Errorn";0 u# @3 C6 ]6 C0 T\" }2 b
std::cerr << "Provide Input Image:n n";; }9 |, n1 c6 |5 \& r
return -1;
( E6 k. p _' z- k5 C9 F$ _ }
5 a( N$ k. j. q) D cv::Mat original_img = cv::imread(argv[1]);
8 A5 u3 Z6 y6 O* v: ?1 ]2 M if(original_img.empty()){
# W/ X: j4 Q! F) ]. y$ f& \ std::cerr << "Errorn";
\" c\" i& U$ G* [$ l7 `, r* i6 G std::cerr << "Cannot Read Imagen";/ o& @) Q' I+ f+ w! X
return -1;
' b6 |5 X& r9 I. Q1 J+ r. l }4 F w$ e5 d+ M/ r/ z
cv::Mat shifted;0 m6 v\" _: z! x) m+ v' q
cv::pyrMeanShiftFiltering(original_img, shifted, 21, 51);
& o\" O1 C/ ?8 y5 a c5 ? showImg("Mean Shifted", shifted);
+ Z; y1 x+ H: f# @2 o% {. ^\" K$ H cv::Mat gray_img;
+ V+ Q( K' z; \+ s# h& s cv::cvtColor(original_img, gray_img, cv::COLOR_BGR2GRAY);
7 Y6 t9 x1 ^3 W* l# n# b7 W0 n showImg("GrayIMg", gray_img);1 Q3 u+ S! M6 `: o
cv::Mat bin_img;2 q+ g5 G+ B# i% `+ u- `( h0 x& d. I: d: Y
cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
6 f) _) U6 C4 S2 B) _8 _6 u showImg("thres img", bin_img);! }* J\" N) U' G! Q& E
cv::Mat sure_bg;
3 U* [' p\" G( f; l! A( v$ { getBackground(bin_img, sure_bg);' w+ c; ~& `- f3 C! g
showImg("Sure Background", sure_bg);\" b4 v$ h/ u/ x! \
cv::Mat sure_fg;$ Y4 Z5 q$ g0 H4 D, O$ q+ T
getForeground(bin_img, sure_fg);- C+ \- D$ u( B\" [- w. U- J) p
showImg("Sure ForeGround", sure_fg);
! @' h) L2 [; P/ R5 t' Z cv::Mat markers = cv::Mat::zeros(sure_bg.size(), CV_32S);9 Q8 X- M5 l! s5 ~
std::vector<std::vector<cv::Point>> contours;
; k3 [$ U7 y! U# {$ W findMarker(sure_bg, markers, contours);
2 N& M9 @# o: i* I, v l6 y1 T3 C cv::circle(markers, cv::Point(5, 5), 3, cv::Scalar(255), -1); // 在标记周围绘制圆圈- [9 ]) m7 F( G9 z' W% K, L3 L C
& H% `2 J; x% o/ H# O cv::watershed(original_img, markers);1 z. ]$ u4 b7 s4 [7 O
cv::Mat mark;! C! |9 F4 o, W3 F6 H
markers.convertTo(mark, CV_8U);
7 K; y Z o E# b1 ~6 f cv::bitwise_not(mark, mark); // 将白色转换为黑色,黑色转换为白色1 c9 q: n5 y' {# h& D, L9 e
showImg("MARKER", mark);1 q: z, R8 E5 H; a1 `' }
// 在图像中突出显示标记 /. S* X& h/ c s% ^
std::vector<cv::Vec3b> colors;
- j! C% V$ L/ H5 y getRandomColor(colors, contours.size()); // 创建结果图像% X: o2 |- ], y% ~3 _( ]& ]! S
cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);) T6 c8 [% ]/ h6 e( s
// 用随机颜色填充标记的对象
W0 K+ E( M: Z6 f7 v/ f' T1 F for (int i = 0; i < markers.rows; i++)
5 o7 B8 Z' u\" R3 L0 H M {+ U. a/ T2 f7 s# J( H; T
for (int j = 0; j < markers.cols; j++): o$ E1 D5 K) ~9 I; ~# |. R$ j
{; m% B+ Y7 w4 f# L0 e$ H2 k/ c4 b& Q
int index = markers.at(i,j);
. P5 y\" f7 q0 M( k a( ? if (index > 0 && index <= static_cast<int>(contours.size()))7 q8 B+ M3 j, Y- `$ ?& B
dst.at<cv::Vec3b>(i,j) = colors[index-1];
1 P2 M. ?! e0 {4 e }
% Y8 ^5 v9 ], _5 e( H\" K$ b; Z P }
1 q+ N( g T$ \) L$ ^! w showImg("Final Result", dst);4 s, c; R2 F) n2 P. u
cv::waitKey(0);( K) O% a0 D/ _/ u1 v N
return 0;
t, R\" S6 }: w- y }
, g a6 T0 Y% z. E, | 复制代码 ———————————————— , }2 L* ^. v- S# T9 I' \9 ]5 h: e, Z
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/matt45m/article/details/138211359 0 Z. R1 N0 L& k5 F. w! }2 W7 G
/ q6 E, q; K, ^7 r
zan