qrcode.cpp 26.1 KB
Newer Older
N
Nesterov Alexander 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// 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 <limits>
#include <cmath>
#include <iostream>

namespace cv
{
17 18
using std::vector;

N
Nesterov Alexander 已提交
19 20
class QRDecode
{
21 22
public:
    void init(Mat src, double eps_vertical_ = 0.2, double eps_horizontal_ = 0.1);
N
Nesterov Alexander 已提交
23 24 25 26
    bool localization();
    bool transformation();
    Mat getBinBarcode() { return bin_barcode; }
    Mat getStraightBarcode() { return straight_barcode; }
27 28 29 30 31 32 33 34 35
    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 已提交
36
    inline double getCosVectors(Point2f a, Point2f b, Point2f c);
37 38 39

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

43

N
Nesterov Alexander 已提交
44 45
void QRDecode::init(Mat src, double eps_vertical_, double eps_horizontal_)
{
46 47 48 49 50 51 52 53 54 55 56 57 58 59
    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 已提交
60 61
    eps_vertical   = eps_vertical_;
    eps_horizontal = eps_horizontal_;
A
Alexander Nesterov 已提交
62
    adaptiveThreshold(barcode, bin_barcode, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 71, 2);
N
Nesterov Alexander 已提交
63 64
}

65
vector<Vec3d> QRDecode::searchVerticalLines()
N
Nesterov Alexander 已提交
66
{
67
    vector<Vec3d> result;
N
Nesterov Alexander 已提交
68
    int temp_length = 0;
69 70
    uint8_t next_pixel, future_pixel;
    vector<double> test_lines;
N
Nesterov Alexander 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

    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)
            {
100
                double length = 0.0, weight = 0.0;
N
Nesterov Alexander 已提交
101 102 103

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

104
                CV_Assert(length > 0);
N
Nesterov Alexander 已提交
105 106
                for (size_t i = 0; i < test_lines.size(); i++)
                {
107 108
                    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 已提交
109 110 111 112 113 114 115 116 117 118 119 120 121 122
                }

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

123
vector<Point2f> QRDecode::separateHorizontalLines(vector<Vec3d> list_lines)
N
Nesterov Alexander 已提交
124
{
125
    vector<Vec3d> result;
N
Nesterov Alexander 已提交
126
    int temp_length = 0;
127 128
    uint8_t next_pixel, future_pixel;
    vector<double> test_lines;
N
Nesterov Alexander 已提交
129 130 131

    for (size_t pnt = 0; pnt < list_lines.size(); pnt++)
    {
A
Alexander Nesterov 已提交
132
        int x = static_cast<int>(list_lines[pnt][0] + list_lines[pnt][2] * 0.5);
133
        int y = static_cast<int>(list_lines[pnt][1]);
N
Nesterov Alexander 已提交
134 135 136 137 138 139 140 141 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

        // --------------- 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)
        {
172
            double length = 0.0, weight = 0.0;
N
Nesterov Alexander 已提交
173 174 175

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

176
            CV_Assert(length > 0);
N
Nesterov Alexander 已提交
177 178
            for (size_t i = 0; i < test_lines.size(); i++)
            {
179 180
                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 已提交
181 182
            }

183 184 185 186
            if(weight < eps_horizontal)
            {
                result.push_back(list_lines[pnt]);
            }
N
Nesterov Alexander 已提交
187 188 189 190

        }
    }

191 192
    vector<Point2f> point2f_result;
    for (size_t i = 0; i < result.size(); i++)
N
Nesterov Alexander 已提交
193
    {
194 195
        point2f_result.push_back(
              Point2f(static_cast<float>(result[i][1]),
A
Alexander Nesterov 已提交
196
                      static_cast<float>(result[i][0] + result[i][2] * 0.5)));
N
Nesterov Alexander 已提交
197
    }
198
    return point2f_result;
N
Nesterov Alexander 已提交
199 200
}

201
void QRDecode::fixationPoints(vector<Point2f> &local_point)
N
Nesterov Alexander 已提交
202 203 204 205 206 207 208 209
{
    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))
210
    / (2 * norm_triangl[1] * norm_triangl[2]);
N
Nesterov Alexander 已提交
211
    cos_angles[1] = (pow(norm_triangl[0], 2) + pow(norm_triangl[2], 2) - pow(norm_triangl[1], 2))
212
    / (2 * norm_triangl[0] * norm_triangl[2]);
N
Nesterov Alexander 已提交
213
    cos_angles[2] = (pow(norm_triangl[0], 2) + pow(norm_triangl[1], 2) - pow(norm_triangl[2], 2))
214 215 216 217 218 219 220 221 222 223 224
    / (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 已提交
225
    {
226
        std::swap(local_point[1], local_point[2]);
N
Nesterov Alexander 已提交
227 228 229
    }
}

230
bool QRDecode::localization()
N
Nesterov Alexander 已提交
231
{
232 233 234 235 236 237 238 239
    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;
240
    if (list_lines_y.size() < 3) { return false; }
241 242 243 244 245
    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 已提交
246 247
    if (localization_points.size() != 3) { return false; }

248
    if (coeff_expansion > 1.0)
N
Nesterov Alexander 已提交
249
    {
250 251 252 253 254 255 256 257 258 259
        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 已提交
260 261
    }

262 263 264 265 266 267 268 269 270 271
    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 已提交
272
    return true;
273

N
Nesterov Alexander 已提交
274 275
}

276
bool QRDecode::computeTransformationPoints()
N
Nesterov Alexander 已提交
277
{
278
    if (localization_points.size() != 3) { return false; }
N
Nesterov Alexander 已提交
279

280 281 282
    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 已提交
283
    {
284 285 286 287
        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 已提交
288
        {
289 290 291
            next_pixel = bin_barcode.at<uint8_t>(
                            static_cast<int>(localization_points[i].y), index + 1);
            if (next_pixel == future_pixel)
N
Nesterov Alexander 已提交
292
            {
293 294 295
                future_pixel = 255 - future_pixel;
                count_test_lines++;
                if (count_test_lines == 2)
N
Nesterov Alexander 已提交
296
                {
297 298 299 300
                    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 已提交
301 302 303
                }
            }
        }
304 305 306 307 308 309 310 311
        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 已提交
312
        {
313 314 315 316 317 318 319
            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 已提交
320 321 322
        }
    }

323 324 325
    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 已提交
326
    {
327
        for (size_t j = 0; j < new_non_zero_elem[2].size(); j++)
N
Nesterov Alexander 已提交
328
        {
329 330 331 332 333 334 335
            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 已提交
336 337
        }
    }
A
Alexander Nesterov 已提交
338

339
    if (down_left_edge_point == Point2f(0, 0) ||
A
Alexander Nesterov 已提交
340 341
        up_right_edge_point  == Point2f(0, 0) ||
        new_non_zero_elem[0].size() == 0) { return false; }
N
Nesterov Alexander 已提交
342

343 344
    double max_area = -1;
    up_left_edge_point = new_non_zero_elem[0][0];
A
Alexander Nesterov 已提交
345

346 347
    for (size_t i = 0; i < new_non_zero_elem[0].size(); i++)
    {
A
Alexander Nesterov 已提交
348 349 350 351 352
        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);

A
Alexander Nesterov 已提交
353
        double temp_area = fabs(contourArea(list_edge_points));
A
Alexander Nesterov 已提交
354

355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
        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;
        }
    }

A
Alexander Nesterov 已提交
375

376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
    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 已提交
398 399
}

400
Point2f QRDecode::intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2)
N
Nesterov Alexander 已提交
401
{
402 403 404 405 406 407 408 409 410 411
    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 已提交
412 413 414
    return result_square_angle;
}

415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
// 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 已提交
443 444 445
{
    size_t angle_size = angle_list.size();
    uint8_t value, mask_value;
446 447
    Mat mask = Mat::zeros(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1);
    Mat fill_bin_barcode = bin_barcode.clone();
N
Nesterov Alexander 已提交
448 449 450 451 452 453 454 455 456 457
    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)
            {
458 459
                floodFill(fill_bin_barcode, mask, line_iter.pos(), 255,
                          0, Scalar(), Scalar(), FLOODFILL_MASK_ONLY);
N
Nesterov Alexander 已提交
460 461 462
            }
        }
    }
463 464
    vector<Point> locations;
    Mat mask_roi = mask(Range(1, bin_barcode.rows - 1), Range(1, bin_barcode.cols - 1));
N
Nesterov Alexander 已提交
465 466 467 468 469

    cv::findNonZero(mask_roi, locations);

    for (size_t i = 0; i < angle_list.size(); i++)
    {
470 471 472
        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 已提交
473 474
    }

475 476 477 478 479
    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 已提交
480
    {
481 482 483
        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 已提交
484 485
    }

A
Alexander Nesterov 已提交
486
    const double experimental_area = fabs(contourArea(hull));
487

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

506
    int start_line[2] = { 0, 0 }, finish_line[2] = { 0, 0 }, unstable_pnt = 0;
N
Nesterov Alexander 已提交
507 508
    for (int i = 0; i < hull_size; i++)
    {
509 510 511 512
        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 已提交
513 514
    }

515
    int index_hull, extra_index_hull, next_index_hull, extra_next_index_hull;
N
Nesterov Alexander 已提交
516 517
    Point result_side_begin[4], result_side_end[4];

518 519 520
    bool bypass_orientation = testBypassRoute(hull, start_line[0], finish_line[0]);
    bool extra_bypass_orientation;

N
Nesterov Alexander 已提交
521 522 523 524
    min_norm = std::numeric_limits<double>::max();
    index_hull = start_line[0];
    do
    {
525
        if (bypass_orientation) { next_index_hull = index_hull + 1; }
N
Nesterov Alexander 已提交
526 527 528 529 530
        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; }

531 532
        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 已提交
533 534

        Point intrsc_line_hull =
535 536 537
        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 已提交
538
        if (min_norm > temp_norm &&
539
            norm(hull[index_hull] - hull[next_index_hull]) >
A
Alexander Nesterov 已提交
540
            norm(angle_list[1] - angle_list[2]) * 0.1)
N
Nesterov Alexander 已提交
541 542
        {
            min_norm = temp_norm;
543 544
            result_side_begin[0] = hull[index_hull];
            result_side_end[0]   = hull[next_index_hull];
N
Nesterov Alexander 已提交
545 546 547 548 549 550 551 552 553
        }


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

    if (min_norm == std::numeric_limits<double>::max())
    {
554 555
        result_side_begin[0] = angle_list[1];
        result_side_end[0]   = angle_list[2];
N
Nesterov Alexander 已提交
556 557 558 559
    }

    min_norm = std::numeric_limits<double>::max();
    index_hull = start_line[1];
560
    bypass_orientation = testBypassRoute(hull, start_line[1], finish_line[1]);
N
Nesterov Alexander 已提交
561 562
    do
    {
563
        if (bypass_orientation) { next_index_hull = index_hull + 1; }
N
Nesterov Alexander 已提交
564 565 566 567 568
        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; }

569 570
        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 已提交
571 572

        Point intrsc_line_hull =
573 574 575
        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 已提交
576
        if (min_norm > temp_norm &&
577
            norm(hull[index_hull] - hull[next_index_hull]) >
A
Alexander Nesterov 已提交
578
            norm(angle_list[0] - angle_list[1]) * 0.05)
N
Nesterov Alexander 已提交
579 580
        {
            min_norm = temp_norm;
581 582
            result_side_begin[1] = hull[index_hull];
            result_side_end[1]   = hull[next_index_hull];
N
Nesterov Alexander 已提交
583 584 585 586 587 588 589 590
        }

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

    if (min_norm == std::numeric_limits<double>::max())
    {
591 592
        result_side_begin[1] = angle_list[0];
        result_side_end[1]   = angle_list[1];
N
Nesterov Alexander 已提交
593 594
    }

595 596
    bypass_orientation = testBypassRoute(hull, start_line[0], unstable_pnt);
    extra_bypass_orientation = testBypassRoute(hull, finish_line[1], unstable_pnt);
N
Nesterov Alexander 已提交
597

598
    vector<Point2f> result_angle_list(4), test_result_angle_list(4);
599
    double min_diff_area = std::numeric_limits<double>::max(), test_diff_area;
N
Nesterov Alexander 已提交
600
    index_hull = start_line[0];
601 602 603
    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 已提交
604 605
    do
    {
606
        if (bypass_orientation) { next_index_hull = index_hull + 1; }
N
Nesterov Alexander 已提交
607 608 609 610 611
        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 已提交
612
        if (norm(hull[index_hull] - hull[next_index_hull]) < standart_norm * 0.1)
613 614
        { index_hull = next_index_hull; continue; }

N
Nesterov Alexander 已提交
615 616 617
        extra_index_hull = finish_line[1];
        do
        {
618
            if (extra_bypass_orientation) { extra_next_index_hull = extra_index_hull + 1; }
N
Nesterov Alexander 已提交
619 620 621 622 623
            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 已提交
624
            if (norm(hull[extra_index_hull] - hull[extra_next_index_hull]) < standart_norm * 0.1)
625 626
            { extra_index_hull = extra_next_index_hull; continue; }

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

A
Alexander Nesterov 已提交
640
            test_diff_area = fabs(fabs(contourArea(test_result_angle_list)) - experimental_area);
641
            if (min_diff_area > test_diff_area)
N
Nesterov Alexander 已提交
642
            {
643
                min_diff_area = test_diff_area;
N
Nesterov Alexander 已提交
644 645 646 647 648 649 650 651 652 653 654 655 656
                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);
A
Alexander Nesterov 已提交
657 658 659 660 661

    if (norm(result_angle_list[0] - angle_list[1]) > 2) { result_angle_list[0] = angle_list[1]; }
    if (norm(result_angle_list[1] - angle_list[0]) > 2) { result_angle_list[1] = angle_list[0]; }
    if (norm(result_angle_list[3] - angle_list[2]) > 2) { result_angle_list[3] = angle_list[2]; }

N
Nesterov Alexander 已提交
662 663 664 665 666 667 668 669
    return result_angle_list;
}

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

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

675
bool QRDecode::transformation()
N
Nesterov Alexander 已提交
676
{
677 678 679 680 681 682 683
    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] -
684
                               transformation_points[(i + 1) % transform_size]);
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 731
        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 已提交
732
    QRDecode qrdec;
733
    qrdec.init(inarr, p->epsX, p->epsY);
N
Nesterov Alexander 已提交
734 735
    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
}