第3章 影像基本操作
3.1 影像顏色空間
3.1.1 顏色模型與轉換
- RGB顏色模型
- YUV顏色模型
- HSV顏色模型
- Lab顏色模型
- GRAY顏色模型
6.不同顏色模型間的互想轉換
cvtColor()函式用于將影像從一個顏色模型轉換為另一個顏色模型
void cv::cvtColor(InputArray src,
OutputArray dst,
int code,
int dstCn = 0
)
- src:待轉換顏色模型原始影像
- dst:轉換顏色模型后的目標影像
- code:顏色空間轉換的標志,如由RGB空間到HSV空間
- dstCn:目標影像中的通道數
代碼清單3-2 影像顏色模型相互轉換
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("E:/BaiduNetdiskDownload/data/lena.png");
if (img.empty())
{
cout << "請確認影像檔案名稱師范正確" << endl;
return -1;
}
Mat gray, HSV, YUV, Lab, img32;
img.convertTo(img32, CV_32F, 1.0 / 255); //將CV_8U型別轉換成CV_32F型別
//img32.convertTo(img,CV_8U,255); //將CV_32F型別轉換成CV_8U型別
cvtColor(img32, HSV, COLOR_BGR2HSV);
cvtColor(img32, YUV, COLOR_BGR2YUV);
cvtColor(img32, Lab, COLOR_BGR2Lab);
cvtColor(img32, gray, COLOR_BGR2GRAY);
imshow("原圖", img32);
imshow("HSV", HSV);
imshow("YUV", YUV);
imshow("Lab", Lab);
imshow("gray", gray);
waitKey(0);
return 0;
}
convertTo()
3.1.2 多通道分離與合并
split()
merge()
代碼清單3-6 實作影像分離與合并
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("lena.png");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat HSV;
cvtColor(img, HSV, COLOR_RGB2HSV);
Mat imgs0, imgs1, imgs2; //用于存放陣列型別的結果
Mat imgv0, imgv1, imgv2; //用于存放vector型別的結果
Mat result0, result1, result2; //多通道合并的結果
//輸入陣列引數的多通道分離與合并
Mat imgs[3];
split(img, imgs);
imgs0 = imgs[0];
imgs1 = imgs[1];
imgs2 = imgs[2];
imshow("RGB-B通道", imgs0); //顯示分離后B通道的像素值
imshow("RGB-G通道", imgs1); //顯示分離后G通道的像素值
imshow("RGB-R通道", imgs2); //顯示分離后R通道的像素值
imgs[2] = img; //將陣列中的影像通道數變成不統一
merge(imgs, 3, result0); //合并影像
//imshow("result0", result0); //imshow最多顯示4個通道,因此結果在Image Watch中查看
Mat zero = cv::Mat::zeros(img.rows, img.cols, CV_8UC1);
imgs[0] = zero;
imgs[2] = zero;
merge(imgs, 3, result1); //用于還原G通道的真實情況,合并結果為綠色
imshow("result1", result1); //顯示合并結果
//輸入vector引數的多通道分離與合并
vector<Mat> imgv;
split(HSV, imgv);
imgv0 = imgv.at(0);
imgv1 = imgv.at(1);
imgv2 = imgv.at(2);
imshow("HSV-H通道", imgv0); //顯示分離后H通道的像素值
imshow("HSV-S通道", imgv1); //顯示分離后S通道的像素值
imshow("HSV-V通道", imgv2); //顯示分離后V通道的像素值
imgv.push_back(HSV); //將vector中的影像通道數變成不統一
merge(imgv, result2); //合并影像
//imshow("result2", result2); /imshow最多顯示4個通道,因此結果在Image Watch中查看
waitKey(0);
return 0;
}
3.2 影像像素操作處理
3.2.1 影像像素統計
1.尋找影像像素最大值與最小值
minMaxLoc()
資料型別 Point
CU::Mat::reshape()
代碼清單3-9 尋找矩陣中的最值
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
system("color F0"); //更改輸出界面顏色
float a[12] = { 1, 2, 3, 4, 5, 10, 6, 7, 8, 9, 10, 0 };
Mat img = Mat(3, 4, CV_32FC1, a); //單通道矩陣
Mat imgs = Mat(2, 3, CV_32FC2, a); //多通道矩陣
double minVal, maxVal; //用于存放矩陣中的最大值和最小值
Point minIdx, maxIdx; 用于存放矩陣中的最大值和最小值在矩陣中的位置
/*尋找單通道矩陣中的最值*/
minMaxLoc(img, &minVal, &maxVal, &minIdx, &maxIdx);
cout << "img中最大值是:" << maxVal << " " << "在矩陣中的位置:" << maxIdx << endl;
cout << "img中最小值是:" << minVal << " " << "在矩陣中的位置:" << minIdx << endl;
/*尋找多通道矩陣中的最值*/
Mat imgs_re = imgs.reshape(1, 4); //將多通道矩陣變成單通道矩陣
minMaxLoc(imgs_re, &minVal, &maxVal, &minIdx, &maxIdx);
cout << "imgs中最大值是:" << maxVal << " " << "在矩陣中的位置:" << maxIdx << endl;
cout << "imgs中最小值是:" << minVal << " " << "在矩陣中的位置:" << minIdx << endl;
return 0;
}
2.計算影像的平均值和標準差
meanStdDev() 函式用于同時計算平均值和標準差
mean() 計算平均值
cv::Scalar 型別
代碼清單3-12 計算矩陣平均值和標準差
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
system("color F0"); //更改輸出界面顏色
float a[12] = { 1, 2, 3, 4, 5, 10, 6, 7, 8, 9, 10, 0 };
Mat img = Mat(3, 4, CV_32FC1, a); //單通道矩陣
Mat imgs = Mat(2, 3, CV_32FC2, a); //多通道矩陣
cout << "/* 用meanStdDev同時求取影像的均值和標準差 */" << endl;
Scalar myMean;
myMean = mean(imgs);
cout << "imgs均值=" << myMean << endl;
cout << "imgs第一個通道的均值=" << myMean[0] << " "
<< "imgs第二個通道的均值=" << myMean[1] << endl << endl;
cout << "/* 用meanStdDev同時求取影像的均值和標準差 */" << endl;
Mat myMeanMat, myStddevMat;
meanStdDev(img, myMeanMat, myStddevMat);
cout << "img均值=" << myMeanMat << " " << endl;
cout << "img標準差=" << myStddevMat << endl << endl;
meanStdDev(imgs, myMeanMat, myStddevMat);
cout << "imgs均值=" << myMeanMat << " " << endl << endl;
cout << "imgs標準差=" << myStddevMat << endl;
return 0;
}
2.2.2 兩影像間的像素操作
1.兩幅影像的比較運算
max()
min()
代碼清單3-14 兩個矩陣或影像進行比較運算
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
float a[12] = { 1, 2, 3.3, 4, 5, 9, 5, 7, 8.2, 9, 10, 2 };
float b[12] = { 1, 2.2, 3, 1, 3, 10, 6, 7, 8, 9.3, 10, 1 };
Mat imga = Mat(3, 4, CV_32FC1, a);
Mat imgb = Mat(3, 4, CV_32FC1, b);
Mat imgas = Mat(2, 3, CV_32FC2, a);
Mat imgbs = Mat(2, 3, CV_32FC2, b);
//對兩個單通道矩陣進行比較運算
Mat myMax, myMin;
max(imga, imgb, myMax);
min(imga, imgb, myMin);
//對兩個多通道矩陣進行比較運算
Mat myMaxs, myMins;
max(imgas, imgbs, myMaxs);
min(imgas, imgbs, myMins);
//對兩張彩色影像進行比較運算
Mat img0 = imread("len.png");
Mat img1 = imread("noobcv.jpg");
if (img0.empty() || img1.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat comMin, comMax;
max(img0, img1, comMax);
min(img0, img1, comMin);
imshow("comMin", comMin);
imshow("comMax", comMax);
//與掩模進行比較運算
Mat src1 = Mat::zeros(Size(512, 512), CV_8UC3);
Rect rect(100, 100, 300, 300);
src1(rect) = Scalar(255, 255, 255); //生成一個低通300*300的掩模
Mat comsrc1, comsrc2;
min(img0, src1, comsrc1);
imshow("comsrc1", comsrc1);
Mat src2 = Mat(512, 512, CV_8UC3, Scalar(0, 0, 255)); //生成一個顯示紅色通道的低通掩模
min(img0, src2, comsrc2);
imshow("comsrc2", comsrc2);
//對兩張灰度影像進行比較運算
Mat img0G, img1G, comMinG, comMaxG;
cvtColor(img0, img0G, COLOR_BGR2GRAY);
cvtColor(img1, img1G, COLOR_BGR2GRAY);
max(img0G, img1G, comMaxG);
min(img0G, img1G, comMinG);
imshow("comMinG", comMinG);
imshow("comMaxG", comMaxG);
waitKey(0);
return 0;
}
2.兩幅影像的邏輯運算
biwise_and()
biwise_or()
biwise_xor()
biwise_not()
代碼清單3-16 兩個黑白影像像素邏輯運算
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("lena.png");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
//創建兩個黑白影像
Mat img0 = Mat::zeros(200, 200, CV_8UC1);
Mat img1 = Mat::zeros(200, 200, CV_8UC1);
Rect rect0(50, 50, 100, 100);
img0(rect0) = Scalar(255);
Rect rect1(100, 100, 100, 100);
img1(rect1) = Scalar(255);
imshow("img0", img0);
imshow("img1", img1);
//進行邏輯運算
Mat myAnd, myOr, myXor, myNot, imgNot;
bitwise_not(img0, myNot);
bitwise_and(img0, img1, myAnd);
bitwise_or(img0, img1, myOr);
bitwise_xor(img0, img1, myXor);
bitwise_not(img, imgNot);
imshow("myAnd", myAnd);
imshow("myOr", myOr);
imshow("myXor", myXor);
imshow("myNot", myNot);
imshow("img", img);
imshow("imgNot", imgNot);
waitKey(0);
return 0;
}
3.2.3 影像二值化
threshold()
adaptiveThreshold()
代碼清單3-19 影像二值化
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("lena.png");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
Mat img_B, img_B_V, gray_B, gray_B_V, gray_T, gray_T_V, gray_TRUNC;
//彩色影像二值化
threshold(img, img_B, 125, 255, THRESH_BINARY);
threshold(img, img_B_V, 125, 255, THRESH_BINARY_INV);
imshow("img_B", img_B);
imshow("img_B_V", img_B_V);
//灰度圖BINARY二值化
threshold(gray, gray_B, 125, 255, THRESH_BINARY);
threshold(gray, gray_B_V, 125, 255, THRESH_BINARY_INV);
imshow("gray_B", gray_B);
imshow("gray_B_V", gray_B_V);
//灰度影像TOZERO變換
threshold(gray, gray_T, 125, 255, THRESH_TOZERO);
threshold(gray, gray_T_V, 125, 255, THRESH_TOZERO_INV);
imshow("gray_T", gray_T);
imshow("gray_T_V", gray_T_V);
//灰度影像TRUNC變換
threshold(gray, gray_TRUNC, 125, 255, THRESH_TRUNC);
imshow("gray_TRUNC", gray_TRUNC);
//灰度影像大津法和三角形法二值化
Mat img_Thr = imread("threshold.png", IMREAD_GRAYSCALE);
Mat img_Thr_O, img_Thr_T;
threshold(img_Thr, img_Thr_O, 100, 255, THRESH_BINARY | THRESH_OTSU);
threshold(img_Thr, img_Thr_T, 125, 255, THRESH_BINARY | THRESH_TRIANGLE);
imshow("img_Thr", img_Thr);
imshow("img_Thr_O", img_Thr_O);
imshow("img_Thr_T", img_Thr_T);
//灰度影像自適應二值化
Mat adaptive_mean, adaptive_gauss;
adaptiveThreshold(img_Thr, adaptive_mean, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 55, 0);
adaptiveThreshold(img_Thr, adaptive_gauss, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 55, 0);
imshow("adaptive_mean", adaptive_mean);
imshow("adaptive_gauss", adaptive_gauss);
waitKey(0);
return 0;
}
3.2.4 LUT
LUT()
代碼清單3-21 對影像進行查找表映射
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
//LUT查找表第一層
uchar lutFirst[256];
for (int i = 0; i<256; i++)
{
if (i <= 100)
lutFirst[i] = 0;
if (i > 100 && i <= 200)
lutFirst[i] = 100;
if (i > 200)
lutFirst[i] = 255;
}
Mat lutOne(1, 256, CV_8UC1, lutFirst);
//LUT查找表第二層
uchar lutSecond[256];
for (int i = 0; i<256; i++)
{
if (i <= 100)
lutSecond[i] = 0;
if (i > 100 && i <= 150)
lutSecond[i] = 100;
if (i > 150 && i <= 200)
lutSecond[i] = 150;
if (i > 200)
lutSecond[i] = 255;
}
Mat lutTwo(1, 256, CV_8UC1, lutSecond);
//LUT查找表第三層
uchar lutThird[256];
for (int i = 0; i<256; i++)
{
if (i <= 100)
lutThird[i] = 100;
if (i > 100 && i <= 200)
lutThird[i] = 200;
if (i > 200)
lutThird[i] = 255;
}
Mat lutThree(1, 256, CV_8UC1, lutThird);
//擁有三通道的LUT查找表矩陣
vector<Mat> mergeMats;
mergeMats.push_back(lutOne);
mergeMats.push_back(lutTwo);
mergeMats.push_back(lutThree);
Mat LutTree;
merge(mergeMats, LutTree);
//計算影像的查找表
Mat img = imread("lena.png");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat gray, out0, out1, out2;
cvtColor(img, gray, COLOR_BGR2GRAY);
LUT(gray, lutOne, out0);
LUT(img, lutOne, out1);
LUT(img, LutTree, out2);
imshow("out0", out0);
imshow("out1", out1);
imshow("out2", out2);
waitKey(0);
return 0;
}
3.3 影像變換
3.3.1 影像連接
vconcat()
hconcat()
代碼清單3-26 影像拼接
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
//矩陣陣列的橫豎連接
Mat matArray[] = { Mat(1, 2, CV_32FC1, cv::Scalar(1)),
Mat(1, 2, CV_32FC1, cv::Scalar(2)) };
Mat vout, hout;
vconcat(matArray, 2, vout);
cout << "影像陣列豎向連接:" << endl << vout << endl;
hconcat(matArray, 2, hout);
cout << "影像陣列橫向連接:" << endl << hout << endl;
//矩陣的橫豎拼接
Mat A = (cv::Mat_<float>(2, 2) << 1, 7, 2, 8);
Mat B = (cv::Mat_<float>(2, 2) << 4, 10, 5, 11);
Mat vC, hC;
vconcat(A, B, vC);
cout << "多個影像豎向連接:" << endl << vC << endl;
hconcat(A, B, hC);
cout << "多個影像橫向連接:" << endl << hC << endl;
//讀取4個子影像,00表示左上角、01表示右上角、10表示左下角、11表示右下角
Mat img00 = imread("lena00.png");
Mat img01 = imread("lena01.png");
Mat img10 = imread("lena10.png");
Mat img11 = imread("lena11.png");
if (img00.empty() || img01.empty() || img10.empty() || img11.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
//顯示4個子影像
imshow("img00", img00);
imshow("img01", img01);
imshow("img10", img10);
imshow("img11", img11);
//影像連接
Mat img, img0, img1;
//影像橫向連接
hconcat(img00, img01, img0);
hconcat(img10, img11, img1);
//橫向連接結果再進行豎向連接
vconcat(img0, img1, img);
//顯示連接影像的結果
imshow("img0", img0);
imshow("img1", img1);
imshow("img", img);
waitKey(0);
return 0;
}
3.3.2 影像尺寸變換
resize()
代碼清單3-28 影像縮放
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat gray = imread("lena.png", IMREAD_GRAYSCALE);
if (gray.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat smallImg, bigImg0, bigImg1, bigImg2;
resize(gray, smallImg, Size(15, 15), 0, 0, INTER_AREA); //先將影像縮小
resize(smallImg, bigImg0, Size(30, 30), 0, 0, INTER_NEAREST); //最近鄰差值
resize(smallImg, bigImg1, Size(30, 30), 0, 0, INTER_LINEAR); //雙線性差值
resize(smallImg, bigImg2, Size(30, 30), 0, 0, INTER_CUBIC); //雙三次差值
namedWindow("smallImg", WINDOW_NORMAL); //影像尺寸太小,一定要設定可以調節視窗大小標志
imshow("smallImg", smallImg);
namedWindow("bigImg0", WINDOW_NORMAL);
imshow("bigImg0", bigImg0);
namedWindow("bigImg1", WINDOW_NORMAL);
imshow("bigImg1", bigImg1);
namedWindow("bigImg2", WINDOW_NORMAL);
imshow("bigImg2", bigImg2);
waitKey(0);
return 0;
}
3.3.3 影像翻轉變換
flip()
代碼清單3-29 影像翻轉
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("lena.png");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat img_x, img_y, img_xy;
flip(img, img_x, 0); //沿x軸對稱
flip(img, img_y, 1); //沿y軸對稱
flip(img, img_xy, -1); //先x軸對稱,再y軸對稱
imshow("img", img);
imshow("img_x", img_x);
imshow("img_y", img_y);
imshow("img_xy", img_xy);
waitKey(0);
return 0;
}
3.3.4 影像放射變換
getRotationMatrix2D()
warpAffine()
getAffineTransform()
代碼清單3-34 影像旋轉與放射變換
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("lena.png");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat rotation0, rotation1, img_warp0, img_warp1;
double angle = 30; //設定影像旋轉的角度
Size dst_size(img.rows, img.cols); //設定輸出影像的尺寸
Point2f center(img.rows / 2.0, img.cols / 2.0); //設定影像的旋轉中心
rotation0 = getRotationMatrix2D(center, angle, 1); //計算放射變換矩陣
warpAffine(img, img_warp0, rotation0, dst_size); //進行仿射變換
imshow("img_warp0", img_warp0);
//根據定義的三個點進行仿射變換
Point2f src_points[3];
Point2f dst_points[3];
src_points[0] = Point2f(0, 0); //原始影像中的三個點
src_points[1] = Point2f(0, (float)(img.cols - 1));
src_points[2] = Point2f((float)(img.rows - 1), (float)(img.cols - 1));
dst_points[0] = Point2f((float)(img.rows)*0.11, (float)(img.cols)*0.20); //放射變換后影像中的三個點
dst_points[1] = Point2f((float)(img.rows)*0.15, (float)(img.cols)*0.70);
dst_points[2] = Point2f((float)(img.rows)*0.81, (float)(img.cols)*0.85);
rotation1 = getAffineTransform(src_points, dst_points); //根據對應點求取仿射變換矩陣
warpAffine(img, img_warp1, rotation1, dst_size); //進行仿射變換
imshow("img_warp1", img_warp1);
waitKey(0);
return 0;
}
3.3.5 影像透視變換
getPerspectiveTransform()
warpPerspective()
代碼清單3-37 二維碼影像透視變換
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img = imread("noobcvqr.png");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Point2f src_points[4];
Point2f dst_points[4];
//通過Image Watch查看的二維碼四個角點坐標
src_points[0] = Point2f(94.0, 374.0);
src_points[1] = Point2f(507.0, 380.0);
src_points[2] = Point2f(1.0, 623.0);
src_points[3] = Point2f(627.0, 627.0);
//期望透視變換后二維碼四個角點的坐標
dst_points[0] = Point2f(0.0, 0.0);
dst_points[1] = Point2f(627.0, 0.0);
dst_points[2] = Point2f(0.0, 627.0);
dst_points[3] = Point2f(627.0, 627.0);
Mat rotation, img_warp;
rotation = getPerspectiveTransform(src_points, dst_points); //計算透視變換矩陣
warpPerspective(img, img_warp, rotation, img.size()); //透視變換投影
imshow("img", img);
imshow("img_warp", img_warp);
waitKey(0);
return 0;
}
3.3.6 極坐標變換
warpPolar()
代碼清單3-39 影像極坐標變換
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("dial.png");
if (!img.data)
{
cout << "請檢查影像檔案名稱是否輸入正確" << endl;
return -1;
}
Mat img1, img2;
Point2f center = Point2f(img.cols / 2, img.rows/2); //極坐標在影像中的原點
//正極坐標變換
warpPolar(img, img1, Size(300,600), center, center.x, INTER_LINEAR + WARP_POLAR_LINEAR);
//逆極坐標變換
warpPolar(img1, img2, Size(img.rows,img.cols), center, center.x, INTER_LINEAR + WARP_POLAR_LINEAR + WARP_INVERSE_MAP);
imshow("原表盤圖", img);
imshow("表盤極坐標變換結果", img1);
imshow("逆變換結果", img2);
waitKey(0);
return 0;
}
3.4 在影像上繪制幾何圖形
3.4.1 繪制圓形
circle()
3.4.2 繪制直線
line()
3.4.3 繪制橢圓
ellipse()
ellipse2Poly()
3.4.4 繪制多邊形
rectangle()
fillPoly()
3.4.5 文字生成
putText()
代碼清單3-47 繪制基本幾何圖形
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img = Mat::zeros(Size(512, 512), CV_8UC3); //生成一個黑色影像用于繪制幾何圖形
//繪制圓形
circle(img, Point(50, 50), 25, Scalar(255, 255, 255), -1); //繪制一個實心圓
circle(img, Point(100, 50), 20, Scalar(255, 255, 255), 4); //繪制一個空心圓
//繪制直線
line(img, Point(100, 100), Point(200, 100), Scalar(255, 255, 255), 2, LINE_4,0); //繪制一條直線
//繪制橢圓
ellipse(img, Point(300, 255), Size(100, 70), 0, 0, 100, Scalar(255, 255, 255), -1); //繪制實心橢圓的一部分
ellipse(img, RotatedRect(Point2f(150, 100), Size2f(30, 20), 0), Scalar(0, 0, 255), 2); //繪制一個空心橢圓
vector<Point> points;
ellipse2Poly(Point(200, 400), Size(100, 70),0,0,360,2,points); //用一些點來近似一個橢圓
for (int i = 0; i < points.size()-1; i++) //用直線把這個橢圓畫出來
{
if (i==points.size()-1)
{
line(img, points[0], points[i], Scalar(255, 255, 255), 2); //橢圓中后于一個點與第一個點連線
break;
}
line(img, points[i], points[i+1], Scalar(255, 255, 255), 2); //當前點與后一個點連線
}
//繪制矩形
rectangle(img, Point(50, 400), Point(100, 450), Scalar(125, 125, 125), -1);
rectangle(img, Rect(400,450,60,50), Scalar(0, 125, 125), 2);
//繪制多邊形
Point pp[2][6];
pp[0][0] = Point(72, 200);
pp[0][1] = Point(142, 204);
pp[0][2] = Point(226, 263);
pp[0][3] = Point(172, 310);
pp[0][4] = Point(117, 319);
pp[0][5] = Point(15, 260);
pp[1][0] = Point(359, 339);
pp[1][1] = Point(447, 351);
pp[1][2] = Point(504, 349);
pp[1][3] = Point(484, 433);
pp[1][4] = Point(418, 449);
pp[1][5] = Point(354, 402);
Point pp2[5];
pp2[0] = Point(350, 83);
pp2[1] = Point(463, 90);
pp2[2] = Point(500, 171);
pp2[3] = Point(421, 194);
pp2[4] = Point(338, 141);
const Point* pts[3] = { pp[0],pp[1],pp2 }; //pts變數的生成
int npts[] = { 6,6,5 }; //頂點個數陣列的生成
fillPoly(img, pts, npts, 3, Scalar(125, 125, 125),8); //繪制3個多邊形
//生成文字
putText(img, "Learn OpenCV 4",Point(100, 400), 2, 1, Scalar(255, 255, 255));
imshow("", img);
waitKey(0);
return 0;
}
3.5 感興趣區域
Rect資料結構和Rang資料結構
copyTo()
代碼清單3-50 截圖、深淺拷貝驗證程式
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img = imread("lena.png");
Mat noobcv = imread("noobcv.jpg");
if (img.empty() || noobcv.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat ROI1, ROI2, ROI2_copy, mask, img2, img_copy, img_copy2;
resize(noobcv, mask, Size(200, 200));
img2 = img; //淺拷貝
//深拷貝的兩種方式
img.copyTo(img_copy2);
copyTo(img, img_copy, img);
//兩種在圖中截取ROI區域的方式
Rect rect(206, 206, 200, 200); //定義ROI區域
ROI1 = img(rect); //截圖
ROI2 = img(Range(300, 500), Range(300, 500)); //第二種截圖方式
img(Range(300, 500), Range(300, 500)).copyTo(ROI2_copy); //深拷貝
mask.copyTo(ROI1); //在影像中加入部分影像
imshow("加入noobcv后影像", img);
imshow("ROI對ROI2的影響", ROI2);
imshow("深拷貝的ROI2_copy", ROI2_copy);
circle(img, Point(300, 300), 20, Scalar(0, 0, 255), -1); //繪制一個圓形
imshow("淺拷貝的img2", img2);
imshow("深拷貝的img_copy", img_copy);
imshow("深拷貝的img_copy2", img_copy2);
imshow("畫圓對ROI1的影響", ROI1);
waitKey(0);
return 0;
}
3.6 影像“金字塔”
3.6.1 高斯“金字塔”
pyrDown()
3.6.2 高斯“金字塔”
pyrUp()
代碼清單3-53 構建 高斯“金字塔” 和 高斯“金字塔”
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img = imread("lena.png");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
vector<Mat> Gauss, Lap; //高斯金字塔和拉普拉斯金字塔
int level = 3; //高斯金字塔下采樣次數
Gauss.push_back(img); //將原圖作為高斯金字塔的第0層
//構建高斯金字塔
for (int i = 0; i < level; i++)
{
Mat gauss;
pyrDown(Gauss[i], gauss); //下采樣
Gauss.push_back(gauss);
}
//構建拉普拉斯金字塔
for (int i = Gauss.size() - 1; i > 0; i--)
{
Mat lap, upGauss;
if (i == Gauss.size() - 1) //如果是高斯金字塔中的最上面一層影像
{
Mat down;
pyrDown(Gauss[i], down); //上采樣
pyrUp(down, upGauss);
lap = Gauss[i] - upGauss;
Lap.push_back(lap);
}
pyrUp(Gauss[i], upGauss);
lap = Gauss[i - 1] - upGauss;
Lap.push_back(lap);
}
//查看兩個金字塔中的影像
for (int i = 0; i < Gauss.size(); i++)
{
string name = to_string(i);
imshow("G" + name, Gauss[i]);
imshow("L" + name, Lap[i]);
}
waitKey(0);
return 0;
}
3.7 視窗互動操作
3.7.1 影像視窗滑動條
createTrackbar()
代碼清單3-55 在影像中創建滑動條改變影像亮度
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
//為了能在被調函式中使用,所以設定成全域的
int value;
void callBack(int, void*); //滑動潭訓呼函式
Mat img1, img2;
int main()
{
img1 = imread("lena.png");
if (!img1.data)
{
cout << "請確認是否輸入正確的影像檔案" << endl;
return -1;
}
namedWindow("滑動條改變影像亮度");
imshow("滑動條改變影像亮度", img1);
value = 100; //滑動條創建時的初值
//創建滑動條
createTrackbar("亮度值百分比", "滑動條改變影像亮度", &value, 600, callBack, 0);
waitKey();
}
static void callBack(int, void*)
{
float a = value / 100.0;
img2 = img1 * a;
imshow("滑動條改變影像亮度", img2);
}
3.7.2 滑鼠相應
setMouseCallback()
MouseCallback型別
代碼清單3-58 繪制滑鼠移動軌跡
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
Mat img, imgPoint; //全域的影像
Point prePoint; //前一時刻滑鼠的坐標,用于繪制直線
void mouse(int event, int x, int y, int flags, void*);
int main()
{
img = imread("lena.png");
if (!img.data)
{
cout << "請確認輸入影像名稱是否正確! " << endl;
return -1;
}
img.copyTo(imgPoint);
imshow("影像視窗 1", img);
imshow("影像視窗 2", imgPoint);
setMouseCallback("影像視窗 1", mouse, 0); //滑鼠影響
waitKey(0);
return 0;
}
void mouse(int event, int x, int y, int flags, void*)
{
if (event == EVENT_RBUTTONDOWN) //單擊右鍵
{
cout << "點擊滑鼠左鍵才可以繪制軌跡" << endl;
}
if (event == EVENT_LBUTTONDOWN) //單擊左鍵,輸出坐標
{
prePoint = Point(x, y);
cout << "軌跡起始坐標" << prePoint << endl;
}
if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) //滑鼠按住左鍵移動第 3 章 影像基本操作
{
//通過改變影像像素顯示滑鼠移動軌跡
imgPoint.at<Vec3b>(y, x) = Vec3b(0, 0, 255);
imgPoint.at<Vec3b>(y, x - 1) = Vec3b(0, 0, 255);
imgPoint.at<Vec3b>(y, x + 1) = Vec3b(0, 0, 255);
imgPoint.at<Vec3b>(y + 1, x) = Vec3b(0, 0, 255);
imgPoint.at<Vec3b>(y + 1, x) = Vec3b(0, 0, 255);
imshow("影像視窗 2", imgPoint);
//通過繪制直線顯示滑鼠移動軌跡
Point pt(x, y);
line(img, prePoint, pt, Scalar(0, 0, 255), 2, 5, 0);
prePoint = pt;
imshow("影像視窗 1", img);
}
}
第四章 影像直方圖與模板匹配
4.1 影像直方圖的繪制
calcHist()
cvRound()
代碼清單4-2 繪制影像直方圖
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img = imread("apple.jpg");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
//設定提取直方圖的相關變數
Mat hist; //用于存放直方圖計算結果
const int channels[1] = { 0 }; //通道索引
float inRanges[2] = { 0,255 };
const float* ranges[1] = { inRanges }; //像素灰度值范圍
const int bins[1] = { 256 }; //直方圖的維度,其實就是像素灰度值的最大值
calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges); //計算影像直方圖
//準備繪制直方圖
int hist_w = 512;
int hist_h = 400;
int width = 2;
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
for (int i = 1; i <= hist.rows; i++)
{
rectangle(histImage, Point(width*(i - 1), hist_h - 1),
Point(width*i - 1, hist_h - cvRound(hist.at<float>(i - 1) / 15)),
Scalar(255, 255, 255), -1);
}
namedWindow("histImage", WINDOW_AUTOSIZE);
imshow("histImage", histImage);
imshow("gray", gray);
waitKey(0);
return 0;
}
4.2 直方圖操作
4.2.1 直方圖歸一化
normalize()
代碼清單4-4 直方圖歸一化操作
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
system("color F0"); //更改輸出界面顏色
vector<double> positiveData = { 2.0, 8.0, 10.0 };
vector<double> normalized_L1, normalized_L2, normalized_Inf, normalized_L2SQR;
//測驗不同歸一化方法
normalize(positiveData, normalized_L1, 1.0, 0.0, NORM_L1); //絕對值求和歸一化
cout << "normalized_L1=[" << normalized_L1[0] << ", "
<< normalized_L1[1] << ", " << normalized_L1[2] << "]" << endl;
normalize(positiveData, normalized_L2, 1.0, 0.0, NORM_L2); //模長歸一化
cout << "normalized_L2=[" << normalized_L2[0] << ", "
<< normalized_L2[1] << ", " << normalized_L2[2] << "]" << endl;
normalize(positiveData, normalized_Inf, 1.0, 0.0, NORM_INF); //最大值歸一化
cout << "normalized_Inf=[" << normalized_Inf[0] << ", "
<< normalized_Inf[1] << ", " << normalized_Inf[2] << "]" << endl;
normalize(positiveData, normalized_L2SQR, 1.0, 0.0, NORM_MINMAX); //偏移歸一化
cout << "normalized_MINMAX=[" << normalized_L2SQR[0] << ", "
<< normalized_L2SQR[1] << ", " << normalized_L2SQR[2] << "]" << endl;
//將影像直方圖歸一化
Mat img = imread("apple.jpg");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat gray, hist;
cvtColor(img, gray, COLOR_BGR2GRAY);
const int channels[1] = { 0 };
float inRanges[2] = { 0,255 };
const float* ranges[1] = { inRanges };
const int bins[1] = { 256 };
calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);
int hist_w = 512;
int hist_h = 400;
int width = 2;
Mat histImage_L1 = Mat::zeros(hist_h, hist_w, CV_8UC3);
Mat histImage_Inf = Mat::zeros(hist_h, hist_w, CV_8UC3);
Mat hist_L1, hist_Inf;
normalize(hist, hist_L1, 1, 0, NORM_L1, -1, Mat());
for (int i = 1; i <= hist_L1.rows; i++)
{
rectangle(histImage_L1, Point(width*(i - 1), hist_h - 1),
Point(width*i - 1, hist_h - cvRound(30 * hist_h*hist_L1.at<float>(i - 1)) - 1),
Scalar(255, 255, 255), -1);
}
normalize(hist, hist_Inf, 1, 0, NORM_INF, -1, Mat());
for (int i = 1; i <= hist_Inf.rows; i++)
{
rectangle(histImage_Inf, Point(width*(i - 1), hist_h - 1),
Point(width*i - 1, hist_h - cvRound(hist_h*hist_Inf.at<float>(i - 1)) - 1),
Scalar(255, 255, 255), -1);
}
imshow("histImage_L1", histImage_L1);
imshow("histImage_Inf", histImage_Inf);
waitKey(0);
return 0;
}
4.2.2 直方圖比較
compareHist()
代碼清單4-6 比較兩個直方圖的相似性
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void drawHist(Mat &hist, int type, string name) //歸一化并繪制直方圖函式
{
int hist_w = 512;
int hist_h = 400;
int width = 2;
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
normalize(hist, hist, 1, 0, type, -1, Mat());
for (int i = 1; i <= hist.rows; i++)
{
rectangle(histImage, Point(width*(i - 1), hist_h - 1),
Point(width*i - 1, hist_h - cvRound(hist_h*hist.at<float>(i - 1)) - 1),
Scalar(255, 255, 255), -1);
}
imshow(name, histImage);
}
//主函式
int main()
{
system("color F0"); //更改輸出界面顏色
Mat img = imread("apple.jpg");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat gray, hist, gray2, hist2, gray3, hist3;
cvtColor(img, gray, COLOR_BGR2GRAY);
resize(gray, gray2, Size(), 0.5, 0.5);
gray3 = imread("lena.png", IMREAD_GRAYSCALE);
const int channels[1] = { 0 };
float inRanges[2] = { 0,255 };
const float* ranges[1] = { inRanges };
const int bins[1] = { 256 };
calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);
calcHist(&gray2, 1, channels, Mat(), hist2, 1, bins, ranges);
calcHist(&gray3, 1, channels, Mat(), hist3, 1, bins, ranges);
drawHist(hist, NORM_INF, "hist");
drawHist(hist2, NORM_INF, "hist2");
drawHist(hist3, NORM_INF, "hist3");
//原圖直方圖與原圖直方圖的相關系數
double hist_hist = compareHist(hist, hist, HISTCMP_CORREL);
cout << "apple_apple=" << hist_hist << endl;
//原圖直方圖與縮小原圖直方圖的相關系數
double hist_hist2 = compareHist(hist, hist2, HISTCMP_CORREL);
cout << "apple_apple256=" << hist_hist2 << endl;
//兩張不同影像直方圖相關系數
double hist_hist3 = compareHist(hist, hist3, HISTCMP_CORREL);
cout << "apple_lena=" << hist_hist3 << endl;
waitKey(0);
return 0;
}
4.3 直方圖應用
4.3.1 直方圖均衡化
equalizeHist()
代碼清單4-8 直方圖均衡化實作
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void drawHist(Mat &hist, int type, string name) //歸一化并繪制直方圖函式
{
int hist_w = 512;
int hist_h = 400;
int width = 2;
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
normalize(hist, hist, 1, 0, type, -1, Mat());
for (int i = 1; i <= hist.rows; i++)
{
rectangle(histImage, Point(width*(i - 1), hist_h - 1),
Point(width*i - 1, hist_h - cvRound(hist_h*hist.at<float>(i - 1)) - 1),
Scalar(255, 255, 255), -1);
}
imshow(name, histImage);
}
//主函式
int main()
{
Mat img = imread("gearwheel.jpg");
if (img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat gray, hist, hist2;
cvtColor(img, gray, COLOR_BGR2GRAY);
Mat equalImg;
equalizeHist(gray, equalImg); //將影像直方圖均衡化
const int channels[1] = { 0 };
float inRanges[2] = { 0,255 };
const float* ranges[1] = { inRanges };
const int bins[1] = { 256 };
calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);
calcHist(&equalImg, 1, channels, Mat(), hist2, 1, bins, ranges);
drawHist(hist, NORM_INF, "hist");
drawHist(hist2, NORM_INF, "hist2");
imshow("原圖", gray);
imshow("均衡化后的影像", equalImg);
waitKey(0);
return 0;
}
4.3.2 直方圖匹配
代碼清單4-9 影像直方圖匹配
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void drawHist(Mat &hist, int type, string name) //歸一化并繪制直方圖函式
{
int hist_w = 512;
int hist_h = 400;
int width = 2;
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
normalize(hist, hist, 1, 0, type, -1, Mat());
for (int i = 1; i <= hist.rows; i++)
{
rectangle(histImage, Point(width*(i - 1), hist_h - 1),
Point(width*i - 1, hist_h - cvRound(20 * hist_h*hist.at<float>(i - 1)) - 1),
Scalar(255, 255, 255), -1);
}
imshow(name, histImage);
}
//主函式
int main()
{
Mat img1 = imread("histMatch.png");
Mat img2 = imread("equalLena.png");
if (img1.empty() || img2.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat hist1, hist2;
//計算兩張影像直方圖
const int channels[1] = { 0 };
float inRanges[2] = { 0,255 };
const float* ranges[1] = { inRanges };
const int bins[1] = { 256 };
calcHist(&img1, 1, channels, Mat(), hist1, 1, bins, ranges);
calcHist(&img2, 1, channels, Mat(), hist2, 1, bins, ranges);
//歸一化兩張影像的直方圖
drawHist(hist1, NORM_L1, "hist1");
drawHist(hist2, NORM_L1, "hist2");
//計算兩張影像直方圖的累積概率
float hist1_cdf[256] = { hist1.at<float>(0) };
float hist2_cdf[256] = { hist2.at<float>(0) };
for (int i = 1; i < 256; i++)
{
hist1_cdf[i] = hist1_cdf[i - 1] + hist1.at<float>(i);
hist2_cdf[i] = hist2_cdf[i - 1] + hist2.at<float>(i);
}
//構建累積概率誤差矩陣
float diff_cdf[256][256];
for (int i = 0; i < 256; i++)
{
for (int j = 0; j < 256; j++)
{
diff_cdf[i][j] = fabs(hist1_cdf[i] - hist2_cdf[j]);
}
}
//生成LUT映射表
Mat lut(1, 256, CV_8U);
for (int i = 0; i < 256; i++)
{
// 查找源灰度級為i的映射灰度
// 和i的累積概率差值最小的規定化灰度
float min = diff_cdf[i][0];
int index = 0;
//尋找累積概率誤差矩陣中每一行中的最小值
for (int j = 1; j < 256; j++)
{
if (min > diff_cdf[i][j])
{
min = diff_cdf[i][j];
index = j;
}
}
lut.at<uchar>(i) = (uchar)index;
}
Mat result, hist3;
LUT(img1, lut, result);
imshow("待匹配影像", img1);
imshow("匹配的模板影像", img2);
imshow("直方圖匹配結果", result);
calcHist(&result, 1, channels, Mat(), hist3, 1, bins, ranges);
drawHist(hist3, NORM_L1, "hist3"); //繪制匹配后的影像直方圖
waitKey(0);
return 0;
}
4.3.3 直方圖反向投影
calcBackProject()
代碼清單4-11 影像直方圖反向攝影
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void drawHist(Mat &hist, int type, string name) //歸一化并繪制直方圖函式
{
int hist_w = 512;
int hist_h = 400;
int width = 2;
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
normalize(hist, hist, 255, 0, type, -1, Mat());
namedWindow(name, WINDOW_NORMAL);
imshow(name, hist);
}
//主函式
int main()
{
Mat img = imread("apple.jpg");
Mat sub_img = imread("sub_apple.jpg");
Mat img_HSV, sub_HSV, hist, hist2;
if (img.empty() || sub_img.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
imshow("img", img);
imshow("sub_img", sub_img);
//轉成HSV空間,提取S、V兩個通道
cvtColor(img, img_HSV, COLOR_BGR2HSV);
cvtColor(sub_img, sub_HSV, COLOR_BGR2HSV);
int h_bins = 32; int s_bins = 32;
int histSize[] = { h_bins, s_bins };
//H通道值的范圍由0到179
float h_ranges[] = { 0, 180 };
//S通道值的范圍由0到255
float s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges }; //每個通道的范圍
int channels[] = { 0, 1 }; //統計的通道索引
//繪制H-S二維直方圖
calcHist(&sub_HSV, 1, channels, Mat(), hist, 2, histSize, ranges, true, false);
drawHist(hist, NORM_INF, "hist"); //直方圖歸一化并繪制直方圖
Mat backproj;
calcBackProject(&img_HSV, 1, channels, hist, backproj, ranges, 1.0); //直方圖反向投影
imshow("反向投影后結果", backproj);
waitKey(0);
return 0;
}
4.4 影像的模板匹配
matchTemplate()
代碼清單4-13 影像的模板匹配
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img = imread("lena.png");
Mat temp = imread("lena_face.png");
if (img.empty() || temp.empty())
{
cout << "請確認影像檔案名稱是否正確" << endl;
return -1;
}
Mat result;
matchTemplate(img, temp, result, TM_CCOEFF_NORMED);//模板匹配
double maxVal, minVal;
Point minLoc, maxLoc;
//尋找匹配結果中的最大值和最小值以及坐標位置
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);
//繪制最佳匹配區域
rectangle(img, cv::Rect(maxLoc.x, maxLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2);
imshow("原圖", img);
imshow("模板影像", temp);
imshow("result", result);
waitKey(0);
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/291957.html
標籤:其他
