QQ登录

只需要一步,快速开始

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

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

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

1198

主题

4

听众

2978

积分

该用户从未签到

跳转到指定楼层
1#
发表于 2024-4-27 10:36 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
分水岭算法:模拟地理形态的图像分割
" n* v0 q; F& }/ F
分水岭算法通过模拟自然地形来实现图像中物体的分类。在这一过程中,每个像素的灰度值被视作其高度,灰度值较高的像素形成山脊,即分水岭,而二值化阈值则相当于水平面,低于这个水平面的区域会被“淹没”。9 {+ W0 V+ k. B. }/ z" ~* t
测地线距离:地形分析的核心: V+ N8 @; ?* ]6 S, q2 _; V2 D
测地线距离是分水岭算法中的一个关键概念,它代表地球表面两点间的最短路径。这一概念在图论中同样适用,指的是图中两节点间的最短路径,与欧氏距离相比,测地线距离考虑的是实际路径。+ h4 j# H* d: r4 W# e
分水岭算法的执行步骤' S: U8 ~9 ^7 z6 w+ v6 f
梯度图像分类:根据灰度值对梯度图像中的像素进行分类,并设定测地距离阈值。起始点标记:选择灰度值最小的像素点作为起始点,这些点通常是局部最小值。水平面上升:随着阈值的增长,测量周围邻域像素到起始点的测地距离。若小于阈值,则淹没这些像素;若大于阈值,则在这些像素上建立“大坝”。大坝设置与区域分区:随着水平面的上升,建立更多的大坝,直到所有区域在分水岭线上相遇,完成图像的分区。避免过度分割的策略
1 b6 e4 o0 @  R( Z1 j/ H
分水岭算法可能会因噪声或干扰导致图像过度分割,形成过多的小区域。解决这一问题的方法包括:
9 ]% f3 P, K! `; L1 B/ ~8 P7 }
高斯平滑:通过高斯平滑减少噪声,合并小分区。基于标记的分水岭算法:选择相对较高的灰度值像素作为起始点,手动标记或使用自动方法如距离变换来确定,从而合并小区域。OpenCV 实现 Watershed 算法函数原型:void watershed( InputArray image, InputOutputArray markers );1参数说明:image:输入的图像,必须是8位的单通道灰度图像。这个图像的梯度信息将被用来模拟水流向低洼地区流动的过程。
( p9 S) y! Z: g3 K( V6 S
markers:输入输出参数,是一个与原图像大小相同的图像,用于存放分割标记。在函数调用前,这个图像应该被初始化,其中包含了用户定义的分割区域的标记。标记是通过正整数索引来表示的,表示用户已知的前景或背景区域。所有未知区域(即算法需要确定的区域)应该被标记为0。函数执行完成后,每个像素点的标记将被更新为“种子”组件的值,或者在区域边界处被设置为-1。
7 [' [+ g2 W5 @' e0 l, |
功能说明:watershed 函数会分析 image 的梯度信息,并使用 markers 中定义的已知区域作为分割的起点(种子点)。算法将从这些种子点开始,逐步对图像中的其他像素点进行区域归属的判定,直到所有像素点都被标记。在分割过程中,如果两个相邻的已知区域(种子点)相遇,算法会在它们之间创建一个边界,以避免这些区域合并在一起,从而实现分割。注意事项:markers 中的标记非常重要,它们直接影响分割的结果。因此,用户需要仔细考虑如何标记已知的前景和背景区域。分水岭算法可能会导致过度分割,特别是当图像中存在大量噪声时。在实际应用中,可能需要对图像进行预处理,如使用高斯模糊去除小的局部最小值,以减少过度分割的问题。
  1. #include <opencv2/imgcodecs.hpp>
    ! q% p( A2 m/ ^! t0 H
  2. #include <opencv2/highgui.hpp>
    ' X% G% k\" U9 x% f- S8 p; A, u% f
  3. #include <opencv2/imgproc.hpp>7 o7 C$ O$ M\" ?2 F6 y
  4. % O\" o' f& t2 X# h
  5. void showImg(const std::string& windowName, const cv::Mat& img){5 A7 w, Z5 G# H6 N) q' V1 F
  6.     cv::imshow(windowName, img);4 w5 S! M2 T$ Z( u0 e
  7. }/ `- P0 H8 b' G  a; s

  8. 9 X, T: w/ |: J5 W
  9. void getBackground(const cv::Mat& source, cv::Mat& dst) {
      H6 C, O) G$ l$ {  k\" w
  10.     cv::dilate(source, dst, cv::Mat::ones(3, 3, CV_8U)); // 3x3 核3 J  J) N& u! u4 f
  11. }- U2 {- ?$ D5 l  u/ j% Q

  12. ( W6 x, [3 s+ S6 l1 e2 F& J
  13. void getForeground(const cv::Mat& source, cv::Mat& dst) {: ?7 A7 U0 ^  F! ^. h8 U$ Y
  14.     cv::distanceTransform(source, dst, cv::DIST_L2, 3, CV_32F);# K% K% h: F2 j
  15.     cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX);+ Q( Q$ [- Z8 o2 V; q: m
  16. }
    ; I; K3 Z& J0 k3 s% s( S: D) I
  17. / G8 H! b6 p\" W' g  W1 Y* R
  18. void findMarker(const cv::Mat& sureBg, cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) {6 D$ Q/ k& e4 x+ o5 ^
  19.     cv::findContours(sureBg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);  b$ l4 b2 J\" s/ d- e
  20.     // 绘制前景标记0 I# R% a, P+ t- p7 w1 q  {1 p: i
  21.     for (size_t i = 0, size = contours.size(); i < size; i++)
    ' c1 i\" E5 H/ _, Z5 a
  22.         drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1);0 l( E# |, ^9 ]! Z# V
  23. }
    . G$ u- ~! v# p% B

  24. . u$ c\" T- q( e2 c8 k, n
  25. void getRandomColor(std::vector<cv::Vec3b>& colors, size_t size) {
    , ^8 V- C; S1 _# ^. O
  26.     for (int i = 0; i < size ; ++i) {
    1 t* i, T% f  D2 w
  27.         int b = cv::theRNG().uniform(0, 256);
    9 p\" @( Y0 ~0 j8 s  M8 f% ]
  28.         int g = cv::theRNG().uniform(0, 256);
    - n& K2 T. i4 S1 `) H6 B8 ?
  29.         int r = cv::theRNG().uniform(0, 256);* q7 q% X) ]* R, l
  30.         colors.emplace_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));\" b6 z$ P! K+ Y
  31.     }- D8 o2 }8 q6 m
  32. }
    \" W* T7 i6 i: g: f% v! r\" l5 J

  33. 1 H/ D, h3 r4 C6 N% J$ e
  34. int main(int argc, char** argv) {2 r9 C  t5 D4 j/ [' ?$ ^! H- m
  35.     if(argc < 2){7 h2 x6 p: C# U\" B
  36.         std::cerr << "Errorn";# o5 }4 {/ i! G, Z
  37.         std::cerr << "Provide Input Image:n n";
    3 j4 q! Z) c5 y
  38.         return -1;
    & L3 b3 r7 x/ n. u
  39.     }
    : E8 b' D5 _- \# \. U
  40.     cv::Mat original_img = cv::imread(argv[1]);2 s- ?) B& ]/ J* U1 ^' L0 ^' n
  41.     if(original_img.empty()){% K: J/ c+ W& ]8 P! v  ]
  42.         std::cerr << "Errorn";' A2 |6 y\" n1 ~2 T8 e8 E\" s2 C
  43.         std::cerr << "Cannot Read Imagen";  \- y\" t: P6 n3 o) C3 x
  44.         return -1;: p% q6 U; S% F( G8 x! X/ k0 V1 B
  45.     }5 j7 j, D* c& W3 c3 m: r3 G/ H
  46.     cv::Mat shifted;
    4 @9 m9 C  [  q, q
  47.     cv::pyrMeanShiftFiltering(original_img, shifted, 21, 51);
    . S+ M/ X1 ^+ \; N- ?! J3 N8 t
  48.     showImg("Mean Shifted", shifted);6 x\" p1 a1 E; F* u4 o: q7 d
  49.     cv::Mat gray_img;0 l! f3 g2 f! l+ H0 G; @' @
  50.     cv::cvtColor(original_img, gray_img, cv::COLOR_BGR2GRAY);
    2 r! P, D+ U/ x# j+ z
  51.     showImg("GrayIMg", gray_img);
    * W0 b, b- d8 `2 w
  52.     cv::Mat bin_img;6 U! u7 d& P$ }3 z/ z
  53.     cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
    : A! `0 w3 n7 p5 D6 t& A
  54.     showImg("thres img", bin_img);
    ; a' Q2 j( T3 t7 W6 y
  55.     cv::Mat sure_bg;
    + W- O( s. A' h: _
  56.     getBackground(bin_img, sure_bg);
    + W# V; I4 S/ e, M
  57.     showImg("Sure Background", sure_bg);
    / Y  c5 u+ Y( c+ o2 }
  58.     cv::Mat sure_fg;
    4 {* ]% L( d: G' T
  59.     getForeground(bin_img, sure_fg);' Y; Y+ e( X4 ~; J5 s
  60.     showImg("Sure ForeGround", sure_fg);4 W! t4 s# c! d; A. l* O% v* t& B
  61.     cv::Mat markers = cv::Mat::zeros(sure_bg.size(), CV_32S);
    . n& p1 T  Z& H' @' D
  62.     std::vector<std::vector<cv::Point>> contours;
    & z  X' A0 e. h2 @5 t/ Y& x9 k
  63.     findMarker(sure_bg, markers, contours);, @7 N3 n; ?( {) _5 a3 K! v7 b
  64.     cv::circle(markers, cv::Point(5, 5), 3, cv::Scalar(255), -1); // 在标记周围绘制圆圈
    5 H5 Y: I) w# T/ q
  65.    
    $ T1 r$ v- N; l8 C, M
  66.     cv::watershed(original_img, markers);
    * F' o0 O; G! f, [, K/ K
  67.     cv::Mat mark;# @  {! G1 |& r1 R0 n7 q
  68.     markers.convertTo(mark, CV_8U);
    % `5 Y( _& |4 W1 [$ ]
  69.     cv::bitwise_not(mark, mark); // 将白色转换为黑色,黑色转换为白色
    + y0 M2 E2 |, _8 R+ a
  70.     showImg("MARKER", mark);
    6 H7 y( U- b. P& K- r  x7 ?8 n* x
  71.     // 在图像中突出显示标记 /  ^6 \2 K0 X+ p2 I0 {) o! f
  72.     std::vector<cv::Vec3b> colors;
    6 t( L  q$ B\" C
  73.     getRandomColor(colors, contours.size()); // 创建结果图像7 m8 }\" G7 c3 J9 J/ `5 w
  74.     cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);
    ( p6 r  w/ L1 A\" B+ \0 w9 C
  75.     // 用随机颜色填充标记的对象
    : x# D7 J\" B7 y9 q) L9 Y  _7 Q$ H
  76.     for (int i = 0; i < markers.rows; i++)
    3 z7 |9 P- g) n\" P
  77.     {
    \" X4 j1 q$ z, w
  78.         for (int j = 0; j < markers.cols; j++)9 f: u4 P; w/ V
  79.         {
    & X) r% R% C; p5 T( b: T% M
  80.             int index = markers.at(i,j);8 l3 G. i4 p; Q; M. ^; X5 F
  81.             if (index > 0 && index <= static_cast<int>(contours.size()))
    2 b# h, ]5 k! x0 W2 p4 t& R
  82.                 dst.at<cv::Vec3b>(i,j) = colors[index-1];
    , i3 }8 s* }4 z! x1 a7 Q
  83.         }8 x: T# V8 Y8 b: t8 s1 p! d
  84.     }
    % s, \# R- r9 `
  85.     showImg("Final Result", dst);
    \" l5 j* d/ O( R0 ^& _
  86.     cv::waitKey(0);
    2 ~) E4 }- m( `/ ]
  87.     return 0;
    \" b1 c/ x% o8 X
  88. }2 Y  w$ ~1 I\" @( r! g0 t9 V+ d$ |5 ?) V
复制代码
————————————————% X8 ]: E( ]$ C  s; }+ ?& V0 Z
                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                        原文链接:https://blog.csdn.net/matt45m/article/details/138211359
7 _9 t/ W& R: X* d& i) l5 n3 }; I' f
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-10-10 09:45 , Processed in 2.886602 second(s), 50 queries .

回顶部