将图片压缩成灰度图像的几种方法:
1.人眼对于彩色的感觉到亮度感觉的转换,这是一个心理学问题,有一个公式:
Grey = 0.299R + 0.587G + 0.114*B
2.直接提取单通道图
Grey=R or G or B
3.取平均值:
Grey=(R+G+B)/3
图像预处理
1.灰度值变换
方法1:线性比例缩放
f(x)=ax+b
当|a|>1时,对比度增加,
当|a|<0时,对比度减小,若a为负数灰度值反转。
当b>0时,亮度增加,反之亮度减小
方法2:灰度值直方图均衡化
使用的前提时灰度值差异较大
对于一幅图片,先要统计他像素中各个灰度级的出现情况,之后用直方图来描绘。灰度直方图描述的时一个图片中某个灰度在图片中所占的比例。
灰度直方图的两个峰,数值大的是前景,数值小的是背景,中间的是最佳阈值区间
2.图像平滑
方法1:
方法2:
高斯滤波:
理想状态下的滤波器:1.线性 2.与位置无关 3.可以控制参数 4.可以多次执行 5.系数阵旋转对称
方法3:
中值滤波:这时一种非线性的滤波,在处理随机噪声上有优势
3.矫正
对图像进行旋转、平移、放大的操作。
二值化应用最广泛的取阈值点方法
最大类间方差法(otus大津法):
适合用于双峰图,最小峰是前景,最大峰值时背景,中间值为最佳阈值区间。假设有一个点可以正好把一幅图分为两大类,这两大类的均值为m1与m2假设的最优点为mg,p1、p2表示的时前景背景所占的比例,默认为1:1。目的就是找到最大的方差σ,因为方差表示的是离散程度,找到一个分类点使得离散程度最高,就可以成功分离开前景和背景。
opencv实现
参数说明
src:源图像,可以为8位的灰度图,也可以为32位的彩色图像。(两者由区别)
dst:输出图像
thresh:阈值
maxval:dst图像中最大值
type:阈值类型,可以具体类型如下:
双阈值分割可以用两次单阈值分割叠加:
threshold(img2, out1, 50, 255, THRESH_BINARY); //大阈值对源灰度图像进行反二进制阈值化操作 threshold(img2, out2,140, 255, THRESH_BINARY_INV);//thresh:阈值,maxval:输出图像中最大值 bitwise_and(out1, out2, img2); //对像素加和
特征提取
区域特征
从区域自身提取来的特征
最简单的方法是计算区域的面积,其可以用计算区域内点的个数来代替
行程编码
在一个联通的二值图中,把图像转换成矩阵后,每一行只存储连续点的开头元素位置和结尾元素位置
有一个字符串“aaabccddddd”,经过行程 编码后可以用“3a1b2c5d
灰度图的方差:可以判断图片是否模糊(要规定感受野的)
灰度图的亮度:来描述图片的连读
几何不变矩
矩是对变量分布和形态分布的一组度量,n阶矩被定义为一变量的n次方与其概率密度函数之积的积分
变量的一阶原始矩等价于[数学期望]、二至四阶中心矩被定义为[方差]、[偏度]
HU不变矩具有平移旋转和尺度不变性,这里表述的数域转移到了二维
形态学
六种基本操作
1.闵科夫斯基加法
在结构元上定义一个中心点,把这个结构元当刷子以中心点为基准遍历目标,目标没有的点就加上去。
2.膨胀:
对结构元先进行对称再进行闵可夫斯基加法,因为结构元大多数是中心对称的,所以一般不区分膨胀和闵可夫斯基加法。
3.闵科夫斯基减法:
膨胀的对偶运算
4.腐蚀:闵可夫斯基加法的对偶
5.闭运算
先膨胀后腐蚀
闭运算
闭运算的效果图如下图所示:
我们也可以得到关于闭运算的几点结论:
(1)闭运算能够填平小湖(即小孔),弥合小裂缝,而总的位置和形状不变。
(2)闭运算是通过填充图像的凹角来滤波图像的。
(3)结构元素大小的不同将导致滤波效果的不同。
(4)不同结构元素的选择导致了不同的分割。
6.开运算
先腐蚀后膨胀
开运算的效果图如下图所示:
我们可以得到关于开运算的几点结论:
(1)开运算能够除去孤立的小点,毛刺和小桥,而总的位置和形状不便。
(2)开运算是一个基于几何运算的滤波器。
(3)结构元素大小的不同将导致滤波效果的不同。
(4)不同的结构元素的选择导致了不同的分割,即提取出不同的特征。
边界提取的原理:
通过对目标图像进行腐蚀和膨胀处理,比较结果图像与原图像的差别来实现。
内边界的提取利用图像的腐蚀处理得到原图像的一个收缩,再将收缩结果与目标图像进行异或运算,实现差值部分的提取,
外边界提取先对图像进行膨胀处理,然后用膨胀结果与原目标图像进行异或运算,也就是求膨胀结果与原目标图像的差集
关键就是找到合适的结构元
一些基本术语
刷子:用来处理对象的图像,通常是较小的,相当于滤波器、窗口
击中:两个对象有交集
对称:一般是关于某一个点的旋转对称
边缘检测
定义:
边缘:局部差异性强,一定不是闭合的
边界:一定是闭合的
轮廓:是在大尺度,在δ比较大的情况下,全局来看,也不一定是闭合的
边缘的分类:
阶梯状边缘:图像灰度值不同的相邻区间内
脉冲状边缘:在该处图像的灰度值会有突增
屋顶状边缘:灰度值的上升和下降都比较平滑,这三类边缘的检测是越来越难的
梯度
对梯度的定义可以有很多,梯度反映的图像在该点,增加最快的方向。可以用梯度局部的最大值来规定边缘。
算子
定义:
广义算子就是一个映射,一维中可以由一个数变成另一个数,就是一个函数;二维中可以把一个矩阵转换成另一个矩阵、或者压缩到一维下的
另一个数值
狭义的算子是函数到函数
泛函是函数到数字
像是罗伯特算子适合一个倾斜45,135的阶梯、脉冲边缘,普瑞维特算子适合横竖的边缘。
几种常见的二阶算子(大部分奇数规模)
首先模板基本要求是核为正数,所有系数的和为0,如拉普拉斯算子:
canny边缘检测步骤
1.先要进行降噪,对图像进行个平滑处理,这里选取的是五阶的高斯平滑算子2.刚才的sobel算子,来计算梯度的幅值和方向
3.非极大值抑制:再池化一次,将边缘描述成少像素的细线条
4.滞后阈值: 滞后阈值需要两个阈值(高阈值和低阈值):
如果某一像素位置的幅值超过 高 阈值, 该像素被保留为边缘像素。
如果某一像素位置的幅值小于 低 阈值, 该像素被排除。
如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于 高 阈值的像素时被保留。
Canny 推荐的 高:低 阈值比在 2:1 到3:1之间。
cvtColor(srcImage, srcGray, CV_BGR2GRAY);//灰度化
GaussianBlur(srcGray, srcGray, Size(3,3),1, 1);//高斯, 120, 255
//blur(srcGray, srcGray, Size(3, 3));
imshow("GaussianBlur", srcGray);
//Canny检测
int edgeThresh = 80;
Mat Canny_result;
Canny(srcGray, Canny_result, 120, 250,3);
imshow("Canny_result", Canny_result);
waitKey(0);
我考虑了用膨胀和腐蚀去点条形码,效果一般
Mat element1 = getStructuringElement(MORPH_CROSS, Size(5, 5));
Mat element2 = getStructuringElement(MORPH_CROSS, Size(6, 6));
Mat element3 = getStructuringElement(MORPH_CROSS, Size(2, 2));
dilate(Canny_result, Canny_result, element1);
erode(Canny_result, Canny_result, element2);
dilate(Canny_result, Canny_result, element1);
erode(Canny_result, Canny_result, element3);
erode(Canny_result, Canny_result, element3);
dilate(Canny_result, Canny_result, element3);
LBP提取纹理(不受光照影响)
LBPH(Local Binary Pattern Histogram) 局部二值模式直方图,是一种用来描述图像局部纹理特征的算子;它具有旋转不变性和灰度不变性等显著的优点,原始的LBP算子定义为在33的窗口内,以窗口中心像素为阈值,将相邻的8个像素的灰度值与其进行比较,若周围像素值大于中心像素值,则该像素点的位置被标记为1,否则为0。这样,33邻域内的8个点经比较可产生8位二进制数(通常转换为十进制数即LBP码,共256种),即得到该窗口中心像素点的LBP值,并用这个值来反映该区域的纹理信息。LBP算法可以很好的提取图片局部的纹理特征,由于其选择的是阈值函数, LBP算法收光照影响不明显。
void LBP(IplImage* src, IplImage* dst)
{
int width = src->width;
int height = src->height;
for (int j = 1; j < width - 1; j++)
{
for (int i = 1; i < height - 1; i++)
{
uchar neighborhood[8] = { 0 };
neighborhood[7] = CV_IMAGE_ELEM(src, uchar, i - 1, j - 1);
neighborhood[6] = CV_IMAGE_ELEM(src, uchar, i - 1, j);
neighborhood[5] = CV_IMAGE_ELEM(src, uchar, i - 1, j + 1);
neighborhood[4] = CV_IMAGE_ELEM(src, uchar, i, j - 1);
neighborhood[3] = CV_IMAGE_ELEM(src, uchar, i, j + 1);
neighborhood[2] = CV_IMAGE_ELEM(src, uchar, i + 1, j - 1);
neighborhood[1] = CV_IMAGE_ELEM(src, uchar, i + 1, j);
neighborhood[0] = CV_IMAGE_ELEM(src, uchar, i + 1, j + 1);
uchar center = CV_IMAGE_ELEM(src, uchar, i, j);
uchar temp = 0;
for (int k = 0; k < 8; k++)
{
temp += (neighborhood[k] >= center) << k;
}
CV_IMAGE_ELEM(dst, uchar, i, j) = temp;
}
}
}