qrcode.cpp 28.7 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;

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

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

42

43
void QRDetect::init(const Mat& src, double eps_vertical_, double eps_horizontal_)
N
Nesterov Alexander 已提交
44
{
45 46
    CV_Assert(!src.empty());
    const double min_side = std::min(src.size().width, src.size().height);
47 48 49
    if (min_side < 512.0)
    {
        coeff_expansion = 512.0 / min_side;
50 51
        const int width  = cvRound(src.size().width  * coeff_expansion);
        const int height = cvRound(src.size().height  * coeff_expansion);
52
        Size new_size(width, height);
53
        resize(src, barcode, new_size, 0, 0, INTER_LINEAR);
54 55 56 57 58 59
    }
    else
    {
        coeff_expansion = 1.0;
        barcode = src;
    }
60

N
Nesterov Alexander 已提交
61 62
    eps_vertical   = eps_vertical_;
    eps_horizontal = eps_horizontal_;
63
    adaptiveThreshold(barcode, bin_barcode, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 83, 2);
N
Nesterov Alexander 已提交
64 65
}

66
vector<Vec3d> QRDetect::searchHorizontalLines()
N
Nesterov Alexander 已提交
67
{
68
    vector<Vec3d> result;
69 70 71 72 73 74 75 76 77
    const int height_bin_barcode = bin_barcode.rows;
    const int width_bin_barcode  = bin_barcode.cols;
    const size_t test_lines_size = 5;
    double test_lines[test_lines_size];
    const size_t count_pixels_position = 1024;
    size_t pixels_position[count_pixels_position];
    size_t index = 0;

    for (int y = 0; y < height_bin_barcode; y++)
N
Nesterov Alexander 已提交
78
    {
79
        const uint8_t *bin_barcode_row = bin_barcode.ptr<uint8_t>(y);
N
Nesterov Alexander 已提交
80

81 82 83
        int pos = 0;
        for (; pos < width_bin_barcode; pos++) { if (bin_barcode_row[pos] == 0) break; }
        if (pos == width_bin_barcode) { continue; }
N
Nesterov Alexander 已提交
84

85 86 87
        index = 0;
        pixels_position[index] = pixels_position[index + 1] = pixels_position[index + 2] = pos;
        index +=3;
N
Nesterov Alexander 已提交
88

89 90 91 92
        uint8_t future_pixel = 255;
        for (int x = pos; x < width_bin_barcode; x++)
        {
            if (bin_barcode_row[x] == future_pixel)
N
Nesterov Alexander 已提交
93
            {
94 95 96
                future_pixel = 255 - future_pixel;
                pixels_position[index] = x;
                index++;
N
Nesterov Alexander 已提交
97
            }
98 99 100 101 102 103 104 105 106 107
        }
        pixels_position[index] = width_bin_barcode - 1;
        index++;
        for (size_t i = 2; i < index - 4; i+=2)
        {
            test_lines[0] = static_cast<double>(pixels_position[i - 1] - pixels_position[i - 2]);
            test_lines[1] = static_cast<double>(pixels_position[i    ] - pixels_position[i - 1]);
            test_lines[2] = static_cast<double>(pixels_position[i + 1] - pixels_position[i    ]);
            test_lines[3] = static_cast<double>(pixels_position[i + 2] - pixels_position[i + 1]);
            test_lines[4] = static_cast<double>(pixels_position[i + 3] - pixels_position[i + 2]);
N
Nesterov Alexander 已提交
108

109
            double length = 0.0, weight = 0.0;
N
Nesterov Alexander 已提交
110

111
            for (size_t j = 0; j < test_lines_size; j++) { length += test_lines[j]; }
N
Nesterov Alexander 已提交
112

113 114 115 116 117 118
            if (length == 0) { continue; }
            for (size_t j = 0; j < test_lines_size; j++)
            {
                if (j == 2) { weight += fabs((test_lines[j] / length) - 3.0/7.0); }
                else        { weight += fabs((test_lines[j] / length) - 1.0/7.0); }
            }
N
Nesterov Alexander 已提交
119

120 121 122 123 124 125 126
            if (weight < eps_vertical)
            {
                Vec3d line;
                line[0] = static_cast<double>(pixels_position[i - 2]);
                line[1] = y;
                line[2] = length;
                result.push_back(line);
N
Nesterov Alexander 已提交
127 128 129 130 131 132
            }
        }
    }
    return result;
}

133
vector<Point2f> QRDetect::separateVerticalLines(const vector<Vec3d> &list_lines)
N
Nesterov Alexander 已提交
134
{
135
    vector<Vec3d> result;
N
Nesterov Alexander 已提交
136
    int temp_length = 0;
137
    uint8_t next_pixel;
138
    vector<double> test_lines;
N
Nesterov Alexander 已提交
139

140

N
Nesterov Alexander 已提交
141 142
    for (size_t pnt = 0; pnt < list_lines.size(); pnt++)
    {
143 144 145 146
        const int x = cvRound(list_lines[pnt][0] + list_lines[pnt][2] * 0.5);
        const int y = cvRound(list_lines[pnt][1]);

        // --------------- Search vertical up-lines --------------- //
N
Nesterov Alexander 已提交
147 148

        test_lines.clear();
149
        uint8_t future_pixel_up = 255;
N
Nesterov Alexander 已提交
150

151
        for (int j = y; j < bin_barcode.rows - 1; j++)
N
Nesterov Alexander 已提交
152
        {
153
            next_pixel = bin_barcode.at<uint8_t>(j + 1, x);
N
Nesterov Alexander 已提交
154
            temp_length++;
155
            if (next_pixel == future_pixel_up)
N
Nesterov Alexander 已提交
156
            {
157
                future_pixel_up = 255 - future_pixel_up;
N
Nesterov Alexander 已提交
158 159 160 161 162 163
                test_lines.push_back(temp_length);
                temp_length = 0;
                if (test_lines.size() == 3) { break; }
            }
        }

164
        // --------------- Search vertical down-lines --------------- //
N
Nesterov Alexander 已提交
165

166
        uint8_t future_pixel_down = 255;
N
Nesterov Alexander 已提交
167 168
        for (int j = y; j >= 1; j--)
        {
169
            next_pixel = bin_barcode.at<uint8_t>(j - 1, x);
N
Nesterov Alexander 已提交
170
            temp_length++;
171
            if (next_pixel == future_pixel_down)
N
Nesterov Alexander 已提交
172
            {
173
                future_pixel_down = 255 - future_pixel_down;
N
Nesterov Alexander 已提交
174 175 176 177 178 179
                test_lines.push_back(temp_length);
                temp_length = 0;
                if (test_lines.size() == 6) { break; }
            }
        }

180
        // --------------- Compute vertical lines --------------- //
N
Nesterov Alexander 已提交
181 182 183

        if (test_lines.size() == 6)
        {
184
            double length = 0.0, weight = 0.0;
N
Nesterov Alexander 已提交
185 186 187

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

188
            CV_Assert(length > 0);
N
Nesterov Alexander 已提交
189 190
            for (size_t i = 0; i < test_lines.size(); i++)
            {
191 192
                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 已提交
193 194
            }

195 196 197 198
            if(weight < eps_horizontal)
            {
                result.push_back(list_lines[pnt]);
            }
N
Nesterov Alexander 已提交
199 200 201 202

        }
    }

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

213
void QRDetect::fixationPoints(vector<Point2f> &local_point)
N
Nesterov Alexander 已提交
214
{
215

N
Nesterov Alexander 已提交
216 217 218 219 220 221
    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]);

222 223 224 225 226 227 228 229 230 231 232 233 234
    cos_angles[0] = (norm_triangl[1] * norm_triangl[1] + norm_triangl[2] * norm_triangl[2]
                  -  norm_triangl[0] * norm_triangl[0]) / (2 * norm_triangl[1] * norm_triangl[2]);
    cos_angles[1] = (norm_triangl[0] * norm_triangl[0] + norm_triangl[2] * norm_triangl[2]
                  -  norm_triangl[1] * norm_triangl[1]) / (2 * norm_triangl[0] * norm_triangl[2]);
    cos_angles[2] = (norm_triangl[0] * norm_triangl[0] + norm_triangl[1] * norm_triangl[1]
                  -  norm_triangl[2] * norm_triangl[2]) / (2 * norm_triangl[0] * norm_triangl[1]);

    const double angle_barrier = 0.85;
    if (fabs(cos_angles[0]) > angle_barrier || fabs(cos_angles[1]) > angle_barrier || fabs(cos_angles[2]) > angle_barrier)
    {
        local_point.clear();
        return;
    }
235 236

    size_t i_min_cos =
237 238
       (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;
239

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
    size_t index_max = 0;
    double max_area = std::numeric_limits<double>::min();
    for (size_t i = 0; i < local_point.size(); i++)
    {
        const size_t current_index = i % 3;
        const size_t left_index  = (i + 1) % 3;
        const size_t right_index = (i + 2) % 3;

        const Point2f current_point(local_point[current_index]),
            left_point(local_point[left_index]), right_point(local_point[right_index]),
            central_point(intersectionLines(current_point,
                              Point2f(static_cast<float>((local_point[left_index].x + local_point[right_index].x) * 0.5),
                                      static_cast<float>((local_point[left_index].y + local_point[right_index].y) * 0.5)),
                              Point2f(0, static_cast<float>(bin_barcode.rows - 1)),
                              Point2f(static_cast<float>(bin_barcode.cols - 1),
                                      static_cast<float>(bin_barcode.rows - 1))));


        vector<Point2f> list_area_pnt;
        list_area_pnt.push_back(current_point);

        vector<LineIterator> list_line_iter;
        list_line_iter.push_back(LineIterator(bin_barcode, current_point, left_point));
        list_line_iter.push_back(LineIterator(bin_barcode, current_point, central_point));
        list_line_iter.push_back(LineIterator(bin_barcode, current_point, right_point));
265

266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
        for (size_t k = 0; k < list_line_iter.size(); k++)
        {
            uint8_t future_pixel = 255, count_index = 0;
            for(int j = 0; j < list_line_iter[k].count; j++, ++list_line_iter[k])
            {
                if (list_line_iter[k].pos().x >= bin_barcode.cols ||
                    list_line_iter[k].pos().y >= bin_barcode.rows) { break; }
                const uint8_t value = bin_barcode.at<uint8_t>(list_line_iter[k].pos());
                if (value == future_pixel)
                {
                    future_pixel = 255 - future_pixel;
                    count_index++;
                    if (count_index == 3)
                    {
                        list_area_pnt.push_back(list_line_iter[k].pos());
                        break;
                    }
                }
            }
        }

        const double temp_check_area = contourArea(list_area_pnt);
        if (temp_check_area > max_area)
        {
            index_max = current_index;
            max_area = temp_check_area;
        }

    }
    if (index_max == i_min_cos) { std::swap(local_point[0], local_point[index_max]); }
    else { local_point.clear(); return; }

    const Point2f rpt = local_point[0], bpt = local_point[1], gpt = local_point[2];
299 300
    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 已提交
301
    {
302
        std::swap(local_point[1], local_point[2]);
N
Nesterov Alexander 已提交
303 304 305
    }
}

306
bool QRDetect::localization()
N
Nesterov Alexander 已提交
307
{
308
    Point2f begin, end;
309
    vector<Vec3d> list_lines_x = searchHorizontalLines();
310
    if( list_lines_x.empty() ) { return false; }
311 312
    vector<Point2f> list_lines_y = separateVerticalLines(list_lines_x);
    if( list_lines_y.size() < 3 ) { return false; }
313 314 315 316

    vector<Point2f> centers;
    Mat labels;
    kmeans(list_lines_y, 3, labels,
317
           TermCriteria( TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1),
318 319 320
           3, KMEANS_PP_CENTERS, localization_points);

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

323
    if (coeff_expansion > 1.0)
N
Nesterov Alexander 已提交
324
    {
325 326
        const int width  = cvRound(bin_barcode.size().width  / coeff_expansion);
        const int height = cvRound(bin_barcode.size().height / coeff_expansion);
327
        Size new_size(width, height);
328 329
        Mat intermediate = Mat::zeros(new_size, CV_8UC1);
        resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR);
330 331 332 333 334
        bin_barcode = intermediate.clone();
        for (size_t i = 0; i < localization_points.size(); i++)
        {
            localization_points[i] /= coeff_expansion;
        }
N
Nesterov Alexander 已提交
335 336
    }

337 338 339 340 341 342 343 344 345 346
    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 已提交
347
    return true;
348

N
Nesterov Alexander 已提交
349 350
}

351
bool QRDetect::computeTransformationPoints()
N
Nesterov Alexander 已提交
352
{
353
    if (localization_points.size() != 3) { return false; }
N
Nesterov Alexander 已提交
354

355 356 357
    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 已提交
358
    {
359 360
        Mat mask = Mat::zeros(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1);
        uint8_t next_pixel, future_pixel = 255;
361
        int count_test_lines = 0, index = cvRound(localization_points[i].x);
362
        for (; index < bin_barcode.cols - 1; index++)
N
Nesterov Alexander 已提交
363
        {
364
            next_pixel = bin_barcode.at<uint8_t>(
365
                            cvRound(localization_points[i].y), index + 1);
366
            if (next_pixel == future_pixel)
N
Nesterov Alexander 已提交
367
            {
368 369 370
                future_pixel = 255 - future_pixel;
                count_test_lines++;
                if (count_test_lines == 2)
N
Nesterov Alexander 已提交
371
                {
372
                    floodFill(bin_barcode, mask,
373
                              Point(index + 1, cvRound(localization_points[i].y)), 255,
374 375
                              0, Scalar(), Scalar(), FLOODFILL_MASK_ONLY);
                    break;
N
Nesterov Alexander 已提交
376 377 378
                }
            }
        }
379 380 381 382 383 384 385 386
        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 已提交
387
        {
388 389 390 391 392 393 394
            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 已提交
395 396 397
        }
    }

398 399 400
    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 已提交
401
    {
402
        for (size_t j = 0; j < new_non_zero_elem[2].size(); j++)
N
Nesterov Alexander 已提交
403
        {
404 405 406 407 408 409 410
            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 已提交
411 412
        }
    }
A
Alexander Nesterov 已提交
413

414
    if (down_left_edge_point == Point2f(0, 0) ||
A
Alexander Nesterov 已提交
415 416
        up_right_edge_point  == Point2f(0, 0) ||
        new_non_zero_elem[0].size() == 0) { return false; }
N
Nesterov Alexander 已提交
417

418 419
    double max_area = -1;
    up_left_edge_point = new_non_zero_elem[0][0];
A
Alexander Nesterov 已提交
420

421 422
    for (size_t i = 0; i < new_non_zero_elem[0].size(); i++)
    {
A
Alexander Nesterov 已提交
423 424 425 426 427
        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 已提交
428
        double temp_area = fabs(contourArea(list_edge_points));
A
Alexander Nesterov 已提交
429

430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
        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 已提交
450

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
    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 已提交
473 474
}

475
Point2f QRDetect::intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2)
N
Nesterov Alexander 已提交
476
{
477 478 479 480 481 482 483 484 485 486
    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 已提交
487 488 489
    return result_square_angle;
}

490
// test function (if true then ------> else <------ )
491
bool QRDetect::testBypassRoute(vector<Point2f> hull, int start, int finish)
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
{
    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; }
}

517
vector<Point2f> QRDetect::getQuadrilateral(vector<Point2f> angle_list)
N
Nesterov Alexander 已提交
518 519 520
{
    size_t angle_size = angle_list.size();
    uint8_t value, mask_value;
521 522
    Mat mask = Mat::zeros(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1);
    Mat fill_bin_barcode = bin_barcode.clone();
N
Nesterov Alexander 已提交
523 524 525 526 527 528 529 530 531 532
    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)
            {
533 534
                floodFill(fill_bin_barcode, mask, line_iter.pos(), 255,
                          0, Scalar(), Scalar(), FLOODFILL_MASK_ONLY);
N
Nesterov Alexander 已提交
535 536 537
            }
        }
    }
538 539
    vector<Point> locations;
    Mat mask_roi = mask(Range(1, bin_barcode.rows - 1), Range(1, bin_barcode.cols - 1));
N
Nesterov Alexander 已提交
540 541 542 543 544

    cv::findNonZero(mask_roi, locations);

    for (size_t i = 0; i < angle_list.size(); i++)
    {
545 546
        int x = cvRound(angle_list[i].x);
        int y = cvRound(angle_list[i].y);
547
        locations.push_back(Point(x, y));
N
Nesterov Alexander 已提交
548 549
    }

550 551 552 553 554
    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 已提交
555
    {
556 557
        float x = saturate_cast<float>(integer_hull[i].x);
        float y = saturate_cast<float>(integer_hull[i].y);
558
        hull[i] = Point2f(x, y);
N
Nesterov Alexander 已提交
559 560
    }

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

563 564
    vector<Point2f> result_hull_point(angle_size);
    double min_norm;
N
Nesterov Alexander 已提交
565 566 567 568
    for (size_t i = 0; i < angle_size; i++)
    {
        min_norm = std::numeric_limits<double>::max();
        Point closest_pnt;
569
        for (int j = 0; j < hull_size; j++)
N
Nesterov Alexander 已提交
570
        {
571 572
            double temp_norm = norm(hull[j] - angle_list[i]);
            if (min_norm > temp_norm)
N
Nesterov Alexander 已提交
573
            {
574 575
                min_norm = temp_norm;
                closest_pnt = hull[j];
N
Nesterov Alexander 已提交
576 577 578 579 580
            }
        }
        result_hull_point[i] = closest_pnt;
    }

581
    int start_line[2] = { 0, 0 }, finish_line[2] = { 0, 0 }, unstable_pnt = 0;
N
Nesterov Alexander 已提交
582 583
    for (int i = 0; i < hull_size; i++)
    {
584 585 586 587
        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 已提交
588 589
    }

590
    int index_hull, extra_index_hull, next_index_hull, extra_next_index_hull;
N
Nesterov Alexander 已提交
591 592
    Point result_side_begin[4], result_side_end[4];

593 594
    bool bypass_orientation = testBypassRoute(hull, start_line[0], finish_line[0]);

N
Nesterov Alexander 已提交
595 596 597 598
    min_norm = std::numeric_limits<double>::max();
    index_hull = start_line[0];
    do
    {
599
        if (bypass_orientation) { next_index_hull = index_hull + 1; }
N
Nesterov Alexander 已提交
600 601 602 603 604
        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; }

605 606
        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 已提交
607 608

        Point intrsc_line_hull =
609 610 611
        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 已提交
612
        if (min_norm > temp_norm &&
613
            norm(hull[index_hull] - hull[next_index_hull]) >
A
Alexander Nesterov 已提交
614
            norm(angle_list[1] - angle_list[2]) * 0.1)
N
Nesterov Alexander 已提交
615 616
        {
            min_norm = temp_norm;
617 618
            result_side_begin[0] = hull[index_hull];
            result_side_end[0]   = hull[next_index_hull];
N
Nesterov Alexander 已提交
619 620 621 622 623 624 625 626 627
        }


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

    if (min_norm == std::numeric_limits<double>::max())
    {
628 629
        result_side_begin[0] = angle_list[1];
        result_side_end[0]   = angle_list[2];
N
Nesterov Alexander 已提交
630 631 632 633
    }

    min_norm = std::numeric_limits<double>::max();
    index_hull = start_line[1];
634
    bypass_orientation = testBypassRoute(hull, start_line[1], finish_line[1]);
N
Nesterov Alexander 已提交
635 636
    do
    {
637
        if (bypass_orientation) { next_index_hull = index_hull + 1; }
N
Nesterov Alexander 已提交
638 639 640 641 642
        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; }

643 644
        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 已提交
645 646

        Point intrsc_line_hull =
647 648 649
        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 已提交
650
        if (min_norm > temp_norm &&
651
            norm(hull[index_hull] - hull[next_index_hull]) >
A
Alexander Nesterov 已提交
652
            norm(angle_list[0] - angle_list[1]) * 0.05)
N
Nesterov Alexander 已提交
653 654
        {
            min_norm = temp_norm;
655 656
            result_side_begin[1] = hull[index_hull];
            result_side_end[1]   = hull[next_index_hull];
N
Nesterov Alexander 已提交
657 658 659 660 661 662 663 664
        }

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

    if (min_norm == std::numeric_limits<double>::max())
    {
665 666
        result_side_begin[1] = angle_list[0];
        result_side_end[1]   = angle_list[1];
N
Nesterov Alexander 已提交
667 668
    }

669
    bypass_orientation = testBypassRoute(hull, start_line[0], unstable_pnt);
670
    const bool extra_bypass_orientation = testBypassRoute(hull, finish_line[1], unstable_pnt);
N
Nesterov Alexander 已提交
671

672
    vector<Point2f> result_angle_list(4), test_result_angle_list(4);
673
    double min_diff_area = std::numeric_limits<double>::max();
N
Nesterov Alexander 已提交
674
    index_hull = start_line[0];
675
    const double standart_norm = std::max(
676 677
        norm(result_side_begin[0] - result_side_end[0]),
        norm(result_side_begin[1] - result_side_end[1]));
N
Nesterov Alexander 已提交
678 679
    do
    {
680
        if (bypass_orientation) { next_index_hull = index_hull + 1; }
N
Nesterov Alexander 已提交
681 682 683 684 685
        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 已提交
686
        if (norm(hull[index_hull] - hull[next_index_hull]) < standart_norm * 0.1)
687 688
        { index_hull = next_index_hull; continue; }

N
Nesterov Alexander 已提交
689 690 691
        extra_index_hull = finish_line[1];
        do
        {
692
            if (extra_bypass_orientation) { extra_next_index_hull = extra_index_hull + 1; }
N
Nesterov Alexander 已提交
693 694 695 696 697
            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 已提交
698
            if (norm(hull[extra_index_hull] - hull[extra_next_index_hull]) < standart_norm * 0.1)
699 700
            { extra_index_hull = extra_next_index_hull; continue; }

N
Nesterov Alexander 已提交
701
            test_result_angle_list[0]
702 703
            = intersectionLines(result_side_begin[0], result_side_end[0],
                                result_side_begin[1], result_side_end[1]);
N
Nesterov Alexander 已提交
704
            test_result_angle_list[1]
705 706
            = intersectionLines(result_side_begin[1], result_side_end[1],
                                hull[extra_index_hull], hull[extra_next_index_hull]);
N
Nesterov Alexander 已提交
707
            test_result_angle_list[2]
708 709
            = intersectionLines(hull[extra_index_hull], hull[extra_next_index_hull],
                                hull[index_hull], hull[next_index_hull]);
N
Nesterov Alexander 已提交
710
            test_result_angle_list[3]
711 712 713
            = intersectionLines(hull[index_hull], hull[next_index_hull],
                                result_side_begin[0], result_side_end[0]);

714 715
            const double test_diff_area
                = fabs(fabs(contourArea(test_result_angle_list)) - experimental_area);
716
            if (min_diff_area > test_diff_area)
N
Nesterov Alexander 已提交
717
            {
718
                min_diff_area = test_diff_area;
N
Nesterov Alexander 已提交
719 720 721 722 723 724 725 726 727 728 729 730 731
                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 已提交
732

733
    // check label points
A
Alexander Nesterov 已提交
734 735 736 737
    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]; }

738 739 740 741 742 743
    // check calculation point
    if (norm(result_angle_list[2] - angle_list[3]) >
       (norm(result_angle_list[0] - result_angle_list[1]) +
        norm(result_angle_list[0] - result_angle_list[3])) * 0.5 )
    { result_angle_list[2] = angle_list[3]; }

N
Nesterov Alexander 已提交
744 745 746 747 748 749 750 751
    return result_angle_list;
}

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

752
inline double QRDetect::getCosVectors(Point2f a, Point2f b, Point2f c)
N
Nesterov Alexander 已提交
753
{
A
Alexander Nesterov 已提交
754
    return ((a - b).x * (c - b).x + (a - b).y * (c - b).y) / (norm(a - b) * norm(c - b));
N
Nesterov Alexander 已提交
755 756
}

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
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);
777 778 779 780 781
    QRDetect qrdet;
    qrdet.init(inarr, p->epsX, p->epsY);
    if (!qrdet.localization()) { return false; }
    if (!qrdet.computeTransformationPoints()) { return false; }
    vector<Point2f> pnts2f = qrdet.getTransformationPoints();
782
    Mat(pnts2f).convertTo(points, points.fixedType() ? points.type() : CV_32FC2);
N
Nesterov Alexander 已提交
783 784 785
    return true;
}

786 787 788 789 790 791 792 793 794
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 已提交
795
}