QQ登录

只需要一步,快速开始

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

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

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

1198

主题

4

听众

2978

积分

该用户从未签到

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

  8. % V- h! r5 e+ D+ u/ v8 b
  9. void getBackground(const cv::Mat& source, cv::Mat& dst) {6 [. d; S! S5 c9 p$ U  {# y  Z: u& v
  10.     cv::dilate(source, dst, cv::Mat::ones(3, 3, CV_8U)); // 3x3 核$ p2 A; u$ d9 N; }
  11. }
    0 ~( M5 J/ D: V1 {2 F
  12. 9 z6 F! v; Y# I; @9 s
  13. void getForeground(const cv::Mat& source, cv::Mat& dst) {: j' W7 M- P7 Q# {4 \
  14.     cv::distanceTransform(source, dst, cv::DIST_L2, 3, CV_32F);
    4 L\" D- r' `+ a2 j0 z
  15.     cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX);
    1 }4 A1 n\" T' q
  16. }
    + u/ P9 V) N2 d5 O7 ]0 {
  17. 7 E: g5 S+ o! V% D
  18. void findMarker(const cv::Mat& sureBg, cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) {2 p2 m2 F% W. ~
  19.     cv::findContours(sureBg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
    $ D  d+ x9 ^, N7 J! k3 P& v5 R
  20.     // 绘制前景标记
    # b, z1 j& I+ t4 e' ]2 R9 h7 M
  21.     for (size_t i = 0, size = contours.size(); i < size; i++)
    # v! N$ X( k+ J$ w: f
  22.         drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1);
    $ T1 g1 R/ j4 Q$ j
  23. }) g8 J) _9 x8 H, r% K

  24. 6 n. v) b8 {0 H5 J3 o) ~# U+ s: ?
  25. void getRandomColor(std::vector<cv::Vec3b>& colors, size_t size) {
    5 G: o% L- L) d( ^8 W
  26.     for (int i = 0; i < size ; ++i) {
    / [- Q5 e6 M\" j) {3 m# l7 B5 P
  27.         int b = cv::theRNG().uniform(0, 256);
    7 P: n7 ]$ K$ H9 V( \
  28.         int g = cv::theRNG().uniform(0, 256);
    . u  J( X' w3 c0 u* \
  29.         int r = cv::theRNG().uniform(0, 256);- N\" @# k1 I( t' X9 D3 a' [) C\" B0 y
  30.         colors.emplace_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));: i/ \2 Q' c# H$ e. v
  31.     }
    ( y, H+ @' c6 Y1 `( A
  32. }1 J* p% e! [& f6 [  G

  33. 2 T0 u' d\" r1 B( N+ h$ o3 s
  34. int main(int argc, char** argv) {& V, _$ r2 O& ^2 Q, P) C5 V! p
  35.     if(argc < 2){& a: S8 F2 m% t6 }
  36.         std::cerr << "Errorn";
    7 D6 D- a% Z\" W, J% Y
  37.         std::cerr << "Provide Input Image:n n";) v. e# k* M1 S3 N
  38.         return -1;, i+ J  U8 X# o+ ~6 I+ O( Q
  39.     }
    \" b' e4 u0 T% X4 g
  40.     cv::Mat original_img = cv::imread(argv[1]);
    3 j) G' ?\" E$ [' p7 e5 N$ v
  41.     if(original_img.empty()){
    7 g& Z* N+ U+ @\" ?; I
  42.         std::cerr << "Errorn";
    / T- B) n( _# ]
  43.         std::cerr << "Cannot Read Imagen";+ P$ l6 D& _; @
  44.         return -1;
    % q2 T9 m) v4 v; [0 E& b
  45.     }
    & Y- c# \( v- P3 |$ d6 L& Q- F7 y& K
  46.     cv::Mat shifted;
    # {* q/ s5 d: e  L7 x# |6 m1 V
  47.     cv::pyrMeanShiftFiltering(original_img, shifted, 21, 51);' Y* s6 ~& y8 Q
  48.     showImg("Mean Shifted", shifted);, }3 Z4 q+ ^/ F- _1 [
  49.     cv::Mat gray_img;$ j! m% ?) r$ u) s0 z  @, B: L
  50.     cv::cvtColor(original_img, gray_img, cv::COLOR_BGR2GRAY);, x( c* u# n' s; B7 c
  51.     showImg("GrayIMg", gray_img);6 v1 G. U+ S, e/ C\" H1 T8 h3 V
  52.     cv::Mat bin_img;
    . \: ^; w3 P/ J. J/ r
  53.     cv::threshold(gray_img, bin_img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);0 q( i! h4 y2 p8 J( t$ v7 W% F8 i
  54.     showImg("thres img", bin_img);8 T7 p# q# ^9 G3 f$ W
  55.     cv::Mat sure_bg;# s5 w3 f) N' f, U/ ]: z) y& V2 H) S
  56.     getBackground(bin_img, sure_bg);5 X! J- ]( [2 t4 V: _- j* B
  57.     showImg("Sure Background", sure_bg);0 u/ T7 g* M9 Q8 F. a$ {
  58.     cv::Mat sure_fg;: S5 O# I, t; x+ q2 @, L7 C) Y$ O
  59.     getForeground(bin_img, sure_fg);
    . d+ S, m& F' u! Z' v
  60.     showImg("Sure ForeGround", sure_fg);
    : p7 O( U; ?3 Q8 a( B( I5 z
  61.     cv::Mat markers = cv::Mat::zeros(sure_bg.size(), CV_32S);
    $ r' \; S$ E6 T/ c
  62.     std::vector<std::vector<cv::Point>> contours;\" T) m/ |  M' f+ p( W\" C
  63.     findMarker(sure_bg, markers, contours);. R% ]7 \' }1 [( R5 `, s+ C
  64.     cv::circle(markers, cv::Point(5, 5), 3, cv::Scalar(255), -1); // 在标记周围绘制圆圈3 R# i& k\" ~/ s0 M& G! N! a- ^
  65.     3 O3 D2 a  a8 y4 L, _2 i5 l
  66.     cv::watershed(original_img, markers);
    2 n\" C3 f  D' {' [# ~0 G8 p0 {; G  D
  67.     cv::Mat mark;% z) V6 U+ Z$ v/ C4 J& X
  68.     markers.convertTo(mark, CV_8U);
    . J! @6 V6 @7 V9 K6 G( M
  69.     cv::bitwise_not(mark, mark); // 将白色转换为黑色,黑色转换为白色
    5 }1 p5 u\" B3 S- B7 L+ `$ e\" X
  70.     showImg("MARKER", mark);3 F# `( m3 j+ l- }; F% T
  71.     // 在图像中突出显示标记 /1 [; B1 o, ~4 C; d3 x+ q. E' K
  72.     std::vector<cv::Vec3b> colors;
    0 `, l; `* b9 m% }% ?, v3 S
  73.     getRandomColor(colors, contours.size()); // 创建结果图像$ G\" t6 ?. N/ h) H
  74.     cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);
    % A' `1 [$ ?& b8 m
  75.     // 用随机颜色填充标记的对象\" A* j. A  F/ n  z1 ]$ F
  76.     for (int i = 0; i < markers.rows; i++)( t1 g; R8 c9 t
  77.     {
    ; ~' J9 B8 S( R2 M8 D0 d# W8 ]
  78.         for (int j = 0; j < markers.cols; j++)1 `; X+ U$ p+ a& K0 ?* e  c  T
  79.         {% G4 l0 f7 J9 T( J' m
  80.             int index = markers.at(i,j);
    6 z) {. N7 ^\" g5 e3 S5 [$ c2 p7 y- n
  81.             if (index > 0 && index <= static_cast<int>(contours.size()))
    + v8 l5 D- n! O( A5 i: l5 u( d) u! J5 C3 \
  82.                 dst.at<cv::Vec3b>(i,j) = colors[index-1];
    \" P  j9 o; D' q6 ^
  83.         }
    ; ?' E2 f9 T( u0 p! d
  84.     }$ _! S' P& H1 e/ w
  85.     showImg("Final Result", dst);5 a  }  _; o' o' c
  86.     cv::waitKey(0);& F3 j2 m6 h\" k% |7 e9 R% s' _- ~
  87.     return 0;( T! H4 K/ Y1 I5 o* \1 o# z
  88. }
    : j  Z( H& z4 t/ P  _
复制代码
————————————————
5 U9 ^' a" @8 f& ]/ W4 w
                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                        原文链接:https://blog.csdn.net/matt45m/article/details/138211359' Z2 K6 f( ?3 u0 S. [' T2 \0 s
+ K4 b" `" @7 t$ A  X- o' j2 V
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 08:45 , Processed in 1.228048 second(s), 51 queries .

回顶部