SVM训练
简单来说,SVM就是用于区分不同的类型(车牌、非车牌)。SVM的训练数据既有特征
又有标签
,通过训练,让机器 可以自己找到特征和标签之间的联系,在面对只有特征没有标签的数据时,可以判断出标签。属于机器学习中的监 督学习。
HOG特征
在训练时,我们将使用HOG来提取一张图片的特征。HOG全称为:Histogram of Oriented Gridients,方向梯度
直方图。是计算机视觉、模式识别领域很常用的一种描述图像局部纹理的特征。
先计算图片某一区域中不同方向上梯度的值,然后进行累积,得到的直方图就可以代表这块区域,也就是作为特 征,可以输入到分类器里面。 简单来说,车牌的边缘与内部文字组成的一组信息(在边缘和角点的梯度值是很大 的,边缘和角点包含了很多物体的形状信息),HOG就是抽取这些信息组成一个直方图。
样本+标签,数据格式
xx.png--->HOG---> Mat(相当于图片数据矩阵)
正样本对应标签----> 1
负样本对应标签----> -1
每张图片放在一行,每行就是一个样本数据
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
XXXXXXXXXX
在OpenCV中对图像使用HOG提取特征:
//1、灰度化,保存至gray
Mat gray;
cvtColor(plate, gray, COLOR_BGR2GRAY); //2、二值化,保存至shold
Mat shold;
threshold(gray, shold, 0, 255, THRESH_OTSU); //3、提取特征,保存至features
Mat features;
getHogFeatures(svmHog, shold, features);
-----------
//提取特征
void CarPlateRecgnize::getHogFeatures(HOGDescriptor *hog, Mat src, Mat &out) {
//重新定义大小 缩放 提取特征的时候数据需要为 :CV_32S 有符号的32位数据
Mat trainImg = Mat(hog->winSize, CV_32S);
resize(src, trainImg, hog->winSize);
//计算特征 获得float集合
vector<float> d;
hog->compute(trainImg, d, Size(8, 8));
Mat features(d);
//特征矩阵
features.copyTo(out);
features.release();
trainImg.release();
}
训练数据
能够提取特征后,接下来就要进行SVM的训练了。opencv中的svm是使用的LIBSVM,LIBSVM是台湾大学林智仁
(Lin Chih-Jen)教授2001年开发的一套支持向量机的库。
深度学习就是调整参数的过程。我们使用的参数都是默认的,肯定不是最好的,但是基本稳定。不同参数效
果不同、训练检测速度不同,同时也影响着模型文件的大小。
我们将使用136x36规格的车牌图片作为正样本,任意136x36的图片作为负样本来进行训练。
struct TrainStruct{
string file;
int label;
};
---------
//样本集合
vector<TrainStruct> svm_data;
----------
//正样本 标签是:1
for(auto file : pos_files){
svm_data.push_back({file,1});
}
//负样本 标签为:-1
vector<string> neg_files;
getFiles(SVM_NEG, neg_files);
for(auto file : neg_files){
svm_data.push_back({file,-1});
}
现在我们的所有样本都被打上标签记录在 svm_data 中。接下来我们对样本进行特征提取并记录。
Mat samples;
vector<int> responses;
for(auto data:svm_data){
auto image = imread(data.file,IMREAD_GRAYSCALE);
if (image.empty()) {
printf("加载样本失败 image: %s.\n", data.file.c_str());
continue;
}
//二值
threshold(image, image, 0, 255, THRESH_BINARY+THRESH_OTSU);
Mat feature;
//获得hog特征
getSvmHOGFeatures(image, feature);
//调整为一行
feature = feature.reshape(1, 1);
// 图片数据:x x x
// samples:x x x
// x x x
//保存特征数据
samples.push_back(feature);
//记录对应的标签
responses.push_back(data.label);
}
现在我们获得了samples
与 labels
Ï两个数据。其中 samples 为样本特征数据,存储在矩阵中,每一行就是一个样 本的特征数据。加上我们正负样本各有三个,那么 samples 中记录的数据就为:宽:x,高为:3+3=6。看起来是 这样的:
同时与 samples 对应的有一个 labels 标签集合,记录这些特征数据所属于的标签。 我们准备样本时,将正样本标 签置为1,负样本为-1。因此现在 labels 中的数据为 {1,1,1,-1,-1,-1}
与 samples 相匹配。
下面我们将 samples 与 lables 包装为OpenCV中的训练数据集: TrainData
训练
我们的训练数据 TrainData 已经准备完成,接下来需要使用OpenCV提供的SVM进行训练。
//训练数据 行
Ptr <TrainData> trainData = TrainData::create(samples, SampleTypes::ROW_SAMPLE, responses);
printf("训练数据准备完成,开始训练!\n");
//其他参数都是默认值 为了把
//最后一个参数设置true 则会创建更平衡的验证子集
//也就是如果是2类分类的话能得到更准确的结果
classifier->trainAuto(trainData, 10, SVM::getDefaultGrid(SVM::C),
SVM::getDefaultGrid(SVM::GAMMA), SVM::getDefaultGrid(SVM::P),
SVM::getDefaultGrid(SVM::NU), SVM::getDefaultGrid(SVM::COEF),
SVM::getDefaultGrid(SVM::DEGREE), true);
classifier->save( SVM_XML );
printf("训练完成 ,模型保存: %s \n", SVM_XML);
HSV 定位
我们已经使用了边缘定位,边缘定位是通过图像的各种处理,将车牌区域连接成一块整体从而进行轮廓查 找得到的候选车牌集合,但是边缘定位在某些情况下可能把背景也混合成一块整体。这样得到的车牌并不是最合适 的。所以我们再加入颜色定位同时进行筛选。以蓝色车牌举例,如果以BGR值来确定颜色,就算B为255,但是一 旦混合了G与R得到的结果可能并不是蓝色。以BGR来进行颜色的分辨并不容易。
而HSV(也可称为HSB)是根据颜色的直观特性创建的一种颜色空间,也称六角锥体模型。这个模型中颜色的参数分 别是:
- 色度(Hue):用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°, 蓝色为240°;
- 饱和度(Saturation): 取值范围为0-1。降低S,饱和度越低,颜色越白;
- 透明度 (Value/brightness ): 取值范围为0-1。降低V,亮度月底,颜色越黑 。
经过查询发现,H为:200°-280°范围,S为0.37-1之间,V也是0.37-1之间。这个范围可能并不是非常精准,但是足 够了。
而OpenCV中H被定义为:0-180的范围(缩小一半)。而S与V都为0-255的范围(乘以255)。
因此最终认为 使用OpenCV获得HSV图像,某个像素点,H在100-140
。S与V都在95-255之间
则该像素点为蓝色。那么使用颜色 定位的流程为:
转换HSV
Mat hsv;
cvtColor(src, hsv, COLOR_BGR2HSV);
颜色匹配
循环处理每个像素点,根据蓝色HSV取值范围,如果像素点为蓝色,则将其HSV数据置为:0,0,255(只保留亮 度);若像素点非蓝色,则置为0,0,0(纯黑色)。这样只有蓝色的区域被保留下来。
//3通道
int chanles = hsv.channels();
//高
int h = hsv.rows;
//宽数据长度
int w = hsv.cols * chanles;
//判断数据是否为一行存储的
//头一行的末尾在内存里和下一行的开头是相连的
if (hsv.isContinuous()) {
w *= h;
h = 1;
}
for (size_t i = 0; i < h; i++)
{
//第i行的数据 一行 hsv的数据 uchar = java byte
uchar* p = hsv.ptr<uchar>(i);
//处理3个数据 所以+=3
for (size_t j = 0; j < w; j+=3)
{
int h = int(p[j]);
int s = int(p[j+1]);
int v = int(p[j + 2]);
//是否为蓝色像素点的标记
bool blue = false;
// h:蓝色就可以了
//当V和S都达到最高值,也就是1时(opencv*255,0-255),颜色是最纯正的。
// 降低S,颜色越发趋向于变白。降低V,颜色趋向于变黑,当V为0时,颜色变为黑色。
if (h >= 100 && h <= 140 && s >= 95 && s <= 255 && v >= 95 && v <= 255) {
blue = true;
}
//把蓝色像素 凸显出来 ,其他的区域全变成黑色
if (blue) {
p[j] = 0;
p[j+1] = 0;
p[j + 2] = 255;
}
else {
//变成黑色
p[j] = 0;
p[j + 1] = 0;
p[j + 2] = 0;
}
}
}
此时,显示出来的图像为:
HSV结果.png之所以会呈现红色,是因为 imshow 会将输出的Mat视为BGR数据进行显示。那么在我们的处理中蓝色被处理为: 0,0,255。如果看为BGR,则为R=255,所以为红色。
分离HSV
vector<Mat> hsv_split;
split(hsv, hsv_split);
对上一步的结果进行分离,得到了三个元素(H、S、V)的向量集合 vector 。在这个集合中,我们直接取出第2个元 素,即为S饱和度数据矩阵。此矩阵Mat即为二值化的图像。
二值化.png接下来的操作和边缘定位一致,对二值图像进行闭操作、查找轮廓、初步筛选最后旋转角度。即可获得候选车牌集 合。
后续处理
//闭操作
Mat close;
Mat element = getStructuringElement(MORPH_RECT, Size(17, 3));
morphologyEx(shold, close, MORPH_CLOSE, element);
imshow("close",close);
waitKey();
//6、查找轮廓
//获得初步筛选车牌轮廓================================================================
//轮廓检测
vector< vector<Point> > contours;
//查找轮廓 提取最外层的轮廓 将结果变成点序列放入 集合
findContours(close, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
//遍历
vector<RotatedRect> vec_color_roi;
for (vector<Point> point : contours) {
RotatedRect rotatedRect = minAreaRect(point);
//rectangle(src, rotatedRect.boundingRect(), Scalar(255, 0, 255));
//进行初步的筛选 把完全不符合的轮廓给排除掉 ( 比如:1x1,5x1000 )
if (verifySizes(rotatedRect)) {
vec_color_roi.push_back(rotatedRect);
}
}
tortuosity(src, vec_color_roi, dst);
补充知识
机器学习算法-------> 核函数
交错数据
例子:
花生,石头,瓜子散落在桌子上,用里拍桌子,物体起落有高有低,就容易在空间维度划分区域
二维线性不可分------> 三维空间
上面的参数可以模拟运行
跳变-----》黑色,白色变化(干扰物比较小)用来区分干扰,因为我们的数据是按照行来的 如GB888888
网友评论