OpenCV——图像分块局部阈值二值化

目录

  • 一、算法原理
    • 1、算法概述
    • 2、参考文献
  • 二、代码实现
  • 三、结果展示

在这里插入图片描述

OpenCV——图像分块局部阈值二值化由CSDN点云侠原创,爬虫自重。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。

一、算法原理

1、算法概述

   针对目前局部阈值二值化结果存在目标虚假或断裂的缺陷,提出了一种基于图像分块的局部阈值二值化方法。首先,将图像分成若干子块并分析每个子块像素灰度变化情况; 接着,取一定大小的局部窗口在图像中移动,比较该局部窗口内与包含窗口自身且比窗口更大区域内的像素灰度变化情况,更大区域由窗口模板当前覆盖的所有子块组成,以此判断窗口内是否为灰度变化平坦( 或剧烈) 区域; 最后,根据不同的区域,给出具体的二值化方案。

2、参考文献

[1] 张洁玉. 基于图像分块的局部阈值二值化方法 [J]. 计算机应用, 2017, 37 (03): 827-831.

二、代码实现

ImageBinarization.h

#pragma once
#include <vector>  
#include <opencv2/opencv.hpp>class ImageBinarization
{
private:// 参数cv::Mat m_src;                // 输入数据cv::Mat m_dst;                // 输出数据cv::Size m_blockSize;         // 分块图像的尺寸cv::Size m_moveWndSize;       // 移动窗口的尺寸int m_segRow = 0;             // 图像分块的行数int m_segCol = 0;             // 图像分块的列数double m_alpha = 0.5;         // 平坦区域与剧烈区域的区分阈值double m_beta = 0.5;          // 剧烈区域,像素中心点与阈值O的距离std::vector<std::tuple<cv::Rect, double, double>> m_imgProperty;// 内部函数double getMatMAD(const cv::Mat& wndImg); // 计算平均绝对偏差std::vector<int>getGridIndices(const int windCenterInSegRow, const int windCenterInSegCol);void imageSegment();// 图像分块void segBlockBin(); // 分块二值化
public:ImageBinarization() {}~ImageBinarization() {}void setInputImage(const cv::Mat& src);void setBlockSize(const cv::Size& blockSize);       // 设置分块图像的尺寸void setMoveWindSize(const cv::Size& moveWndSize);  // 设置移动窗口的尺寸void setAlphaValue(double alpha);                   // 设置区分阈值void setBetaValue(double beta);                     // 设置像素中心点与阈值的距离void blockBinResult(cv::Mat& dst);                  // 分块二值化结果输出
};

ImageBinarization.cpp


#include<cstdlib>#include"ImageBinarization.h"// 参数设置
// 输入图像
void ImageBinarization::setInputImage(const cv::Mat& src)
{m_src = src;
}
// 分块尺寸
void ImageBinarization::setBlockSize(const cv::Size& blockSize)
{m_blockSize = blockSize;
}
// 滑动窗口尺寸
void ImageBinarization::setMoveWindSize(const cv::Size& moveWndSize)
{m_moveWndSize = moveWndSize;
}
// 平滑与剧烈区域分割阈值
void ImageBinarization::setAlphaValue(double alpha)
{m_alpha = alpha;
}
// 剧烈区域距离阈值
void ImageBinarization::setBetaValue(double beta)
{m_beta = beta;
}// 计算平均绝对偏差
double ImageBinarization::getMatMAD(const cv::Mat& wndImg)
{CV_Assert(wndImg.type() == CV_8UC1);// 计算均值cv::Scalar myMean = cv::mean(wndImg);double sum = 0.0;for (int y = 0; y < wndImg.rows; ++y){for (int x = 0; x < wndImg.cols; ++x){// 计算每一个像素灰度减去平均值的绝对值int diffAbs = cv::abs(wndImg.at<uchar>(y, x) - myMean[0]);sum += diffAbs; // 计算绝对值之和}}return sum / (wndImg.rows * wndImg.cols); // 平均绝对偏差
}
// 中心点八邻域格网号计算
std::vector<int> ImageBinarization::getGridIndices(const int windCenterInSegRow, const int windCenterInSegCol)
{// p8,p7,p6// p1,p0,p5// p2,p3,p4std::vector<int>nGridIndices;// 邻域格网索引号容器nGridIndices.resize(9);// 中心点p0所在格网int p0Row = windCenterInSegRow;int p0Col = windCenterInSegCol;int p0Grid = -1;if (p0Row < m_segRow && p0Col < m_segCol){p0Grid = p0Row * m_segCol + p0Col;}// 中心点越界则计算结束else{std::cerr << "中心点越界" << std::endl;abort();}nGridIndices[0] = p0Grid;// p0左侧p1所在格网int p1Row = p0Row;int p1Col = p0Col - 1;int p1Grid = -1;if (p1Col >= 0){p1Grid = p1Row * m_segCol + p1Col;}nGridIndices[1] = p1Grid;// p0左下方p2所在格网int p2Row = p0Row + 1;int p2Col = p0Col - 1;int p2Grid = -1;if (p2Row < m_segRow && p2Col >= 0){p2Grid = p2Row * m_segCol + p2Col;}nGridIndices[2] = p2Grid;// p0正下方p3所在格网int p3Row = p0Row + 1;int p3Col = p0Col;int p3Grid = -1;if (p3Row < m_segRow){p3Grid = p3Row * m_segCol + p3Col;}nGridIndices[3] = p3Grid;// p0右下方p4所在格网int p4Row = p0Row + 1;int p4Col = p0Col + 1;int p4Grid = -1;if (p4Row < m_segRow && p4Col < m_segCol){p4Grid = p4Row * m_segCol + p4Col;}nGridIndices[4] = p4Grid;// p0右侧p5所在格网int p5Row = p0Row;int p5Col = p0Col + 1;int p5Grid = -1;if (p5Col < m_segCol){p5Grid = p5Row * m_segCol + p5Col;}nGridIndices[5] = p5Grid;// p0右上方p6所在格网int p6Row = p0Row - 1;int p6Col = p0Col + 1;int p6Grid = -1;if (p6Row >= 0 && p6Col < m_segCol){p6Grid = p6Row * m_segCol + p6Col;}nGridIndices[6] = p6Grid;// p0正上方p7所在格网int p7Row = p0Row - 1;int p7Col = p0Col;int p7Grid = -1;if (p7Row >= 0){p7Grid = p7Row * m_segCol + p7Col;}nGridIndices[7] = p7Grid;// p0左上方p8所在格网int p8Row = p0Row - 1;int p8Col = p0Col - 1;int p8Grid = -1;if (p8Row >= 0 && p8Col >= 0){p8Grid = p8Row * m_segCol + p8Col;}nGridIndices[8] = p8Grid;return nGridIndices;
}
// 图像分块
void ImageBinarization::imageSegment()
{// OpenCV异常检测CV_Assert((m_blockSize.width <= m_src.cols) && (m_blockSize.height <= m_src.rows));// 1、获取分块图像的尺寸const int blockHeight = m_blockSize.height; // 图像分块的高度const int blockWidth = m_blockSize.width;   // 图像分块的宽度// 2、根据分块尺寸计算分块的个数m_segRow = cvCeil(m_src.rows / static_cast<double>(blockHeight)); // 图像分块的行数m_segCol = cvCeil(m_src.cols / static_cast<double>(blockWidth));  // 图像分块的列数// 3、使用裁剪函数进行分块cv::Mat roiImg;for (int i = 0; i < m_segRow; ++i){int rectHeight = 0;// 裁剪矩形框的高度int rectWidth = 0; // 裁剪矩形框的宽度// 如果剩余像素的高度小于分块的高度,则裁剪矩形框的高度为剩余像素的高度rectHeight = m_src.rows - i * blockHeight < blockHeight ? m_src.rows - i * blockHeight : blockHeight;for (int j = 0; j < m_segCol; ++j){// 如果剩余像素的宽度小于分块的宽度,则裁剪矩形框的宽度为剩余像素的宽度rectWidth = m_src.cols - j * blockWidth < blockWidth ? m_src.cols - j * blockWidth : blockWidth;// 获取分块图像cv::Rect rect(j * blockWidth, i * blockHeight, rectWidth, rectHeight);m_src(rect).copyTo(roiImg);// 计算每个分块的平均绝对偏差double madValue = getMatMAD(roiImg);// 计算每个分块的otsu阈值cv::Mat imgOtsu;double otsuValue = cv::threshold(roiImg, imgOtsu, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);// 分块信息、平均绝对偏差和otsu阈值存储到vector容器m_imgProperty.push_back(std::make_tuple(rect, madValue, otsuValue));}}
}
// 分块二值化
void ImageBinarization::segBlockBin()
{CV_Assert(m_src.type() == CV_8UC1);CV_Assert((m_moveWndSize.width % 2 == 1) && (m_moveWndSize.height % 2 == 1));CV_Assert((m_moveWndSize.width <= m_src.cols) && (m_moveWndSize.height <= m_src.rows));// 1、计算每个窗口的阈值m_dst = cv::Mat::zeros(m_src.rows, m_src.cols, CV_8UC1);for (int y = m_moveWndSize.height / 2; y <= m_src.rows - m_moveWndSize.height / 2 - 1; ++y){for (int x = m_moveWndSize.width / 2; x <= m_src.cols - m_moveWndSize.width / 2 - 1; ++x){// 获取以(x,y)为中心的滑动窗口范围内的灰度值cv::Point topLeftPoint = cv::Point(x - m_moveWndSize.width / 2, y - m_moveWndSize.height / 2);cv::Rect moveWindRect = cv::Rect(topLeftPoint.x, topLeftPoint.y, m_moveWndSize.width, m_moveWndSize.height);cv::Mat moveWindMat = m_src(moveWindRect);double delta = getMatMAD(moveWindMat);     // 局部窗口像素灰度平均绝对偏差double windMean = cv::mean(moveWindMat)[0];// 局部窗口像素灰度平均值double T2 = 0.0;                           // 区分平坦区域和非平坦区域的阈值double windOtsu = 0.0;                     // 平坦区域局部窗口阈值// -----------------------计算滑动窗口中心点所在的分块格网号---------------------const int windCenterInSegRow = cvFloor(y / m_blockSize.height); // 行号const int windCenterInSegCol = cvFloor(x / m_blockSize.width);  // 列号// ----------------------------中心点八邻域格网号计算----------------------------std::vector<int>nGridIndices = getGridIndices(windCenterInSegRow, windCenterInSegCol);// -----------------------------根据子块计算阈值--------------------------------for (size_t gridIdx = 0; gridIdx < nGridIndices.size(); ++gridIdx){cv::Rect Intersection; // 重叠区域 int n = nGridIndices[gridIdx];if (n != -1){Intersection = moveWindRect & std::get<0>(m_imgProperty[n]);}if (Intersection.area() > 0){double ki = 1.0 * Intersection.area() / moveWindRect.area();T2 += ki * std::get<1>(m_imgProperty[n]);windOtsu += ki * std::get<2>(m_imgProperty[n]);}}int value = m_src.at<uchar>(y, x);// 平坦区域if (delta < m_alpha * T2){// 大于阈值的部分为黑色if (value > windOtsu){m_dst.at<uchar>(y, x) = 0;}// 小于阈值的部分为白色else{m_dst.at<uchar>(y, x) = 255;}}// 剧烈区域else{// img(x,y)>=(1-B)*Oif (value >= (1 + m_beta) * windOtsu){m_dst.at<uchar>(y, x) = 0;}// img(x,y)<(1-B)*Oelse if (value < (1 - m_beta) * windOtsu){m_dst.at<uchar>(y, x) = 255;}else{// img(x,y)< Mean + 0.5*deltaif (value < windMean + 0.5 * delta){m_dst.at<uchar>(y, x) = 255;}// img(x,y)>= Mean + 0.5*deltaelse{m_dst.at<uchar>(y, x) = 0;}}}}}
}
// 分块二值化结果输出
void ImageBinarization::blockBinResult(cv::Mat& dst)
{imageSegment();segBlockBin();// 结果输出dst = m_dst;
}

main.cpp

#include<string>
#include<iostream>
#include"ImageBinarization.h"int main()
{cv::Mat img = cv::imread("CG.jpg");// 转灰度图cv::Mat gray;cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);cv::Size windSize(3, 3); // 分块窗口大小cv::Size moveWind(5, 5); // 移动窗口大小cv::Mat img_Thr_O;       // 二值化结果ImageBinarization ib;ib.setInputImage(gray);ib.setBlockSize(windSize);ib.setMoveWindSize(moveWind);ib.setAlphaValue(0.5);ib.setBetaValue(0.7);ib.blockBinResult(img_Thr_O);cv::imshow("origion_pic", img);cv::imshow("img_Thr_O", img_Thr_O);cv::waitKey(0);}

三、结果展示

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

from_pretrained明明以及下载好模型,却突然不能加载了报错

本人报错&#xff1a;OSError: Error no file named model_index.json found in directory /home/xxx/我的python学习/textToImage/sdxl-turbo. 原因&#xff1a;路径错误导致无法加载模型的配置文件 pipe AutoPipelineForText2Image.from_pretrained("stabilityai/sdx…

HORROR SYSTEM

HORROR SYSTEM是一个创新的工具包,允许开发者在Unity3D中创建独特的原创恐怖游戏。 HORROR SYSTEM是一款强大而灵活的工具,旨在基于Unity3D引擎创建沉浸式第三人称恐怖游戏。 这项资产易于使用且直观,可以让任何经验水平的开发人员将他们的想法付诸实践,创造出高质量、充满…

文献速递:深度学习胶质瘤诊断---空间细胞结构预测胶质母细胞瘤的预后

Title 题目 Spatial cellular architecture predicts prognosis in glioblastoma 空间细胞结构预测胶质母细胞瘤的预后 01文献速递介绍 胶质母细胞瘤的治疗耐药性的关键驱动因素是肿瘤内的异质性和细胞状态的可塑性。在这里&#xff0c;我们调查了空间细胞组织与胶质母细胞瘤…

python爬虫 - 爬取html中的script数据(zum.com新闻信息 )

文章目录 1. 分析页面内容数据格式2. 使用re.findall方法&#xff0c;编写爬虫代码3. 使用re.search 方法&#xff0c;编写爬虫代码 1. 分析页面内容数据格式 &#xff08;1&#xff09;打开 https://zum.com/ &#xff08;2&#xff09;按F12&#xff08;或 在网页上右键 --…

C++中的五种高级初始化技术:从reserve到piecewise_construct等

C高级初始化技术&#xff1a;reserve、emplace_back、constinit、Lambda表达式、piecewise_construct 一、简介二、reserve 结合 emplace_back三、C 20的constinit四、Lambda表达式和初始化五、make_unique_for_overwrite六、piecewise_construct 和 forward_as_tuple七、总结 …

SpringBoot xxl-job 任务调度

首先官网下载xxl-job的源代码&#xff0c;然后切换到jdk8&#xff0c;等Maven下载依赖 执行mysql的脚本&#xff0c;修改连接配置&#xff0c;启动admin站点 默认地址 http://localhost:8080/xxl-job-admin/ 先新增一个任务执行器&#xff0c;指向未来任务代码的站点 然后在…

探索亚马逊云科技「生成式 AI 精英速成计划」

目录 前言「生成式 AI 精英速成计划」技术开发课程学习课程学习 总结 前言 亚马逊云科技&#xff08;Amazon Web Services&#xff0c;简称AWS&#xff09;作为全球领先的云计算服务提供商&#xff0c;一直以来在推动人工智能&#xff08;AI&#xff09;领域的发展中扮演着重要…

MATLAB将多张小图整合到一张大图形成模板图

MATLAB将多张小图整合到一张大图形成模板图 代码如下: clc;close all;clear all;warning off;%清除变量 rand(seed, 100); randn(seed, 100); format long g;foldername字符模板; [datacell,filenamecell,filenameAllcell]readfun_1n(foldername); K2length(filenamecell);% …

读天才与算法:人脑与AI的数学思维笔记08_生物的创造力

1. 生物的创造力 1.1. 在进化树中是否有其他的物种已经具有与我们人类相当的创造力水平 1.2. 20世纪50年代中期&#xff0c;动物学家德斯蒙德莫里斯&#xff08;Desmond Morris&#xff09;在伦敦动物园做了这样一个试验 1.2.1. 动物学家给…

Laravel 6 - 第十四章 响应

​ 文章目录 Laravel 6 - 第一章 简介 Laravel 6 - 第二章 项目搭建 Laravel 6 - 第三章 文件夹结构 Laravel 6 - 第四章 生命周期 Laravel 6 - 第五章 控制反转和依赖注入 Laravel 6 - 第六章 服务容器 Laravel 6 - 第七章 服务提供者 Laravel 6 - 第八章 门面 Laravel 6 - …

《ESP8266通信指南》4-以Client进行TCP通信(AT指令)

往期 《ESP8266通信指南》3-常用AT指令详解-8266连WIFI-CSDN博客 《ESP8266通信指南》2-ESP8266 AT测试-CSDN博客 《ESP8266通信指南》1-ESP8266 简介-CSDN博客 1. 小节目标 通过 AT 指令使用 8266 进行 TCP 通信 2. 书接上回 复习以下&#xff0c;上一小节我们讲到了 8…

hyperf 三十一 极简DB组件

一 安装及配置 composer require hyperf/db php bin/hyperf.php vendor:publish hyperf/db 默认配置 config/autoload/db.php 如下&#xff0c;数据库支持多库配置&#xff0c;默认为 default。 配置项类型默认值备注driverstring无数据库引擎 支持 pdo 和 mysqlhoststringl…

如何搭建邮箱服务器?mail系统架设的两种方法

邮件mail通信是常用的办公场景&#xff0c;对于技术和网管等人员&#xff0c;往往需要搭建自己的邮箱服务器。那么&#xff0c;如何架设邮箱系统呢&#xff1f;通常有两种方案&#xff0c;一种是在在本地主机部署&#xff0c;另一种是在云端如云服务器上部署应用。根据主机IP情…

立即刷新导致请求的response没有来得及加载造成的this request has no response data available

1、前端递归调用后端接口 const startProgress () > {timer.value setInterval(() > {if (progress.value < 100) {time.value--;progress.value Math.ceil(100 / wait_time.value);} else {clearInterval(timer.value);progress.value 0;timer.value null;time.…

40. UE5 RPG给火球术增加特效和音效

前面&#xff0c;我们将火球的转向和人物的转向问题解决了&#xff0c;火球术可以按照我们的想法朝向目标发射。现在&#xff0c;我们解决接下来的问题&#xff0c;在角色释放火球术时&#xff0c;会产生释放音效&#xff0c;火球也会产生对应的音效&#xff0c;在火球击中目标…

【深度学习】DDoS-Detection-Challenge aitrans2024 入侵检测,基于机器学习(深度学习)判断网络入侵

当了次教练&#xff0c;做了个比赛的Stage1&#xff0c;https://github.com/AItransCompetition/DDoS-Detection-Challenge&#xff0c;得了100分。 一些记录&#xff1a; 1、提交的flowid不能重复&#xff0c;提交的是非入侵的数量和数据flowid,看check.cpp可知。 2、Stage…

大数据入门——概念、工具等

目录 一、基本概念 1.大数据技术 2.大数据特点 3.常见概念 4.数据分析师、数据开发工程师 二、相关工具 三、应用场景 四、大数据业务流程及组织结构 一、基本概念 1.大数据技术 主要解决海量数据的采集、存储和分析计算问题 2.大数据特点 大量、高速、多样、价值、…

【六十】【算法分析与设计】用一道题目解决dfs深度优先遍历,dfs中节点信息,dfs递归函数模板进入前维护出去前回溯,唯一解的剪枝飞升返回值true

路径之谜 题目描述 小明冒充X星球的骑士,进入了一个奇怪的城堡。 城堡里边什么都没有,只有方形石头铺成的地面。 假设城堡地面是nn个方格。如下图所示。 按习俗,骑士要从西北角走到东南角。可以横向或纵向移动,但不能斜着音走,也不能跳跃。每走到一个新方格,就要向正北 方和正西…

ESP32开发

目录 1、简介 1.1 种类 1.2 特点 1.3 管脚功能 1.4 接线方式 1.5 工作模式 2、基础AT指令介绍 2.1 AT指令类型 2.2 基础指令及其描述 2.3 使用AT指令需要注意的事 3、AT指令分类和提示信息 3.1 选择是否保存到Flash的区别 3.2 提示信息 3.3 其他会保存到Flash的A…

基础SQL DQL语句

基础查询 select * from 表名; 查询所有字段 create table emp(id int comment 编号,workno varchar(10) comment 工号,name varchar(10) comment 姓名,gender char(1) comment 性别,age tinyint unsigned comment 年龄,idcard char(18) comment 身份证号,worka…