1. 程式人生 > >使用CImg實現canny邊緣檢測和跟蹤

使用CImg實現canny邊緣檢測和跟蹤

第一步,函式toGrayScale 將彩色影象轉換為灰度影象,根據RGB值和公式計算出灰度值即可,比較簡單。

CImg<uchar> canny::toGrayScale()
{
	grayscaled = CImg<uchar>(img.width(), img.height(), 1, 1);
	cimg_forXY(grayscaled, x, y) {
		int r = img(x, y, 0);
		int g = img(x, y, 1);
		int b = img(x, y, 2);

		double newValue = (r * 0.2126 + g *
0.7152 + b * 0.0722); for (int i = 0; i < 3; ++i) grayscaled(x, y) = newValue; } return grayscaled; }

第二步,函式createFilter 建立一個高斯濾波器,目的是為了降噪。使用正態分佈計算權重矩陣,然後正規化。計算公式
在這裡插入圖片描述

vector<vector<double>> canny::createFilter(int row, int column, double sigmaIn)
{
	vector<vector<double>>
filter; for (int i = 0; i < row; i++) { vector<double> col; for (int j = 0; j < column; j++) { col.push_back(-1); } filter.push_back(col); } float coordSum = 0; float constant = 2.0 * sigmaIn * sigmaIn; // Sum is for normalization float sum = 0.0; for (int x = -row / 2; x <=
row / 2; x++) { for (int y = -column / 2; y <= column / 2; y++) { coordSum = (x*x + y * y); filter[x + row / 2][y + column / 2] = (exp(-(coordSum) / constant)) / (M_PI * constant); sum += filter[x + row / 2][y + column / 2]; } } // Normalize the Filter for (int i = 0; i < row; i++) for (int j = 0; j < column; j++) filter[i][j] /= sum; return filter; }

第三步,函式 useFilter 使用第二步建立的濾波器,建立一個3*3的權重矩陣。
任何邊緣檢測演算法都不可能在未經處理的原始資料上很好地處理,對原始資料與高斯平滑模板作卷積,得到的影象與原始影象相比有些輕微的模糊。這樣,單獨的一個畫素噪聲在經過高斯平滑的影象上變得幾乎沒有影響。

CImg<uchar> canny::useFilter(CImg<uchar> img_in, vector<vector<double>> filterIn)
{
	int size = (int)filterIn.size() / 2;
	CImg<uchar> filteredImg(img_in.width() - 2 * size, img_in.height() - 2 * size, 1, 1);
	for (int i = size; i < img_in.width() - size; i++)
	{
		for (int j = size; j < img_in.height() - size; j++)
		{
			double sum = 0;

			for (int x = 0; x < filterIn.size(); x++)
				for (int y = 0; y < filterIn.size(); y++)
				{
					sum += filterIn[x][y] * (double)(img_in(i + x - size, j + y - size));
				}

			filteredImg(i - size, j - size) = sum;
		}

	}
	return filteredImg;
}

第四步,函式 sobel 使用索伯運算元處理影象,用來尋找影象中的亮度梯度。
計算公式:
在這裡插入圖片描述
分別對橫向和縱向進行處理。影象的每一個畫素的橫向及縱向梯度近似值可用以下的公式
結合:在這裡插入圖片描述 ,梯度方向:在這裡插入圖片描述

這裡注意因為CImg(x,y)是以左上角為原點,橫向為x,縱向為y訪問畫素,而opencv的at(x,y)訪問的是第x行第y列的畫素,所以這裡計算sumx和sumy的模板交換。
在這裡插入圖片描述
在後面的操作中,都要注意這個問題。

CImg<uchar> canny::sobel()
{
	//Sobel X Filter
	double x1[] = { 1.0, 0, -1.0 };
	double x2[] = { 2.0, 0, -2.0 };
	double x3[] = { 1.0, 0, -1.0 };

	vector<vector<double>> xFilter(3);
	xFilter[0].assign(x1, x1 + 3);
	xFilter[1].assign(x2, x2 + 3);
	xFilter[2].assign(x3, x3 + 3);

	//Sobel Y Filter
	double y1[] = { -1.0, -2.0, -1.0 };
	double y2[] = { 0, 0, 0 };
	double y3[] = { 1.0, 2.0, 1.0 };

	vector<vector<double>> yFilter(3);
	yFilter[0].assign(y1, y1 + 3);
	yFilter[1].assign(y2, y2 + 3);
	yFilter[2].assign(y3, y3 + 3);

	//Limit Size
	int size = (int)xFilter.size() / 2;

	CImg<uchar> filteredImg(gFiltered.width() - 2 * size, gFiltered.height() - 2 * size, 1, 1);

	angles = CImg<float>(gFiltered.width() - 2 * size, gFiltered.height() - 2 * size, 1, 1); //AngleMap

	for (int i = size; i < gFiltered.width() - size; i++)
	{
		for (int j = size; j < gFiltered.height() - size; j++)
		{
			double sumx = 0;
			double sumy = 0;

			for (int x = 0; x < xFilter.size(); x++)
				for (int y = 0; y < yFilter.size(); y++)
				{
					sumx += yFilter[x][y] * (double)(gFiltered(i + x - size, j + y - size)); //Sobel_X Filter Value
					sumy += xFilter[x][y] * (double)(gFiltered(i + x - size, j + y - size)); //Sobel_Y Filter Value
				}
			double sumxsq = sumx * sumx;
			double sumysq = sumy * sumy;

			double sq2 = sqrt(sumxsq + sumysq);

			if (sq2 > 255) //Unsigned Char Fix
				sq2 = 255;
			filteredImg(i - size, j - size) = sq2;

			if (sumx == 0) //Arctan Fix
				angles(i - size, j - size) = 90;
			else
				angles(i - size, j - size) = atan(sumy / sumx)*(180.0 / M_PI);
		}
	}
	
	return filteredImg;
}

第五步,函式 nonMaxSupp 執行非極大值抑制,在8鄰域內尋找畫素點區域性最大值,將非極大值點所對應的灰度值置為0,這樣可以剔除掉一大部分非邊緣的點。
在這裡插入圖片描述

CImg<uchar> canny::nonMaxSupp()
{
	CImg<uchar> nonMaxSupped(sFiltered.width() - 2, sFiltered.height() - 2, 1, 1);
	for (int i = 1; i < sFiltered.width() - 1; i++) {
		for (int j = 1; j < sFiltered.height() - 1; j++) {
			float Tangent = angles(i, j);
			//cout << Tangent << " ";
			nonMaxSupped(i - 1, j - 1) = sFiltered(i, j);
			//Horizontal Edge
			if (((-22.5 < Tangent) && (Tangent <= 22.5)) || ((157.5 < Tangent) && (Tangent <= -157.5)))
			{
				if ((sFiltered(i, j) < sFiltered(i+1, j )) || (sFiltered(i, j) < sFiltered(i-1, j )))
					nonMaxSupped(i - 1, j - 1) = 0;
			}
			//Vertical Edge
			if (((-112.5 < Tangent) && (Tangent <= -67.5)) || ((67.5 < Tangent) && (Tangent <= 112.5)))
			{
				if ((sFiltered(i, j) < sFiltered(i , j+1)) || (sFiltered(i, j) < sFiltered(i , j-1)))
					nonMaxSupped(i - 1, j - 1) = 0;
			}

			//-45 Degree Edge
			if (((-67.5 < Tangent) && (Tangent <= -22.5)) || ((112.5 < Tangent) && (Tangent <= 157.5)))
			{
				//if ((sFiltered(i, j) < sFiltered(i - 1, j + 1)) || (sFiltered(i, j) < sFiltered(i + 1, j - 1)))
				if ((sFiltered(i, j) < sFiltered(i + 1, j + 1)) || (sFiltered(i, j) < sFiltered(i - 1, j - 1)))
					nonMaxSupped(i - 1, j - 1) = 0;
			}

			//45 Degree Edge
			if (((-157.5 < Tangent) && (Tangent <= -112.5)) || ((22.5 < Tangent) && (Tangent <= 67.5)))
			{
				//if ((sFiltered(i, j) < sFiltered(i + 1, j + 1)) || (sFiltered(i, j) < sFiltered(i - 1, j - 1)))
				if ((sFiltered(i, j) < sFiltered(i - 1, j + 1)) || (sFiltered(i, j) < sFiltered(i + 1, j - 1)))
					nonMaxSupped(i - 1, j - 1) = 0;
			}
		}
	}
	return nonMaxSupped;
}

第六步,函式 threshold 執行滯後閾值化,由於噪聲的影響,經常會在本應該連續的邊緣出現斷裂的問題。滯後閾值化設定兩個閾值:一個為高閾值,一個為低閾值。如果任何畫素邊緣運算元的影響超過高閾值,將這些畫素標記為邊緣;響應超過低閾值(高低閾值之間)的畫素,如果與已經標記為邊緣的畫素8-鄰接,則將這些畫素也標記為邊緣。

CImg<uchar> canny::threshold(CImg<uchar> imgin, int low, int high)
{
	if (low > 255)
		low = 255;
	if (high > 255)
		high = 255;

	CImg<uchar> EdgeMat(imgin.width(), imgin.height(), 1, 1);

	for (int i = 0; i < imgin.width(); i++)
	{
		for (int j = 0; j < imgin.height(); j++)
		{
			EdgeMat(i, j) = imgin(i, j);
			if (EdgeMat(i, j) > high)
				EdgeMat(i, j) = 255;
			else if (EdgeMat(i, j) < low)
				EdgeMat(i, j) = 0;
			else
			{
				bool anyHigh = false;
				bool anyBetween = false;
				for (int x = i - 1; x < i + 2; x++)
				{
					for (int y = j - 1; y < j + 2; y++)
					{
						if (x <= 0 || y <= 0 || x >= EdgeMat.width() || y >= EdgeMat.height()) //Out of bounds
							continue;
						else
						{
							if (EdgeMat(x, y) > high)
							{
								EdgeMat(i, j) = 255;
								anyHigh = true;
								break;
							}
							else if (EdgeMat(x, y) <= high && EdgeMat(x, y) >= low)
								anyBetween = true;
						}
					}
					if (anyHigh)
						break;
				}
				if (!anyHigh && anyBetween)
					for (int x = i - 2; x < i + 3; x++)
					{
						for (int y = j - 1; y < j + 3; y++)
						{
							if (x < 0 
            
           

相關推薦

使用CImg實現canny邊緣檢測跟蹤

第一步,函式toGrayScale 將彩色影象轉換為灰度影象,根據RGB值和公式計算出灰度值即可,比較簡單。 CImg<uchar> canny::toGrayScale() { grayscaled = CImg<uchar>(img

OpenCV 實現canny邊緣檢測

近期,整理了一些之前做過的影象處理內容,算是複習下基礎吧; 涉及canny邊緣檢測的OpenCV實現; 影象邊緣資訊主要集中在高頻段,通常說影象銳化或檢測邊緣,實質就是高頻濾波。Canny是常用的邊緣檢測方法,其特點是試圖將獨立邊的候選畫素拼裝成輪廓。 canny邊緣檢

opencv讀影象C語言實現canny邊緣檢測

1 ////////第四步:非極大值抑制 2 //注意事項 權重的選取,也就是離得近權重大 3 ///////////////////////////////////////////////////////////////// 4 IplImage * N;//非極大值抑制結果 5

【OpenCV學習筆記 004】 影象的縮放、Canny邊緣檢測影象的二值化

一、影象的縮放 本篇將介紹使用OpenCV來縮放圖片。首先介紹幾個關鍵函式——cvResize和cvCreateImage 1.主要函式介紹 1.1 cvResize 函式功能:影象大小變換 函式原型: voidcvResize(   const CvArr* src,

【算法隨記】Canny邊緣檢測算法實現優化分析。

輸入 放置 位圖 code 計算 並且 比較 lan 開篇   以前的博文大部分都寫的非常詳細,有很多分析過程,不過寫起來確實很累人,一般一篇好的文章要整理個三四天,但是,時間越來越緊張,後續的一些算法可能就以隨記的方式,把實現過程的一些比較容易出錯和有價值的細節部分加以描

Canny邊緣檢測算法原理及其VC實現詳解(一)

常用 差分 實現圖 還需要 鏈接 傳感器 出了 關系 位置 轉自:http://blog.csdn.net/likezhaobin/article/details/6892176 圖象的邊緣是指圖象局部區域亮度變化顯著的部分,該區域的灰度剖面一般可以看作是一個階躍,既從

Shader特效——“Canny邊緣檢測”的實現 【GLSL】

演算法參考自: http://blog.sina.com.cn/s/blog_676b40ec0100z2pt.html http://blog.csdn.net/xiajun07061225/article/details/6926108 在寫這篇文章的時候,發現網上關於ca

Canny邊緣檢測演算法原理及C語言實現詳解

Canny運算元是John Canny在1986年提出的,那年老大爺才28歲,該文章發表在PAMI頂級期刊上的(1986. A computational approach to edge detection. IEEE Transactions on Pattern Analy

基於SobelCanny邊緣檢測

影象邊緣是由灰度突變產的,屬於一種影象的特徵,是目標探測、識別的重要手段之一。 檢測邊緣通常為求原影象的梯度或二階導數,有如下結論: (1)一階導數通常在影象中產生較粗的邊緣 (2)二階導數對精細細

Canny邊緣檢測演算法的實現

影象邊緣資訊主要集中在高頻段,通常說影象銳化或檢測邊緣,實質就是高頻濾波。我們知道微分運算是求訊號的變化率,具有加強高頻分量的作用。在空域運算中來說,對影象的銳化就是計算微分。由於數字影象的離散訊號,微分運算就變成計算差分或梯度。影象處理中有多種邊緣檢測(梯度)運算元,常用的

利用Canny邊緣檢測運算元進行邊緣檢測的原理及OpenCV程式碼實現

Canny運算元是John Canny在1986年發表的論文中首次提出的邊緣檢測運算元,該運算元檢測效能比較好,應用廣泛。 Canny運算元進行邊緣檢測的原理和步驟如下: ⑴消除噪聲。邊緣檢測的演算法主要是基於影象強度的一階和二階微分操作,但導數通常對噪聲很敏感,邊緣檢測

OpenCV使用Canny邊緣檢測器實現影象邊緣檢測

效果圖 原始碼 Canny邊緣檢測器是一種被廣泛使用的演算法,並被認為是邊緣檢測最優的演算法,該方法使用了比高斯差分演算法更復雜的技巧,如多向灰度梯度和滯後閾值化。 Canny

Canny邊緣檢測原理及C++實現

從11.21開始寫部落格,到今天一個多月了,寫了20多篇了,希望自己能堅持下去吧! 在這裡祝大家聖誕節快樂! Canny邊緣檢測演算法是澳大利亞科學家John F. Canny在1986年提出來的,不得不提一下的是當年John Canny本人才28歲!到今天已經30年過

邊緣檢測︱基於 HED網路TensorFlow OpenCV 實現圖片邊緣檢測

. 一、邊緣檢測 1、傳統邊緣檢測 Google 搜尋 opencv scan document,是可以找到好幾篇相關的教程的,這些教程裡面的技術手段,也都大同小異,關鍵步驟就是呼叫 OpenCV 裡面的兩個函式,cv2.Canny() 和 c

Canny邊緣檢測演算法(基於OpenCV的Java實現

目錄 Canny邊緣檢測演算法(基於OpenCV的Java實現) 緒論 Canny邊緣檢測演算法的發展歷史 Canny邊緣檢測演算法的處理流程 用高斯濾波器平滑影象 彩色RGB

canny邊緣檢測 demo

filename channels rfi 作用 namespace str named names amp #include <iostream> #include <string> #include <sstream>

六 OpenCV圖像處理4 Canny 邊緣檢測

alt left 最大的 max plt src 分享 body 邊界 1.Canny 邊緣檢測原理 步驟: ·1噪聲去除: 由於邊緣檢測很容易受到噪聲影響,所以第一步是使用 5x5 的高斯濾波器 去除噪聲 ·2計算圖像梯度:

OpenCV學習代碼記錄——canny邊緣檢測

scalar pos down 輸入 canny emp 函數 color 什麽 很久之前學習過一段時間的OpenCV,當時沒有做什麽筆記,但是代碼都還在,這裏把它貼出來做個記錄。 代碼放在碼雲上,地址在這裏https://gitee.com/solym/OpenCVTes

基於opencv下對視頻的灰度變換,高斯濾波,canny邊緣檢測處理,同窗體顯示並保存

rmi 其他 AS info ali 利用 測試結果 14. 中間 如題:使用opencv打開攝像頭或視頻文件,實時顯示原始視頻,將視頻每一幀依次做灰度轉換、高斯濾波、canny邊緣檢測處理(原始視頻和這3個中間步驟處理結果分別在一個窗口顯示),最後將邊緣檢測結果保存為一個

OpenCV學習筆記(11)——Canny邊緣檢測

bubuko nal die pan 一個 變化 我們 大小 ima 了解Canny邊緣檢測的概念 1.原理   Canny邊緣檢測是一種非常流行的邊緣檢測算法,是 John F。Canny在1986年提出的。它是一個有很多步構成的算法 1)噪聲去除   使用5*5的高