自适应阈值大津法(OTSU)介绍及代码实现

2024-08-27 00:32

本文主要是介绍自适应阈值大津法(OTSU)介绍及代码实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

https://blog.csdn.net/a153375250/article/details/50970104

算法原理
最大类间方差法是由日本学者大津于1979年提出的,是一种自适应的阈值确定法,又叫大津法,简称OTSU。

我用最简单的方式解释一下算法原理:

这个算法的思想就是假设阈值T将图像分成了前景和背景两个部分。

求出这两个部分的类间方差:

前景像素个数占比x(前景平均灰度 - 全图平均灰度)2 + 背景像素个数占比x(背景平均灰度 - 全图平均灰度)2

将阈值从0~255遍历一次,使上述类间方差最大的阈值T即为所求。

类间方差计算
我们接下来将算法原理中的类间方差化简为一个比较简单的形式,下面的过程也是代码实现的过程。

定义变量:

总像素数:N
前景像素数:fN
背景像素数:bN
前景像素灰度和:fSum
背景像素灰度和:bSum
前景像素平均灰度:fu
背景像素平均灰度:bu
图像总灰度值:Sum
图像平均灰度:u
前景像素占比:fw
背景图像占比:bw
类间方差:g
1
2
3
4
5
6
7
8
9
10
11
12
前景、背景占比:

前景像素占比:fw = fN/N
背景像素占比:bw = bN/N
1
2
前景、背景占比满足:

fw + bw =1  (1)
1
图片平均灰度值:

图片灰度直方图 : Histogram[256]
图片总灰度值 Sum : for(i=0; i<N; i++){ Sum += Histogram[i];}
图片平均灰度值 u : Sum/N
1
2
3
阈值为T时前景平均灰度:

阈值为T时前景像素数 fN : for(i=0; i<=T; i ++) { fN += Histogram[i];}
阈值为T时前景像素灰度和 fSum : for(i=0; i<=T; i++) { fSum += Histogram[i]*i;}
阈值为T时前景平均灰度 fu : fSum/fN
1
2
3
阈值为T时背景平均灰度:

阈值为T时背景像素数 bN : N-fN
阈值为T时背景像素灰度和 bSum : Sum-fSum
阈值为T时背景平均灰度 bu : bSum/bN    
1
2
3
平均灰度满足:

u = fu*fw + bu*bw  (2)
1
类间方差:

类间方差 g:fw*(fu-u)^2+bw*(bu-u)^2  (3)
1
将(1)(2)带入(3)式:

g = bw*fw*(fu-bu)^2
1
代码实现
假设读入图片为单通道Mat型灰度图,我们可以用以下代码实现:

#include <opencv2/opencv.hpp>  
#include <cv.h>
#include <highgui.h>
#include <cxcore.h>

using namespace std;
using namespace cv;

Mat otsuGray(const Mat src) {
    Mat img = src;
    int c = img.cols; //图像列数
    int r = img.rows; //图像行数
    int T = 0; //阈值
    uchar* data = img.data; //数据指针
    int ftNum = 0; //前景像素个数
    int bgNum = 0; //背景像素个数
    int N = c*r; //总像素个数
    int ftSum = 0; //前景总灰度值
    int bgSum = 0; //背景总灰度值
    int graySum = 0;
    double w0 = 0; //前景像素个数占比
    double w1 = 0; //背景像素个数占比
    double u0 = 0; //前景平均灰度
    double u1 = 0; //背景平均灰度
    double Histogram[256] = {0}; //灰度直方图
    double temp = 0; //临时类间方差
    double g = 0; //类间方差

    //灰度直方图
    for(int i = 0; i < r ; i ++) {
        for(int j = 0; j <c; j ++) {
            Histogram[img.at<uchar>(i,j)]++;
        }
    }
    //求总灰度值
    for(int i = 0; i < 256; i ++) {
        graySum += Histogram[i]*i;
    }

    for(int i = 0; i < 256; i ++) {
        ftNum += Histogram[i];  //阈值为i时前景个数
        bgNum = N - ftNum;      //阈值为i时背景个数
        w0 = (double)ftNum/N; //前景像素占总数比
        w1 = (double)bgNum/N; //背景像素占总数比
        if(ftNum == 0) continue;
        if(bgNum == 0) break;
        //前景平均灰度
        ftSum += i*Histogram[i];
        u0 = ftSum/ftNum;

        //背景平均灰度
        bgSum = graySum - ftSum;
        u1 = bgSum/bgNum;

        g = w0*w1*(u0-u1)*(u0-u1);
        if(g > temp) {
            temp = g;
            T = i;
        }
    }

    for(int i=0; i<img.rows; i++)
    {
        for(int j=0; j<img.cols; j++)
        {
            if((int)img.at<uchar>(i,j)>T)
                img.at<uchar>(i,j) = 255;
            else
                img.at<uchar>(i,j) = 0;
        }
    }
    return img;
}
--------------------- 
作者:Easy-Sir 
来源:CSDN 
原文:https://blog.csdn.net/a153375250/article/details/50970104 
版权声明:本文为博主原创文章,转载请附上博文链接!

这篇关于自适应阈值大津法(OTSU)介绍及代码实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1110119

相关文章

python实现svg图片转换为png和gif

《python实现svg图片转换为png和gif》这篇文章主要为大家详细介绍了python如何实现将svg图片格式转换为png和gif,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录python实现svg图片转换为png和gifpython实现图片格式之间的相互转换延展:基于Py

Python利用ElementTree实现快速解析XML文件

《Python利用ElementTree实现快速解析XML文件》ElementTree是Python标准库的一部分,而且是Python标准库中用于解析和操作XML数据的模块,下面小编就来和大家详细讲讲... 目录一、XML文件解析到底有多重要二、ElementTree快速入门1. 加载XML的两种方式2.

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

redis过期key的删除策略介绍

《redis过期key的删除策略介绍》:本文主要介绍redis过期key的删除策略,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录第一种策略:被动删除第二种策略:定期删除第三种策略:强制删除关于big key的清理UNLINK命令FLUSHALL/FLUSHDB命

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Python实现图片分割的多种方法总结

《Python实现图片分割的多种方法总结》图片分割是图像处理中的一个重要任务,它的目标是将图像划分为多个区域或者对象,本文为大家整理了一些常用的分割方法,大家可以根据需求自行选择... 目录1. 基于传统图像处理的分割方法(1) 使用固定阈值分割图片(2) 自适应阈值分割(3) 使用图像边缘检测分割(4)

Android实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl

C# foreach 循环中获取索引的实现方式

《C#foreach循环中获取索引的实现方式》:本文主要介绍C#foreach循环中获取索引的实现方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、手动维护索引变量二、LINQ Select + 元组解构三、扩展方法封装索引四、使用 for 循环替代

Spring Security+JWT如何实现前后端分离权限控制

《SpringSecurity+JWT如何实现前后端分离权限控制》本篇将手把手教你用SpringSecurity+JWT搭建一套完整的登录认证与权限控制体系,具有很好的参考价值,希望对大家... 目录Spring Security+JWT实现前后端分离权限控制实战一、为什么要用 JWT?二、JWT 基本结构

Java实现优雅日期处理的方案详解

《Java实现优雅日期处理的方案详解》在我们的日常工作中,需要经常处理各种格式,各种类似的的日期或者时间,下面我们就来看看如何使用java处理这样的日期问题吧,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言一、日期的坑1.1 日期格式化陷阱1.2 时区转换二、优雅方案的进阶之路2.1 线程安全重构2