/**********(6)图像预处理***********/
1. 像素点操作和卷积
(1)利用指针访问(快)
for(int y = 0;y < Row;y++)
{
for(int x = 0;x < Col;x++)
{
for(int c = 0;c < channel;c++)
{
srcimg.ptr<Vec3b>(y)[x][c] = 0; //srcimg.at<ushort>(i,j) = 0;
}
}
}
2. 动态访问(慢)
for(int y = 0;y < Row;y++)
{
for(int x = 0;x < Col;x++)
{
for(int c = 0;c < channel;c++)
{
srcImg.at<Vec3b>(x,y)[c] = 0;
}
}
}
/**********(7)滤波操作***********/
Mat kernel = (Mat_<double>(3,3)<<
-1,0,1,
-2,0,2,
-1,-0,1,
);
Mat dst;
cv::filter2D(srcImg,dst,srcImg.depth(),kernel);
/**********(8)直方图处理***********/
vector<Mat> bgr_planes;
split(src,bgr_planes);
int histSize = 256;
//设置值的范围
float range[] = {0,256};
const float* histRange = {range};
//初始化时,清除直方图
bool uniform = true;
bool accumulate = false;
//创建Mat对象分别保存直方图,每个通道均为1
Mat b_hist,g_hist,r_hist;
calcHist(&bgr_planes[0],1,0,Mat(),b_hist,1,&histSize,&histRange,uniform,accumulate);
calcHist(&bgr_planes[1],1,0,Mat(),g_hist,1,&histSize,&histRange,uniform,accumulate);
calcHist(&bgr_planes[2],1,0,Mat(),r_hist,1,&histSize,&histRange,uniform,accumulate);
//创建一个图像histImage显示直方图
int hist_w = 512;int hist_h = 400;
int bin_w = cvRound((double)hist_w / histSize);
Mat histImage(hist_h,hist_w,CV_8UC3,Scalar(0,0,0));
//归一化直方图
normalize(b_hist,b_hist,0,histImage.rows,NORM_MINMAX,-1,Mat());
normalize(g_hist,g_hist,0,histImage.rows,NORM_MINMAX,-1,Mat());
normalize(r_hist,r_hist,0,histImage.rows,NORM_MINMAX,-1,Mat());
for(int i = 1;i < histSize;i++)
{
line(histImage,Point(bin_w * (i - 1),hist_h - cvRound(b_hist.at<float>(i - 1))),
Point(bin_w*(i),hist_h - cvRound(b_hist.at<float>(i)),
Scalar(255,0,0),2,8,0);
line(histImage,Point(bin_w * (i - 1),hist_h - cvRound(g_hist.at<float>(i - 1))),
Point(bin_w*(i),hist_h - cvRound(g_hist.at<float>(i)),
Scalar(0,255,0),2,8,0);
line(histImage,Point(bin_w * (i - 1),hist_h - cvRound(r_hist.at<float>(i - 1))),
Point(bin_w*(i),hist_h - cvRound(r_hist.at<float>(i)),
Scalar(0,0,255),2,8,0);
}
/**********(9)直方图匹配***********/
Mat gray_hist;
void CalHistogram(Mat &img);
void HistMap(Mat& srcImg,Mat& dstImg);
int main()
{
Mat srcImg = imread("D:\\01 Codes\\00_OpencvBaseCPlus\\Fighting\\1.jpg",1);
Mat objImg = imread("D:\\01 Codes\\00_OpencvBaseCPlus\\Fighting\\2.jpg",1);
Mat dstImg;
if(!srcImg.data || !objImg.data)
{
cout << "Open picture error!" << endl;
}
//namedWindow("SrcImg",WINDOW_NORMAL);
//namedWindow("objImg",WINDOW_NORMAL);
//分割源图像通道
vector<Mat> src_channels;
Mat src_blue,src_green,src_red;
split(srcImg,src_channels);
src_blue = src_channels.at(0);
src_green = src_channels.at(1);
src_red = src_channels.at(2);
//分割目标通道
vector<Mat> obj_channels;
Mat obj_blue,obj_green,obj_red;
split(objImg,obj_channels);
obj_blue = obj_channels.at(0);
obj_green = obj_channels.at(1);
obj_red = obj_channels.at(2);
//分别对BGR通道进行直方图规定化操作
HistMap(src_blue,obj_blue);
HistMap(src_green,obj_green);
HistMap(src_red,obj_red);
//合并通道,输出结果
merge(src_channels,dstImg);
imshow("objImg",objImg);
imshow("srcImg",srcImg);
imshow("dstImg",dstImg);
waitKey(0);
return 0;
}
void CalHistogram(Mat &img)
{
if(img.empty())
return;
//设定bin数目
int histSize = 256;
//设定取值范围
float range[] = {0,256};
const float* histRange = {range};
calcHist(&img,1,0,Mat(),gray_hist,1,&histSize,&histRange);
}
void HistMap(Mat& srcImg,Mat& objImg)
{
int i,j;
double gray_temp = 0;
double totalPixel;//像素总数
//计算源图像直方图,并归一化到(0,1)
CalHistogram(srcImg);
totalPixel = srcImg.cols * srcImg.rows;
double srcHist[256];
for(i = 0;i < 256;i++)
{
srcHist[i] = gray_hist.at<float>(i) / totalPixel;
}
//计算源图像直方图的累计概率0~1
double srcCountHist[256];
for(i = 0;i < 256;i++)
{
gray_temp = 0;
for(j = 0;j <= i;j++)
{
gray_temp += srcHist[j];
}
srcCountHist[i] = gray_temp;
}
//计算目标图像直方图
CalHistogram(objImg);
totalPixel = objImg.cols * objImg.rows;
double objHist[256];
for(i = 0;i < 256;i++)
{
objHist[i] = gray_hist.at<float>(i) / totalPixel;
}
//计算源图像直方图的累计概率0~1
double objCountHist[256];
for(i = 0;i < 256;i++)
{
gray_temp = 0;
for(j = 0;j <= i;j++)
{
gray_temp += objHist[j];
}
objCountHist[i] = gray_temp;
}
//GML组映射
double min = 1; //设置成一个≥1的数即可
uchar map[256]; //输入到输出的映射关系
uchar groop[256]; //分组序号
for(i = 0;i < 256;i++)
{
groop[i] = -1; //初始化
}
for(i = 0;i < 256;i++)
{
if(objHist[i] == 0)
{
//如果该位置的直方图为0,则可以跳出这次循环,因为不会有点映射到这里
if(i > 0)
groop[i] = groop[i - 1];
continue;
}
min = 1;
for(j = 0;j < 256;j++)
{
if(abs(objCountHist[i] - srcCountHist[j]) < min)
{
min = abs(objCountHist[i] - srcCountHist[j]);
groop[i] = j; //最接近的直方图位置(源图像),记录分组序号
}
}
if(i == 0) //灰度值为0的情况有点特殊
{
for(int pos = 0;pos <= groop[i];pos++)
map[pos] = 0;
}
else{
for(int pos = groop[i - 1] + 1;pos <= groop[i];pos++)
//属于同一组内的元素,映射到同一个灰度值
map[pos] = i;//建立映射关系
}
}
//根据映射关系进行点运算,修改源图像
uchar* p = NULL;//用于遍历像素的指针
int width = srcImg.cols;
int height = srcImg.rows;
for(i = 0; i < height; i++)
{
p = srcImg.ptr<uchar>(i);//获取第i行的首地址
for(j = 0;j < width;j++)
{
p[j] = map[p[j]];
}
}
}
/**********(10)滤波操作***********/
GaussianBlur(src,out,Size(7,7),0,0);
medianBlur(src,out,7);
bilateralFilter(src,dst,9,60,15);
/**********(11)分割操作***********/
全局阈值,自适应阈值,OTSU阈值,区域生长算法
/**********(12)鼠标单击选单点漫水填充算法***********/
//对鼠标的动作进行处理
void on_MouseHandle(int event,int x,int y,int flags,void* param)
{
//如果鼠标左键按下,则使用区域生长算法
if(event == EVENT_LBUTTONDOWN)
{
//输出当前鼠标相对于图像的位置
cout << x << " " <<y <<endl;
//tmp = src.clone();
floodFill(src,Point(x,y),Scalar(R,G,B),0,Scalar(dt,dt,dt),Scalar(dt,dt,dt));
flag = true;
}
}
#define WINDOW_NAME "floodFill"
#define dt 1
Mat tmp;
Mat srcImg,src;
bool flag;
int R,G,B;
//对鼠标的动作进行处理
void on_MouseHandle(int event,int x,int y,int flags,void* param);
int main()
{
srcImg = imread("D:\\01 Codes\\00_OpencvBaseCPlus\\Fighting\\3.jpg",1);
Mat dstImg;
if(!srcImg.data)
{
cout << "Open picture error!" << endl;
}
namedWindow("src",CV_WINDOW_AUTOSIZE);
imshow("srcImg",srcImg);
srcImg.copyTo(src);
namedWindow(WINDOW_NAME);
//设置鼠标控制函数
createTrackbar("R",WINDOW_NAME,&R,255);
createTrackbar("G",WINDOW_NAME,&G,255);
createTrackbar("B",WINDOW_NAME,&B,255);
setMouseCallback(WINDOW_NAME,on_MouseHandle,0);
flag = true;
cout<<srcImg.rows << "x" <<srcImg.cols << endl;
while(1)
{
if(flag)
{
imshow(WINDOW_NAME,src);
flag = false;
}
if(waitKey(10) == 'q')
break;
}
//waitKey(0);
return 0;
}
/**********(13)分水岭算法***********/
//对鼠标的动作进行处理
static void on_Mouse(int event,int x,int y,int flags,void*);
Mat g_mMarkerMask,g_mImg;
Point prevPt(-1,-1);
namedWindow("image",1);
Mat imgGray;
srcImg.copyTo(g_mImg);
cvtColor(g_mImg,g_mMarkerMask,COLOR_BGR2GRAY);
cvtColor(g_mMarkerMask,imgGray,COLOR_GRAY2BGR);
g_mMarkerMask = Scalar::all(0);
imshow("image",g_mImg);
//设置鼠标操作函数
setMouseCallback("image",on_Mouse,0);
for(;;)
{
char c = (char)waitKey(0);
if(c == 27)
break;
//重置种子点集合
if(c == 'r')
{
g_mMarkerMask = Scalar::all(0);
srcImg.copyTo(g_mImg);
imshow("image",g_mImg);
}
if(c == 'w' || c == ' ')
{
int i,j,compCount = 0;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
//将鼠标选择的线条转换为种子点的位置
findContours(g_mMarkerMask,contours,hierarchy,RETR_CCOMP,CHAIN_APPROX_SIMPLE);
//查找轮廓
if(contours.empty())
continue;
Mat markers(g_mMarkerMask.size(),CV_32S);
markers = Scalar::all(0);
int idx = 0;
for(;idx >= 0;idx = hierarchy[idx][0],compCount++)
//绘制轮廓
drawContours(markers,contours,idx,Scalar::all(compCount+1),-1,8,hierarchy,INT_MAX);
if(compCount == 0)
continue;
//生成随机颜色,用于显示不同的分割区域
vector<Vec3b> colorTab;
for(int i = 0;i < compCount;i++)
{
int b = theRNG().uniform(0,255);
int g = theRNG().uniform(0,255);
int r = theRNG().uniform(0,255);
colorTab.push_back(Vec3b((uchar)b,(uchar)g,(uchar)r));
}
//调用分水岭算法函数
watershed(srcImg,markers);
Mat wshed(markers.size(),CV_8UC3);
//为分割后的区域上色
for(int i = 0;i < markers.rows;i++)
{
for(int j = 0;j < markers.cols;j++)
{
int index = markers.at<int>(i,j);
if(index == -1)
{
wshed.at<Vec3b>(i,j) = Vec3b(255,255,255);
}
else if(index <= 0 || index > compCount)
{
wshed.at<Vec3b>(i,j) = Vec3b(0,0,0);
}
else{
wshed.at<Vec3b>(i,j) = colorTab[index -1];
}
}
}
//与源图像合成,显示分割后的图像
wshed = wshed * 0.5 + imgGray * 0.5;
imshow("watershed transform",wshed);
}
}
//对鼠标的动作进行处理
static void on_Mouse(int event,int x,int y,int flags,void*)
{
//边界判断
if(x < 0|| x >= g_mImg.cols || y < 0 || y >= g_mImg.rows)
return;
//松开鼠标,初始化起点
if(event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON)) //鼠标左键不是拖拽动作
{
prevPt = Point(-1,-1);
}
//初始化起始点
else if(event == EVENT_LBUTTONDOWN)
{
prevPt = Point(x,y);
}
//移动鼠标,绘制移动轨迹,设置种子点
else if(event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
{
Point pt(x,y);
if(prevPt.x < 0)
{
prevPt = pt;
}
line(g_mMarkerMask,prevPt,pt,Scalar::all(255),5,8,0);
line(g_mImg,prevPt,pt,Scalar::all(255),5,8,0);
prevPt = pt;
imshow("image",g_mImg);
}
}
/**********(14)数字形态学***********/
(1)腐蚀与膨胀
快速消除图像中的小噪声块,都是对图像中的高亮区域进行“领域扩张”或者“领域缩小”;
(2)开运算和闭运算;
(3)形态学梯度:突出图像的外轮廓;
(4)顶帽:源图像与开运算结果图求差,开运算结果放大了裂缝或局部降低亮度的区域,背景提取功能;
(5)图像金字塔:图像多尺度表达的一张方式,高斯金字塔:向下采样,图像变小,拉普拉斯金字塔:向上重建上层未采样图像。
/**********(15) 边缘检测***********/
Sobel算子:分为x方向和y方向完成;
Laplacian算子:模板不同,0,1,0,1,-4,1,0,1,0;求两阶导数,
Canny算法:
(1)去噪
(2)计算图像梯度幅值和方向
(3)非极大值抑制
(4)滞后阈值(高低阈值,小于排除,高于边缘,中间看周围是否有边缘点相连)