zoukankan      html  css  js  c++  java
  • openCV 简单实现身高测量(未考虑相机标定,windows)

    (一) OpenCV3.1.0+VS2015开发环境配置

    • 下载OpenCV安装包(笔者下载3.1.0版本)
    • 环境变量配置(opencv安装路径uildx64vc14in,注意的是x64文件夹下分为vc12和vc14两个文件夹,他们对应于VS的版本,vc8 = Visual Studio 2005,vc9 = Visual Studio 2008,vc10 = Visual Studio 2010,vc11 = Visual Studio 2012,vc12 = Visual Studio 2013,vc14 = Visual Studio 2015)
    • VS2015配置。进入属性管理器(View—>Other Windows—>Property Manger,展开目录,选中Debug|Win64中的Microsoft.Cpp.x64.user,并右键点击属性(Properties)进入属性界面。)
      1.  包含目录配置(通用属性(Common Properties)—>VC ++目录—>包含目录(Include Directories))。添加路径:D:CodeCopencvuildinclude;D:CodeCopencvuildincludeopencv;D:CodeCopencvuildincludeopencv2
      2. 配置库文件目录(Library Directories)。D:CodeCopencvuildx64vc14lib(注意VS版本)
      3. 配置动态链接库(Linker(链接库)—>Input(输入)—>Additional Dependencies(添加依赖))。D:CodeCopencvuildx64vc12in路径下的。opencv_world310.lib和opencv_world310d.lib,这里两个库文件的区别就是:opencv_world310.lib是Release模式版本,而opencv_world310d.lib是Debug模式版本。

    (二)算法思路

    • HOG特征提取获得定位矩形框,笔者使用SVM分类优化+多尺度检测获得带有矩形框的dst
    /******************************HOG detector************************************************/
        dst = src.clone();
        vector<Rect> findrects, findrect;
        HOGDescriptor HOG;
        //SVM分类器
        HOG.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
        //多尺度检测
        HOG.detectMultiScale(src, findrects, 0, Size(4, 4), Size(0, 0), 1.05, 2);
        //若rects有嵌套,则取最外面的矩形存入rect
        for (int i = 0; i < findrects.size(); i++)
        {
            Rect rect = findrects[i];
            int j = 0;
            for (; j < findrects.size(); j++)
                if (j != i && (rect & findrects[j]) == rect)
                    break;
            if (j == findrects.size())
                findrect.push_back(rect);
        }
        Rect r;//用来选中所测图像中的测量对象。
        r.height = -1;
        //框选出检测结果并选中图片测量对象
        for (int i = 0; i < findrect.size(); i++)
            r = r.height > findrect[i].height ? r : findrect[i];
      //HOG detector返回的矩形框比真正图像轮廓大,所以我们减少矩形框的大小来使得更符合外界边框
        r.x += cvRound(r.width*0.1);
        r.width = cvRound(r.width*0.8);
        r.y += cvRound(r.height*0.07);
        r.height = cvRound(r.height*0.8);
        Scalar color = Scalar(0,0,255);
        rectangle(dst, r.tl(), r.br(), color, 2);
    //    imshow("src", src);
    //    imshow("dst", dst);

    原图像和得到的dst图像

    • Grabcut算法分割
    /********************************grabCut**********************************************/
        cv::Mat mask = Mat::zeros(src.size(), CV_8UC1);//分割后的结果
        //两个临时矩阵变量,作为算法的中间变量使用
        cv::Mat bgModel, fgModel;
        cv::Rect rectangle(r.tl(),r.br());//图像的前景对象也就是矩形选中图像
        // GrabCut 分段
        cv::grabCut(src,    //输入图像
            mask,   //分段结果
            rectangle,// 包含前景的矩形 
            bgModel, fgModel, // 前景、背景
            1,        // 迭代次数
            cv::GC_INIT_WITH_RECT); // 用矩形
        
        // 得到可能是前景的像素
        //比较函数保留值为GC_PR_FGD的像素
        cv::compare(mask, cv::GC_PR_FGD, mask, cv::CMP_EQ);
        // 产生输出图像
        cv::Mat foreground(src.size(), CV_8UC3, cv::Scalar(0, 0, 0));
        //背景值为 GC_BGD=0,作为掩码
        src.copyTo(foreground, mask);
        imshow("foreground", foreground);

    处理后图像

    • BorderMatting边缘细化处理(由于版权问题,没有相应接口需自己编写)主要思想:把前景颜色值与估计值对比,选择较小的差异值颜色代替,使得平滑最好
    /********************************BorderMatting**********************************************/
        BorderMatting bm;
        Mat rst = Mat(src.size(), src.type());
        Mat rstBm;
        src.copyTo(rst);
        for (int i = 0; i<rst.rows; i++)
            for (int j = 0; j < rst.cols; j++)
            {
                if (mask.at<uchar>(i, j) == 0)
                    rst.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
            }
        bm.Initialize(src, mask);
        rstBm=bm.Run();
        imshow("bordingmatting", rstBm);

    笔者自己创建的BorderMatting类。

    结果图:

    •  灰度化、二值化便于计算
    /******************************灰度化、二值化************************************************/
    
        Mat  rstGray, rstBin;
        cvtColor(rstBm, rstGray, CV_BGR2GRAY);//灰度化
    //    imshow("灰度图", rstGray);
        threshold(rstGray, rstBin, 100, 255, CV_THRESH_BINARY);//二值化
        imshow("二值图", rstBin);

    •  最长像素距离计算。笔者一开始用欧氏距离遍历计算得到,结果处理时间过长,就采用最简单的简化方法,最长像素点距离≈最高像素点与最低像素点欧氏距离。
    /**********************************计算最长像素距离********************************************/
        double MAXPX = 0;
        int x1 = 0, y1 = 0;
        int x2 = 0, y2 = 0;
        int maxX1, maxX2, maxY1, maxY2;
        int i, j;
        int flag = 0;
        for (i = 0; i < rstBin.rows; i++)
        {
            for (j = 0; j < rstBin.cols; j++)
                if (rstBin.at<uchar>(i, j) == 255)
                {
                    x1 = i;
                    y1 = j;
                    flag = 1;
                    break;
                }
            if (flag)
                break;
        }
        flag = 0;
        for (i = rstBin.rows; i >= 0; i--)
        {
            for (j = 0; j < rstBin.cols; j++)
                if (rstBin.at<uchar>(i, j) == 255)
                {
                    x2 = i;
                    y2 = j;
                    flag = 1;
                    break;
                }
            if (flag)
                break;
        }
        cout << x1 << " " << y1 << endl;
        cout << x2 << " " << y2 << endl;
        line(src, Point(y1, x1), Point(y2, x2), Scalar(0, 0, 255), 3);
    /*    for ( i = 1; i <= rstBin.rows*rstBin.cols; i++)//第一个像素点开始遍历
        {
            x1 = i / rstBin.rows;
            y1 = !x1 ? (i - 1) : (i % x1 - 1);
            if (rstBin.at<uchar>(x1, y1) == 0)
                continue;
            for ( j = i + 1; j <= rstBin.rows*rstBin.cols; j++)
            {
                x2 = j / rstBin.rows;
                y2 = !x2 ? (j - 1) : (j % x2 - 1);
                if (rstBin.at<uchar>(x2, y2) == 255)//同为白色像素点时计算距离
                {
                    double distance=abs(x2 - x1) +abs(y2 - y1);//避免欧氏距离计算浪费时间
                    if (MAXPX < distance)
                    {    
                        maxX1 = x1;
                        maxX2 = x2;
                        maxY1 = y1;
                        maxY2 = y2;
                        MAXPX = distance;
                    }
                }
            }
        }
        */
        /*MAXPX = sqrt((maxX1 - maxX2)*(maxX1 - maxX2) + (maxY1 - maxY2)*(maxY1 - maxY2));
        cout << MAXPX << endl;
        cout << maxX1 << " " << maxY1 << endl;
        cout << maxX2 << " " << maxY2 << endl;
        line(src, Point(maxY1, maxX1), Point(maxY2, maxX2), Scalar(0, 0, 255), 3);
        */imshow("final", src);

    注意像素点坐标顺序。结果如下

    像素与毫米的转换 转换还需要知道另一个参数:PPI(每英寸多少像素) 象素数 / PPI = 英寸数 英寸数 * 25.4 = 毫米数

    笔者 设定系统参数焦距(3.5mm)不会变动(图为35mm焦距所拍),物距1.1m,ppi:400(笔者电脑PPI260)

    那么身高大概为:({[((709-152)^2+(281-247)^2)^0.5]/260}*25.4/35)*1.1≈1.71m

  • 相关阅读:
    349. Intersection of Two Arrays
    346. Moving Average from Data Stream
    345. Reverse Vowels of a String
    344. Reverse String
    342. Power of Four
    POJ2823 Sliding Window
    《STL源码剖析》笔记
    [jobdu]扑克牌顺子
    [jobdu]第一个只出现一次的字符
    [jobdu]包含min函数的栈
  • 原文地址:https://www.cnblogs.com/cc-xiao5/p/10785454.html
Copyright © 2011-2022 走看看