摄像头距离标定方法研究(得到像素和毫米的转换比)

     一般在高精度测量时需要做以下几个标定,一光学畸变标定(如果您不是用的软件镜头,一般都必须标定),二投影畸变的标定,也就是因为您安装位置误差代表的图像畸变校正,三物像空间的标定,也就是具体算出每个像素对应物空间的尺寸。

      前两者应该都可以通过“张正友”标定方法进行解决;对于空间的标定,基本上都是通过获得比对现实中的已经知道长度的物体,获得像素当量到长度的转化。
      现实拍摄的物体,或多或少会有噪音干扰,在标定的过程中还需要图像处理算法进行纠正。
摄像头距离标定方法研究(得到像素和毫米的转换比)
     比如我获得这样的图片。这幅图片的获取,基于我自己改造的图像处理支架,对于普通的实验来说,已经比较纯净了。由于使用的是钢尺,反光比较厉害。为了便于标定,我将10厘米和20厘米两个地方用绿色胶带缠绕住。
    下一步,就是要获得关键区域。通过GOPre的分析,原始图像在YUV域下,比较方便分割
摄像头距离标定方法研究(得到像素和毫米的转换比)
编写代码分割,阈值分析
    Mat src = imread("E:\\sandbox\\1.bmp");
    Mat dst;
    Mat tmp;
    vector<MatmatSplit;
    cvtColor(src,tmp,COLOR_BGR2YUV);
    split(tmp,matSplit);
    dst = matSplit[1];
    threshold(dst,dst,100,255, COLOR_BGR2Lab)  );
    cv::waitKey();
摄像头距离标定方法研究(得到像素和毫米的转换比)
这个时候还是有噪音的,但是已经比较理想了。最合适的方法就是投影分析。由于GOCVHelper中的投影分析本来是对字符投影进行分割的,所以需要适当地修正。
void projection4ruler(Mat srcintdown1,intup2 ,int direction){
    Mat tmp = src.clone();
    vector<intvdate;
    if (DIRECTION_X == direction){
        for (int i=0;i<tmp.cols;i++){
            Mat data = tmp.col(i);
            int itmp = countNonZero(data);
            if (itmp > 10)
            {
                int jjj=0;
            }
            vdate.push_back(itmp);
        }
    }else{
        for (int i=0;i<tmp.rows;i++){
            Mat data = tmp.row(i);
            int itmp = countNonZero(data);
            vdate.push_back(itmp);
        }
    }
    //过滤掉所有噪音
    //寻找第一个下边沿和第二个上边沿
    down1 = 1;
    up2   = src.cols - 1;
    for (int i=0;i<tmp.cols-1;i++)
    {
        if (vdate[i] >=  100 && vdate[i+1] < 100)
        {
          down1 = i;
          break;
        }
    }
    for (int idown1;i<tmp.cols - 1;i++)
    {
        if (vdate[i] < 100 && vdate[i+1] >= 100)
        {
            up2 = i;
            return;
        }
    }
}
寻找并且标注出来,非常准确。
图上(1767 - 771  =  996)对于10厘米,那么,那么1个像素相当于0.1毫米(这个整数只是巧合)
摄像头距离标定方法研究(得到像素和毫米的转换比)
 
多次测量,验证算法的稳定性,并运用于实际项目。
 
小结
基于算法库和成熟的思路,能够加速解决问题速度。
比较好的视觉环境也很重要。
附全部代码
//GoCvHelper的demo程序
#include "stdafx.h"
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "GoCvHelper.h"
 
using namespace std;
using namespace cv;
using namespace GO;
#define  VP  vector<cv::Point>  //用VP符号代替 vector<point>
 
void projection4ruler(Mat srcintdown1,intup2 ,int direction){
    Mat tmp = src.clone();
    vector<intvdate;
    if (DIRECTION_X == direction){
        for (int i=0;i<tmp.cols;i++){
            Mat data = tmp.col(i);
            int itmp = countNonZero(data);
            if (itmp > 10)
            {
                int jjj=0;
            }
            vdate.push_back(itmp);
        }
    }else{
        for (int i=0;i<tmp.rows;i++){
            Mat data = tmp.row(i);
            int itmp = countNonZero(data);
            vdate.push_back(itmp);
        }
    }
    //过滤掉所有噪音
    //寻找第一个下边沿和第二个上边沿
    down1 = 1;
    up2   = src.cols - 1;
    for (int i=0;i<tmp.cols-1;i++)
    {
        if (vdate[i] >=  100 && vdate[i+1] < 100)
        {
          down1 = i;
          break;
        }
    }
    for (int idown1;i<tmp.cols - 1;i++)
    {
        if (vdate[i] < 100 && vdate[i+1] >= 100)
        {
            up2 = i;
            return;
        }
    }
}
 
int _tmain(int argc_TCHARargv[])
{    
    Mat src = imread("E:\\sandbox\\1.bmp");
    Mat dst;
    Mat tmp;
    int up1 = 0;
    int down2 = 0;
 
    vector<MatmatSplit;
    cvtColor(src,tmp,COLOR_BGR2Lab);
    split(tmp,matSplit);
    dst = matSplit[1];
    threshold(dst,dst,100,255,THRESH_OTSU);
    threshold(dst,dst,0,255,THRESH_BINARY_INV);//以白色为有数据
 
    projection4ruler(dst,up1,down2,DIRECTION_X);
    line(src,Point(up1,0),Point(up1,src.rows-1),Scalar(0,0,255),1);
    line(src,Point(down2,0),Point(down2,src.rows-1),Scalar(0,0,255),1);
    cv::waitKey();
    return 0;
}
 





附件列表

 

目前方向:图像拼接融合、图像识别 联系方式:[email protected]