我想获得没有白色边框或没有任何其他背景图像的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;
}
}