(数字图像处理MATLAB+Python)第十章图像分割-第四,五节:分水岭分割和综合案例

文章目录

  • 一:分水岭分割
    • (1)原理
    • (2)程序
  • 二:综合案例:答题卡图像分割
    • (1)设计思路
    • (2)各模块设计
    • (3)代码

一:分水岭分割

(1)原理

分水岭分割:图像处理中常用的一种分割方法,它基于图像中灰度或颜色的变化来划分不同的区域。分水岭分割算法的原理是基于地理学上的分水岭概念。将图像看作一个地貌图,在图像中低洼的部分被看作水池,而高处则表示山脉。通过在图像中加入水并让其逐渐充满,当水位上升到高峰时,不同山脉之间的低洼部分就形成了分割边界。其算法步骤如下

  • 预处理:对原始图像进行预处理,包括去噪、平滑和增强等操作,以提高分割结果的准确性
  • 标记区域:通过手动标记或自动选择一些种子像素来指定感兴趣的目标区域和背景区域。这些种子像素可以根据图像的特征进行选择,如明暗度、颜色等
  • 计算距离变换:利用标记区域生成距离变换图,其中每个像素的值表示该像素到最近标记点的距离。距离变换可以将图像中的低洼区域与高处区域进行区分
  • 寻找分割线:根据距离变换图,通过寻找局部最大值点和极小值点来确定分水岭线。这些分水岭线将图像分为不同的区域
  • 分割结果优化:根据具体需求,可以对分割结果进行后处理操作,如合并相邻区域、去除小区域等,以得到更好的分割效果

分水岭分割方法在图像处理领域有着广泛的应用,特别适用于复杂背景下的目标提取和图像分割任务。但它也存在一些问题,例如对噪声敏感,容易产生过分割或欠分割等情况,因此在实际应用中需要结合其他方法进行改进和优化

在这里插入图片描述

(2)程序

如下图

在这里插入图片描述


matlab实现

clear,clc,close all;
image=im2double(rgb2gray(imread('bricks.jpg')));
figure,imshow(image),title('原图');
hv=fspecial('prewitt');
hh=hv.';
gv=abs(imfilter(image,hv,'replicate'));
gh=abs(imfilter(image,hh,'replicate'));
% g=sqrt(gv.^2+gh.^2);
g=abs(gv)+abs(gh);
figure,imshow(g),title('梯度图像');
L=watershed(g);
wr=L==0;
figure,imshow(wr),title('分水岭');
image(wr)=0;
figure,imshow(image),title('分割结果');
% imwrite(g,'watergrad.jpg');
% imwrite(wr,'fenshuiling.jpg');
% imwrite(image,'waterresult.jpg');
import numpy as np
import cv2
import matplotlib.pyplot as plt

# 读取图像
image = cv2.imread('bricks.jpg')
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
image_gray = image_gray.astype(np.float64) / 255.0

# 显示原图
plt.figure()
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('原图')

# 计算梯度
hx = cv2.Sobel(image_gray, cv2.CV_64F, 1, 0, ksize=3)
hy = cv2.Sobel(image_gray, cv2.CV_64F, 0, 1, ksize=3)
gx = np.abs(hx)
gy = np.abs(hy)
gradient = gx + gy

# 显示梯度图像
plt.figure()
plt.imshow(gradient, cmap='gray')
plt.title('梯度图像')

# 分水岭分割
ret, markers = cv2.connectedComponents(cv2.threshold(np.uint8(gradient * 255), 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1])
markers = markers + 1
markers[gradient == 0] = 0
labels = cv2.watershed(image, markers)
segmented = image.copy()
segmented[labels == -1] = [0, 0, 255]

# 显示分水岭结果
plt.figure()
plt.imshow(cv2.cvtColor(segmented, cv2.COLOR_BGR2RGB))
plt.title('分割结果')

plt.show()

二:综合案例:答题卡图像分割

(1)设计思路

要求:将答题卡分割成不同区域。采用所学基础处理方法实现题目要求
操作

  • 几何校正
  • 裁切
  • 上下区域分割
  • 信息区和答题区分割

(2)各模块设计

主程序

在这里插入图片描述

几何校正

在这里插入图片描述

在这里插入图片描述

裁切

在这里插入图片描述

在这里插入图片描述

信息区域分割:通过检测边缘来进行分割:canny边缘检测、边缘滤波、边界修复和区域定位四个步骤

在这里插入图片描述

答题区分割

在这里插入图片描述

在这里插入图片描述

(3)代码

    clear,clc,close all;
    RGB=im2double(imread('card1.jpg'));
    figure,imshow(RGB),title('原图');
    adjustI=correction(RGB);

    figure,imshow(adjustI),title('几何校正结果图');
    [cropIu,cropId]=crop(adjustI);
 
    rectup(cropIu);
    rectdown(cropId);
    
    
function out=correction(in)
    bw=prepro(in);
    lines=linedetect(bw,2);
    line1=[lines(1).point1;lines(1).point2];
    line2=[lines(2).point1;lines(2).point2];
    angle1=abs(atan((line1(2,2)-line1(1,2))/(line1(2,1)-line1(1,1)))*180/pi);
    angle2=abs(atan((line2(2,2)-line2(1,2))/(line2(2,1)-line2(1,1)))*180/pi);
    
    if angle1<angle2
        temp=angle1;
        angle1=angle2;
        angle2=temp;
        temp=line1;
        line1=line2;
        line2=temp;   
    end
    first=line1(1,:);    second=line1(2,:);    third=line2(1,:);    fourth=line2(2,:);
    input_points=[first;second;third;fourth];
    
    first(2)=(first(2)+second(2))/2;    second(2)=first(2);
    third(1)=first(1);    fourth(1)=second(1);
    third(2)=(third(2)+fourth(2))/2;    fourth(2)=third(2);
    base_points=[first;second;third;fourth];
    
    tform=cp2tform(input_points,base_points,'projective');
    
    out=1-in(:,:,:);
    out=imtransform(out,tform);  
    out(:,:,:)=1-out(:,:,:);
end
function out=prepro(in)
    bw=1-imbinarize(rgb2gray(in));
    se=strel('square',2);
    out=imopen(bw,se);
end
function [lines,width]=linedetect(bw,n)
    [B,L]=bwboundaries(bw);
    [N,M]=size(bw);
    STATS=regionprops(L,'MajorAxisLength','MinorAxisLength');%统计几何特征
    len=length(STATS);
    for i=1:len          
        if STATS(i).MajorAxisLength<M/2 || STATS(i).MinorAxisLength>10
            L(L==i)=0;
        end
    end
    L(L~=0)=1;
    [B,L]=bwboundaries(L);
    STATS=regionprops(L,'MinorAxisLength');%统计几何特征
    len=length(STATS);
    width=0;
    for i=1:len   
        width=width+STATS(i).MinorAxisLength;
    end
    width=width/len;
    [h,theta,rho]=hough(L,'RhoResolution',0.5,'ThetaResolution',0.5);
    P=houghpeaks(h,n);
    lines=houghlines(L,theta,rho,P);
end

function [out1,out2]=crop(in)
    gray=1-rgb2gray(in);
    sumy=sum(gray,2);
    sumx=sum(gray);
 
    avery=mean(sumy);
    averx=mean(sumx);
    posy=find(sumy>avery);
    posx=find(sumx>averx);
    [C,maxx]=max(sumx);
    out=in(posy(1)-3:posy(end),posx(1)-3:maxx,:);    
    
    bw=prepro(out);
    [N,M]=size(bw);
    [lines,width]=linedetect(bw,2);
    line1=[lines(1).point1;lines(1).point2];
    line2=[lines(2).point1;lines(2).point2];
    if line1(1,2)>line2(1,2)
        temp=line1;
        line1=line2;
        line2=temp;
    end
    left=1;
    right=(line1(2,1)+line2(2,1))/2;
    right=floor(right+(M-right)*2/3);
    top=1;
    middle=(line1(1,2)+line1(2,2))/2;
    bottom=floor((line2(1,2)+line2(2,2))/2-width);
    out1=out(top:middle-width,left:right,:);   
    out2=out(middle+width/2:bottom,left:right,:); 
end

function out=rectup(in)
    out=imresize(in,2,'bilinear');
    gray=rgb2gray(out);       
    bw=edge(gray,'canny');
    [B,L]=bwboundaries(bw);
    STATS=regionprops(L,'MajorAxisLength');%统计几何特征
    len=length(STATS);
    [N,M]=size(gray);
    for i=1:len   
        if STATS(i).MajorAxisLength<M/8
            bw(L==i)=0;
        end
    end
    bw=restore(bw);
    bw=imfill(bw,'holes');
    se=strel('square',3);
    bw=imopen(bw,se);
    [B,L]=bwboundaries(bw);
    STATS=regionprops(L,'BoundingBox');%统计几何特征
    len=length(STATS);
    figure,imshow(out),title('个人信息区定位');
    hold on;
    for i=1:len
        rect=STATS(i).BoundingBox;
        rectangle('position',rect,'edgecolor','b');
    end
end
function out=restore(in)
    [N,M]=size(in);
    for x=2:M-1
        for y=2:N-1
            i=x;  j=y;
            while j<=N-1 && i<=M-1 && i>=2 && j>=2 && in(j,i)~=0
                neighbor=[in(j-1,i-1) in(j-1,i) in(j-1,i+1) in(j,i-1) in(j,i+1) in(j+1,i-1) in(j+1,i) in(j+1,i+1)];
                pos=find(neighbor~=0);
                if size(pos)==1
                    switch pos(1)
                        case 1
                            i=i+1;j=j+1;     
                        case 2
                            j=j+1;
                        case 3
                            i=i-1;j=j+1;
                        case 4
                            i=i+1;
                        case 5
                            i=i-1;
                        case 6
                            i=i+1;j=j-1;
                        case 7
                            j=j-1;
                        case 8
                            i=i-1;j=j-1;
                    end
                    in(j,i)=1;
                else
                    break;
                end
            end
        end
    end
    out=in;                        
end
function out=rectdown(in)
    hsv=rgb2hsv(in);
    s=hsv(:,:,2);
    v=hsv(:,:,3);
    [N,M]=size(v);
    sbw=imbinarize(s);
    se=strel('disk',3);
    sbw=imopen(sbw,se);

    [B,L]=bwboundaries(sbw);
    STATS=regionprops(L,'Area','BoundingBox');
    len=length(STATS);
    area=[];
    for i=1:len
        area=[area;STATS(i).Area];
    end
    [Y,Index]=sort(abs(area),'descend'); 
    if len>3
        count=3;
    else
        count=len;
    end
    for i=1:count
        rect=STATS(Index(i)).BoundingBox;
        v(rect(2):rect(2)+rect(4),rect(1):rect(1)+rect(3))=v(1,1);        
    end

    vbw=edge(v,'canny');
    [B,L]=bwboundaries(vbw);
    STATS=regionprops(L,'Area','MajorAxisLength','MinorAxisLength');%统计几何特征
    len=length(STATS);
    for i=1:len   
        if STATS(i).MajorAxisLength>M/16 || STATS(i).MinorAxisLength<3 || STATS(i).Area<10
            L(L==i)=0;
        end
    end
    L(L~=0)=1;
    se=strel('line',M/25,0);
    L=imclose(L,se);
    se=strel('line',N/35,90);
    L=imclose(L,se);
    se=strel('square',3);
    L=imopen(L,se);
    L=imfill(L,'holes');
    [B,L]=bwboundaries(L);
    STATS=regionprops(L,'BoundingBox');%统计几何特征
    len=length(STATS);
    figure,imshow(in),title('答题区定位');
    hold on;
    for i=1:len
        rect=STATS(i).BoundingBox;
        rectangle('position',rect,'edgecolor','b');
    end
end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/101934.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

three.js(二):webpack + three.js + ts

用webpackts 开发 three.js 项目 webpack 依旧是主流的模块打包工具;ts和three.js 是绝配&#xff0c;three.js本身就是用ts写的&#xff0c;ts可以为three 项目提前做好规则约束&#xff0c;使项目的开发更加顺畅。 1.创建一个目录&#xff0c;初始化 npm mkdir demo cd de…

第五章 树与二叉树 二、二叉树的定义和常考考点,WPL的算法

一、定义 二叉树可以用以下方式详细定义&#xff1a; 二叉树是由节点构成的树形结构&#xff0c;每个节点最多可以有两个子节点。每个节点有以下几个属性&#xff1a; 值&#xff1a;存储该节点的数据。左子节点&#xff1a;有一个左子节点&#xff0c;如果没有则为空。右子节…

Visual Studio 2017安装和项目配置

目录 前言1. What、Why and How1.1 What1.2 Why1.3 How 2. 安装3. 创建新项目4. 配置OpenCV库4.1 下载opencv安装包4.2 配置系统环境变量4.3 VS项目环境配置4.4 总结 5. 已有项目添加6. Tips6.1 常用快捷键6.2 字体和颜色选择6.3 配置编译路径 结语下载链接参考 前言 最近因为项…

【STM32教程】第二章 通用输入输出口GPIO

资料下载链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1hsIibEmsB91xFclJd-YTYA?pwdjauj 提取码&#xff1a;jauj 1. GPIO的基本结构 1.1 概述 GPIO&#xff08;General Purpose Input Output&#xff09;意思是通用输入输出口可配置为8种输入输出模式&a…

I IntelliJ IDEA 2023.2 最新解锁方式,支持java20

在 IntelliJ IDEA 2023.1 中&#xff0c;我们根据用户的宝贵反馈对新 UI 做出了大量改进。 我们还实现了性能增强&#xff0c;从而更快导入 Maven&#xff0c;以及在打开项目时更早提供 IDE 功能。 新版本通过后台提交检查提供了简化的提交流程。 IntelliJ IDEA Ultimate 现在支…

【Terraform学习】使用 Terraform创建DynamoDB添加项目(Terraform-AWS最佳实战学习)

本站以分享各种运维经验和运维所需要的技能为主 《python》&#xff1a;python零基础入门学习 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8》暂未更新 《docker学习》暂未更新 《ceph学习》ceph日常问题解…

Django静态文件媒体文件文件上传

文章目录 一、静态文件和媒体文件1.在django中使用静态文件实践2.在django中使用媒体文件 二、文件上传单文件上传实践多文件上传 一、静态文件和媒体文件 媒体文件: 用户上传的文件&#xff0c;叫做media 静态文件:存放在服务器的css,js,image,font等 叫做static1.在django中…

Docker(三) 创建Docker镜像

一、在Docker中拉取最基本的Ubuntu系统镜像 搜索Ubuntu镜像 Explore Dockers Container Image Repository | Docker Hub 下载镜像 docker pull ubuntu:22.04 二、在镜像中添加自己的内容 使用ubuntu镜像创建容器 docker run -it ubuntu:20.04 /bin/bash 在容器中创建了一个文…

文心一言接入Promptulate,开发复杂LLM应用程序

简介 最近在尝试将文心一言的LLM能力接入Promptulate&#xff0c;故写了一篇博客记录一下&#xff0c;Promptulate 是 Promptulate AI 旗下的大语言模型自动化与应用开发框架&#xff0c;旨在帮助开发者通过更小的成本构建行业级的大模型应用&#xff0c;其包含了LLM领域应用层…

CP Autosar-Ethernet配置

文章目录 前言一、Eth层级结构介绍二、Autosar实践2.1 ETH Driver2.2 Eth InterfaceEth Interface Autosar配置2.3 TcpIp模块Eth TcpIp Autosar配置2.4 SoAdEth SoAd配置前言 因汽车E/E架构和功能的复杂度提升而带来的对车辆数据传输带宽提高和通讯方式改变(基于服务的通讯-S…

万字长文:Stable Diffusion 保姆级教程

万字长文&#xff1a;Stable Diffusion 保姆级教程 2022年绝对是人工智能爆发的元年&#xff0c;前有 stability.ai 开源 Stable Diffusion 模型&#xff0c;后有 Open AI 发布 ChatGPT&#xff0c;二者都是里程碑式的节点事件&#xff0c;其重要性不亚于当年苹果发布iPhone&a…

ELK安装、部署、调试 (七)kibana的安装与配置

1.介绍 Kibana 是一个基于浏览器的开源可视化工具&#xff0c;主要用于分析大量日志&#xff0c;以折线图、条形图、饼图、热图、区域图、坐标图、仪表、目标、时间等形式。预测或查看输入源的错误或其他重大事件趋势的变化。Kibana 与 Elasticsearch 和 Logstash 同步工作&am…

【C++习题集】-- 顺序表、链表

&#xff08;用于复习&#xff09; 目录 线性表 顺序表 链表 单链表 单向 \ 双向 带哨兵位 \ 不带哨兵位 循环 \ 非循环 无头单向非循环链表实现 oj题 203. 移除链表元素 206. 反转链表 快慢指针 141.环形链表 【解题思路】 带头双向循环链表 顺序表和链表的区…

Redis—常用数据结构

Redis—常用数据结构 &#x1f50e;数据结构与内部编码 Redis 中常用的数据结构包括 Strings—字符串Hashes—哈希表Lists—列表Sets—集合Sorted sets—有序集合 Redis 底层在实现上述数据结构时, 会在源码层面针对上述实现进行特定优化, 以达到节省时间 / 节省空间的效果 …

51单片机智能电风扇控制系统proteus仿真设计( 仿真+程序+原理图+报告+讲解视频)

51单片机智能电风扇控制系统仿真设计( proteus仿真程序原理图报告讲解视频&#xff09; 讲解视频1.主要功能&#xff1a;2.仿真3. 原理图4. 程序代码5.设计报告6. 设计资料内容清单 51单片机智能电风扇控制系统仿真设计( proteus仿真程序原理图报告讲解视频&#xff09; 仿真图…

Java学习笔记之----I/O(输入/输出)一

在变量、数组和对象中存储的数据是暂时存在的&#xff0c;程序结束后它们就会丢失。想要永久地存储程序创建的数据&#xff0c;就需要将其保存在磁盘文件中(就是保存在电脑的C盘或D盘中&#xff09;&#xff0c;而只有数据存储起来才可以在其他程序中使用它们。Java的I/O技术可…

pip切换源

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

搬家快递服务小程序的便利性

在当今快节奏的生活中&#xff0c;搬家可能是很多人都需要面对的问题。无论是新房子还是新办公室&#xff0c;都需要高效、便捷的搬家服务。本文将介绍如何使用第三方小程序制作平台&#xff0c;如乔拓云平台&#xff0c;开发一款高效便捷的搬家服务小程序。 1. 注册登录第三方…

DRM全解析 —— ADD_FB(2)

接前一篇文章&#xff1a;DRM全解析 —— ADD_FB&#xff08;1&#xff09; 本文参考以下博文&#xff1a; DRM驱动&#xff08;四&#xff09;之ADD_FB 特此致谢&#xff01; 上一回围绕libdrm与DRM在Linux内核中的接口&#xff1a; DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, d…

Vue框架--Vue中el和data的两种写法

data与el的2种写法 1.el有2种写法 (1).new Vue时候配置el属性。 (2).先创建Vue实例&#xff0c;随后再通过vm.$mount(#root)指定el的值。 2.data有2种写法 (1).对象式 (2).函数式 如何选择&#xff1a;目前哪种写法都可以&#xff0c;以后学习到组件时&#xff…