欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++ OpenCV單峰三角閾值法Thresh_Unimodal詳解

 更新時(shí)間:2021年12月09日 10:40:54   作者:翟天保Steven  
本文主要介紹了適合當(dāng)圖像的直方圖具有明顯單峰特征時(shí)使用,結(jié)合了三角法的原理而設(shè)計(jì)的圖像分割方法,感興趣的小伙伴可以了解一下

需求說明

在對(duì)圖像進(jìn)行處理時(shí),經(jīng)常會(huì)有這類需求:想通過閾值對(duì)圖像進(jìn)行二值化分割,以提取自己感興趣的區(qū)域,常見的閾值分割方法有常數(shù)分割、最大類間方差法、雙峰分割、三角法等等,不同的場(chǎng)景應(yīng)用不同的閾值方法。

今天要講的方法,適合當(dāng)圖像的直方圖具有明顯單峰特征時(shí)使用,結(jié)合了三角法的原理而設(shè)計(jì),相比較OpenCV自帶的三角法,好處是可以根據(jù)自身需求合理修改函數(shù);如果用OpenCV庫的函數(shù),只有一個(gè)接口,若不能達(dá)到較理想的應(yīng)用效果,就束手無策了。

下面介紹具體實(shí)現(xiàn)流程。

具體流程

1)取圖像的灰度圖,并遍歷統(tǒng)計(jì)0-255各個(gè)灰度值所出現(xiàn)的次數(shù)。

cv::Mat src = imread("test.jpg", 0);
cv::Mat hist = cv::Mat::zeros(1, 256, CV_32FC1);
for (int i = 0; i < src.rows; ++i)
{
	for (int j = 0; j < src.cols; ++j)
	{
		hist.at<float>(0, src.at <uchar>(i, j))++;
	}
}

2)去除0和255的直方圖數(shù)據(jù),這一步就是OpenCV三角法所沒有的。很多人可能不理解為什么要這一步,在你對(duì)圖像進(jìn)行閾值化時(shí)如果提前進(jìn)行了相關(guān)的運(yùn)算,可能導(dǎo)致結(jié)果大于255的數(shù)值全部變?yōu)?55,或者數(shù)值低于0的數(shù)值全部變?yōu)?,這就使得0和255的數(shù)值其實(shí)涵蓋了許多數(shù)值,呈累加態(tài),很容易形成雙峰,這樣就很難找到我們真正想要的峰。例如0和255的數(shù)值都是10000左右,0略大一些,而我們的真峰是在250左右的灰度值,數(shù)值只有8000多,那么在后續(xù)閾值計(jì)算時(shí)就會(huì)因?yàn)榉宓姆较蝈e(cuò)了而帶來毀滅性打擊。別覺得我說夸張了,只有自己去碰碰壁才能深刻領(lǐng)悟我說的。

hist.at<float>(0, 255) = 0;
hist.at<float>(0, 0) = 0;

3)確認(rèn)峰值位置,maxidx是峰值對(duì)應(yīng)的灰度值,max是峰值高度,也是灰度值對(duì)應(yīng)數(shù)據(jù)的個(gè)數(shù)。

float max = 0;
int maxidx = 0;
for (int i = 0; i < 256; ++i)
{
	if (hist.at<float>(0, i) > max)
	{
		max = hist.at<float>(0, i);
		maxidx = i;
	}
}

4)判斷峰值在左側(cè)還是右側(cè),true為左側(cè),false為右側(cè)。

bool lr = maxidx < 127;

5)當(dāng)在左側(cè)時(shí),連接峰值(maxidx,max)和(255,0)點(diǎn),用兩點(diǎn)建立直線公式,如下圖所示公式。 L的表達(dá)式可以轉(zhuǎn)換為Ax+By+C=0的形式,A是-max,B是maxidx-255,C是max*255,在結(jié)合距離公式可以計(jì)算出直方圖曲線上每個(gè)點(diǎn)到直線的距離,取距離最長(zhǎng)的那個(gè)點(diǎn)作為閾值。

if (lr)
{
	float A = float(-max);
	float B = float(maxidx - 255);
	float C = float(max * 255);
 
	for (int i = maxidx + 1; i < 256; ++i)
	{
		float x0 = float(i);
		float y0 = hist.at<float>(0, i);
		float d = abs(A * x0 + B * y0 + C) / std::sqrt(A * A + B * B);
		if (d > maxd)
		{
			maxd = d;
			maxdidx = i;
		}
	}
}

6)右側(cè)同理,連接峰值(maxidx,max)和(0,0)點(diǎn),公式ABC如代碼所示。

else {
	float A = float(-max);
	float B = float(maxidx);
	float C = 0.0f;
 
	for (int i = 0; i < maxidx; ++i)
	{
		float x0 = float(i);
		float y0 = hist.at<float>(0, i);
		float d = abs(A * x0 + B * y0 + C) / std::sqrt(A * A + B * B);
		if (d > maxd)
		{
			maxd = d;
			maxdidx = i;
		}
	}
}

7)二值化,完成。

result.setTo(255, src > maxdidx);
idx = maxdidx;
return result;

功能函數(shù)

// 單峰三角閾值法
cv::Mat Thresh_Unimodal(cv::Mat &src, int& idx)
{
	cv::Mat result = cv::Mat::zeros(src.size(), CV_8UC1);
 
	// 統(tǒng)計(jì)直方圖
	cv::Mat hist = cv::Mat::zeros(1, 256, CV_32FC1);
	for (int i = 0; i < src.rows; ++i)
	{
		for (int j = 0; j < src.cols; ++j)
		{
			hist.at<float>(0, src.at<uchar>(i, j))++;
		}
	}
	hist.at<float>(0, 255) = 0;
	hist.at<float>(0, 0) = 0;
	// 搜索最大值位置
	float max = 0;
	int maxidx = 0;
	for (int i = 0; i < 256; ++i)
	{
		if (hist.at<float>(0, i) > max)
		{
			max = hist.at<float>(0, i);
			maxidx = i;
		}
	}
	// 判斷最大點(diǎn)在哪一側(cè),true為左側(cè),false為右側(cè)
	bool lr = maxidx < 127;
 
	float maxd = 0;
	int maxdidx = 0;
	// 假設(shè)在左側(cè)
	if (lr)
	{
		float A = float(-max);
		float B = float(maxidx - 255);
		float C = float(max * 255);
 
		for (int i = maxidx + 1; i < 256; ++i)
		{
			float x0 = float(i);
			float y0 = hist.at<float>(0, i);
			float d = abs(A * x0 + B * y0 + C) / std::sqrt(A * A + B * B);
			if (d > maxd)
			{
				maxd = d;
				maxdidx = i;
			}
		}
	}
	// 假設(shè)在右側(cè)
	else {
		float A = float(-max);
		float B = float(maxidx);
		float C = 0.0f;
 
		for (int i = 0; i < maxidx; ++i)
		{
			float x0 = float(i);
			float y0 = hist.at<float>(0, i);
			float d = abs(A * x0 + B * y0 + C) / std::sqrt(A * A + B * B);
			if (d > maxd)
			{
				maxd = d;
				maxdidx = i;
			}
		}
	}
 
	// 二值化
	result.setTo(255, src > maxdidx);
	idx = maxdidx;
	return result;
}

C++測(cè)試代碼

#include <iostream>
#include <time.h>
#include <opencv2/opencv.hpp>
 
using namespace std;
using namespace cv;
 
cv::Mat DrawHistImg(cv::Mat &hist);
cv::Mat Thresh_Unimodal(cv::Mat &src, int& idx);
 
int main()
{
	cv::Mat src = imread("test.jpg", 0);
 
	// 繪制均衡化后直方圖
	cv::Mat hrI = DrawHistImg(src);
 
	// 單峰三角閾值法
	int thresh;
	cv::Mat result = Thresh_Unimodal(src, thresh);
 
	cout << " thresh: " << thresh << endl;
 
	imshow("original", src);
	imshow("hist", hrI);
	imshow("result", result);
	waitKey(0);
 
	return 0;
}
 
 
// 繪制簡(jiǎn)易直方圖
cv::Mat DrawHistImg(cv::Mat &src)
{
	cv::Mat hist = cv::Mat::zeros(1, 256, CV_32FC1);
	for (int i = 0; i < src.rows; ++i)
	{
		for (int j = 0; j < src.cols; ++j)
		{
			hist.at<float>(0, src.at <uchar>(i, j))++;
		}
	}
	cv::Mat histImage = cv::Mat::zeros(540, 1020, CV_8UC1);
	const int bins = 255;
	double maxValue;
	cv::Point2i maxLoc;
	cv::minMaxLoc(hist, 0, &maxValue, 0, &maxLoc);
	int scale = 4; 
	int histHeight = 540;
 
	for (int i = 0; i < bins; i++)
	{
		float binValue = (hist.at<float>(i));
		int height = cvRound(binValue * histHeight / maxValue);
		cv::rectangle(histImage, cv::Point(i * scale, histHeight),
			cv::Point((i + 1) * scale - 1, histHeight - height), cv::Scalar(255), -1);
 
	}
	return histImage;
}
 
// 單峰三角閾值法
cv::Mat Thresh_Unimodal(cv::Mat &src, int& idx)
{
	cv::Mat result = cv::Mat::zeros(src.size(), CV_8UC1);
 
	// 統(tǒng)計(jì)直方圖
	cv::Mat hist = cv::Mat::zeros(1, 256, CV_32FC1);
	for (int i = 0; i < src.rows; ++i)
	{
		for (int j = 0; j < src.cols; ++j)
		{
			hist.at<float>(0, src.at<uchar>(i, j))++;
		}
	}
	hist.at<float>(0, 255) = 0;
	hist.at<float>(0, 0) = 0;
	// 搜索最大值位置
	float max = 0;
	int maxidx = 0;
	for (int i = 0; i < 256; ++i)
	{
		if (hist.at<float>(0, i) > max)
		{
			max = hist.at<float>(0, i);
			maxidx = i;
		}
	}
	// 判斷最大點(diǎn)在哪一側(cè),true為左側(cè),false為右側(cè)
	bool lr = maxidx < 127;
 
	float maxd = 0;
	int maxdidx = 0;
	// 假設(shè)在左側(cè)
	if (lr)
	{
		float A = float(-max);
		float B = float(maxidx - 255);
		float C = float(max * 255);
 
		for (int i = maxidx + 1; i < 256; ++i)
		{
			float x0 = float(i);
			float y0 = hist.at<float>(0, i);
			float d = abs(A * x0 + B * y0 + C) / std::sqrt(A * A + B * B);
			if (d > maxd)
			{
				maxd = d;
				maxdidx = i;
			}
		}
	}
	// 假設(shè)在右側(cè)
	else {
		float A = float(-max);
		float B = float(maxidx);
		float C = 0.0f;
 
		for (int i = 0; i < maxidx; ++i)
		{
			float x0 = float(i);
			float y0 = hist.at<float>(0, i);
			float d = abs(A * x0 + B * y0 + C) / std::sqrt(A * A + B * B);
			if (d > maxd)
			{
				maxd = d;
				maxdidx = i;
			}
		}
	}
 
	// 二值化
	result.setTo(255, src > maxdidx);
	idx = maxdidx;
	return result;
}

測(cè)試效果

圖1 原圖灰度圖

圖2 直方圖

圖3 閾值圖

圖4 閾值結(jié)果

通過imagewatch插件可以觀察閾值203是不是在距離最遠(yuǎn)的位置,答案是肯定的。

如果函數(shù)有什么可以改進(jìn)完善的地方,非常歡迎大家指出,一同進(jìn)步何樂而不為呢~?

以上就是C++ OpenCV單峰三角閾值法Thresh_Unimodal詳解的詳細(xì)內(nèi)容,更多關(guān)于C++ OpenCV單峰三角閾值法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 字符串的模式匹配詳解--BF算法與KMP算法

    字符串的模式匹配詳解--BF算法與KMP算法

    這篇文章記錄一下串里面的模式匹配,模式匹配,顧名思義就是給定一個(gè)被匹配的字符串,然后用一個(gè)字符串模式(模型)去匹配上面說的字符串,看后者是否在前者里面出現(xiàn)。常用的有2種算法可以實(shí)現(xiàn),下面我們來具體探討下
    2014-08-08
  • C語言實(shí)現(xiàn)棧的示例詳解

    C語言實(shí)現(xiàn)棧的示例詳解

    棧是一種特殊的線性表,只允許從一端進(jìn)出數(shù)據(jù),稱為后進(jìn)先出,先進(jìn)后出。本文主要為大家介紹了C語言實(shí)現(xiàn)棧的示例代碼,感興趣的可以了解一下
    2022-06-06
  • c++ base64編解碼使用示例

    c++ base64編解碼使用示例

    這篇文章主要介紹了c++的base64編解碼使用示例,需要的朋友可以參考下
    2014-02-02
  • C++ 中this指針的用途詳解

    C++ 中this指針的用途詳解

    這篇文章主要給大家介紹了關(guān)于C++ 中this指針的用途,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-09-09
  • C++編譯器和鏈接器工作原理及使用方法完全指南

    C++編譯器和鏈接器工作原理及使用方法完全指南

    本文將詳細(xì)介紹C++中的編譯器和鏈接器以及它們的工作原理及使用方法全面詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • C語言多文件編程問題解析

    C語言多文件編程問題解析

    在某些場(chǎng)景中,考慮到編譯效率和可移植性,#pragma once 和 #ifndef 經(jīng)常被結(jié)合使用來避免頭文件被 重復(fù)引入,這里介紹用 _Pragma 操作符避免頭文件重復(fù)引入的問題,感興趣的朋友跟隨小編一起看看吧
    2022-12-12
  • VC++ 中ListCtrl經(jīng)驗(yàn)總結(jié)

    VC++ 中ListCtrl經(jīng)驗(yàn)總結(jié)

    這篇文章主要介紹了VC++ 中ListCtrl經(jīng)驗(yàn)總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2015-06-06
  • 使用C語言操作文件的基本函數(shù)整理

    使用C語言操作文件的基本函數(shù)整理

    這篇文章主要介紹了使用C語言操作文件的基本函數(shù)整理,包括創(chuàng)建和打開以及關(guān)閉文件的操作方法,需要的朋友可以參考下
    2015-08-08
  • C++設(shè)計(jì)模式之抽象工廠模式

    C++設(shè)計(jì)模式之抽象工廠模式

    這篇文章主要介紹了C++設(shè)計(jì)模式之抽象工廠模式,本文要講的抽象工廠模式,就是工廠方法模式的擴(kuò)展和延伸,需要的朋友可以參考下
    2014-09-09
  • C/C++判斷傳入的UTC時(shí)間是否當(dāng)天的實(shí)現(xiàn)方法

    C/C++判斷傳入的UTC時(shí)間是否當(dāng)天的實(shí)現(xiàn)方法

    在項(xiàng)目中經(jīng)常會(huì)顯示一個(gè)時(shí)間,如果這個(gè)時(shí)間在今日內(nèi)就顯示為時(shí)分秒,否則顯示為年月日,有需要的朋友可以參考一下
    2014-01-01

最新評(píng)論