QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1925|回复: 0
打印 上一主题 下一主题

OpenCV 使用分水岭算法进行图像分割

[复制链接]
字体大小: 正常 放大

1198

主题

4

听众

2977

积分

该用户从未签到

跳转到指定楼层
1#
发表于 2024-4-27 10:36 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
分水岭算法:模拟地理形态的图像分割
  ^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 中的标记非常重要,它们直接影响分割的结果。因此,用户需要仔细考虑如何标记已知的前景和背景区域。分水岭算法可能会导致过度分割,特别是当图像中存在大量噪声时。在实际应用中,可能需要对图像进行预处理,如使用高斯模糊去除小的局部最小值,以减少过度分割的问题。
  1. #include <opencv2/imgcodecs.hpp>) M1 ~' c) Y9 @! }! ]/ X+ w( W
  2. #include <opencv2/highgui.hpp>: P* j/ c/ e. N, Q: n8 p
  3. #include <opencv2/imgproc.hpp>
    2 a% S! Q' k6 w5 p( v' b4 x: {# b

  4. # z5 T& `1 t0 k* B) l; H
  5. void showImg(const std::string& windowName, const cv::Mat& img){. p+ F- x+ B\" r/ L
  6.     cv::imshow(windowName, img);% W. G2 M/ ~' n1 m3 y6 M
  7. }
    / Q) n) t5 g8 R( H& Q+ g: }  }2 p\" X

  8. ! h, |! x* E5 V' u9 A
  9. void getBackground(const cv::Mat& source, cv::Mat& dst) {
    3 ^4 h( ?\" h( n( j
  10.     cv::dilate(source, dst, cv::Mat::ones(3, 3, CV_8U)); // 3x3 核
    ( B  v' ^3 x, i- z2 ]
  11. }
    . W& F. D. H; f0 a3 A

  12. 8 o/ d4 o4 h+ ~6 R+ E6 @
  13. void getForeground(const cv::Mat& source, cv::Mat& dst) {
    & }' B  d, N' x  z2 M' I
  14.     cv::distanceTransform(source, dst, cv::DIST_L2, 3, CV_32F);; I9 Q' _+ W9 c
  15.     cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX);, y8 _$ u; |5 ~; ?
  16. }
    ' m, ?! d5 J\" }; c( N, Q# n1 ^

  17. $ A( b- Y; p3 r! u- L$ J! \: R
  18. void findMarker(const cv::Mat& sureBg, cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) {
    ) U. v0 S8 G8 T  t4 Z
  19.     cv::findContours(sureBg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);8 C$ {& f- E# g! @- }, d* P0 {1 T
  20.     // 绘制前景标记2 p( L7 Q4 n8 Z
  21.     for (size_t i = 0, size = contours.size(); i < size; i++)
    ) Z! |9 F1 [+ s: Y$ g3 o, O
  22.         drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1);) v: O1 Q4 ~, P, g* c& {
  23. }& x/ H9 I% f8 W. p& G
  24. ( i$ p/ @5 |: B- a  P\" N( q
  25. void getRandomColor(std::vector<cv::Vec3b>& colors, size_t size) {3 g$ S- `6 t* v) a1 f5 R' P! y, a5 t' [
  26.     for (int i = 0; i < size ; ++i) {/ {  y& X8 {, q/ h' E4 X- W% t, d
  27.         int b = cv::theRNG().uniform(0, 256);
    3 e6 L5 U/ @& I0 ^\" w4 S
  28.         int g = cv::theRNG().uniform(0, 256);
    0 }) a( H/ }% k* G
  29.         int r = cv::theRNG().uniform(0, 256);
    ! g9 j% I) i& o+ f; G
  30.         colors.emplace_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));5 [2 C1 n( j8 E) F! p; F( I2 F
  31.     }( s- C, B+ o, b8 j% r5 Q
  32. }+ W! G0 v7 p3 l% L. B. A* y
  33. 3 i! W\" s3 d4 f  S3 e# x
  34. int main(int argc, char** argv) {
    ' ^* L6 D4 N- `8 x2 o
  35.     if(argc < 2){( t6 n9 [( U3 v
  36.         std::cerr << "Errorn";0 u# @3 C6 ]6 C0 T\" }2 b
  37.         std::cerr << "Provide Input Image:n n";; }9 |, n1 c6 |5 \& r
  38.         return -1;
    ( E6 k. p  _' z- k5 C9 F$ _
  39.     }
    5 a( N$ k. j. q) D
  40.     cv::Mat original_img = cv::imread(argv[1]);
    8 A5 u3 Z6 y6 O* v: ?1 ]2 M
  41.     if(original_img.empty()){
    # W/ X: j4 Q! F) ]. y$ f& \
  42.         std::cerr << "Errorn";
    \" c\" i& U$ G* [$ l7 `, r* i6 G
  43.         std::cerr << "Cannot Read Imagen";/ o& @) Q' I+ f+ w! X
  44.         return -1;
    ' b6 |5 X& r9 I. Q1 J+ r. l
  45.     }4 F  w$ e5 d+ M/ r/ z
  46.     cv::Mat shifted;0 m6 v\" _: z! x) m+ v' q
  47.     cv::pyrMeanShiftFiltering(original_img, shifted, 21, 51);
    & o\" O1 C/ ?8 y5 a  c5 ?
  48.     showImg("Mean Shifted", shifted);
    + Z; y1 x+ H: f# @2 o% {. ^\" K$ H
  49.     cv::Mat gray_img;
    + V+ Q( K' z; \+ s# h& s
  50.     cv::cvtColor(original_img, gray_img, cv::COLOR_BGR2GRAY);
    7 Y6 t9 x1 ^3 W* l# n# b7 W0 n
  51.     showImg("GrayIMg", gray_img);1 Q3 u+ S! M6 `: o
  52.     cv::Mat bin_img;2 q+ g5 G+ B# i% `+ u- `( h0 x& d. I: d: Y
  53.     cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
    6 f) _) U6 C4 S2 B) _8 _6 u
  54.     showImg("thres img", bin_img);! }* J\" N) U' G! Q& E
  55.     cv::Mat sure_bg;
    3 U* [' p\" G( f; l! A( v$ {
  56.     getBackground(bin_img, sure_bg);' w+ c; ~& `- f3 C! g
  57.     showImg("Sure Background", sure_bg);\" b4 v$ h/ u/ x! \
  58.     cv::Mat sure_fg;$ Y4 Z5 q$ g0 H4 D, O$ q+ T
  59.     getForeground(bin_img, sure_fg);- C+ \- D$ u( B\" [- w. U- J) p
  60.     showImg("Sure ForeGround", sure_fg);
    ! @' h) L2 [; P/ R5 t' Z
  61.     cv::Mat markers = cv::Mat::zeros(sure_bg.size(), CV_32S);9 Q8 X- M5 l! s5 ~
  62.     std::vector<std::vector<cv::Point>> contours;
    ; k3 [$ U7 y! U# {$ W
  63.     findMarker(sure_bg, markers, contours);
    2 N& M9 @# o: i* I, v  l6 y1 T3 C
  64.     cv::circle(markers, cv::Point(5, 5), 3, cv::Scalar(255), -1); // 在标记周围绘制圆圈- [9 ]) m7 F( G9 z' W% K, L3 L  C
  65.    
    & H% `2 J; x% o/ H# O
  66.     cv::watershed(original_img, markers);1 z. ]$ u4 b7 s4 [7 O
  67.     cv::Mat mark;! C! |9 F4 o, W3 F6 H
  68.     markers.convertTo(mark, CV_8U);
    7 K; y  Z  o  E# b1 ~6 f
  69.     cv::bitwise_not(mark, mark); // 将白色转换为黑色,黑色转换为白色1 c9 q: n5 y' {# h& D, L9 e
  70.     showImg("MARKER", mark);1 q: z, R8 E5 H; a1 `' }
  71.     // 在图像中突出显示标记 /. S* X& h/ c  s% ^
  72.     std::vector<cv::Vec3b> colors;
    - j! C% V$ L/ H5 y
  73.     getRandomColor(colors, contours.size()); // 创建结果图像% X: o2 |- ], y% ~3 _( ]& ]! S
  74.     cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);) T6 c8 [% ]/ h6 e( s
  75.     // 用随机颜色填充标记的对象
      W0 K+ E( M: Z6 f7 v/ f' T1 F
  76.     for (int i = 0; i < markers.rows; i++)
    5 o7 B8 Z' u\" R3 L0 H  M
  77.     {+ U. a/ T2 f7 s# J( H; T
  78.         for (int j = 0; j < markers.cols; j++): o$ E1 D5 K) ~9 I; ~# |. R$ j
  79.         {; m% B+ Y7 w4 f# L0 e$ H2 k/ c4 b& Q
  80.             int index = markers.at(i,j);
    . P5 y\" f7 q0 M( k  a( ?
  81.             if (index > 0 && index <= static_cast<int>(contours.size()))7 q8 B+ M3 j, Y- `$ ?& B
  82.                 dst.at<cv::Vec3b>(i,j) = colors[index-1];
    1 P2 M. ?! e0 {4 e
  83.         }
    % Y8 ^5 v9 ], _5 e( H\" K$ b; Z  P
  84.     }
    1 q+ N( g  T$ \) L$ ^! w
  85.     showImg("Final Result", dst);4 s, c; R2 F) n2 P. u
  86.     cv::waitKey(0);( K) O% a0 D/ _/ u1 v  N
  87.     return 0;
      t, R\" S6 }: w- y
  88. }
    , 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/1382113590 Z. R1 N0 L& k5 F. w! }2 W7 G
/ q6 E, q; K, ^7 r
zan
转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
您需要登录后才可以回帖 登录 | 注册地址

qq
收缩
  • 电话咨询

  • 04714969085
fastpost

关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

手机版|Archiver| |繁體中文 手机客户端  

蒙公网安备 15010502000194号

Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

GMT+8, 2026-9-22 16:58 , Processed in 0.571624 second(s), 51 queries .

回顶部