如何在opencv中获得没有边框的qr代码图像?

时间:2015-07-25 08:39:07

标签: c++ qr-code opencv3.0 roi

我想获得没有白色边框或没有任何其他背景图像的QR码。到目前为止,我只能使用精确边缘检测来检测取景器模式。我还将QR码图像转换为二进制图像,现在我只想获得没有任何其他内容的qr代码。

我的代码:

//-----------------------------------------------------------------------------------
// Objective : To detect and decode QR-Code using a webcam 
// Author    : Abhimanyu Saharan
// Version   : 1
// todo      :      1. The program sometimes crashes at startup (23 July,     2015)
//              Error : Segmentation Fault
//              Reason: 1. When the video is way out of focus.
    //-----------------------------------------------------------------------------------

//-----------------------------------------------------------------------------------
// Headers
//-----------------------------------------------------------------------------------

#include <opencv2/opencv.hpp>
#include <iostream>

//-----------------------------------------------------------------------------------
// Namespaces
//-----------------------------------------------------------------------------------

using namespace cv;
using namespace std;

//------------------------------------------------------------------------------------
// Function Headers
//------------------------------------------------------------------------------------

// Returns the distance between two points
float qr_distance(Point2f, Point2f);

// Returns the perpendicular distance of the a point from the line
float qr_lineEquation(Point2f, Point2f, Point2f);

// Returns the slope of the line by two points on it; Slope of line, S = (x1 - x2) / (y1 - y2)
float qr_lineSlope(Point2f, Point2f, int&);

// Function to calculate 4 corners of the marker in image space using region partioning
void qr_getVertices(vector<vector<Point> >, int, float, vector<Point2f>&);

// Compare a point if it is more far than previously recorded farthest distance
void qr_updateCorner(Point2f, Point2f, float&, Point2f&);

//------------------------------------------------------------------------------------
// Global Variables
//-------------------------------------------------------------------------------------

// OpenCV Frame
Mat image, imageGray, imageCanny, imageBinary, imageBinaryInv;

// Camera Index
int cameraIndex = 0;

// OpenCV Wait Key
char key;

// QR Code Orientation
const int QR_NORTH = 0;
const int QR_EAST  = 1;
const int QR_SOUTH = 2;
const int QR_WEST  = 3;

//-----------------------------------------------------------------------------------
// @function main
//-----------------------------------------------------------------------------------

int main(int argc, char** argv){
// Camera Index given by user
if(argc == 2){
    cameraIndex = atoi(argv[1]);
}

// Initialize the video
VideoCapture capture(cameraIndex);

// Set parameters for display window
namedWindow("Result", CV_WINDOW_KEEPRATIO);
namedWindow("Canny", CV_WINDOW_KEEPRATIO);
namedWindow("Binary", CV_WINDOW_KEEPRATIO);

if(capture.isOpened()){
    // Vector for storing contours
    vector<vector<Point> > contours;
    vector<Vec4i>          hierarchy;

    int   mark, A, B, C, top, right, bottom, median1, median2, outlier;
    float AB, BC, CA, distance, slope, areaT, areaR, areaB, large, padding;

    int align, orientation;

    // Capture a frame from image input for creating and initializing manipulation variables
    capture >> image;

    if(image.empty()){
        cerr << "ERROR: Unable to query image from capture device\n" << endl;
        exit(EXIT_FAILURE);
    }

    while(key != 'q'){
        // Capture image from image input
        capture >> image;

        // Convert captured image into gray scale
        cvtColor(image, imageGray, CV_RGB2GRAY);

        // We detect edges in the image using the Canny Edge Detection Algorithm. This will
        // return a binary image, one where pixel values will be 255 for pixels that are edges
        // and 0 otherwise.
        Canny(imageGray, imageCanny, 100, 200, 3);

        // Convert Detected image to binary image; this binary image will later be used for data
        // extraction
        threshold(imageGray, imageBinary, 200, 255, THRESH_BINARY);

        // Once we have a binary image, we can look for contours in it. FindContours will scan
        // through the image and store the connected contours in `contours`. CV_RETR_TREE means
        // that the function will create a contour hierarchy. CV_CHAIN_APPROX_SIMPLE means that
        // all contour points will be stored. Finally, an offset value can be specified, but we
        // set it to (0, 0)
        findContours(imageCanny, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

        if(!contours.empty() && !hierarchy.empty()){
            // Reset all detected marker count for this frame
            mark = 0;
            // Get the moments from contours and the center mass
            vector<Moments> mu(contours.size());
            vector<Point2f> mc(contours.size());
            for(int i = 0 ; i < (int)contours.size() ; i++){
                mu[i] = moments(contours[i], false);
                mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
            }

            /* Compute Image features */

            // Find 3 Finder Patterns
            // Contours enclosing other contour is assumed to be the 3 Finder Patterns for the QR Code
            for(int i = 0 ; i < (int)contours.size() ; i++){
                int k = i;
                int c = 0;

                while(hierarchy[k][2] != -1){
                    k = hierarchy[k][2];
                    c = c + 1;
                }

                if(hierarchy[k][2] != -1) c = c + 1;

                if(c >= 5){
                    // Marker A is found
                    if(mark == 0)      A = i;
                    // Marker A is already found, assign current contour to Marker B
                    else if(mark == 1) B = i;
                    // Marker A and Marker B are already found, assign current contour to Marker C
                    else if(mark == 2) C = i;

                    mark = mark + 1;
                }
            }

            // We ensure that we have three markers, namely A, B & C which corresponds to the 3 Finder Patterns
            if(mark >= 2){
                // Visualize the 3 Finder Pattern
                line(image, mc[A], mc[B], Scalar(0, 0, 255), 2, 8, 0);
                line(image, mc[B], mc[C], Scalar(0, 0, 255), 2, 8, 0);
                line(image, mc[C], mc[A], Scalar(0, 0, 255), 2, 8, 0);

                // Now we determine the `top` marker
                // Vertex of the triangle not involved in the longest side is the `outlier`
                AB = qr_distance(mc[A], mc[B]);
                BC = qr_distance(mc[B], mc[C]);
                CA = qr_distance(mc[C], mc[A]);

                if(AB > BC && AB > CA){
                    outlier = C;
                    median1 = A;
                    median2 = B;
                }else if(CA > AB && CA > BC){
                    outlier = B;
                    median1 = A;
                    median2 = C;
                }else if(BC > AB && BC > CA){
                    outlier = A;
                    median1 = B;
                    median2 = C;
                }

                // The obvious choice
                top = outlier;

                // Get the perpendicular distance of the outlier from the longest side
                distance = qr_lineEquation(mc[median1], mc[median2], mc[outlier]);
                // Calculate the slope of the longest side
                slope    = qr_lineSlope(mc[median1], mc[median2], align);

                // Now we have the orientation of the line formed by the median1 and median2
                // and we also have position of the outlier w.r.t. the line
                // Now, we determine the `bottom` and the `right` markers

                if(align == 0){
                    bottom = median1;
                    right  = median2;
                }else if(slope < 0 && distance < 0){
                    // Orientation - North
                    bottom      = median1;
                    right       = median2;
                    orientation = QR_NORTH;
                }else if(slope > 0 && distance < 0){
                    // Orientation - East
                    bottom      = median1;
                    right       = median2;
                    orientation = QR_EAST;
                }else if(slope < 0 && distance > 0){
                    // Orientation - South
                    bottom      = median1;
                    right       = median2;
                    orientation = QR_SOUTH;
                }else if(slope > 0 && distance > 0){
                    // Orientation - West
                    bottom      = median1;
                    right       = median2;
                    orientation = QR_WEST;
                }

                if(top < contours.size() && right < contours.size() && bottom < contours.size() && contourArea(contours[top]) > 10 && contourArea(contours[right]) > 1 && contourArea(contours[bottom]) > 1){

                    // Draw contours on the image
                    drawContours(image, contours, top,    Scalar(255, 200, 0), 3, 8, hierarchy, 0);
                    drawContours(image, contours, right,  Scalar(0, 0, 255),   3, 8, hierarchy, 0);
                    drawContours(image, contours, bottom, Scalar(255, 100, 0), 3, 8, hierarchy, 0);

                    //cout << "QR Code Image Detected" << endl;

                }
            }
        }

        // Display the result
        imshow("Result", image);
        imshow("Canny", imageCanny);
        imshow("Binary", imageBinary);

        // Wait for 1ms before accessing the next frame
        key = waitKey(1);
    }
}else{
    cerr << "ERROR: Unable to open video source" << endl;
    exit(EXIT_FAILURE);
}

return 0;
}

// @function    qr_distance(Point2f, Point2f)
// @description Returns the distance between two points
// @params      P Point2f Point 1
//              Q Point2f Point 2
// @returns float
float qr_distance(Point2f P, Point2f Q){
return sqrt(pow(abs(P.x - Q.x), 2) + pow(abs(P.y - Q.y), 2));
}

// @function    qr_lineEquation(Point2f, Point2f, Point2f)
// @description Given 3 points, the function derives the line equation of the 1st two 
//      points, calculates and returns the perpendicular distance of the line
//      equation of the 1st two points.
// @params  P Point2f Point 1 making the line
//          Q Point2f Point 2 making the line
//          X Point2f Point 3
// @returns float
float qr_lineEquation(Point2f P, Point2f Q, Point2f X){
float a, b, c, pDist;

a = -((Q.y - P.y) / (Q.x - P.x));
b = 1.0;
c = (((Q.y - P.y) / (Q.x - P.x)) * P.x) - P.y;

// Now that we have a, b, c from the equation ax + by + c, time to substitute 
// (x, y) by values from point X
pDist = (a * X.x + (b * X.y) + c) / sqrt((a * a) + (b * b));

return pDist;
}

// @function    qr_lineSlope(Point2f, Point2f)
// @description It returns the slope of the line formed by two given points, the alignment
//      flag indicates the line is vertical and the slope is infinity
// @params  P         Point2f Point 1
//          Q         Point2f Point 2
//          alignment int&    Used as a flag so that we don't divide by zero by mistake
// @returns float
float qr_lineSlope(Point2f P, Point2f Q, int& alignement)
{
float dx,dy;

dx = Q.x - P.x;
dy = Q.y - P.y;

if ( dy != 0){   
    alignement = 1;
    return (dy / dx);
}
else{    
    alignement = 0;
    return 0.0;
}
}

// @function    qr_getVertices(vector<vector<Point> >, int, float, vector<Point2f>&)
// @description OpenCV contours stores all points that describe it and these point lie
//      on the perimeter of the polygon. This function chooses the farthest
//      point of the polygon, exactly the points we are looking for. To choose
//      the farthest point, the polygon is partitioned into 4 equal regions using
//      bounding box. Distance algorithm is applied between the center of the bounding
//      box and every contour point in that region, the farthest point is deemed as the
//      vertex of that region. Calculating for all 4 regions, we obtain the 4 corners
//      of the polygon.
// @params  contours vector<vector<Point> >
//          cId  int
//          slope    float
//                  quad     vector<Point2f>
// @returns void
void qr_getVertices(vector<vector<Point> > contours, int cId, float slope, vector<Point2f>& quad){
float dMax[4];
float pd1, pd2;

Point2f M0, M1, M2, M3;
Point2f A, B, C, D, W, X, Y, Z;

Rect box = boundingRect(contours[cId]);

A   = box.tl();
B.x = box.br().x;
B.y = box.tl().y;
C   = box.br();
D.x = box.tl().x;
D.y = box.br().y;

W.x = (A.x + B.x) / 2;
W.y = A.y;

X.x = B.x;
X.y = (B.y + C.y) / 2;

Y.x = (C.x + D.x) / 2;
Y.y = C.y;

Z.x = D.x;
Z.y = (D.y + A.y) / 2;

dMax[0] = 0.0;
dMax[1] = 0.0;
dMax[2] = 0.0;
dMax[3] = 0.0;

pd1 = 0.0;
pd2 = 0.0;

if(slope > 5 || slope < -5){
    for(int i = 0 ; i < (int)contours[cId].size() ; i++){
        // Position of point w.r.t. diagonal AC
        pd1 = qr_lineEquation(C, A, contours[cId][i]);
        // Position of point w.r.t. diagonal BD
        pd1 = qr_lineEquation(B, D, contours[cId][i]);

        if((pd1 >= 0.0) && (pd2 > 0.0)){
            qr_updateCorner(contours[cId][i], W, dMax[1], M1);
        }else if((pd1 > 0.0) && (pd2 <= 0.0)){
            qr_updateCorner(contours[cId][i], X, dMax[2], M2);
        }else if((pd1 <= 0.0) && (pd2 < 0)){
            qr_updateCorner(contours[cId][i], Y, dMax[3], M3);
        }else if((pd1 < 0.0) && (pd2 >= 0.0)){
            qr_updateCorner(contours[cId][i], Z, dMax[0], M0);
        }else{
            continue;
        }
    }
}else{
    int halfX = (A.x + B.x) / 2;
    int halfY = (A.x + D.x) / 2;

    for(int i = 0 ; i < (int)contours[cId].size() ; i++){
        if((contours[cId][i].x < halfX) && (contours[cId][i].y <= halfY)){
            qr_updateCorner(contours[cId][i], C, dMax[2], M0);
        }else if((contours[cId][i].x >= halfX) && (contours[cId][i].y < halfY)){
            qr_updateCorner(contours[cId][i], D, dMax[3], M1);
        }else if((contours[cId][i].x > halfX) && (contours[cId][i].y >= halfY)){
            qr_updateCorner(contours[cId][i], A, dMax[0], M2);
        }else if((contours[cId][i].x <= halfX) && (contours[cId][i].y > halfY)){
            qr_updateCorner(contours[cId][i], B, dMax[1], M3);
        }
    }
}

quad.push_back(M0);
quad.push_back(M1);
quad.push_back(M2);
quad.push_back(M3);
}

// @function    qr_updateCorner(Point2f, Point2f, float&, Point2f&)
// @description Farthest point detection using reference point and baseline distance
// @params  P        Point2f
//          ref      Point2f
//          baseline float&
//          corner   Point2f
// @returns void
void qr_updateCorner(Point2f P, Point2f ref, float& baseline, Point2f& corner){
float tempDistance = qr_distance(P, ref);

if(tempDistance > baseline){
    // The farthest distance is the new baseline
    baseline = tempDistance;
    // P is the farthest point
    corner   = P;
}
}

0 个答案:

没有答案