首先我们根据博客9.2 c++搭建opencv环境-CSDN博客搭建opencv环境,然后对图片进行处理,在这里,对图片的分割没有用opencv里的函数,而是根据自己的理解用循环和做差写的函数,分割的效果和opencv的相似。
用visua studio建立的文件路径如下:
1.建立 imgchuli2.cpp 文件,该文件用来写处理图片的函数
以下代码的具体原理和解释可以看博客2 图片的分割处理和亚像素精度处理(c++和python)_亚像素处理-CSDN博客。
函数代码如下:
// OpencvPractice.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include<opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include<list>
#include<vector>
#include <typeinfo>
using namespace cv;
using namespace std;
#define KERNEL_SUM 8
int mainss2()
{
static Mat kernels[KERNEL_SUM];
if (kernels[0].empty())
{
int k = 0;
kernels[k++] = (Mat_<float>(3, 3) << 1, 2, 1, 0, 0, 0, -1, -2, -1); // 270°
kernels[k++] = (Mat_<float>(3, 3) << 2, 1, 0, 1, 0, -1, 0, -1, -2); // 315°
kernels[k++] = (Mat_<float>(3, 3) << 1, 0, -1, 2, 0, -2, 1, 0, -1); // 0°
kernels[k++] = (Mat_<float>(3, 3) << 0, -1, -2, 1, 0, -1, 2, 1, 0); // 45°
flip(kernels[0], kernels[k++], 0); // 90°
kernels[k++] = (Mat_<float>(3, 3) << -2, -1, 0, -1, 0, 1, 0, 1, 2); // 135°
flip(kernels[2], kernels[k++], 1); // 180°
kernels[k++] = (Mat_<float>(3, 3) << 0, 1, 2, -1, 0, 1, -2, -1, 0); // 225°
}
// 梯度图像
Mat gradients[KERNEL_SUM];
// 检测图像, 路径自己更改, 注意要是单通道图像
Mat imgsrc = imread("E:\\vs\\daima\\opencvs\\OpencvPractice\\4.jpg");//IMREAD_ANYCOLOR
for (int k = 0; k < KERNEL_SUM; k++)
{
filter2D(imgsrc, gradients[k], CV_16S, kernels[k]);// CV_64F //
Mat imgshowt;
normalize(gradients[k], imgshowt, 0, 255, NORM_MINMAX);
cv::Mat rlt;
imgshowt.convertTo(rlt, CV_8UC1);//将imgshowt转换为无符号单通道的整型并赋值给rlt CV_8UC1
//
cvtColor(rlt, gradients[k], cv::COLOR_BGR2GRAY);//将rlt转换为灰度图并赋值给gradients[k]
//imshow("img", rlt);
//waitKey(0);
}
//2. 梯度方向
// (1. 角度列表
const short angle_list[] = { 270, 315, 0, 45, 90, 135, 180, 225 };
// (2. 总幅值矩阵
Mat amplitude(imgsrc.rows, imgsrc.cols, CV_8UC1, Scalar::all(0));
// (3. 角度矩阵, 后面初始化成 -64 只是为了归一化之后能显示角度 0
Mat angle(imgsrc.rows, imgsrc.cols, CV_16SC1, Scalar::all(-64));//CV_16SC1 -64
for (int r = 0; r < imgsrc.rows; r++)
{
short* pAng = angle.ptr<short>(r);//short
for (int c = 0; c < imgsrc.cols; c++)
{
// 找出最大值
for (int i = 0; i < KERNEL_SUM; i++)
{
if (amplitude.ptr<unsigned char>(r)[c] < gradients[i].ptr<unsigned char>(r)[c])
{
amplitude.ptr<unsigned char>(r)[c] = gradients[i].ptr<unsigned char>(r)[c];
pAng[c] = angle_list[i];
}
}
}
}
Mat imgshow;
imshow("amplitude", amplitude);
//imwrite("D:\Datas\\1.jpg",amplitude);
waitKey(0);
normalize(angle, imgshow, 0, 255, NORM_MINMAX);
imgshow.convertTo(imgshow, CV_8UC1);
imshow("amplitude", imgshow);
waitKey(0);
// 3.单像素边缘,整数坐标边缘图像
//cout << "===============> start 单像素边缘 <================" << endl;
// 阈值
double thres = 158; // 此处为增加
Mat edge(imgsrc.rows, imgsrc.cols, CV_8UC1, Scalar::all(0));
for (int r = 1; r < imgsrc.rows - 1; r++)
{
// 3 * 3 邻域, 所以用 3 个指针, 一个指针指一行
const unsigned char* pAmp1 = amplitude.ptr<unsigned char>(r - 1);
const unsigned char* pAmp2 = amplitude.ptr<unsigned char>(r);
const unsigned char* pAmp3 = amplitude.ptr<unsigned char>(r + 1);
const short* pAng = angle.ptr<short>(r);
unsigned char* pEdge = edge.ptr<unsigned char>(r);
for (int c = 1; c < imgsrc.cols - 1; c++)
{
// 以下判断为增加部分
if (pAmp2[c] < thres)
{
continue;
}
//
switch (pAng[c])
{
case 270:
if (pAmp2[c] > pAmp1[c] && pAmp2[c] >= pAmp3[c])
{
pEdge[c] = 255;
}
break;
case 90:
if (pAmp2[c] >= pAmp1[c] && pAmp2[c] > pAmp3[c])
{
pEdge[c] = 255;
}
break;
case 315:
if (pAmp2[c] > pAmp1[c - 1] && pAmp2[c] >= pAmp3[c + 1])
{
pEdge[c] = 255;
}
break;
case 135:
if (pAmp2[c] >= pAmp1[c - 1] && pAmp2[c] > pAmp3[c + 1])
{
pEdge[c] = 255;
}
break;
case 0:
if (pAmp2[c] > pAmp2[c - 1] && pAmp2[c] >= pAmp2[c + 1])
{
pEdge[c] = 255;
}
break;
case 180:
if (pAmp2[c] >= pAmp2[c - 1] && pAmp2[c] > pAmp2[c + 1])
{
pEdge[c] = 255;
}
break;
case 45:
if (pAmp2[c] >= pAmp1[c + 1] && pAmp2[c] > pAmp3[c - 1])
{
pEdge[c] = 255;
}
break;
case 225:
if (pAmp2[c] > pAmp1[c + 1] && pAmp2[c] >= pAmp3[c - 1])
{
pEdge[c] = 255;
}
break;
default:
break;
}
}
}
imshow("edg", edge);//总共有462个点为255(白色)
imwrite("D:\Datas\\2.jpg", edge);
waitKey(0);
//cout << "===============> end 单像素边缘 <================" << endl;
// 4. 亚像素坐标
cout << "===============> start 亚像素坐标 <================" << endl;
// 根号2
const double root2 = sqrt(2.0);
// 三角函数表
double tri_list[2][KERNEL_SUM] = { 0 };
for (int i = 0; i < KERNEL_SUM; i++)
{
tri_list[0][i] = cos(angle_list[i] * CV_PI / 180.0);
// sin前面的负号非常关键, 因为图像的y方向和直角坐标系的y方向相反
tri_list[1][i] = -sin(angle_list[i] * CV_PI / 180.0);
}
// vector 方式记录小数坐标
vector<Point3f> vPts;
// Mat 方式记录小数坐标, 注意这里是双通道
Mat coordinate(imgsrc.rows, imgsrc.cols, CV_32FC2, Scalar::all(0));
for (int r = 1; r < imgsrc.rows - 1; r++)
{
// 3 * 3 邻域, 所以用3个指针, 一个指针指一行
const short* pAmp1 = amplitude.ptr<short>(r - 1);
const short* pAmp2 = amplitude.ptr<short>(r);
const short* pAmp3 = amplitude.ptr<short>(r + 1);
const short* pAng = angle.ptr<short>(r);
const short* pEdge = edge.ptr<short>(r);
float* pCoordinate = coordinate.ptr<float>(r);
for (int c = 1; c < imgsrc.cols - 1; c++)
{
if (pEdge[c])
{
int nAngTmp = 0;
double dTmp = 0;
switch (pAng[c])
{
case 270:
nAngTmp = 0;
dTmp = ((double)pAmp1[c] - pAmp3[c]) / (pAmp1[c] + pAmp3[c] - 2 * pAmp2[c] + 0.000001) * 0.5;
break;
case 90:
nAngTmp = 4;
dTmp = -((double)pAmp1[c] - pAmp3[c]) / (pAmp1[c] + pAmp3[c] - 2 * pAmp2[c] + 0.000001) * 0.5;
break;
case 315:
nAngTmp = 1;
dTmp = ((double)pAmp1[c - 1] - pAmp3[c + 1]) / (pAmp1[c - 1] + pAmp3[c + 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
break;
case 135:
nAngTmp = 5;
dTmp = -((double)pAmp1[c - 1] - pAmp3[c + 1]) / (pAmp1[c - 1] + pAmp3[c + 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
break;
case 0:
nAngTmp = 2;
dTmp = ((double)pAmp2[c - 1] - pAmp2[c + 1]) / (pAmp2[c - 1] + pAmp2[c + 1] - 2 * pAmp2[c] + 0.000001) * 0.5;
break;
case 180:
nAngTmp = 6;
dTmp = -((double)pAmp2[c - 1] - pAmp2[c + 1]) / (pAmp2[c - 1] + pAmp2[c + 1] - 2 * pAmp2[c] + 0.000001) * 0.5;
break;
case 45:
nAngTmp = 3;
dTmp = ((double)pAmp3[c - 1] - pAmp1[c + 1]) / (pAmp1[c + 1] + pAmp3[c - 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
break;
case 225:
nAngTmp = 7;
dTmp = -((double)pAmp3[c - 1] - pAmp1[c + 1]) / (pAmp1[c + 1] + pAmp3[c - 1] - 2 * pAmp2[c] + 0.000001) * root2 * 0.5;
break;
default:
break;
}
const double x = c + dTmp * tri_list[0][nAngTmp];
const double y = r + dTmp * tri_list[1][nAngTmp];
const short z = angle_list[nAngTmp];
// vector方式
vPts.push_back(Point3f((float)x, (float)y, (short)z));
// Mat 方式
pCoordinate[c << 1] = (float)x;
pCoordinate[(c << 1) + 1] = (float)y;
}
}
}
//cout << "" << vPts.size() << endl;//总共有462个点为255(白色)
//for (size_t i = 0; i < vPts.size(); i++)
//{
// cout << vPts[i].z << ": " << vPts[i].x << ", " << vPts[i].y <<endl;
//}
cout << "===============> end 亚像素坐标 <================" << endl;
return 0;
}
以上代码已经做了具体的注释,在这里不多做赘述。
2.建立 imgchuli2.h 文件
代码如下:
int mainss2();
3.建立OpencvPractice.cpp函数
代码如下:
#include<iostream>
#include "imgschuli.h"//mainss();
#include "Polynomial.h" //mains();
#include "imgchuli2.h"//mainss2();
#include "imgchuli3.h"//mainss3();
#include "listss.h"//mainss3();
using namespace std;
//using namespace cv;
int main()
{
cout << "===============> start <================" << endl;
cout << endl;
//.........................start...........................
//1.读取Mat类的元素
//mainss();
//2. 创建不同的3x3的卷积核,并对图片进行 filter2D 处理,显示不同卷积核处理后的效果
mainss2();
//2. 创建不同的3x3的卷积核,并对图片进行 filter2D 处理,快速版显示不同卷积核处理后的效果
//mainss3();
lists();
//.........................end.............................
cout << endl;
cout << "===============> end <================" << endl;
int ss = 8;
int r = ss % 5;
cout << r << endl;
return 0;
}
4.图片的处理结果
原图片:
处理一:
处理二:
处理三: