一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C/C++ - C++ OpenCV單峰三角閾值法Thresh_Unimodal詳解

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

2022-03-11 13:22翟天保Steven C/C++

本文主要介紹了適合當圖像的直方圖具有明顯單峰特征時使用,結合了三角法的原理而設計的圖像分割方法,感興趣的小伙伴可以了解一下

需求說明

在對圖像進行處理時,經常會有這類需求:想通過閾值對圖像進行二值化分割,以提取自己感興趣的區域,常見的閾值分割方法有常數分割、最大類間方差法、雙峰分割、三角法等等,不同的場景應用不同的閾值方法。

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

下面介紹具體實現流程。

 

具體流程

1)取圖像的灰度圖,并遍歷統計0-255各個灰度值所出現的次數。

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的直方圖數據,這一步就是OpenCV三角法所沒有的。很多人可能不理解為什么要這一步,在你對圖像進行閾值化時如果提前進行了相關的運算,可能導致結果大于255的數值全部變為255,或者數值低于0的數值全部變為0,這就使得0和255的數值其實涵蓋了許多數值,呈累加態,很容易形成雙峰,這樣就很難找到我們真正想要的峰。例如0和255的數值都是10000左右,0略大一些,而我們的真峰是在250左右的灰度值,數值只有8000多,那么在后續閾值計算時就會因為峰的方向錯了而帶來毀滅性打擊。別覺得我說夸張了,只有自己去碰碰壁才能深刻領悟我說的。

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

3)確認峰值位置,maxidx是峰值對應的灰度值,max是峰值高度,也是灰度值對應數據的個數。

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)判斷峰值在左側還是右側,true為左側,false為右側。

bool lr = maxidx < 127;

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

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

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)右側同理,連接峰值(maxidx,max)和(0,0)點,公式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;

 

功能函數

// 單峰三角閾值法
cv::Mat Thresh_Unimodal(cv::Mat &src, int& idx)
{
	cv::Mat result = cv::Mat::zeros(src.size(), CV_8UC1);

	// 統計直方圖
	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;
		}
	}
	// 判斷最大點在哪一側,true為左側,false為右側
	bool lr = maxidx < 127;

	float maxd = 0;
	int maxdidx = 0;
	// 假設在左側
	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;
			}
		}
	}
	// 假設在右側
	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++測試代碼

#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;
}


// 繪制簡易直方圖
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);

	// 統計直方圖
	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;
		}
	}
	// 判斷最大點在哪一側,true為左側,false為右側
	bool lr = maxidx < 127;

	float maxd = 0;
	int maxdidx = 0;
	// 假設在左側
	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;
			}
		}
	}
	// 假設在右側
	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++ OpenCV單峰三角閾值法Thresh_Unimodal詳解

圖1 原圖灰度圖

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

圖2 直方圖

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

圖3 閾值圖

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

圖4 閾值結果

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

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

以上就是C++ OpenCV單峰三角閾值法Thresh_Unimodal詳解的詳細內容,更多關于C++ OpenCV單峰三角閾值法的資料請關注服務器之家其它相關文章!

原文鏈接:https://blog.csdn.net/zhaitianbao/article/details/121790192

延伸 · 閱讀

精彩推薦
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內存中的數據都是暫時的,當程序結束時,它們都將丟失,為了永久性的保存大量的數據,C語言提供了對文件的操作,這篇文章主要給大家介紹了關于C語言中文件...

    針眼_6702022-01-24
  • C/C++深入理解goto語句的替代實現方式分析

    深入理解goto語句的替代實現方式分析

    本篇文章是對goto語句的替代實現方式進行了詳細的分析介紹,需要的朋友參考下...

    C語言教程網7342020-12-03
  • C/C++C/C++經典實例之模擬計算器示例代碼

    C/C++經典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關于C/C++經典實例之模擬計算器的相關資料,文中通過示...

    jia150610152021-06-07
  • C/C++C語言實現電腦關機程序

    C語言實現電腦關機程序

    這篇文章主要為大家詳細介紹了C語言實現電腦關機程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數使用

    詳解c語言中的 strcpy和strncpy字符串函數使用

    strcpy 和strcnpy函數是字符串復制函數。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++學習C++編程的必備軟件

    學習C++編程的必備軟件

    本文給大家分享的是作者在學習使用C++進行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
  • C/C++c++ 單線程實現同時監聽多個端口

    c++ 單線程實現同時監聽多個端口

    這篇文章主要介紹了c++ 單線程實現同時監聽多個端口的方法,幫助大家更好的理解和學習使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
主站蜘蛛池模板: 色色色色色色网 | 和肥岳在厨房激情 | 日本高免费观看在线播放 | 国内9lporm自拍视频区 | 亚洲剧情在线观看 | 天天色一色 | 亚洲国产成人久久午夜 | 美女操批 | 国产高清在线视频一区二区三区 | 久久午夜夜伦痒痒想咳嗽P 久久无码AV亚洲精品色午夜麻豆 | 九九精品视频在线观看 | 亚洲午夜精品久久久久 | 精品国产乱码久久久久久免费流畅 | 45分钟做受片免费观看 | 欧美人禽杂交狂配无删完整 | 国内久久 | 日本一卡二卡3卡四卡无卡网址 | 久久精品国产免费 | 天堂俺去俺来也www久久婷婷 | 大桥未久midd—962在线 | 国产精品香蕉一区二区三区 | 波多野结衣178部中文字幕 | 91九色最新地址 | 国产成人h视频在线播放网站 | 人人人人人看碰人人免费 | 男人在女人下面狂躁 | 国产成人精品999在线 | 日本在线国产 | 亚洲日本中文字幕天堂网 | 91视频一区 | 99久久一区二区精品 | 国产精品怡红院永久免费 | 乌克兰肛交影视 | 日韩精品久久不卡中文字幕 | 国产欧美一区视频在线观看 | 免费被靠视频动漫 | 火影小南被爆羞羞网站进入 | 扒开黑女人p大荫蒂老女人 扒开大腿狠狠挺进视频 | 亚洲福利一区二区精品秒拍 | 51香蕉视频| 成年人免费在线视频 |