Goal: Histogram processing for image enhancement
了解如何通過直方圖處理來增強圖像
應用查找表來增強圖像
學習直方圖均衡化、直方圖反向傳播和均值偏移
通過直方圖匹配練習圖像檢索。
作業環境
Window 11
Visual Studio 2019
OpenCV 4.5.5
CMake 3.24.3
CPU:11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz 2.42 GHz
RAM:16.0 GB
#include <iostream>
using namespace std;
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "histogram.h"
int main()
{
// Read input image
cv::Mat image= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/woonwoodog.png",0);//灰階影像
if (!image.data)
return 0;
// save grayscale image
cv::imwrite("C:/Users/User/Desktop/CV_testimage/P2/Histogram/Gray_woonwoodog.png", image);
// Display the image
cv::namedWindow("Image");
cv::imshow("Image",image);
// The histogram object
Histogram1D h;
// 計算質方圖
cv::Mat histo= h.getHistogram(image);//histo是一維陣列,有256個entries
// 讀取每個bin(直方圖中儲存為float值) Loop over each bin
for (int i=0; i<256; i++)
cout << "Value " << i << " = " << histo.at<float>(i) << endl;
// Display a histogram as an image
//顯示直方圖
cv::namedWindow("Histogram");
cv::imshow("Histogram",h.getHistogramImage(image));
// re-display the histagram with chosen threshold indicated
cv::Mat hi = h.getHistogramImage(image);
cv::line(hi, cv::Point(70, 0), cv::Point(70, 255), cv::Scalar(128));
cv::namedWindow("Histogram with threshold value");
cv::imshow("Histogram with threshold value", hi);
// creating a binary image by thresholding at the valley
cv::Mat thresholded; // 輸出 binary image
cv::threshold(image,thresholded,
70, // threshold value
255, // 超過threshold value的值->255
cv::THRESH_BINARY); // thresholding type
// Display the thresholded image
cv::namedWindow("Binary Image");
cv::imshow("Binary Image",thresholded);
thresholded = 255 - thresholded;
cv::imwrite("C:/Users/User/Desktop/CV_testimage/P2/Histogram/Binary_woonwoodog.bmp",thresholded);
// Show the new histogram
cv::namedWindow("Binary Image");
cv::imshow("Binary Image", h.getHistogramImage(thresholded));
// Equalize the image
//直方圖均衡化
cv::Mat eq= h.equalize(image);
// Show the result
cv::namedWindow("Equalized Image");
cv::imshow("Equalized Image",eq);
// Show the new histogram
cv::namedWindow("Equalized H");
cv::imshow("Equalized H",h.getHistogramImage(eq));
// Stretch the image, setting the 1% of pixels at black and 1% at white
//延展直方圖以提高影像對比度
cv::Mat str= h.stretch(image,0.01f);
// Show the result
cv::namedWindow("Stretched Image");
cv::imshow("Stretched Image",str);
// Show the new histogram
cv::namedWindow("Stretched H");
cv::imshow("Stretched H",h.getHistogramImage(str));
// 創建一個圖像反轉的table Create an image inversion table
cv::Mat lut(1,256,CV_8U); // 1x256 matrix
// Or:
// int dim(256);
// cv::Mat lut(1, // 1 dimension
// &dim, // 256 entries
// CV_8U); // uchar
//0變成255、1變成254...
for (int i=0; i<256; i++) {
lut.at<uchar>(i)= 255-i;
}
// Apply lookup and display negative image
cv::namedWindow("Negative image");
cv::imshow("Negative image",h.applyLookUp(image,lut));
cv::Mat neg = h.applyLookUp(image, lut);
// Show the new histogram
cv::namedWindow("Negative image");
cv::imshow("Negative image", h.getHistogramImage(neg));
cv::waitKey();
return 0;
}
直方圖:表示圖像中具有某個值的像素數量。灰階影像的直方圖有 256 個bin。如果把直方圖的所有bin進行累加,得到的結果是像素的總數。也可以把直方圖歸一化,即所有bin的累加和等於 1,每個bin的數值表示對應的像素數量佔總數的百分比。
在 OpenCV 中計算直方圖,可用 cv::calcHist 函數,一個通用的直方圖計算函數,可處理包含任何值類型和範圍的多通道圖像。histogram.cpp指定一個專門用於處理單通道灰階影像的class。
histogram.h
創建灰階影像的直方圖,設定初始值。
histogram.h
用左圖的方法計算灰階直方圖
histogram.h
用getImageOfHistogram可以得到直方圖影像。
以柱狀圖形式顯示。
histogram.h
查找表(lookup table)
histogram.h
Stretch the image
重新映射強度值
使 imin 的值變成強度值 0,imax 的值變成強度值 255。
兩者之間的 i 進行線性映射: 255.0*(i-imin)/(imax-imin);
histogram.cpp
延展1%
imgproc.hpp
(左上)equalizeHist的定義
histogram.h
(左下)直方圖均衡化
程式輸出結果
原圖
灰階圖、灰階直方圖
Histogram with threshold value
cv::Mat hi = h.getHistogramImage(image);
cv::line(hi, cv::Point(70, 0), cv::Point(70, 255), cv::Scalar(128));
Binary
直方圖只有0、255
輸出反轉
thresholded = 255 - thresholded;
原binary直方圖 VS 反轉質方圖
可以看到0、255的值相反
直方圖均衡化(左)vs灰階原圖(右)
黑白較不明顯
Stretched(左) vs 灰階原圖(右)延展1%,對比度稍強
黑白反轉
黑白反轉直方圖(左)vs 原灰階直方圖(右)
可以看到直方圖完全相反
自己測試
threshold value 改成10
自己測試
threshold value 改成200
直方圖0的部分明顯變多
分析&感想:了解如何應用直方圖。
在執行過程中有遇到imshow的視窗跑偏的問題,且無法移動(有時候會,大部分不會),但是我忘記截圖。解決方法是將cv2.namedWindow('image') 改成,cv2.namedWindow('image',cv2.WINDOW_NORMAL),cv2.WINDOW_NORMAL也可以改成0,就可以移動視窗了。
#include <iostream>
using namespace std;
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "histogram.h"
#include "contentFinder.h"
#include "colorhistogram.h"
int main()
{
// Read input image
cv::Mat image= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/t3.jpg",0);
if (!image.data)
return 0;
// define image ROI
cv::Mat imageROI;
imageROI= image(cv::Rect(216,100,24,30)); // 雲的區域 Cloud region
// Display reference patch
cv::namedWindow("Reference");
cv::imshow("Reference",imageROI);
// Find histogram of reference
Histogram1D h;
cv::Mat hist= h.getHistogram(imageROI);
cv::namedWindow("Reference Hist");
cv::imshow("Reference Hist",h.getHistogramImage(imageROI));
// Create the content finder
//創建內容搜尋器
ContentFinder finder;
// set histogram to be back-projected
//設定用來back-projected的直方圖
finder.setHistogram(hist);
finder.setThreshold(-1.0f);
// Get back-projection
cv::Mat result1;
result1= finder.find(image);
// Create negative image and display result
cv::Mat tmp;
result1.convertTo(tmp,CV_8U,-1.0,255.0);//反色處理(提高可讀性)
cv::namedWindow("Backprojection result");
cv::imshow("Backprojection result",tmp);
// Get binary back-projection
finder.setThreshold(0.12f);
result1= finder.find(image);
// Draw a rectangle around the reference area
cv::rectangle(image, cv::Rect(216, 100, 24, 30), cv::Scalar(0, 0, 0));
// Display image
cv::namedWindow("Image");
cv::imshow("Image",image);
// 得到最有可能為目標的結果 Display result
cv::namedWindow("Detection Result");
cv::imshow("Detection Result",result1);
// Load color image
//裝載彩色圖像
ColorHistogram hc;
cv::Mat color= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/t3.jpg");
// 提取ROI extract region of interest
imageROI= color(cv::Rect(0,0,100,45)); // 藍色天空區域 blue sky area
// Get 3D colour histogram (8 bins per channel)
//取得3D直方圖
hc.setSize(8); // 8x8x8
cv::Mat shist= hc.getHistogram(imageROI);
// set histogram to be back-projected
finder.setHistogram(shist);
finder.setThreshold(0.05f);
// Get back-projection of color histogram
result1= finder.find(color);
cv::namedWindow("Color Detection Result");
cv::imshow("Color Detection Result",result1);
// Second color image
cv::Mat color2= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/t2.jpg");
cv::namedWindow("Second Image");
cv::imshow("Second Image",color2);
// Get back-projection of color histogram
cv::Mat result2= finder.find(color2);
cv::namedWindow("Result color (2)");
cv::imshow("Result color (2)",result2);
// Get ab color histogram
hc.setSize(256); // 256x256
cv::Mat colorhist= hc.getabHistogram(imageROI);
// display 2D histogram
colorhist.convertTo(tmp,CV_8U,-1.0,255.0);
cv::namedWindow("ab histogram");
cv::imshow("ab histogram",tmp);
// set histogram to be back-projected
finder.setHistogram(colorhist);
finder.setThreshold(0.05f);
// Convert to Lab space
cv::Mat lab;
cv::cvtColor(color, lab, cv::COLOR_BGR2Lab);
// Get back-projection of ab histogram
int ch[2]={1,2};
result1= finder.find(lab,0,256.0f,ch);
cv::namedWindow("Result ab (1)");
cv::imshow("Result ab (1)",result1);
// Second colour image
cv::cvtColor(color2, lab, cv::COLOR_BGR2Lab);
// Get back-projection of ab histogram
result2= finder.find(lab,0,256.0,ch);
cv::namedWindow("Result ab (2)");
cv::imshow("Result ab (2)",result2);
// Draw a rectangle around the reference sky area
cv::rectangle(color,cv::Rect(0,0,100,45),cv::Scalar(0,0,0));
cv::namedWindow("Color Image");
cv::imshow("Color Image",color);
// Get Hue colour histogram
hc.setSize(180); // 180 bins
colorhist= hc.getHueHistogram(imageROI);
// set histogram to be back-projected
finder.setHistogram(colorhist);
// Convert to HSV space
cv::Mat hsv;
cv::cvtColor(color, hsv, cv::COLOR_BGR2HSV);
// Get back-projection of hue histogram
ch[0]=0;
result1= finder.find(hsv,0.0f,180.0f,ch);
cv::namedWindow("Result Hue (1)");
cv::imshow("Result Hue (1)",result1);
// Second color image
color2= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/t2.jpg");
// Convert to HSV space
cv::cvtColor(color2, hsv, cv::COLOR_BGR2HSV);
// Get back-projection of hue histogram
result2= finder.find(hsv,0.0f,180.0f,ch);
cv::namedWindow("Result Hue (2)");
cv::imshow("Result Hue (2)",result2);
cv::waitKey();
return 0;
}
如果圖像的某個區域含有特定的紋理或物體,這個區域的直方圖就可以看作一個函數,返回某個像素屬於這個特殊紋理或物體的機率。
希望在圖像中檢測出特定的內容。
cv::normalize(h,histogram,1.0);
back-projecting a histogram
此函數可以從歸一化後的直方圖中讀取機率值並把輸入圖像中的每個像素,替換成對應的機率值。
find 可以進行反向投影(使用圖像的三個通道)。
程式輸出結果
原圖
Reference(左) &Reference Hist(右)
Backprojection result
機率分佈圖,對圖像做反色處理提高可讀性,屬於該區域的機率從亮(低機率)到暗(高機率)
Detection Result
對上圖做Threshold
Image
Detection Result
Color Detection Result
Second Image
Result color (2)
ab histogram
Result ab (1)
Result ab (2)
Color Image
Result Hue (1)
Result Hue (2)
分析&感想:我覺得我用出來的效果沒有很好。
#include <iostream>
#include <vector>
using namespace std;
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/video/tracking.hpp>
#include "contentFinder.h"
#include "colorhistogram.h"
int main()
{
// Read reference image
cv::Mat image= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/t9.jpg");
if (!image.data)
return 0;
// initial window position
//window初始位置
cv::Rect rect(75, 40, 110, 100);
cv::rectangle(image, rect, cv::Scalar(0, 0, 255));
// Baboon's face ROI
//目標區域的ROI
cv::Mat imageROI = image(rect);
cv::namedWindow("Image 1");
cv::imshow("Image 1",image);
// Get the Hue histogram of the Baboon's face
//得到目標區域的直方圖
int minSat=65;
ColorHistogram hc;
cv::Mat colorhist= hc.getHueHistogram(imageROI,minSat);
//把得到的直方圖傳給 ContentFinder
ContentFinder finder;
finder.setHistogram(colorhist);
finder.setThreshold(0.2f);
// Convert to HSV space (just for display)
cv::Mat hsv;
cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);
// Split the image
vector<cv::Mat> v;
cv::split(hsv,v);
// Eliminate pixels with low saturation
cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);
cv::namedWindow("Saturation mask");
cv::imshow("Saturation mask",v[1]);
//--------------
// Second image
image= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/t5.jpg");
cv::namedWindow("Image 2");
cv::imshow("Image 2",image);
// 轉換成HSV色彩空間 Convert to HSV space
cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);
// Get back-projection of hue histogram
//得到色調直方圖的back-projection
int ch[1]={0};
finder.setThreshold(-1.0f); // no thresholding
cv::Mat result= finder.find(hsv,0.0f,180.0f,ch);
// Display back projection result
cv::namedWindow("Backprojection on second image");
cv::imshow("Backprojection on second image",result);
// initial window position
cv::rectangle(image, rect, cv::Scalar(0,0,255));
// search objet with mean shift
//用mean shift方法搜索物體
cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
10, // iterate max 10 times
1); // 或 重心移動距離小於一個像素or until the change in centroid position is less than 1px
cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;
// draw output window
cv::rectangle(image, rect, cv::Scalar(0,255,0));
// Display image
cv::namedWindow("Image 2 result");
cv::imshow("Image 2 result",image);
cv::waitKey();
return 0;
}
如果已知圖像中某個物體的大致位置,就可以用機率分佈圖找到物體的準確位置。窗口中機率最大的位置就是物體最可能出現的位置。因此,可從一個初始位置開始,在周圍反覆移動以提高局部匹配機率,也許就能找到物體的準確位置。
colorhistogram.h
獲得色調直方圖
finder.cpp
用mean shift方法搜索物體
程式輸出結果
原圖
Image 1
Saturation mask
Image 2
Backprojection on second image
Image 2 result
紅色框:初始位置
綠色框:新位置
分析&感想:如果圖像中需要辨識的物體,變成近距離或換個角度影像就不太能分辨出來
#include <iostream>
using namespace std;
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "imageComparator.h"
int main()
{
// Read reference image
cv::Mat image= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/t3.jpg");
if (!image.data)
return 0;
// Display image
cv::namedWindow("Query Image");
cv::imshow("Query Image",image);
ImageComparator c;
c.setReferenceImage(image);
// Read an image and compare it with reference
cv::Mat input= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/t2.jpg");
cout << "waves vs t2: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/4t2.jpg");
cout << "waves vs 4t2: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/4t3.jpg");
cout << "waves vs 4t3: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/4t4.jpg");
cout << "waves vs 4t4: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/4t5.jpg");
cout << "waves vs 4t5: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/4t6.jpg");
cout << "waves vs 4t6: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/4t7.jpg");
cout << "waves vs 4t7: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/4t8.jpg");
cout << "waves vs 4t8: " << c.compare(input) << endl;
cv::waitKey();
return 0;
}
比較圖片的相似度
imageComparator.h
class ImageComparator: 引用了一個基準圖像和一個輸入圖像。
因為要用顏色直方圖來進行比較,所以有class ColorHistogram
imageComparator.h
compare:將基準圖像和指定的輸入圖像進行對比。返回一個分數,表示兩幅圖像的相似程度。
程式輸出結果
原圖
t2
4t2
4t3
4t4
4t5
4t6
4t7
4t8
分數
分析&感想:和原圖最相似的是4t2,最不相似的是4t8。原本以為4t4會最不相似,因為其他都是風景,而4t4式室內排球比賽,沒有想到是4t8最不相似。
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "integral.h"
int main()
{
cv::Mat image= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/5t22.jpg",0);
if (!image.data)
return 0;
// rotate the image for easier display
cv::transpose(image, image);
cv::flip(image, image, 0);
// display original image
cv::namedWindow("Original Image");
cv::imshow("Original Image",image);
// using a fixed threshold
//使用固定的threshold
cv::Mat binaryFixed;
cv::Mat binaryAdaptive;
cv::threshold(image,binaryFixed,70,255,cv::THRESH_BINARY);
// using as adaptive threshold
int blockSize= 21; // 鄰域的大小 size of the neighborhood
int threshold=10; // 像素將與mean-threshold進行比較 pixel will be compared to (mean-threshold)
int64 time;
time= cv::getTickCount();
cv::adaptiveThreshold(image, // input image
binaryAdaptive, // output binary image
255, // 輸出最大值 max value for output
cv::ADAPTIVE_THRESH_MEAN_C, // 方法 adaptive method
cv::THRESH_BINARY, // threshold type
blockSize, // size of the block
threshold); // threshold used
time= cv::getTickCount()-time;
std::cout << "time (adaptiveThreshold)= " << time << std::endl;
// compute integral image
IntegralImage<int,1> integral(image);
// test integral result
std::cout << "sum=" << integral(18,45,30,50) << std::endl;
cv::Mat test(image,cv::Rect(18,45,30,50));
cv::Scalar t= cv::sum(test);
std::cout << "sum test=" << t[0] << std::endl;
cv::namedWindow("Fixed Threshold");
cv::imshow("Fixed Threshold",binaryFixed);
cv::namedWindow("Adaptive Threshold");
cv::imshow("Adaptive Threshold",binaryAdaptive);
cv::Mat binary= image.clone();
time= cv::getTickCount();
int nl= binary.rows; // number of lines
int nc= binary.cols; // total number of elements per line
// compute integral image
//計算積分圖像
cv::Mat iimage;
cv::integral(image,iimage,CV_32S);
// 逐行 for each row
int halfSize= blockSize/2;
for (int j = halfSize; j<nl - halfSize - 1; j++) {
// 得到第j行的地址 get the address of row j
uchar* data = binary.ptr<uchar>(j);
int* idata1 = iimage.ptr<int>(j - halfSize);
int* idata2 = iimage.ptr<int>(j + halfSize + 1);
// 一個線條的每個像素 for pixel of a line
for (int i = halfSize; i<nc - halfSize - 1; i++) {
// 計算累加值 compute sum
int sum = (idata2[i + halfSize + 1] - idata2[i - halfSize] -
idata1[i + halfSize + 1] + idata1[i - halfSize]) / (blockSize*blockSize);
// apply adaptive threshold
if (data[i]<(sum - threshold))
data[i] = 0;
else
data[i] = 255;
}
}
// add white border
for (int j = 0; j<halfSize; j++) {
uchar* data = binary.ptr<uchar>(j);
for (int i = 0; i<binary.cols; i++) {
data[i] = 255;
}
}
for (int j = binary.rows - halfSize-1; j<binary.rows; j++) {
uchar* data = binary.ptr<uchar>(j);
for (int i = 0; i<binary.cols; i++) {
data[i] = 255;
}
}
for (int j = halfSize; j<nl - halfSize - 1; j++) {
uchar* data = binary.ptr<uchar>(j);
for (int i = 0; i<halfSize; i++) {
data[i] = 255;
}
for (int i = binary.cols-halfSize-1; i<binary.cols; i++) {
data[i] = 255;
}
}
time= cv::getTickCount()-time;
std::cout << "time integral= " << time << std::endl;
cv::namedWindow("Adaptive Threshold (integral)");
cv::imshow("Adaptive Threshold (integral)",binary);
// adaptive threshold using image operators
time= cv::getTickCount();
cv::Mat filtered;
cv::Mat binaryFiltered;
// box filter計算矩形區域內像素的平均值 box filter compute avg of pixels over a rectangular region
cv::boxFilter(image,filtered,CV_8U,cv::Size(blockSize,blockSize));
// check if pixel greater than (mean + threshold)
binaryFiltered= image>= (filtered-threshold);
time= cv::getTickCount()-time;
std::cout << "time filtered= " << time << std::endl;
cv::namedWindow("Adaptive Threshold (filtered)");
cv::imshow("Adaptive Threshold (filtered)",binaryFiltered);
cv::waitKey();
}
積分圖像適合用來執行多次像素累計值的統計。透過適應性設置threshold強化區域色塊及清除不必要的資訊。
這個函數得到的結果與使用積分圖像的結果完全相同。
可以用 OpenCV 的圖像運算符來編寫自適應閾值化過程
程式輸出結果
原圖
Original Image
Fixed Threshold
Adaptive Threshold
Adaptive Threshold (integral)
Adaptive Threshold (filtered)
filter 處理時間最長point 處理時間最短
Original Image
Fixed Threshold
Adaptive Threshold
Adaptive Threshold (integral)
Adaptive Threshold (filtered)
filter 處理時間最長point 處理時間最短
分析&感想:都是filter 處理時間最長point 處理時間最短 。原圖大小不同,處理時間不同。自己測試的圖片有手寫部分,手寫部分因為較小看不清楚,雖然其他也沒有很清楚,其中適應性閾值比固定閾值效果好。
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <vector>
#include "histogram.h"
#include "integral.h"
int main()
{
// Open image
cv::Mat image= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/6t1.jpg",0);
// 定義圖像的ROI define image roi
int xo=45, yo=95;
int width=40, height=55;
cv::Mat roi(image,cv::Rect(xo,yo,width,height));
// compute sum
// returns a Scalar to work with multi-channel images
//計算累加值
//返回一個多通道圖像下的Scalar數值
cv::Scalar sum= cv::sum(roi);
std::cout << sum[0] << std::endl;
// compute integral image
//計算積分圖像
cv::Mat integralImage;
cv::integral(image,integralImage,CV_32S);
// get sum over an area using three additions/subtractions
//用三個加/減運算,得到一個區域的累加值
int sumInt= integralImage.at<int>(yo+height,xo+width)
-integralImage.at<int>(yo+height,xo)
-integralImage.at<int>(yo,xo+width)
+integralImage.at<int>(yo,xo);
std::cout << sumInt << std::endl;
// histogram of 16 bins
Histogram1D h;
h.setNBins(16);
// compute histogram over image roi
cv::Mat refHistogram= h.getHistogram(roi);
cv::namedWindow("Reference Histogram");
cv::imshow("Reference Histogram",h.getHistogramImage(roi,16));
std::cout << refHistogram << std::endl;
// first create 16-plane binary image
//首先創建16個plane的binary image
cv::Mat planes;
convertToBinaryPlanes(image,planes,16);
// 然後計算積分圖像 then compute integral image
IntegralImage<float,16> intHisto(planes);
// for testing compute a histogram of 16 bins with integral image
cv::Vec<float,16> histogram= intHisto(xo,yo,width,height);
std::cout<< histogram << std::endl;
cv::namedWindow("Reference Histogram (2)");
cv::Mat im= h.getImageOfHistogram(cv::Mat(histogram),16);
cv::imshow("Reference Histogram (2)",im);
// search in second image
cv::Mat secondImage= cv::imread("C:/Users/User/Desktop/CV_testimage/P2/6t2.jpg",0);
if (!secondImage.data)
return 0;
// first create 16-plane binary image
convertToBinaryPlanes(secondImage,planes,16);
// then compute integral image
IntegralImage<float,16> intHistogram(planes);
// compute histogram of 16 bins with integral image (testing)
histogram= intHistogram(135,114,width,height);
std::cout<< histogram << std::endl;
cv::namedWindow("Current Histogram");
cv::Mat im2= h.getImageOfHistogram(cv::Mat(histogram),16);
cv::imshow("Current Histogram",im2);
std::cout << "Distance= " << cv::compareHist(refHistogram,histogram, cv::HISTCMP_INTERSECT) << std::endl;
double maxSimilarity=0.0;
int xbest, ybest;
// loop over a horizontal strip around girl location in initial image
//遍歷原始圖像中女孩位置周圍的水平長條
for (int y=110; y<120; y++) {
for (int x=0; x<secondImage.cols-width; x++) {
// compute histogram of 16 bins using integral image
//用積分計算16 bins的直方圖
histogram= intHistogram(x,y,width,height);
// compute distance with reference histogram
//計算與基準的直方圖差距
double distance= cv::compareHist(refHistogram,histogram, cv::HISTCMP_INTERSECT);
// find position of most similar histogram
//找到最相似直方圖的位置
if (distance>maxSimilarity) {
xbest= x;
ybest= y;
maxSimilarity= distance;
}
std::cout << "Distance(" << x << "," << y << ")=" << distance << std::endl;
}
}
std::cout << "Best solution= (" << xbest << "," << ybest << ")=" << maxSimilarity << std::endl;
// draw a rectangle around target object
//在最準確的位置畫矩形
cv::rectangle(image,cv::Rect(xo,yo,width,height),0);
cv::namedWindow("Initial Image");
cv::imshow("Initial Image",image);
cv::namedWindow("New Image");
cv::imshow("New Image",secondImage);
// draw rectangle at best location
cv::rectangle(secondImage,cv::Rect(xbest,ybest,width,height),0);
// draw rectangle around search area
cv::rectangle(secondImage,cv::Rect(0,110,secondImage.cols,height+10),255);
cv::namedWindow("Object location");
cv::imshow("Object location",secondImage);
cv::waitKey();
}
用直方圖追蹤物體,可用直方圖表示物體外觀的全局特徵。搜尋一個所呈現直方圖與目標物體相似的圖像區域,演示如何在圖像中定位物體。
integral.h
從一個灰度圖像創建多圖層圖像
integral.h
以把積分圖像的計算過程放進模板
tracking.h
搜索時,遍歷可能出現目標的位置,並將它的直方圖與基准直方圖做比較,目的是找到與直方圖最相似的位置
程式輸出結果
原圖
Reference Histogram
Reference Histogram (2)
Current Histogram
Initial Image
New Image
Object location
分析&感想:白色矩形表示搜索的區域。可以找到最佳解,但不一定非常精準。
Reference
OpenCV 4 Computer Vision Application Programming Cookbook, by D. M. Escrivá, R. Laganiere, Fourth Edition, Packt Publishing, 2019
OpenCV计算机视觉编程攻略(第3版),2018年 5 月北京第 1 次印刷 ,R. Laganiere,翻譯 相银初
Resizing the output window of imshow function ,Jan.22.2016,sturkmen,
https://answers.opencv.org/question/84985/resizing-the-output-window-of-imshow-function/