opencv手形识别

Home / C++ MrLee 2017-2-14 4836

1)获取视频帧
2)将视频帧转换到YCrCb颜色空间,并分割通道
3)基于CrCb两个通道做肤色区域的分割,得到肤色区域二值图像
4)将二值图像分别做膨胀和腐蚀处理,得到前景和背景的标记(marker)图像,应用分水岭算法,得到大块肤色区域的边缘轮廓
5)对4)中得到的边缘轮廓用8向种子算法处理,对不同的肤色区域做了标记,并返回了不同肤色区域的边界范围,这些肤色区域作为人手区域的候选区域
6)将5)中得到的候选区域与准备好的人手模板(Cr通道)进行模板匹配,匹配前先将候选区域缩放到与模板相同的大小;使用的方法是平方差匹配法,得到每个候选区域的匹配值(越小越接近)
7)对5)中得到的候选区域中肤色像素的比例进行统计
8)根据6)与7)中得到的结果对候选区域进行筛选,认为匹配值 <0.02(0为最匹配,1为最不匹配),且肤色区域比例<0.65的区域为人手区域(因为人脸区域一般肤色占比比较高)
9)在输出帧中对确定的人手区域画长方形框做标记
 
讨论:
1)之所以使用肤色区域比例的筛选方法,是因为基于肤色的情况下,人脸和人手非常容易混淆,经过尝试发现增进模板的数量效果并不好,因此采用了这种方法,但这种方法带来的问题就是人手必须张开(从而降低肤色在候选框中的比例)才能稳定被找到。
2)其实不做模板匹配只统计肤色比例应该也是可以的,但是稳定性会比较差。
3)分水岭算法本站有
#include "stdafx.h"  
#include <iostream>  
#include <vector>  
#include <string>  
#include <list>  
#include <map>  
#include <stack>  
#include <opencv2/core/core.hpp>  
#include <opencv2/features2d/features2d.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
#include <opencv2/calib3d/calib3d.hpp>  
  
  
using namespace std;  
using namespace cv;  
  
  
//8邻接种子算法,并返回每块区域的边缘框  
void Seed_Filling(const cv::Mat& binImg, cv::Mat& labelImg, int& labelNum, int (&ymin)[20], int(&ymax)[20], int(&xmin)[20], int(&xmax)[20])   //种子填充法    
{  
    if (binImg.empty() ||  
        binImg.type() != CV_8UC1)  
    {  
        return;  
    }  
  
    labelImg.release();  
    binImg.convertTo(labelImg, CV_32SC1);  
    int label = 1;  
    int rows = binImg.rows - 1;  
    int cols = binImg.cols - 1;  
    for (int i = 1; i < rows - 1; i++)  
    {  
        int* data = labelImg.ptr<int>(i);  
        for (int j = 1; j < cols - 1; j++)  
        {  
            if (data[j] == 1)  
            {  
                std::stack<std::pair<int, int>> neighborPixels;  
                neighborPixels.push(std::pair<int, int>(j, i));     // 像素位置: <j,i>    
                ++label;  // 没有重复的团,开始新的标签   
                ymin[label] = i;  
                ymax[label] = i;  
                xmin[label] = j;  
                xmax[label] = j;  
                while (!neighborPixels.empty())  
                {  
                    std::pair<int, int> curPixel = neighborPixels.top(); //如果与上一行中一个团有重合区域,则将上一行的那个团的标号赋给它    
                    int curX = curPixel.first;  
                    int curY = curPixel.second;  
                    labelImg.at<int>(curY,curX) = label;  
  
                    neighborPixels.pop();  
  
                    if ((curX>0)&&(curY>0)&&(curX<(cols-1))&&(curY<(rows-1)))  
                    {   
                        if (labelImg.at<int>(curY - 1,curX) == 1)                     //上  
                        {  
                            neighborPixels.push(std::pair<int, int>(curX, curY - 1));  
                            //ymin[label] = curY - 1;  
                        }  
                        if (labelImg.at<int>( curY + 1,curX) == 1)                        //下  
                        {  
                            neighborPixels.push(std::pair<int, int>(curX, curY + 1));  
                            if ((curY+1)>ymax[label])  
                                ymax[label] = curY + 1;  
                        }  
                        if (labelImg.at<int>(curY,curX - 1) == 1)                     //左  
                        {  
                            neighborPixels.push(std::pair<int, int>(curX - 1, curY));  
                            if ((curX - 1)<xmin[label])  
                                xmin[label] = curX - 1;  
                        }  
                        if (labelImg.at<int>(curY,curX + 1) == 1)                     //右  
                        {  
                            neighborPixels.push(std::pair<int, int>(curX + 1, curY));  
                            if ((curX + 1)>xmax[label])  
                                xmax[label] = curX + 1;  
                        }  
                        if (labelImg.at<int>(curY - 1,curX-1) == 1)                   //左上  
                        {    
                            neighborPixels.push(std::pair<int, int>(curX - 1, curY - 1));  
                            //ymin[label] = curY - 1;  
                            if ((curX - 1)<xmin[label])  
                                xmin[label] = curX - 1;  
                        }  
                        if (labelImg.at<int>(curY + 1,curX+1) == 1)                   //右下  
                        {    
                            neighborPixels.push(std::pair<int, int>(curX+1, curY + 1));  
                            if ((curY + 1)>ymax[label])  
                                ymax[label] = curY + 1;  
                            if ((curX + 1)>xmax[label])  
                                xmax[label] = curX + 1;  
  
                        }  
                        if (labelImg.at<int>( curY + 1,curX - 1) == 1)                    //左下  
                        {   
                            neighborPixels.push(std::pair<int, int>(curX - 1, curY+1));  
                            if ((curY + 1)>ymax[label])  
                                ymax[label] = curY + 1;  
                            if ((curX - 1)<xmin[label])  
                                xmin[label] = curX - 1;  
                        }  
                        if (labelImg.at<int>( curY - 1,curX + 1) == 1)                    //右上  
                        {  
                            neighborPixels.push(std::pair<int, int>(curX + 1, curY-1));  
                            //ymin[label] = curY - 1;  
                            if ((curX + 1)>xmax[label])  
                                xmax[label] = curX + 1;  
  
                        }  
                    }  
                }  
            }  
        }  
    }  
    labelNum = label-1;  
  
}  
  
class WatershedSegmenter {  
private:  
    cv::Mat markers;  
public:  
    void setMarkers(const cv::Mat& markerImage) {  
  
        // Convert to image of ints  
        markerImage.convertTo(markers, CV_32S);  
    }  
    cv::Mat process(const cv::Mat &image) {  
  
        // Apply watershed  
        cv::watershed(image, markers);  
        return markers;  
    }  
    // Return result in the form of an image  
    cv::Mat getSegmentation() {  
  
        cv::Mat tmp;  
        // all segment with label higher than 255  
        // will be assigned value 255  
        markers.convertTo(tmp, CV_8U);  
        return tmp;  
    }  
  
    // Return watershed in the form of an image  
    cv::Mat getWatersheds() {  
        cv::Mat tmp;  
        markers.convertTo(tmp, CV_8U,255, 255);  
        return tmp;  
    }  
};  
  
  
  
  
  
int main()  
{  
//设置视频读入,括号里面的数字是摄像头的选择,一般自带的是0  
cv::VideoCapture cap(0);  
if (!cap.isOpened())  
{  
    return -1;  
}  
Mat frame;  
Mat binImage,tmp;  
Mat Y, Cr, Cb;  
vector<Mat> channels;  
//模板图片,是Cr颜色通道的人手图像截图  
Mat tmpl = imread("bwz.jpg",CV_8UC1);  
  
  
bool stop = false;  
while (!stop)  
{  
    //读入视频帧,转换颜色空间,并分割通道  
    cap >> frame;  
    cvtColor(frame, binImage, CV_BGR2GRAY);  
    frame.copyTo(tmp);  
    cvtColor(tmp, tmp, CV_BGR2YCrCb);  
    split(tmp, channels);  
    Cr = channels.at(1);  
    Cb = channels.at(2);  
  
    //肤色检测,输出二值图像  
    for (int j = 1; j < Cr.rows - 1; j++)  
    {  
        uchar* currentCr = Cr.ptr< uchar>(j);  
        uchar* currentCb = Cb.ptr< uchar>(j);  
        uchar* current = binImage.ptr< uchar>(j);  
        for (int i = 1; i < Cb.cols - 1; i++)  
        {  
            if ((currentCr[i] > 140) && (currentCr[i] < 170)  &&(currentCb[i] > 77) && (currentCb[i] < 123))  
                current[i] = 255;  
            else  
                current[i] = 0;  
        }  
    }  
  
    //形态学处理  
    //dilate(binImage, binImage, Mat());  
    dilate(binImage, binImage, Mat());  
  
    //分水岭算法  
    cv::Mat fg;  
    cv::erode(binImage, fg, cv::Mat(), cv::Point(-1, -1), 6);  
    // Identify image pixels without objects  
    cv::Mat bg;  
    cv::dilate(binImage, bg, cv::Mat(), cv::Point(-1, -1), 6);  
    cv::threshold(bg, bg, 1, 128, cv::THRESH_BINARY_INV);  
    // Show markers image  
    cv::Mat markers(binImage.size(), CV_8U, cv::Scalar(0));  
    markers = fg + bg;  
    // Create watershed segmentation object  
    WatershedSegmenter segmenter;  
    segmenter.setMarkers(markers);  
    segmenter.process(frame);  
    Mat waterShed;  
    waterShed = segmenter.getWatersheds();  
    //imshow("watershed", waterShed);  
    //获得区域边框  
    threshold(waterShed, waterShed, 1, 1, THRESH_BINARY_INV);  
      
    //8向种子算法,给边框做标记  
    Mat labelImg;  
    int label, ymin[20], ymax[20], xmin[20], xmax[20];  
    Seed_Filling(waterShed, labelImg, label, ymin, ymax, xmin, xmax);  
  
    //根据标记,对每块候选区就行缩放,并与模板比较  
    Size dsize = Size(tmpl.cols, tmpl.rows);  
    float simi[20];  
    for (int i = 0; i < label; i++)  
    {  
        simi[i] = 1;  
        if (((xmax[2 + i] - xmin[2 + i])>50) && ((ymax[2 + i] - ymin[2 + i]) > 50))  
        {  
            //rectangle(frame, Point(xmin[2 + i], ymin[2 + i]), Point(xmax[2 + i], ymax[2 + i]), Scalar::all(255), 2, 8, 0);  
            Mat rROI = Mat(dsize, CV_8UC1);  
            resize(Cr(Rect(xmin[2 + i], ymin[2 + i], xmax[2 + i] - xmin[2 + i], ymax[2 + i] - ymin[2 + i])), rROI, dsize);  
            Mat result;  
            matchTemplate(rROI, tmpl, result, CV_TM_SQDIFF_NORMED);  
            simi[i] = result.ptr<float>(0)[0];  
            //cout << simi[i] << endl;  
        }  
    }  
      
    //统计一下区域中的肤色区域比例  
    float fuseratio[20];  
    for (int k = 0; k < label; k++)  
    {  
        fuseratio[k] = 1;  
        if (((xmax[2 + k] - xmin[2 + k])>50) && ((ymax[2 + k] - ymin[2 + k]) > 50))  
        {  
            int fusepoint=0;  
            for (int j = ymin[2+k]; j < ymax[2+k]; j++)  
            {  
                uchar* current = binImage.ptr< uchar>(j);  
                for (int i = xmin[2+k]; i < xmax[2+k]; i++)  
                {  
                    if (current[i] == 255)  
                        fusepoint += 1;  
                }  
            }  
            fuseratio[k] = float(fusepoint) / ((xmax[2 + k] - xmin[2 + k])*(ymax[2 + k] - ymin[2 + k]));  
            //cout << fuseratio[k] << endl;  
        }  
    }  
  
    //给符合阈值条件的位置画框  
    for (int i = 0; i < label; i++)  
    {  
        if ((simi[i]<0.02)&&(fuseratio[i]<0.65))  
            rectangle(frame, Point(xmin[2 + i], ymin[2 + i]), Point(xmax[2 + i], ymax[2 + i]), Scalar::all(255), 2, 8, 0);  
    }  
      
    imshow("frame", frame);  
    //processor.writeNextFrame(frame);  
    imshow("test", binImage);  
  
    if (waitKey(1) >= 0)  
        stop = true;  
}  
cout << "ss" << endl;  
//cv::waitKey();  
return 0;  
}  

其中的模板图片为

本文链接:https://www.it72.com/11814.htm

推荐阅读
最新回复 (0)
返回