qrcode.cpp 25.9 KB
Newer Older
N
Nesterov Alexander 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.

#include "precomp.hpp"
#include "opencv2/objdetect.hpp"
// #include "opencv2/calib3d.hpp"

#include <limits>
#include <cmath>
#include <iostream>

namespace cv
{
18 19
using std::vector;

N
Nesterov Alexander 已提交
20 21
class QRDecode
{
22 23
public:
    void init(Mat src, double eps_vertical_ = 0.2, double eps_horizontal_ = 0.1);
N
Nesterov Alexander 已提交
24 25 26 27 28
    void binarization();
    bool localization();
    bool transformation();
    Mat getBinBarcode() { return bin_barcode; }
    Mat getStraightBarcode() { return straight_barcode; }
29 30 31 32 33 34 35 36 37
    vector<Point2f> getTransformationPoints() { return transformation_points; }
protected:
    bool computeTransformationPoints();
    vector<Vec3d> searchVerticalLines();
    vector<Point2f> separateHorizontalLines(vector<Vec3d> list_lines);
    void fixationPoints(vector<Point2f> &local_point);
    Point2f intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2);
    vector<Point2f> getQuadrilateral(vector<Point2f> angle_list);
    bool testBypassRoute(vector<Point2f> hull, int start, int finish);
A
Alexander Nesterov 已提交
38
    inline double getCosVectors(Point2f a, Point2f b, Point2f c);
39 40 41

    Mat barcode, bin_barcode, straight_barcode;
    vector<Point2f> localization_points, transformation_points;
42
    double eps_vertical, eps_horizontal, coeff_expansion;
N
Nesterov Alexander 已提交
43 44
};

45

N
Nesterov Alexander 已提交
46 47
void QRDecode::init(Mat src, double eps_vertical_, double eps_horizontal_)
{
48 49 50 51 52 53 54 55 56 57 58 59 60 61
    double min_side = std::min(src.size().width, src.size().height);
    if (min_side < 512.0)
    {
        coeff_expansion = 512.0 / min_side;
        int width  = static_cast<int>(src.size().width  * coeff_expansion);
        int height = static_cast<int>(src.size().height  * coeff_expansion);
        Size new_size(width, height);
        resize(src, barcode, new_size);
    }
    else
    {
        coeff_expansion = 1.0;
        barcode = src;
    }
N
Nesterov Alexander 已提交
62 63 64 65 66 67 68 69
    eps_vertical   = eps_vertical_;
    eps_horizontal = eps_horizontal_;
}

void QRDecode::binarization()
{
    Mat filter_barcode;
    GaussianBlur(barcode, filter_barcode, Size(3, 3), 0);
A
Alexander Nesterov 已提交
70
    threshold(filter_barcode, bin_barcode, 100, 255, THRESH_BINARY + THRESH_OTSU);
N
Nesterov Alexander 已提交
71 72
}

73
vector<Vec3d> QRDecode::searchVerticalLines()
N
Nesterov Alexander 已提交
74
{
75
    vector<Vec3d> result;
N
Nesterov Alexander 已提交
76
    int temp_length = 0;
77 78
    uint8_t next_pixel, future_pixel;
    vector<double> test_lines;
N
Nesterov Alexander 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

    for (int x = 0; x < bin_barcode.rows; x++)
    {
        for (int y = 0; y < bin_barcode.cols; y++)
        {
            if (bin_barcode.at<uint8_t>(x, y) > 0) { continue; }

            // --------------- Search vertical lines --------------- //

            test_lines.clear();
            future_pixel = 255;

            for (int i = x; i < bin_barcode.rows - 1; i++)
            {
                next_pixel = bin_barcode.at<uint8_t>(i + 1, y);
                temp_length++;
                if (next_pixel == future_pixel)
                {
                    future_pixel = 255 - future_pixel;
                    test_lines.push_back(temp_length);
                    temp_length = 0;
                    if (test_lines.size() == 5) { break; }
                }
            }

            // --------------- Compute vertical lines --------------- //

            if (test_lines.size() == 5)
            {
108
                double length = 0.0, weight = 0.0;
N
Nesterov Alexander 已提交
109 110 111

                for (size_t i = 0; i < test_lines.size(); i++) { length += test_lines[i]; }

112
                CV_Assert(length > 0);
N
Nesterov Alexander 已提交
113 114
                for (size_t i = 0; i < test_lines.size(); i++)
                {
115 116
                    if (i == 2) { weight += fabs((test_lines[i] / length) - 3.0/7.0); }
                    else        { weight += fabs((test_lines[i] / length) - 1.0/7.0); }
N
Nesterov Alexander 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130
                }

                if (weight < eps_vertical)
                {
                    Vec3d line;
                    line[0] = x; line[1] = y, line[2] = length;
                    result.push_back(line);
                }
            }
        }
    }
    return result;
}

131
vector<Point2f> QRDecode::separateHorizontalLines(vector<Vec3d> list_lines)
N
Nesterov Alexander 已提交
132
{
133
    vector<Vec3d> result;
N
Nesterov Alexander 已提交
134
    int temp_length = 0;
135 136
    uint8_t next_pixel, future_pixel;
    vector<double> test_lines;
N
Nesterov Alexander 已提交
137 138 139

    for (size_t pnt = 0; pnt < list_lines.size(); pnt++)
    {
A
Alexander Nesterov 已提交
140
        int x = static_cast<int>(list_lines[pnt][0] + list_lines[pnt][2] * 0.5);
141
        int y = static_cast<int>(list_lines[pnt][1]);
N
Nesterov Alexander 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179

        // --------------- Search horizontal up-lines --------------- //
        test_lines.clear();
        future_pixel = 255;

        for (int j = y; j < bin_barcode.cols - 1; j++)
        {
            next_pixel = bin_barcode.at<uint8_t>(x, j + 1);
            temp_length++;
            if (next_pixel == future_pixel)
            {
                future_pixel = 255 - future_pixel;
                test_lines.push_back(temp_length);
                temp_length = 0;
                if (test_lines.size() == 3) { break; }
            }
        }

        // --------------- Search horizontal down-lines --------------- //
        future_pixel = 255;

        for (int j = y; j >= 1; j--)
        {
            next_pixel = bin_barcode.at<uint8_t>(x, j - 1);
            temp_length++;
            if (next_pixel == future_pixel)
            {
                future_pixel = 255 - future_pixel;
                test_lines.push_back(temp_length);
                temp_length = 0;
                if (test_lines.size() == 6) { break; }
            }
        }

        // --------------- Compute horizontal lines --------------- //

        if (test_lines.size() == 6)
        {
180
            double length = 0.0, weight = 0.0;
N
Nesterov Alexander 已提交
181 182 183

            for (size_t i = 0; i < test_lines.size(); i++) { length += test_lines[i]; }

184
            CV_Assert(length > 0);
N
Nesterov Alexander 已提交
185 186
            for (size_t i = 0; i < test_lines.size(); i++)
            {
187 188
                if (i % 3 == 0) { weight += fabs((test_lines[i] / length) - 3.0/14.0); }
                else            { weight += fabs((test_lines[i] / length) - 1.0/ 7.0); }
N
Nesterov Alexander 已提交
189 190
            }

191 192 193 194
            if(weight < eps_horizontal)
            {
                result.push_back(list_lines[pnt]);
            }
N
Nesterov Alexander 已提交
195 196 197 198

        }
    }

199 200
    vector<Point2f> point2f_result;
    for (size_t i = 0; i < result.size(); i++)
N
Nesterov Alexander 已提交
201
    {
202 203
        point2f_result.push_back(
              Point2f(static_cast<float>(result[i][1]),
A
Alexander Nesterov 已提交
204
                      static_cast<float>(result[i][0] + result[i][2] * 0.5)));
N
Nesterov Alexander 已提交
205
    }
206
    return point2f_result;
N
Nesterov Alexander 已提交
207 208
}

209
void QRDecode::fixationPoints(vector<Point2f> &local_point)
N
Nesterov Alexander 已提交
210 211 212 213 214 215 216 217
{
    double cos_angles[3], norm_triangl[3];

    norm_triangl[0] = norm(local_point[1] - local_point[2]);
    norm_triangl[1] = norm(local_point[0] - local_point[2]);
    norm_triangl[2] = norm(local_point[1] - local_point[0]);

    cos_angles[0] = (pow(norm_triangl[1], 2) + pow(norm_triangl[2], 2) - pow(norm_triangl[0], 2))
218
    / (2 * norm_triangl[1] * norm_triangl[2]);
N
Nesterov Alexander 已提交
219
    cos_angles[1] = (pow(norm_triangl[0], 2) + pow(norm_triangl[2], 2) - pow(norm_triangl[1], 2))
220
    / (2 * norm_triangl[0] * norm_triangl[2]);
N
Nesterov Alexander 已提交
221
    cos_angles[2] = (pow(norm_triangl[0], 2) + pow(norm_triangl[1], 2) - pow(norm_triangl[2], 2))
222 223 224 225 226 227 228 229 230 231 232
    / (2 * norm_triangl[0] * norm_triangl[1]);

    size_t i_min_cos =
    (cos_angles[0] < cos_angles[1] && cos_angles[0] < cos_angles[2]) ? 0 :
    (cos_angles[1] < cos_angles[0] && cos_angles[1] < cos_angles[2]) ? 1 : 2;

    std::swap(local_point[0], local_point[i_min_cos]);

    Point2f rpt = local_point[0], bpt = local_point[1], gpt = local_point[2];
    Matx22f m(rpt.x - bpt.x, rpt.y - bpt.y, gpt.x - rpt.x, gpt.y - rpt.y);
    if( determinant(m) > 0 )
N
Nesterov Alexander 已提交
233
    {
234
        std::swap(local_point[1], local_point[2]);
N
Nesterov Alexander 已提交
235 236 237
    }
}

238
bool QRDecode::localization()
N
Nesterov Alexander 已提交
239
{
240 241 242 243 244 245 246 247
    Point2f begin, end;
    vector<Vec3d> list_lines_x = searchVerticalLines();
    if( list_lines_x.empty() ) { return false; }
    vector<Point2f> list_lines_y = separateHorizontalLines(list_lines_x);
    if( list_lines_y.empty() ) { return false; }

    vector<Point2f> centers;
    Mat labels;
248
    if (list_lines_y.size() < 3) { return false; }
249 250 251 252 253
    kmeans(list_lines_y, 3, labels,
           TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 10, 1.0),
           3, KMEANS_PP_CENTERS, localization_points);

    fixationPoints(localization_points);
N
Nesterov Alexander 已提交
254 255
    if (localization_points.size() != 3) { return false; }

256
    if (coeff_expansion > 1.0)
N
Nesterov Alexander 已提交
257
    {
258 259 260 261 262 263 264 265 266 267
        int width  = static_cast<int>(bin_barcode.size().width  / coeff_expansion);
        int height = static_cast<int>(bin_barcode.size().height / coeff_expansion);
        Size new_size(width, height);
        Mat intermediate;
        resize(bin_barcode, intermediate, new_size);
        bin_barcode = intermediate.clone();
        for (size_t i = 0; i < localization_points.size(); i++)
        {
            localization_points[i] /= coeff_expansion;
        }
N
Nesterov Alexander 已提交
268 269
    }

270 271 272 273 274 275 276 277 278 279
    for (size_t i = 0; i < localization_points.size(); i++)
    {
        for (size_t j = i + 1; j < localization_points.size(); j++)
        {
            if (norm(localization_points[i] - localization_points[j]) < 10)
            {
                return false;
            }
        }
    }
N
Nesterov Alexander 已提交
280
    return true;
281

N
Nesterov Alexander 已提交
282 283
}

284
bool QRDecode::computeTransformationPoints()
N
Nesterov Alexander 已提交
285
{
286
    if (localization_points.size() != 3) { return false; }
N
Nesterov Alexander 已提交
287

288 289 290
    vector<Point> locations, non_zero_elem[3], newHull;
    vector<Point2f> new_non_zero_elem[3];
    for (size_t i = 0; i < 3; i++)
N
Nesterov Alexander 已提交
291
    {
292 293 294 295
        Mat mask = Mat::zeros(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1);
        uint8_t next_pixel, future_pixel = 255;
        int count_test_lines = 0, index = static_cast<int>(localization_points[i].x);
        for (; index < bin_barcode.cols - 1; index++)
N
Nesterov Alexander 已提交
296
        {
297 298 299
            next_pixel = bin_barcode.at<uint8_t>(
                            static_cast<int>(localization_points[i].y), index + 1);
            if (next_pixel == future_pixel)
N
Nesterov Alexander 已提交
300
            {
301 302 303
                future_pixel = 255 - future_pixel;
                count_test_lines++;
                if (count_test_lines == 2)
N
Nesterov Alexander 已提交
304
                {
305 306 307 308
                    floodFill(bin_barcode, mask,
                              Point(index + 1, static_cast<int>(localization_points[i].y)), 255,
                              0, Scalar(), Scalar(), FLOODFILL_MASK_ONLY);
                    break;
N
Nesterov Alexander 已提交
309 310 311
                }
            }
        }
312 313 314 315 316 317 318 319
        Mat mask_roi = mask(Range(1, bin_barcode.rows - 1), Range(1, bin_barcode.cols - 1));
        findNonZero(mask_roi, non_zero_elem[i]);
        newHull.insert(newHull.end(), non_zero_elem[i].begin(), non_zero_elem[i].end());
    }
    convexHull(Mat(newHull), locations);
    for (size_t i = 0; i < locations.size(); i++)
    {
        for (size_t j = 0; j < 3; j++)
N
Nesterov Alexander 已提交
320
        {
321 322 323 324 325 326 327
            for (size_t k = 0; k < non_zero_elem[j].size(); k++)
            {
                if (locations[i] == non_zero_elem[j][k])
                {
                    new_non_zero_elem[j].push_back(locations[i]);
                }
            }
N
Nesterov Alexander 已提交
328 329 330
        }
    }

331 332 333
    double pentagon_diag_norm = -1;
    Point2f down_left_edge_point, up_right_edge_point, up_left_edge_point;
    for (size_t i = 0; i < new_non_zero_elem[1].size(); i++)
N
Nesterov Alexander 已提交
334
    {
335
        for (size_t j = 0; j < new_non_zero_elem[2].size(); j++)
N
Nesterov Alexander 已提交
336
        {
337 338 339 340 341 342 343
            double temp_norm = norm(new_non_zero_elem[1][i] - new_non_zero_elem[2][j]);
            if (temp_norm > pentagon_diag_norm)
            {
                down_left_edge_point = new_non_zero_elem[1][i];
                up_right_edge_point  = new_non_zero_elem[2][j];
                pentagon_diag_norm = temp_norm;
            }
N
Nesterov Alexander 已提交
344 345
        }
    }
346 347
    if (down_left_edge_point == Point2f(0, 0) ||
        up_right_edge_point  == Point2f(0, 0)) { return false; }
N
Nesterov Alexander 已提交
348

349 350 351 352
    double max_area = -1;
    up_left_edge_point = new_non_zero_elem[0][0];
    for (size_t i = 0; i < new_non_zero_elem[0].size(); i++)
    {
A
Alexander Nesterov 已提交
353 354 355 356 357 358 359
        vector<Point2f> list_edge_points;
        list_edge_points.push_back(new_non_zero_elem[0][i]);
        list_edge_points.push_back(down_left_edge_point);
        list_edge_points.push_back(up_right_edge_point);

        double temp_area = contourArea(list_edge_points);

360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
        if (max_area < temp_area)
        {
            up_left_edge_point = new_non_zero_elem[0][i];
            max_area = temp_area;
        }
    }

    Point2f down_max_delta_point, up_max_delta_point;
    double norm_down_max_delta = -1, norm_up_max_delta = -1;
    for (size_t i = 0; i < new_non_zero_elem[1].size(); i++)
    {
        double temp_norm_delta = norm(up_left_edge_point - new_non_zero_elem[1][i])
                               + norm(down_left_edge_point - new_non_zero_elem[1][i]);
        if (norm_down_max_delta < temp_norm_delta)
        {
            down_max_delta_point = new_non_zero_elem[1][i];
            norm_down_max_delta = temp_norm_delta;
        }
    }

    for (size_t i = 0; i < new_non_zero_elem[2].size(); i++)
    {
        double temp_norm_delta = norm(up_left_edge_point - new_non_zero_elem[2][i])
                               + norm(up_right_edge_point - new_non_zero_elem[2][i]);
        if (norm_up_max_delta < temp_norm_delta)
        {
            up_max_delta_point = new_non_zero_elem[2][i];
            norm_up_max_delta = temp_norm_delta;
        }
    }

    transformation_points.push_back(down_left_edge_point);
    transformation_points.push_back(up_left_edge_point);
    transformation_points.push_back(up_right_edge_point);
    transformation_points.push_back(
        intersectionLines(down_left_edge_point, down_max_delta_point,
                          up_right_edge_point, up_max_delta_point));

    vector<Point2f> quadrilateral = getQuadrilateral(transformation_points);
    transformation_points = quadrilateral;

    return true;
N
Nesterov Alexander 已提交
402 403
}

404
Point2f QRDecode::intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2)
N
Nesterov Alexander 已提交
405
{
406 407 408 409 410 411 412 413 414 415
    Point2f result_square_angle(
                              ((a1.x * a2.y  -  a1.y * a2.x) * (b1.x - b2.x) -
                               (b1.x * b2.y  -  b1.y * b2.x) * (a1.x - a2.x)) /
                              ((a1.x - a2.x) * (b1.y - b2.y) -
                               (a1.y - a2.y) * (b1.x - b2.x)),
                              ((a1.x * a2.y  -  a1.y * a2.x) * (b1.y - b2.y) -
                               (b1.x * b2.y  -  b1.y * b2.x) * (a1.y - a2.y)) /
                              ((a1.x - a2.x) * (b1.y - b2.y) -
                               (a1.y - a2.y) * (b1.x - b2.x))
                              );
N
Nesterov Alexander 已提交
416 417 418
    return result_square_angle;
}

419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
// test function (if true then ------> else <------ )
bool QRDecode::testBypassRoute(vector<Point2f> hull, int start, int finish)
{
    int index_hull = start, next_index_hull, hull_size = (int)hull.size();
    double test_length[2] = { 0.0, 0.0 };
    do
    {
        next_index_hull = index_hull + 1;
        if (next_index_hull == hull_size) { next_index_hull = 0; }
        test_length[0] += norm(hull[index_hull] - hull[next_index_hull]);
        index_hull = next_index_hull;
    }
    while(index_hull != finish);

    index_hull = start;
    do
    {
        next_index_hull = index_hull - 1;
        if (next_index_hull == -1) { next_index_hull = hull_size - 1; }
        test_length[1] += norm(hull[index_hull] - hull[next_index_hull]);
        index_hull = next_index_hull;
    }
    while(index_hull != finish);

    if (test_length[0] < test_length[1]) { return true; } else { return false; }
}

vector<Point2f> QRDecode::getQuadrilateral(vector<Point2f> angle_list)
N
Nesterov Alexander 已提交
447 448 449
{
    size_t angle_size = angle_list.size();
    uint8_t value, mask_value;
450 451
    Mat mask = Mat::zeros(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1);
    Mat fill_bin_barcode = bin_barcode.clone();
N
Nesterov Alexander 已提交
452 453 454 455 456 457 458 459 460 461
    for (size_t i = 0; i < angle_size; i++)
    {
        LineIterator line_iter(bin_barcode, angle_list[ i      % angle_size],
                                            angle_list[(i + 1) % angle_size]);
        for(int j = 0; j < line_iter.count; j++, ++line_iter)
        {
            value = bin_barcode.at<uint8_t>(line_iter.pos());
            mask_value = mask.at<uint8_t>(line_iter.pos() + Point(1, 1));
            if (value == 0 && mask_value == 0)
            {
462 463
                floodFill(fill_bin_barcode, mask, line_iter.pos(), 255,
                          0, Scalar(), Scalar(), FLOODFILL_MASK_ONLY);
N
Nesterov Alexander 已提交
464 465 466
            }
        }
    }
467 468
    vector<Point> locations;
    Mat mask_roi = mask(Range(1, bin_barcode.rows - 1), Range(1, bin_barcode.cols - 1));
N
Nesterov Alexander 已提交
469 470 471 472 473

    cv::findNonZero(mask_roi, locations);

    for (size_t i = 0; i < angle_list.size(); i++)
    {
474 475 476
        int x = static_cast<int>(angle_list[i].x);
        int y = static_cast<int>(angle_list[i].y);
        locations.push_back(Point(x, y));
N
Nesterov Alexander 已提交
477 478
    }

479 480 481 482 483
    vector<Point> integer_hull;
    convexHull(Mat(locations), integer_hull);
    int hull_size = (int)integer_hull.size();
    vector<Point2f> hull(hull_size);
    for (int i = 0; i < hull_size; i++)
N
Nesterov Alexander 已提交
484
    {
485 486 487
        float x = static_cast<float>(integer_hull[i].x);
        float y = static_cast<float>(integer_hull[i].y);
        hull[i] = Point2f(x, y);
N
Nesterov Alexander 已提交
488 489
    }

A
Alexander Nesterov 已提交
490
    const double experimental_area = contourArea(hull);
491

492 493
    vector<Point2f> result_hull_point(angle_size);
    double min_norm;
N
Nesterov Alexander 已提交
494 495 496 497
    for (size_t i = 0; i < angle_size; i++)
    {
        min_norm = std::numeric_limits<double>::max();
        Point closest_pnt;
498
        for (int j = 0; j < hull_size; j++)
N
Nesterov Alexander 已提交
499
        {
500 501
            double temp_norm = norm(hull[j] - angle_list[i]);
            if (min_norm > temp_norm)
N
Nesterov Alexander 已提交
502
            {
503 504
                min_norm = temp_norm;
                closest_pnt = hull[j];
N
Nesterov Alexander 已提交
505 506 507 508 509
            }
        }
        result_hull_point[i] = closest_pnt;
    }

510
    int start_line[2] = { 0, 0 }, finish_line[2] = { 0, 0 }, unstable_pnt = 0;
N
Nesterov Alexander 已提交
511 512
    for (int i = 0; i < hull_size; i++)
    {
513 514 515 516
        if (result_hull_point[2] == hull[i]) { start_line[0] = i; }
        if (result_hull_point[1] == hull[i]) { finish_line[0] = start_line[1] = i; }
        if (result_hull_point[0] == hull[i]) { finish_line[1] = i; }
        if (result_hull_point[3] == hull[i]) { unstable_pnt = i; }
N
Nesterov Alexander 已提交
517 518
    }

519
    int index_hull, extra_index_hull, next_index_hull, extra_next_index_hull;
N
Nesterov Alexander 已提交
520 521
    Point result_side_begin[4], result_side_end[4];

522 523 524
    bool bypass_orientation = testBypassRoute(hull, start_line[0], finish_line[0]);
    bool extra_bypass_orientation;

N
Nesterov Alexander 已提交
525 526 527 528
    min_norm = std::numeric_limits<double>::max();
    index_hull = start_line[0];
    do
    {
529
        if (bypass_orientation) { next_index_hull = index_hull + 1; }
N
Nesterov Alexander 已提交
530 531 532 533 534
        else { next_index_hull = index_hull - 1; }

        if (next_index_hull == hull_size) { next_index_hull = 0; }
        if (next_index_hull == -1) { next_index_hull = hull_size - 1; }

535 536
        Point angle_closest_pnt =  norm(hull[index_hull] - angle_list[1]) >
        norm(hull[index_hull] - angle_list[2]) ? angle_list[2] : angle_list[1];
N
Nesterov Alexander 已提交
537 538

        Point intrsc_line_hull =
539 540 541
        intersectionLines(hull[index_hull], hull[next_index_hull],
                          angle_list[1], angle_list[2]);
        double temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt);
N
Nesterov Alexander 已提交
542
        if (min_norm > temp_norm &&
543
            norm(hull[index_hull] - hull[next_index_hull]) >
A
Alexander Nesterov 已提交
544
            norm(angle_list[1] - angle_list[2]) * 0.1)
N
Nesterov Alexander 已提交
545 546
        {
            min_norm = temp_norm;
547 548
            result_side_begin[0] = hull[index_hull];
            result_side_end[0]   = hull[next_index_hull];
N
Nesterov Alexander 已提交
549 550 551 552 553 554 555 556 557
        }


        index_hull = next_index_hull;
    }
    while(index_hull != finish_line[0]);

    if (min_norm == std::numeric_limits<double>::max())
    {
558 559
        result_side_begin[0] = angle_list[1];
        result_side_end[0]   = angle_list[2];
N
Nesterov Alexander 已提交
560 561 562 563
    }

    min_norm = std::numeric_limits<double>::max();
    index_hull = start_line[1];
564
    bypass_orientation = testBypassRoute(hull, start_line[1], finish_line[1]);
N
Nesterov Alexander 已提交
565 566
    do
    {
567
        if (bypass_orientation) { next_index_hull = index_hull + 1; }
N
Nesterov Alexander 已提交
568 569 570 571 572
        else { next_index_hull = index_hull - 1; }

        if (next_index_hull == hull_size) { next_index_hull = 0; }
        if (next_index_hull == -1) { next_index_hull = hull_size - 1; }

573 574
        Point angle_closest_pnt =  norm(hull[index_hull] - angle_list[0]) >
        norm(hull[index_hull] - angle_list[1]) ? angle_list[1] : angle_list[0];
N
Nesterov Alexander 已提交
575 576

        Point intrsc_line_hull =
577 578 579
        intersectionLines(hull[index_hull], hull[next_index_hull],
                          angle_list[0], angle_list[1]);
        double temp_norm = getCosVectors(hull[index_hull], intrsc_line_hull, angle_closest_pnt);
N
Nesterov Alexander 已提交
580
        if (min_norm > temp_norm &&
581
            norm(hull[index_hull] - hull[next_index_hull]) >
A
Alexander Nesterov 已提交
582
            norm(angle_list[0] - angle_list[1]) * 0.05)
N
Nesterov Alexander 已提交
583 584
        {
            min_norm = temp_norm;
585 586
            result_side_begin[1] = hull[index_hull];
            result_side_end[1]   = hull[next_index_hull];
N
Nesterov Alexander 已提交
587 588 589 590 591 592 593 594
        }

        index_hull = next_index_hull;
    }
    while(index_hull != finish_line[1]);

    if (min_norm == std::numeric_limits<double>::max())
    {
595 596
        result_side_begin[1] = angle_list[0];
        result_side_end[1]   = angle_list[1];
N
Nesterov Alexander 已提交
597 598
    }

599 600
    bypass_orientation = testBypassRoute(hull, start_line[0], unstable_pnt);
    extra_bypass_orientation = testBypassRoute(hull, finish_line[1], unstable_pnt);
N
Nesterov Alexander 已提交
601

602
    vector<Point2f> result_angle_list(4), test_result_angle_list(4);
603
    double min_diff_area = std::numeric_limits<double>::max(), test_diff_area;
N
Nesterov Alexander 已提交
604
    index_hull = start_line[0];
605 606 607
    double standart_norm = std::max(
        norm(result_side_begin[0] - result_side_end[0]),
        norm(result_side_begin[1] - result_side_end[1]));
N
Nesterov Alexander 已提交
608 609
    do
    {
610
        if (bypass_orientation) { next_index_hull = index_hull + 1; }
N
Nesterov Alexander 已提交
611 612 613 614 615
        else { next_index_hull = index_hull - 1; }

        if (next_index_hull == hull_size) { next_index_hull = 0; }
        if (next_index_hull == -1) { next_index_hull = hull_size - 1; }

A
Alexander Nesterov 已提交
616
        if (norm(hull[index_hull] - hull[next_index_hull]) < standart_norm * 0.1)
617 618
        { index_hull = next_index_hull; continue; }

N
Nesterov Alexander 已提交
619 620 621
        extra_index_hull = finish_line[1];
        do
        {
622
            if (extra_bypass_orientation) { extra_next_index_hull = extra_index_hull + 1; }
N
Nesterov Alexander 已提交
623 624 625 626 627
            else { extra_next_index_hull = extra_index_hull - 1; }

            if (extra_next_index_hull == hull_size) { extra_next_index_hull = 0; }
            if (extra_next_index_hull == -1) { extra_next_index_hull = hull_size - 1; }

A
Alexander Nesterov 已提交
628
            if (norm(hull[extra_index_hull] - hull[extra_next_index_hull]) < standart_norm * 0.1)
629 630
            { extra_index_hull = extra_next_index_hull; continue; }

N
Nesterov Alexander 已提交
631
            test_result_angle_list[0]
632 633
            = intersectionLines(result_side_begin[0], result_side_end[0],
                                result_side_begin[1], result_side_end[1]);
N
Nesterov Alexander 已提交
634
            test_result_angle_list[1]
635 636
            = intersectionLines(result_side_begin[1], result_side_end[1],
                                hull[extra_index_hull], hull[extra_next_index_hull]);
N
Nesterov Alexander 已提交
637
            test_result_angle_list[2]
638 639
            = intersectionLines(hull[extra_index_hull], hull[extra_next_index_hull],
                                hull[index_hull], hull[next_index_hull]);
N
Nesterov Alexander 已提交
640
            test_result_angle_list[3]
641 642 643
            = intersectionLines(hull[index_hull], hull[next_index_hull],
                                result_side_begin[0], result_side_end[0]);

A
Alexander Nesterov 已提交
644
            test_diff_area = fabs(contourArea(test_result_angle_list) - experimental_area);
645
            if (min_diff_area > test_diff_area)
N
Nesterov Alexander 已提交
646
            {
647
                min_diff_area = test_diff_area;
N
Nesterov Alexander 已提交
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
                for (size_t i = 0; i < test_result_angle_list.size(); i++)
                {
                    result_angle_list[i] = test_result_angle_list[i];
                }
            }

            extra_index_hull = extra_next_index_hull;
        }
        while(extra_index_hull != unstable_pnt);

        index_hull = next_index_hull;
    }
    while(index_hull != unstable_pnt);
    return result_angle_list;
}

//      / | b
//     /  |
//    /   |
//  a/    | c

A
Alexander Nesterov 已提交
669
inline double QRDecode::getCosVectors(Point2f a, Point2f b, Point2f c)
N
Nesterov Alexander 已提交
670
{
A
Alexander Nesterov 已提交
671
    return ((a - b).x * (c - b).x + (a - b).y * (c - b).y) / (norm(a - b) * norm(c - b));
N
Nesterov Alexander 已提交
672 673
}

674
bool QRDecode::transformation()
N
Nesterov Alexander 已提交
675
{
676 677 678 679 680 681 682
    if(!computeTransformationPoints()) { return false; }

    double max_length_norm = -1;
    size_t transform_size = transformation_points.size();
    for (size_t i = 0; i < transform_size; i++)
    {
        double len_norm = norm(transformation_points[i % transform_size] -
683
                               transformation_points[(i + 1) % transform_size]);
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
        max_length_norm = std::max(max_length_norm, len_norm);
    }

    Point2f transformation_points_[] =
    {
        transformation_points[0],
        transformation_points[1],
        transformation_points[2],
        transformation_points[3]
    };

    Point2f perspective_points[] =
    {
        Point2f(0.f, 0.f), Point2f(0.f, (float)max_length_norm),
        Point2f((float)max_length_norm, (float)max_length_norm),
        Point2f((float)max_length_norm, 0.f)
    };

    Mat H = getPerspectiveTransform(transformation_points_, perspective_points);

    warpPerspective(bin_barcode, straight_barcode, H,
                    Size(static_cast<int>(max_length_norm),
                         static_cast<int>(max_length_norm)));
    return true;
}


struct QRCodeDetector::Impl
{
public:
    Impl() { epsX = 0.2; epsY = 0.1; }
    ~Impl() {}

    double epsX, epsY;
};

QRCodeDetector::QRCodeDetector() : p(new Impl) {}
QRCodeDetector::~QRCodeDetector() {}

void QRCodeDetector::setEpsX(double epsX) { p->epsX = epsX; }
void QRCodeDetector::setEpsY(double epsY) { p->epsY = epsY; }

bool QRCodeDetector::detect(InputArray in, OutputArray points) const
{
    Mat inarr = in.getMat();
    CV_Assert(!inarr.empty());
    CV_Assert(inarr.type() == CV_8UC1);
N
Nesterov Alexander 已提交
731
    QRDecode qrdec;
732
    qrdec.init(inarr, p->epsX, p->epsY);
N
Nesterov Alexander 已提交
733 734 735
    qrdec.binarization();
    if (!qrdec.localization()) { return false; }
    if (!qrdec.transformation()) { return false; }
736 737
    vector<Point2f> pnts2f = qrdec.getTransformationPoints();
    Mat(pnts2f).convertTo(points, points.fixedType() ? points.type() : CV_32FC2);
N
Nesterov Alexander 已提交
738 739 740
    return true;
}

741 742 743 744 745 746 747 748 749
CV_EXPORTS bool detectQRCode(InputArray in, std::vector<Point> &points, double eps_x, double eps_y)
{
    QRCodeDetector qrdetector;
    qrdetector.setEpsX(eps_x);
    qrdetector.setEpsY(eps_y);

    return qrdetector.detect(in, points);
}

N
Nesterov Alexander 已提交
750
}