OpenCV实现单目尺寸估计的案例详解

一个摄像头无法获取深度信息,两个摄像头双目标定可以实现双目测距。

但是我现在只有一个摄像头,如果想实现测量尺寸,我的思路:选一张固定尺寸的背景,例如一张A4纸,从图像中提取A4纸并进行透视变换进行图像矫正,A4纸的尺寸我可以确定,那么也可以确定图像中的物体长宽信息(高度忽略不计的情况,例如:卡片)。当摄像头距离目标物距离L,此时像素所占面积为S,那么理论上来说,目标物图像变化后的面积为S1,则距离L1=(L/S)*S1,假定目标物上面贴有很多个面积为1平方厘米的正方形贴纸,那么获取四个角点和四条边的信息通过算法可以获取出物体在深度方向上的偏移量。有想法就实践。

1.在地板上放一张A4纸随便放一些物体。利用opencv打开摄像头获取图像并显示。

2.转灰度图像

3.如果直接使用canny的画周围地板的线条不好去除,所以先二值化分割。

4.观察图像中存在噪点,使用中值滤波处理

5.使用canny进行边缘检测

6.使用累加器方法进行直线拟合

7.得到了四条线段,此时可以求交点,但是我这里为了方便直接角点检测

8.得到角点排序后进行透视变换

实现1-8效果代码:

#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
double x_1[4];
double y_1[4];
double x_2[4];
double y_2[4];
double line_k[4];
double line_b[4];
int line_number=0;

// 获取交点
//void getCross()
//{
//    for (int i = 0; i <line_number; i++)
//    {
//        for(int j=i+1;j<line_number;j++)
//        {
//            if(int(abs(line_k[i]))==0&&int(abs(line_k[j]))==0)
//            {
//                cout<<"i:"<<i<<" j:"<<j<<" is "<<" true"<<endl;
//            }
//        }
//    }
//}
void drawLine(Mat &img, //要标记直线的图像
      vector<Vec2f> lines,   //检测的直线数据
      double rows,   //原图像的行数(高)
     double cols,  //原图像的列数(宽)
     Scalar scalar,  //绘制直线的颜色
     int n  //绘制直线的线宽
 )
 {
     int image_channels=img.channels();
     Point pt1, pt2;
     for (size_t i = 0; i < lines.size(); i++)
     {
        float rho = lines[i][0];  //直线距离坐标原点的距离
         float theta = lines[i][1];  //直线过坐标原点垂线与x轴夹角
         double a = cos(theta);  //夹角的余弦值
         double b = sin(theta);  //夹角的正弦值
         double x0 = a*rho, y0 = b*rho;  //直线与过坐标原点的垂线的交点
         double length = max(rows, cols);  //图像高宽的最大值
                                           //计算直线上的一点
         pt1.x = cvRound(x0 + length  * (-b));
         pt1.y = cvRound(y0 + length  * (a));
         //计算直线上另一点
         pt2.x = cvRound(x0 - length  * (-b));
         pt2.y = cvRound(y0 - length  * (a));
         //两点绘制一条直线
         if(i==0&&image_channels!=1)
         {
             scalar=Scalar(255,0,0);//blue
         }
         else if(i==1&&image_channels!=1)
         {
             scalar=Scalar(255,255,0);//yellow
         }
         else if(i==2&&image_channels!=1)
         {
             scalar=Scalar(0,0,255);//red
         }
         else if(i==3&&image_channels!=1)
         {
             scalar=Scalar(0,255,0);//green
         }
         else;

         if(image_channels==1)
         {
             scalar=Scalar(255,255,255);
         }

         line(img, pt1, pt2, scalar, n);
         //计算直线方程
         x_1[i]=pt1.x;
         y_1[i]=pt1.y;
         x_2[i]=pt2.x;
         y_2[i]=pt2.y;

         line_k[i]=(y_2[i]-y_1[i])/(x_2[i]-x_1[i]);
         line_b[i]=y_1[i]-line_k[i]*x_1[i];
         cout<<i+1<<":"<<"y="<<line_k[i]<<"*x+"<<line_b[i]<<endl;
     }
     cout<<"lines_number:"<<lines.size()<<endl;
     line_number=lines.size();
//     getCross();
}
int main(int argc, char *argv[])
{
    VideoCapture cap;
    cap.open(0);
    Mat frame;
    Mat src;
    while(line_number!=4)
    {
        cap>>frame;
        src=frame;
        imshow("frame",frame);
        Mat frame_gray;
        cvtColor(frame,frame_gray,COLOR_BGR2GRAY);
        imshow("frame_gray",frame_gray);

        Mat frame_threshold;
        threshold(frame_gray,frame_threshold,160,255,THRESH_BINARY);//frame_gray(x,y)>160  frame_threshold(x,y)=255 else 0
        imshow("frame_threshold",frame_threshold);

        Mat frame_medianBlur;
        medianBlur(frame_threshold, frame_medianBlur, 3);
        imshow("frame_medianBlur",frame_medianBlur);

        Mat frame_Canny;
        Canny(frame_medianBlur, frame_Canny, 10, 180, 3, false);
        imshow("frame_Canny",frame_Canny);

        //累加器进行检测直线
        vector<Vec2f> lines;
        HoughLines(frame_Canny, lines, 1, CV_PI / 180, 100, 0, 0);
        Mat frame_HoughLines=frame;
        drawLine(frame_HoughLines, lines, frame_HoughLines.rows, frame_HoughLines.cols, Scalar(0,0,0), 1);
        imshow("frame_HoughLines",frame_HoughLines);

        Mat frame_zeros = Mat::zeros(frame_HoughLines.rows, frame_HoughLines.cols, CV_8UC1);
        drawLine(frame_zeros, lines, frame_HoughLines.rows, frame_HoughLines.cols, Scalar(0,0,0), 1);
        imshow("frame_zeros",frame_zeros);

        vector<Point2f> conners;//检测到的角点
        int maxConers = 4;//检测角点上限
        double qualityLevel = 0.1;//最小特征值
        double minDistance = 20;//最小距离
        Mat frame_ShiTomasi=frame;
        goodFeaturesToTrack(frame_zeros, conners, maxConers, qualityLevel, minDistance);
        cout<<"Shi-Tomasi(x,y):"<<conners<<endl;
        //角点绘制
        for (int i = 0; i < conners.size(); i++)
        {
//            string text=to_string(i)+"(x,y):"+"("+to_string((int)conners[i].x)+","+to_string((int)conners[i].y)+")";
//            cv::putText(frame_ShiTomasi, text, conners[i], FONT_HERSHEY_COMPLEX, 0.6, cv::Scalar(0, 0, 0), 1.4, 8, 0);
            circle(frame_ShiTomasi, conners[i], 3, Scalar(0,255,0), 2, 8, 0);
        }
        //分割四个坐标
        int width_flag=frame_HoughLines.cols/2;
        int height_flag=frame_HoughLines.rows/2;
        vector<Point2f>srcpoint(4);//存放变换前四顶点
        for (int i = 0; i < conners.size(); i++)
        {
            if(conners[i].x<width_flag&&conners[i].y<height_flag)
            {
//                cv::putText(frame_ShiTomasi, "left1", conners[i], FONT_HERSHEY_COMPLEX, 0.6, cv::Scalar(0, 0, 0), 1.4, 8, 0);
                srcpoint[0]=conners[i];
            }
            else if(conners[i].x>width_flag&&conners[i].y<height_flag)
            {
//                cv::putText(frame_ShiTomasi, "right1", conners[i], FONT_HERSHEY_COMPLEX, 0.6, cv::Scalar(0, 0, 0), 1.4, 8, 0);
                srcpoint[1]=conners[i];
            }
            else if(conners[i].x<width_flag&&conners[i].y>height_flag)
            {
//                cv::putText(frame_ShiTomasi, "left2", conners[i], FONT_HERSHEY_COMPLEX, 0.6, cv::Scalar(0, 0, 0), 1.4, 8, 0);
                srcpoint[2]=conners[i];
            }
            else if(conners[i].x>width_flag&&conners[i].y>height_flag)
            {
//                cv::putText(frame_ShiTomasi, "right2", conners[i], FONT_HERSHEY_COMPLEX, 0.6, cv::Scalar(0, 0, 0), 1.4, 8, 0);
                srcpoint[3]=conners[i];
            }
            else;
        }
        imshow("frame_ShiTomasi",frame_ShiTomasi);
        waitKey(30);
        //透视变换
        vector<Point2f>dstpoint(4);//存放变换后四顶点
        //mm
        float a4_width=2100/4;
        float a4_height=2970/4;

        Mat result = Mat::zeros(a4_width, a4_height,frame.type());
        //定义矫正后四顶点
        dstpoint[0] = Point2f(0, result.rows);
        dstpoint[1] = Point2f(0, 0);
        dstpoint[2] = Point2f(result.cols, result.rows);
        dstpoint[3] = Point2f(result.cols, 0);
        Mat M = getPerspectiveTransform(srcpoint, dstpoint);

        Mat frame_result=src;
        imshow("1",frame_result);
        warpPerspective(frame_result, result, M, result.size());
        imshow("result", result);
    }
    cap.release();
    waitKey(0);
    return 0;
}

9.进行尺寸估计(将矫正后图像传入,最小外接矩形,然后阈值划分,取出区域求长宽,按照比例关系估计最后的长宽比)下面代码仅仅实现了找出最小矩形和输出一些点信息。由于时间有限,计算距离算法部分后续更新。

void get_dist(Mat src)
{
    cvtColor(src,src,COLOR_BGR2GRAY);
    threshold(src,src,160,255,THRESH_BINARY);//frame_gray(x,y)>160  frame_threshold(x,y)=255 else 0
    medianBlur(src, src, 3);
    Canny(src, src, 10, 180, 3, false);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(src, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point());
    vector<Rect> boundRect(contours.size());
    Mat drawingPicture = Mat::zeros(src.size(), CV_8UC1); //最小外接矩形画布
    int width_i=2100/8;
    int height_i=2970/8;
    for (int i = 0; i < contours.size(); i++)
    {
        //绘制轮廓的最小外结矩形
        RotatedRect rect = minAreaRect(contours[i]);
        Point2f P[4];
        rect.points(P);
        for (int j = 0; j <= 3; j++)
        {
            line(src, P[j], P[(j + 1) % 4], Scalar(255), 1);
            cout<<"P[j],P[(j + 1) % 4]:"<<P[j]<<","<< P[(j + 1) % 4]<<endl;
        }
    }
    imshow("dist",src);
}

以上就是OpenCV实现单目尺寸估计的案例详解的详细内容,更多关于OpenCV单目尺寸估计的资料请关注我们其它相关文章!

(0)

相关推荐

  • 基于python opencv单目相机标定的示例代码

    相机固定不动,通过标定版改动不同方位的位姿进行抓拍 import cv2 camera=cv2.VideoCapture(1) i = 0 while 1: (grabbed, img) = camera.read() cv2.imshow('img',img) if cv2.waitKey(1) & 0xFF == ord('j'): # 按j保存一张图片 i += 1 u = str(i) firename=str('./img'+u+'.jpg') cv2.imwrite(firename

  • Python Opencv实现单目标检测的示例代码

    一 简介 目标检测即为在图像中找到自己感兴趣的部分,将其分割出来进行下一步操作,可避免背景的干扰.以下介绍几种基于opencv的单目标检测算法,算法总体思想先尽量将目标区域的像素值全置为1,背景区域全置为0,然后通过其它方法找到目标的外接矩形并分割,在此选择一张前景和背景相差较大的图片作为示例. 环境:python3.7 opencv4.4.0 二 背景前景分离 1 灰度+二值+形态学 轮廓特征和联通组件 根据图像前景和背景的差异进行二值化,例如有明显颜色差异的转换到HSV色彩空间进行分割. 1

  • C++利用opencv实现单目测距的实现示例

    闲来无事,用C++做了一个简易的单目测距.算法用的cv自带的,改改参数就行.实现了读取照片测距,读取笔记本摄像头测距,读取视频测距三个功能. 为什么不用双目测距?因为没钱买摄像头-- 原理:相似三角形原理,已知焦距的情况下检测被测物在图片中所占的像素宽度来判断被测物与摄像头的距离,同时也可以得出被测物的大概长宽.注意:摄像头要和被测物体平行,如果不平行在侧面看来摄像头和物体之间的连线就与水平方向有一个夹角a,被测物体在图片中的大小会受到影响,从而影响测量效果. 误差分析:导致测量效果不好的原因有

  • OpenCV实现单目尺寸估计的案例详解

    一个摄像头无法获取深度信息,两个摄像头双目标定可以实现双目测距. 但是我现在只有一个摄像头,如果想实现测量尺寸,我的思路:选一张固定尺寸的背景,例如一张A4纸,从图像中提取A4纸并进行透视变换进行图像矫正,A4纸的尺寸我可以确定,那么也可以确定图像中的物体长宽信息(高度忽略不计的情况,例如:卡片).当摄像头距离目标物距离L,此时像素所占面积为S,那么理论上来说,目标物图像变化后的面积为S1,则距离L1=(L/S)*S1,假定目标物上面贴有很多个面积为1平方厘米的正方形贴纸,那么获取四个角点和四条

  • OpenCV实现单目摄像头对图像目标测距

    使用opencv对单目摄像头中的目标实现测量距离(python实现),供大家参考,具体内容如下 1.方法介绍: 根据相似三角形的方法: F = P×D / W , 其中W为物体的实际宽度,D为物体平面与相机平面的距离,照片上物体的像素宽度为P,相机焦距为FD = F×W / P ,这样知道相机的内参焦距就可以求得物体平面与相机平面的距离D 2.代码: import cv2 import imutils import numpy as np from imutils import paths #

  • HTML form表单提交方法案例详解

    form表单提交方式总结一下: 一.利用submit按钮实现提交,当点击submit按钮时,触发onclick事件,由JavaScript里函数判断输入内容是否为空,如果为空,返回false, 不提交,如果不为空,提交到由action指定的地址. <script type="text/javascript"> function check(form) { if(form.userId.value=='') { alert("请输入用户帐号!"); for

  • C++ opencv霍夫圆检测使用案例详解

    本程序是一个最简单的霍夫圆检测函数的使用案例,刚刚学会的用法,发一下,可以参考,参数啥的可根据图片调节. #pragma once #include<quickopencv.h> #include<vector> #include <stdio.h> #include <iostream> #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgpr

  • Python+Opencv实现物体尺寸测量的方法详解

    目录 1.效果展示 2.项目介绍 3.项目搭建 4.utils.py文件代码展示与讲解 5.项目代码展示与讲解 6.项目资源 7.项目总结 1.效果展示 我们将以两种方式来展示我们这个项目的效果. 下面这是视频的实时检测,我分别用了盒子和盖子来检测,按理来说效果不应该怎么差的,但我实在没有找到合适的背景与物体.且我的摄像头使用的是外设,我不得不手持,所以存在一点点的抖动,但我可以保证,它是缺少了适合检测物体与背景. 我使用手机拍了一张照片并经过了ps修改了背景,效果不错. 2.项目介绍 本项目中

  • Python实现图片压缩的案例详解

    目录 1.引言 2.PIL模块 2.1 quality 方式 2.2 thumbnail方式 3.OpenCV模块 3.1 安装 3.2 执行代码 4.总结 1.引言 小屌丝:鱼哥,求助,求助 小鱼:啥情况,这火急火燎的? 小屌丝: 我要在某站进行认证,上传图片时提示,图片超过本站最大xxx限制. 小鱼:就这?? 小屌丝:对啊,我又不想换照片,又不像照片失真. 小鱼:就这要求? 小屌丝:对,能赶紧帮我不处理不? 小鱼:嗯~ 理论上是可以. 小屌丝:什么都别说,我懂,枸杞一袋! 小鱼:懂我,五分钟

  • Python OpenCV学习之特征点检测与匹配详解

    目录 背景 一.Harris角点 二.Shi-Tomasi角点检测 三.SIFT关键点 四.SIFT描述子 五.SURF 六.ORB 七.暴力特征匹配(BF) 八.FLANN特征匹配 九.图像查找 总结 背景 提取图像的特征点是图像领域中的关键任务,不管在传统还是在深度学习的领域中,特征代表着图像的信息,对于分类.检测任务都是至关重要的: 特征点应用的一些场景: 图像搜索:以图搜图(电商.教育领域) 图像拼接:全景拍摄(关联图像拼接) 拼图游戏:游戏领域 一.Harris角点 哈里斯角点检测主要

  • OpenCV学习之图像的分割与修复详解

    目录 背景 一.分水岭法 二.GrabCut法 三.MeanShift法 四.MOG前景背景分离法 五.拓展方法 六.图像修复 总结 背景 图像分割本质就是将前景目标从背景中分离出来.在当前的实际项目中,应用传统分割的并不多,大多是采用深度学习的方法以达到更好的效果:当然,了解传统的方法对于分割的整体认知具有很大帮助,本篇将介绍些传统分割的一些算法: 一.分水岭法 原理图如下: 利用二值图像的梯度关系,设置一定边界,给定不同颜色实现分割: 实现步骤: 标记背景 —— 标记前景 —— 标记未知区域

  • Vue 过渡(动画)transition组件案例详解

    Vue过度(动画),本质走的是CSS3:transtion,animation. 控制器div显示/隐藏,代码如下: <div id="box"> <input type="button" value="按钮" @click="toggle"> <div id="div1" v-show="isShow"></div> </div&g

  • Apache 文件上传与文件下载案例详解

    写一个Apache文件上传与文件下载的案例:以供今后学习 web.xml配置如下: <span style="font-family:SimSun;font-size:14px;"><?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns=&

随机推荐