- 在线时间
- 478 小时
- 最后登录
- 2026-4-9
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7788 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2922
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1171
- 主题
- 1186
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
分水岭算法:模拟地理形态的图像分割
- N2 T7 N8 {' F H* K分水岭算法通过模拟自然地形来实现图像中物体的分类。在这一过程中,每个像素的灰度值被视作其高度,灰度值较高的像素形成山脊,即分水岭,而二值化阈值则相当于水平面,低于这个水平面的区域会被“淹没”。3 U3 r H: x+ R( h
测地线距离:地形分析的核心
0 T7 \8 y2 v7 j/ _ Y测地线距离是分水岭算法中的一个关键概念,它代表地球表面两点间的最短路径。这一概念在图论中同样适用,指的是图中两节点间的最短路径,与欧氏距离相比,测地线距离考虑的是实际路径。
- i' w1 V5 X: X4 K( P) w分水岭算法的执行步骤9 l* e) f! @6 `4 e; z
梯度图像分类:根据灰度值对梯度图像中的像素进行分类,并设定测地距离阈值。起始点标记:选择灰度值最小的像素点作为起始点,这些点通常是局部最小值。水平面上升:随着阈值的增长,测量周围邻域像素到起始点的测地距离。若小于阈值,则淹没这些像素;若大于阈值,则在这些像素上建立“大坝”。大坝设置与区域分区:随着水平面的上升,建立更多的大坝,直到所有区域在分水岭线上相遇,完成图像的分区。避免过度分割的策略
' V; O: S u! l分水岭算法可能会因噪声或干扰导致图像过度分割,形成过多的小区域。解决这一问题的方法包括:8 [ K3 n8 {6 c; t
高斯平滑:通过高斯平滑减少噪声,合并小分区。基于标记的分水岭算法:选择相对较高的灰度值像素作为起始点,手动标记或使用自动方法如距离变换来确定,从而合并小区域。OpenCV 实现 Watershed 算法函数原型:void watershed( InputArray image, InputOutputArray markers );1参数说明:image:输入的图像,必须是8位的单通道灰度图像。这个图像的梯度信息将被用来模拟水流向低洼地区流动的过程。
- I! q) c& r6 g. r& D" [( ^markers:输入输出参数,是一个与原图像大小相同的图像,用于存放分割标记。在函数调用前,这个图像应该被初始化,其中包含了用户定义的分割区域的标记。标记是通过正整数索引来表示的,表示用户已知的前景或背景区域。所有未知区域(即算法需要确定的区域)应该被标记为0。函数执行完成后,每个像素点的标记将被更新为“种子”组件的值,或者在区域边界处被设置为-1。
' ?: I i, }5 @5 h功能说明:watershed 函数会分析 image 的梯度信息,并使用 markers 中定义的已知区域作为分割的起点(种子点)。算法将从这些种子点开始,逐步对图像中的其他像素点进行区域归属的判定,直到所有像素点都被标记。在分割过程中,如果两个相邻的已知区域(种子点)相遇,算法会在它们之间创建一个边界,以避免这些区域合并在一起,从而实现分割。注意事项:markers 中的标记非常重要,它们直接影响分割的结果。因此,用户需要仔细考虑如何标记已知的前景和背景区域。分水岭算法可能会导致过度分割,特别是当图像中存在大量噪声时。在实际应用中,可能需要对图像进行预处理,如使用高斯模糊去除小的局部最小值,以减少过度分割的问题。- #include <opencv2/imgcodecs.hpp>% y) H. n( T% u3 {+ R5 V' @5 ?
- #include <opencv2/highgui.hpp>3 T' G+ d4 q% T5 {$ x7 h
- #include <opencv2/imgproc.hpp>) I( N/ q. v4 K9 c7 A/ D
- # K7 {3 e1 `) D& i% M6 {. U/ a2 W
- void showImg(const std::string& windowName, const cv::Mat& img){7 M% o; @3 E4 d/ O; Z3 ]
- cv::imshow(windowName, img);3 G4 T1 ^4 `1 b! u$ N$ t8 F
- }
, b7 k8 O* k# ]' M, n - 8 b/ B2 h( B! y7 w
- void getBackground(const cv::Mat& source, cv::Mat& dst) {+ N7 ?0 j+ A$ ^9 a6 `
- cv::dilate(source, dst, cv::Mat::ones(3, 3, CV_8U)); // 3x3 核
5 E5 W2 F+ z6 u. U - }
& d\" h! E! Y8 h( ] - + t# ^. }- W: [& e Q& C
- void getForeground(const cv::Mat& source, cv::Mat& dst) {
1 L0 I( J7 \9 n: { J3 E, ?! f; X - cv::distanceTransform(source, dst, cv::DIST_L2, 3, CV_32F);
* Z/ f% J, m) q - cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX);
+ \5 d- o/ X* Z; ]8 K6 e, P# p - }: l% d6 I1 V0 b; C
) {/ V8 V2 k c8 r! ^- void findMarker(const cv::Mat& sureBg, cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) { R1 w0 t, \$ b: W- {% P: z/ F- ~
- cv::findContours(sureBg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);$ w+ C- _* ?! ]1 a
- // 绘制前景标记/ n% W5 U) P5 r+ h7 Z1 r
- for (size_t i = 0, size = contours.size(); i < size; i++)
) |$ Q( U5 p/ O# ?3 x* n% U - drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1);
7 Y( q$ M* x- \2 b2 ^2 k - }
e. [\" v: g N4 b- @: @4 r7 `, P - 0 s# H\" a% o' B
- void getRandomColor(std::vector<cv::Vec3b>& colors, size_t size) {
* {: j, o7 Q% P n - for (int i = 0; i < size ; ++i) {) W: j/ f- q0 `' n% t
- int b = cv::theRNG().uniform(0, 256);
$ l- S) o8 F+ m. D: D) z\" @ e, Z1 b - int g = cv::theRNG().uniform(0, 256);
$ J( P# e2 ~3 h\" S3 K2 v - int r = cv::theRNG().uniform(0, 256);
0 L* Q6 a# ]4 E! c2 Z$ V3 v3 N - colors.emplace_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));
\" W/ w; Q7 ?( @/ q6 A - }
! a& |) D) F7 ?9 m. U! B# k - }; c7 K* n, i% ~$ C
9 t0 D/ h+ h2 R9 b- int main(int argc, char** argv) {+ [8 _* [+ j: w( V1 ]8 Z
- if(argc < 2){' `$ C: |4 P; e* j1 s1 `7 C& j
- std::cerr << "Errorn";
\" |, N( {6 K1 ^3 E8 e! Y - std::cerr << "Provide Input Image:n n";
/ O4 I7 {: ?9 A# a% n! G* [ - return -1;1 e9 B; k- I6 t8 ]: q- M
- }& M; e+ ^) _' w, d: T
- cv::Mat original_img = cv::imread(argv[1]);
- |$ M, X0 M- P F - if(original_img.empty()){
2 ~( p2 `8 g3 ? - std::cerr << "Errorn";' R# a& d( K5 ]) X+ J7 z/ v
- std::cerr << "Cannot Read Imagen";' ^. r1 K3 W9 w4 _\" b
- return -1;
: d( r# w( }/ C* v8 Q) V* _ - }5 P3 t) ^# w8 d
- cv::Mat shifted;
7 M$ W' F, u( _3 v: l1 i - cv::pyrMeanShiftFiltering(original_img, shifted, 21, 51);& h I\" L$ f( b4 ~3 F5 s* Y/ p* v5 _2 k) B( ^
- showImg("Mean Shifted", shifted);$ k1 l\" Q1 N\" I g8 f$ P
- cv::Mat gray_img;1 [ n\" d$ [6 u, g
- cv::cvtColor(original_img, gray_img, cv::COLOR_BGR2GRAY);
7 a6 F1 V' M5 H9 v% {- A8 n5 p - showImg("GrayIMg", gray_img);
' k: g: o/ w' r8 R# J. Z; @0 q - cv::Mat bin_img;
$ w; L\" v' ~; f& c- E1 l5 @ - cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
\" M; N# N- N1 i0 P$ q - showImg("thres img", bin_img);
9 K- A( s( A3 H. K; M5 m: o d - cv::Mat sure_bg;1 G+ W0 S7 i$ T5 n0 z! T, t) I
- getBackground(bin_img, sure_bg);
9 _% o+ `( C# z - showImg("Sure Background", sure_bg);
9 o- q a2 G\" W* H) w3 z - cv::Mat sure_fg;
1 |1 i/ F1 W' Q5 l4 A - getForeground(bin_img, sure_fg); H6 S, m2 s0 T) } ?3 c
- showImg("Sure ForeGround", sure_fg);( ]% ^. g E' D: Z( p/ ~1 n! k
- cv::Mat markers = cv::Mat::zeros(sure_bg.size(), CV_32S);3 ~\" g( H. T0 A/ H% r7 ~
- std::vector<std::vector<cv::Point>> contours;
' ?% h* i1 |8 z; |/ S) E, N& Y( ~ - findMarker(sure_bg, markers, contours);4 m# Q, v3 ^7 B# R4 m! v
- cv::circle(markers, cv::Point(5, 5), 3, cv::Scalar(255), -1); // 在标记周围绘制圆圈
, }6 x# N, p# ]) O3 e8 g9 n -
. z# p5 |9 G; K+ [' C - cv::watershed(original_img, markers);
( y$ ^$ o\" x7 a7 A - cv::Mat mark;$ y: H3 L9 l+ a' f
- markers.convertTo(mark, CV_8U);9 a# I3 {5 ], n. g$ g
- cv::bitwise_not(mark, mark); // 将白色转换为黑色,黑色转换为白色6 C- q( _- W9 y9 T
- showImg("MARKER", mark);
\" ?, n8 Q$ L# ^, x( u\" F' s) Y' f - // 在图像中突出显示标记 /- P\" J' h0 { E& a, Z
- std::vector<cv::Vec3b> colors;7 N5 O. p \$ o0 b+ ?
- getRandomColor(colors, contours.size()); // 创建结果图像4 F0 T4 p* U( y- G) f
- cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);, ?) W( q- T0 x' t
- // 用随机颜色填充标记的对象+ ~! r( _) `4 f w* P\" E
- for (int i = 0; i < markers.rows; i++)
( I4 C9 ~/ ?\" d8 j, E - {' o; N6 ^7 v7 w6 M% v$ P5 N( {
- for (int j = 0; j < markers.cols; j++)) `5 b; N: w8 {
- {
& h( ]* s& M% |$ u5 b$ b! y - int index = markers.at(i,j); n# P( s; X% d! s d\" H
- if (index > 0 && index <= static_cast<int>(contours.size()))9 J5 i. c8 M! ?/ R
- dst.at<cv::Vec3b>(i,j) = colors[index-1];
7 F7 ^4 F3 u: U4 h4 R, l - }5 ^# h+ @# u- Z2 \2 `
- }
/ i% e5 C2 q* M% V - showImg("Final Result", dst);
; m2 H& r0 e6 c% g8 F - cv::waitKey(0);, O/ b: n$ S: F, ]4 H
- return 0;
1 q* n2 I8 i0 X0 _, u* R - }
6 D$ z0 K* p, D' @# ~
复制代码 ————————————————
; t" x: X* D9 Q 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/matt45m/article/details/138211359
4 R2 ]& [5 X( D% J9 D: E, w* {3 R9 f
9 [8 a* `( x4 y6 B4 m4 X |
zan
|