openCV

2022-06-17  本文已影响0人  carlwu_186

人脸识别追踪

  1. 开辟一个OpenCV的Mat
    //1、高 2、宽,rows表示高,cols表示宽
    Mat src(h + h / 2, w, CV_8UC1, data);
因为data[ ]是NV21格式的YUV420数据, NV21

数据实际高度是h*3/2。

  1. 将 nv21的yuv数据转成了rgba
//将 nv21的yuv数据转成了rgba
cvtColor(src, src, COLOR_YUV2RGBA_NV21);
  1. 根据摄像头的前后,进行旋转、水平翻转处理
   if (cameraId == 1) {
        //前置摄像头,需要逆时针旋转90度
        rotate(src, src, ROTATE_90_COUNTERCLOCKWISE);
        //水平翻转 镜像
        flip(src, src, 1);
    } else {
        //顺时针旋转90度
        rotate(src, src, ROTATE_90_CLOCKWISE);
    }
  1. 灰度处理、对比度增强
    //灰色
    cvtColor(src, gray, COLOR_RGBA2GRAY);
    //增强对比度 (直方图均衡)
    equalizeHist(gray, gray);
  1. 加载分类器、跟踪器
    分类器的加载需要传入模型文件地址,分类器可以定位出人脸区域。跟踪器是对分类器的优化,在视频中可以根据上一次分类器的定位结果优化下一次定位的运算过程。
  2. 跟踪器运行,得到定位区域列表,在原图中绘制人脸范围矩形框
    //定位人脸 N个
    tracker->process(gray);
    tracker->getObjects(faces);
    for (Rect face : faces) {
        //画矩形
        //分别指定 bgra
        rectangle(src, face, Scalar(255, 0, 255));
    }
  1. 调用ANativeWindowAPI将Mat src绘制到window中
    //显示
    if (window) {
        //设置windows的属性
        // 因为旋转了 所以宽、高需要交换
        //这里使用 cols 和rows 代表 宽、高 就不用关心上面是否旋转了
        ANativeWindow_setBuffersGeometry(window, src.cols, src.rows, WINDOW_FORMAT_RGBA_8888);
        ANativeWindow_Buffer buffer;
        do {
            //lock失败 直接brek出去
            if (ANativeWindow_lock(window, &buffer, 0)) {
                ANativeWindow_release(window);
                window = 0;
                break;
            }
            //src.data : rgba的数据
            //把src.data 拷贝到 buffer.bits 里去
            // 一行一行的拷贝
            uint8_t *dst_data = static_cast<uint8_t *>(buffer.bits);
            int dst_linesize = buffer.stride * 4;
            uint8_t *src_data = src.data;
            int src_linesize = src.cols * 4;
            for (int i = 0; i < src.rows; ++i) {
                memcpy(dst_data + i * dst_linesize, src_data + i * src_linesize,  dst_linesize);
            }
            //提交刷新
            ANativeWindow_unlockAndPost(window);
        } while (0);
    }

车牌识别

第一步 车牌的定位

有两种方式:

Sobel过程分析

  1. 高斯模糊(平滑、降噪 )。
  2. 灰度化 去掉颜色 因为它对于我们这里没用 降噪。
  3. 边缘检测 让车牌更加突出 在调用时需要以16位来保存数据 在后续操作 以及显示的时候需要转回8位。
  4. 二值化 黑白,采用了 大律法 最大类间算法。
  5. 闭操作,将相邻的白色区域扩大 连接成一个整体。
  6. 轮廓检测findContours,将结果变成点序列放入 集合。
  7. 利用minAreaRect 将 点序列 得到最小包裹的 有角度矩形RotatedRect
  8. 进行初步的筛选 把完全不符合的轮廓给排除掉 ( 比如:1x1,5x1000 )。
  9. 矫正,因为可能斜的,处理扭曲。

HSV过程分析(颜色模型,H色调、S饱和度、V明亮度)

  1. 原Mat转换成HSV格式的Mat hsv。
  2. Mat hsv的数据在内存中一行是按照h、s、v连续分布的,所以需要遍历每一行,得到具体的h、s、v数值,比较hsv分布表: hsv.png

    ,判断一组h、s、v是否在蓝色范围内,把蓝色像素 凸显出来 ,其他的区域全变成黑色:

if (h >= 100 && h <= 124 && s >= 43 && s <= 255 && v >= 46 && v <= 255) {
    p[j] = 0;
    p[j + 1] = 0;
    p[j + 2] = 255;
} else {
    p[j] = 0;
    p[j + 1] = 0;
    p[j + 2] = 0;
}
  1. 把Mat hsv的h、s、v分离出来
    vector<Mat> hsv_split;
    split(hsv, hsv_split);
    Mat target = hsv_split[2];//凸显出来的图像
  1. 二值化 黑白,采用了 大律法 最大类间算法。
  2. 闭操作,将相邻的白色区域扩大 连接成一个整体。
  3. 轮廓检测findContours,将结果变成点序列放入 集合。
  4. 利用minAreaRect 将 点序列 得到最小包裹的 有角度矩形RotatedRect
  5. 进行初步的筛选 把完全不符合的轮廓给排除掉 ( 比如:1x1,5x1000 )。
  6. 矫正,因为可能斜的,处理扭曲。

使用SVM测评Sobel和HSV定位的结果

  1. 灰度化 去掉颜色 因为它对于我们这里没用 降噪。
  2. 二值化 黑白,采用了 大律法 最大类间算法。
  3. 提取特征,把特征置为一行。
  4. 调用svm测评特征,得到评分。
  5. 评分最小,即为最优定位结果。

SVM测评的模型需要大量的正样本进行训练:


image.png

SVM介绍

svm,英文全称为 Support Vector Machine,中文名叫支持向量机,是机器学习最为经典的分类方法之一。
SVM是一种线性分类器,分类的对象要求是线性可分。因此我们首先要了解什么是线性可分与线性不可分。
假如在课桌“三八线”的两旁分别放了一堆苹果和一堆荔枝,通过“三八线”这样一条直线就能把苹果和荔枝这两种类别的水果分开了(如左下图),这种情况就是线性可分的。但是如果苹果和荔枝的放置位置是苹果包围荔枝的局面(如右下图),就无法通过一条直线将它们分开(即这样的直线是不存在的),这种情况则是线性不可分的情形。

image.png
因此,只有当样本数据是线性可分的,才能找到一条线性分割线或分割面等,SVM分类才能成立。假如样本特征数据是线性不可分的,则这样的线性分割线或分割面是根本不存在的,SVM分类也就无法实现。
在二维的平面课桌上,一条直线就足以将桌面一分为二。但如果扩展到三维空间中,则需要一个平面(比如一面墙、一扇屏风等)才能将立体空间区域一分为二。
如何处理线性不可分?
在前面苹果和荔枝的例子当中,我们已经了解到 SVM 要求样本数据是线性可分的,这样才会存在分类超平面。而如果样本数据是非线性的情况,那将如何处理呢?SVM的解决办法就是先将数据变成线性可分的,再构造出最优分类超平面。
SVM 通过选择一个核函数 K ,将低维非线性数据映射到高维空间中。原始空间中的非线性数据经过核函数映射转换后,在高维空间中变成线性可分的数据,从而可以构造出最优分类超平面。
如下图所示:原始样本数据在二维空间里无法线性分割,经过核函数映射到三维空间中则可构造出分类超平面进行二类划分。
image.png
举个形象的例子:一把瓜子和一把钢珠混在一起铺在桌面,它们在二维空间是线性不可分的。如果用手在桌面上用力一拍,瓜子会弹得更高,在三维空间,瓜子和钢珠就变成了线性可分的了。

第二步 车牌的识别

在第一步中定位到了车牌整体的轮廓,还要继续把车牌中的字符Mat抠出来,把每一个字符Mat识别对应的文字。

  1. 如何抠字符?
    第一步定位后的的车牌先转灰度图,然后二值化,得到了 image.png
    可以发现底部有一些干扰(固定螺丝),可以逐行读取Mat,分析黑白跳变的次数,如果次数较少就可以把这一行的数据全部转为黑色,得到下图:
    image.png

接下来查找轮廓findContours得到轮廓的Poin集合,再用boundingRect包裹Pont集合得到若干个矩形Rect,按照宽高情况可以筛除一下不是字符的Rect,最终得到字符的Rect集合,绘制到原图中这个样子:

image.png

然后可以在整个车牌图片宽度的2/7左右出找到城市代号字母,响应的它的左边就是省份的简称汉子。可以把这些字符分为两类:汉字+字母or数字,接下来把抠出来的这些文字图片识别即可。

  1. 如何识别字符?
    刚抠出来的字符Mat是二值化后的,接下来:
  2. 提取特征,把特征置为一行。
  3. 调用ANN_MLP测评特征。汉字由于是省份的简称数量有限,因此训练模型时可以将汉字作为单独一个模型,每个省份简称汉字单独一个文件夹存放:


    image.png
    image.png

    这样训练出来的模型测评就会返回最匹配的文件下下标,我们就可以知道对应的汉字是什么了。
    同理,可以把0-9 和A-Z这些字符在一个模型中测评。


    image.png
    image.png
  1. 所有的字符抠图经过测评后,就可以拼接成最终的车牌号了。
上一篇下一篇

猜你喜欢

热点阅读