1. 程式人生 > >OpenCV_用積分影象統計畫素

OpenCV_用積分影象統計畫素

在累加多個影象區域的畫素時,積分影象顯得非常有用。

取影象左上側的全部畫素計算累加和,並用這個累加和替換影象中的每一個畫素,用這種方式得到的影象稱為積分影象。計算積分影象時只需要對影象掃描一次,這是因為當前積分值等於上一畫素的積分值加上當前行的累計值。因此積分影象就是一個包含畫素累加和的新影象。

*自適應的閾值化

通過對影象應用閾值來建立二值影象是從影象中提取有意義元素的好方法。

現在我隨手拍張書本照片,如下:


當應用單一閾值時 ,結果是這樣的:


程式碼:

int main()
{
	cv::Mat image = cv::imread("book1.jpg");
	cv::cvtColor(image, image, CV_BGR2GRAY);
	cv::imshow("原圖",image);
	cv::Mat binaryFixed;
	cv::threshold(image, binaryFixed, 150, 255, cv::THRESH_BINARY);
	cv::imshow("單一閾值結果", binaryFixed);
	cvWaitKey();
}
不管選用什麼閾值,影象都會丟失一部分文字,還有部分文字會消失在陰影下,要解決這個問題,有一個辦法就是採用區域性閾值,即根據每個畫素的鄰域計算閾值,這種策略稱為自適應閾值化,包括將每個畫素的值與鄰域的平均值進行比較。如果某畫素的值與它的區域性平均值差別很大,就會被當作異常值在閾值化過程中剔除。
自適應閾值化需要計算每個畫素周圍的區域性平均值,可通過積分影象提高計算效率。

可以看出,自適應閾值比單一閾值的效果要好得多:


程式碼:

int main()
{
	cv::Mat image = cv::imread("book1.jpg");
	cv::cvtColor(image, image, CV_BGR2GRAY);
	cv::imshow("原圖", image);
	cv::Mat iimage;
	cv::Mat result;
	cv::integral(image, iimage, CV_32S);
	int blockSize = 21;//鄰域尺寸
	int threshold = 8;
	int halfSize = blockSize / 2;
	for (int j = halfSize; j < iimage.rows - halfSize - 1; j++)
	{
		uchar* data = image.ptr<uchar>(j);
		int* idata1 = iimage.ptr<int>(j - halfSize);
		int* idata2 = iimage.ptr<int>(j + halfSize + 1);
		for (int i = halfSize; i < iimage.cols - halfSize - 1; i++)
		{
			int sum = (idata2[i + halfSize + 1] - idata2[i - halfSize] - idata1[i + halfSize + 1] + idata1[i - halfSize]) / (blockSize*blockSize);
			if (data[i] < (sum - threshold))
				data[i] = 0;
			else
				data[i] = 255;
		}
	}
	cv::imshow("自適應閾值結果", image);
	cvWaitKey();
}

在OpenCV中也給出了自適應閾值化的函式cv::adaptiveThreshold()

除了在閾值化中使用區域性平均值,還可以使用高斯(Gaussian)加權累計值(ADAPTIVE_THRESH_GAUSSIAN_C標誌),這種實現方式比呼叫cv::adaptiveThreshold快一點

*直方圖實現視覺追蹤

有一種特殊情況,即由0和1組成的二值影象生成積分影象,這時的積分累計值就是指定區域內值為1 的畫素總和。

此處已經研究了一天,程式碼仍是有誤,先mark,回頭看(問題出現在IngegralImage模板類的中符號過載函式的at函式)

IntegralImage模板類:

template<typename T, int N>class IntegralImage
{
	cv::Mat integralImage;
public:
	IntegralImage(cv::Mat image)
	{
		cv::integral(image, integralImage, CV_32S);
	}
	cv::Vec<T, N> operator() (int xo, int yo, int width, int height)
	{
		return (integralImage.at<cv::Vec<T, N>>(yo + height, xo + width)
			- integralImage.at<cv::Vec<T, N>>(yo + height, xo)
			- integralImage.at<cv::Vec<T, N>>(yo, xo + width)
			+ integralImage.at<cv::Vec<T, N>>(yo, xo));
	}
};
main函式:
int main()
{
	cv::Mat image = cv::imread("bike.jpg");
	cv::Mat imageROI = image(cv::Rect(350, 76, 100, 100));
	cv::rectangle(image, cv::Rect(350, 76, 100, 100),0);
	cv::imshow("手動識別圖", image);
	Histogram1D h;
	h.setNBins(16);
	cv::Mat refHistogram = h.getHistogram(imageROI);
	cv::Mat newImage = cv::imread("bike2.jpg");
	cv::imshow("待檢測影象", newImage);
	//首先建立16個平面的二值影象
	cv::Mat planes;
	convertToBinaryPlanes(newImage, planes, 16);
	//然後計算積分影象
	IntegralImage<float, 16> intHistogram(planes);
	double maxSimilarity = 0.0;
	int xbest = 0, ybest = 0;
	double distance = 0;
	cv::Mat histogram;
	for (int y = 0; y < newImage.rows; y++)
	{
		for (int x = 0; x < newImage.cols - 100; x++)
		{
			histogram = intHistogram(x, y, 100, 100);
			distance = cv::compareHist(refHistogram, histogram, CV_COMP_INTERSECT);
			if (distance > maxSimilarity)
			{
				xbest = x;
				ybest = y;
				maxSimilarity = distance;
			}
		}
	}
	cv::rectangle(newImage, cv::Rect(xbest, ybest, 100, 100), 0);
	cv::imshow("檢測影象", newImage);
	cvWaitKey();

}