-Android-
对于计算机视觉的理解——Android实验室第一次培训
大一寒假Android实验室培训了这方面的内容。第一次写技术类的贴,这是一篇记录的贴(也是实验室作业,这种被推着奋进的感觉不错但是还得培养自己的主观能动性),不太熟练,总之咱们先开始吧。
计算机视觉是什么?
视觉——即与图像处理有关->图像处理相对于其他数据的处理更为复杂->机器学习->上升到三维空间里。
计算机视觉与计算机图形学的区别在哪?
其实所用处理方法相通,但是计算机图形学更注重美感。
openCV初步学习
- 来段代码:
#include<iostream>//必要前缀
#include<opencv2/opencv.hpp>//必要前缀
#define Pi 3.1415926
using namespace std;
using namespace cv;
int main(int argc, char ** argv)
{
VideoCapture video(0);//摄像头捕捉信息(这个做起来还是相当有意思的)
while (1)
{
Mat frame;
video >> frame;
cvtColor(frame, frame, COLOR_RGB2GRAY);
namedWindow("frame",CV_WINDOW_AUTOSIZE);
imshow("frame", frame);
waitKey(30);//每三十毫秒刷新一次
}
Mat src = imread("F:/girl.jpg", 1);//注意反斜杠
cvtColor(src, src, COLOR_BGR2GRAY);//将图转变成黑白
namedWindow("src", WINDOW_AUTOSIZE);//新建窗口
imshow("src", src);
waitKey(0);
return 0;
}
- 这里还有一些opencv与c的一些不同用法:
char-UCHAR int-CV32S float-CV32S double-CV64F
现在opencv的最基础用法我们已经熟悉的差不多了。
做一个例子:实现图像X方向一阶差分,观察图像的边缘提取情况
#include<iostream>//必要前缀
#include<opencv2/opencv.hpp>//必要前缀
#define Pi 3.1415926
using namespace std;
using namespace cv;
int main(int argc, char ** argv)
{
Mat dImg = Mat(src.rows, src.cols - 2, CV_8UC1);//C1 = CHANNAL,差分!!
for (int i = 0; i < src.rows; i++)
{
for (int j = 1; j < src.cols - 1; j++)
{
dImg.at<uchar>(i, j - 1) = src.at<uchar>(i, j + 1) - src.at<uchar>(i, j - 1);
}
}//图像只有单方向,卷积核也只有单方向,所以需要两层循环来进行遍历
namedWindow("dst", CV_WINDOW_AUTOSIZE);
imshow("dst",dImg);
waitKey(0);
Mat src = imread("D:/a.jpg", 1);//注意反斜杠
cvtColor(src, src, COLOR_BGR2GRAY);//将图转变成黑白
namedWindow("src",WINDOW_AUTOSIZE);//新建窗口
imshow("src", src);
return 0;
}
我们明确几点概念:
1.图像
计算机产生的图像实际上是由三个矩阵产生的。也可以看作一组波。
2.取样和量化
我们可以将图像块状分割,从每块取样。采样点越少,图片越模糊。
3.灰度级
离散化后的像素值用一个字节表示(8位)。颜色模式RGB每个字母所代表的程度都用一个字节表示。也就是说一共有256256256种颜色。
既然图像还可以是一组波,那么我们将图片离散化的时候是否也可以将波联系起来呢?
我们提出傅里叶级数的概念。
其本质就是把周期信号波转化为无限多个离散的正弦波。
我们提出一个问题:如果一张图里有白雪公主,我们如何把白雪公主拿出来并且不破坏背景图?
- 如图,进行傅里叶级数计算后,我们从空间域视角转换为频率域视角。其实,白雪公主可能就是其中的一条,我们只要从频率域视角中选出来并且剔除掉,就可能实现问题中的效果。
接下来我们又提出了一个问题。如何去除下图中的噪点??
- 我们又认识到了一个新概念,图像滤波。图像滤波分为两种:平滑化滤波和锐化滤波。
- 以上铅笔图中,有很多杂点,我们称之为噪点。平滑化滤波可以去噪,也就是去除噪点。噪点的像素值与周围的像素值非常不同,而去噪就是让其像素值变得与周围相同。锐化恰恰相反,他能让图像的边界变得更加明显。
接下来我们必须熟悉一下图像求导和图像微分的知识,才能更好的编写代码
我们先类比连续函数求导:
这是图像求导:我们可以使用有限差分表示图像的导数或者偏导数(离散数学没学到所以不是很理解)。
这里使用差分近似的。
-
差分有 前向差分f(x ) – f(x - 1) (在此不做特殊说明) 中心差分f(x + 1) – f(x - 1) (矩阵的一小条,可以成为卷积。边界效应使边界丢失。(中心差分)) 了解一下:雅各比矩阵是一阶偏导构成的,海森矩阵是二阶偏导构成的。
-
下面是相关代码,做一个X方向的一阶差分,观察图像的边缘提取情况
Mat dImg = Mat(src.rows, src.cols - 2, CV_8UC1);//C1 = CHANNAL,差分!!
for (int i = 0; i < src.rows; i++)
{
for (int j = 1; j < src.cols - 1; j++)
{
dImg.at<uchar>(i, j - 1) = src.at<uchar>(i, j + 1) - src.at<uchar>(i, j - 1);
}
}//两层循环,一层为卷积模板的x轴,一层为操作图片的x轴
namedWindow("dst", CV_WINDOW_AUTOSIZE);
imshow("dst",dImg);
waitKey(0);
Mat src = imread("D:/a.jpg", 1);//注意反斜杠
cvtColor(src, src, COLOR_BGR2GRAY);//将图转变成黑白
namedWindow("src",WINDOW_AUTOSIZE);//新建窗口
imshow("src", src);
Robert算子计算,边缘会比较粗糙。这是为什么呢?
卷积是什么,咱们可以看作是一种结合了矩阵运算的计算方法。
中心差分如下
将算子与左上角的矩阵相乘,将相乘结果矩阵的中心数字填入到相应位置里面去。
从左往右卷积,从上到下卷积。
高斯模糊
-
实际上变模糊,就是物与物之间的边界变得更加不清楚了。 也可以这么说,这是将像素值设为相邻像素平均值。 那么可以容易理解,相邻像素范围越大,就越模糊。
-
但是距离远近也是影响平均值的重要因素之一,所以为了更加“平均”,这里的高斯模糊产生的像素值准确的说是相邻像素加权平均的结果。
-
一说加权平均,我就想到了数学中学过的正态分布。不过在这里我们不在是在直角坐标系中运用,而是三维坐标系中,公式不一样了。为什么要在三维坐标系中了呢?我认为,我们要求处理一张图象,如图。
最后咱们必须知道点滤波的常用api才能更好的写代码呀 边缘检测(微分) Sobel,Laplac算子 (用于锐化滤波) 图像去噪/平滑(积分) GaussianBlur
下面来写个高斯模糊实例吧。
#include <opencv2/opencv.hpp>
#include <iostream>
#define PI 3.1415926
using namespace std;
using namespace cv;
int main(int argc, char ** argv)
{
Mat dst = src.clone();
GaussianBlur(src, dst, Size(15, 7), 380);
//5*5,3*3等……接下来写5*5的
Mat model = Mat(5, 5, CV_64FC1);
double sigma = 80;//超参数,开始运算时的定值。根据经验得来的。
for (int i = -2; i <= 2; i++)
{
for (int j = -2; j <= 2; j++)
{
model.at<double>(i + 2, j + 2) =
exp(-(i * i + j * j) / (2 * sigma * sigma)) /
(2 * PI * sigma * sigma);//这里就是加权求,运用正态分布运算
}
}
//求卷积核
double gaussSum = 0;
gaussSum = sum(model).val[0];
for (int i = 0; i < model.rows; i++)
{
for (int j = 0; j < 5; j++)
{
model.at<double>(i, j) = model.at<double>(i, j) / gaussSum;
}
}//对高斯核进行归一化操作
//卷积操作
Mat dst = Mat(src.rows - 4, src.cols - 4, CV_8UC1);
for (int i = 2; i < src.rows - 2; i++)//为什么从二开始???这个地方不太懂
{
for (int j = 2; j < src.cols - 2; j++)
{
double sum = 0;
//遍历卷积核
for (int m = 0; m < 5; m++)
{
for (int n = 0; n < 5; n++)
{
sum += (double)src.at<uchar>(i + m - 2,j+n-2)*
model.at<double>(m,n);
}
}
dst.at<uchar>(i - 2, j - 2) = (uchar)sum;
}
}//内两层遍历5*5卷积核,外两层遍历将要处理的图像。
namedWindow("gaussBlur",WINDOW_AUTOSIZE);
imshow("gaussBlur",dst);
waitKey(0);
//效果不强,
return 0;
}