qrcode.cpp 27.6 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 38 39 40 41 42 43 44
    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);
    double getQuadrilateralArea(Point2f a, Point2f b, Point2f c, Point2f d);
    double getTriangleArea(Point2f a, Point2f b, Point2f c);
    double getCosVectors(Point2f a, Point2f b, Point2f c);

    Mat barcode, bin_barcode, straight_barcode;
    vector<Point2f> localization_points, transformation_points;
    double experimental_area, eps_vertical, eps_horizontal, coeff_expansion;
N
Nesterov Alexander 已提交
45 46
};

47

N
Nesterov Alexander 已提交
48 49
void QRDecode::init(Mat src, double eps_vertical_, double eps_horizontal_)
{
50 51 52 53 54 55 56 57 58 59 60 61 62 63
    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 已提交
64 65 66 67 68 69 70 71 72 73 74
    eps_vertical   = eps_vertical_;
    eps_horizontal = eps_horizontal_;
}

void QRDecode::binarization()
{
    Mat filter_barcode;
    GaussianBlur(barcode, filter_barcode, Size(3, 3), 0);
    threshold(filter_barcode, bin_barcode, 0, 255, THRESH_BINARY + THRESH_OTSU);
}

75
vector<Vec3d> QRDecode::searchVerticalLines()
N
Nesterov Alexander 已提交
76
{
77
    vector<Vec3d> result;
N
Nesterov Alexander 已提交
78
    int temp_length = 0;
79 80
    uint8_t next_pixel, future_pixel;
    vector<double> test_lines;
N
Nesterov Alexander 已提交
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 108 109

    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)
            {
110
                double length = 0.0, weight = 0.0;
N
Nesterov Alexander 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131

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

                for (size_t i = 0; i < test_lines.size(); i++)
                {
                    if (i == 2) { weight += abs((test_lines[i] / length) - 3.0/7.0); }
                    else        { weight += abs((test_lines[i] / length) - 1.0/7.0); }
                }

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

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

    for (size_t pnt = 0; pnt < list_lines.size(); pnt++)
    {
141 142
        int x = static_cast<int>(list_lines[pnt][0] + list_lines[pnt][2] / 2);
        int y = static_cast<int>(list_lines[pnt][1]);
N
Nesterov Alexander 已提交
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 180

        // --------------- 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)
        {
181
            double length = 0.0, weight = 0.0;
N
Nesterov Alexander 已提交
182 183 184 185 186 187 188 189 190

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

            for (size_t i = 0; i < test_lines.size(); i++)
            {
                if (i % 3 == 0) { weight += abs((test_lines[i] / length) - 3.0/14.0); }
                else            { weight += abs((test_lines[i] / length) - 1.0/ 7.0); }
            }

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 204
        point2f_result.push_back(
              Point2f(static_cast<float>(result[i][1]),
                      static_cast<float>(result[i][0] + result[i][2] / 2)));
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 248 249 250 251 252
    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;
    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 已提交
253 254
    if (localization_points.size() != 3) { return false; }

255
    if (coeff_expansion > 1.0)
N
Nesterov Alexander 已提交
256
    {
257 258 259 260 261 262 263 264 265 266
        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 已提交
267 268
    }

269 270 271 272 273 274 275 276 277 278
    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 已提交
279
    return true;
280

N
Nesterov Alexander 已提交
281 282
}

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

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

330 331 332
    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 已提交
333
    {
334
        for (size_t j = 0; j < new_non_zero_elem[2].size(); j++)
N
Nesterov Alexander 已提交
335
        {
336 337 338 339 340 341 342
            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 已提交
343 344
        }
    }
345 346
    if (down_left_edge_point == Point2f(0, 0) ||
        up_right_edge_point  == Point2f(0, 0)) { return false; }
N
Nesterov Alexander 已提交
347

348 349 350 351 352 353 354 355 356 357 358 359 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 402 403
    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++)
    {
        double temp_area = getTriangleArea(new_non_zero_elem[0][i],
                                           down_left_edge_point,
                                           up_right_edge_point);
        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));



    experimental_area = getQuadrilateralArea(transformation_points[0],
                                             transformation_points[1],
                                             transformation_points[2],
                                             transformation_points[3]);

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

    return true;
N
Nesterov Alexander 已提交
404 405
}

406
Point2f QRDecode::intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2)
N
Nesterov Alexander 已提交
407
{
408 409 410 411 412 413 414 415 416 417
    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 已提交
418 419 420
    return result_square_angle;
}

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 447 448
// 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 已提交
449 450 451
{
    size_t angle_size = angle_list.size();
    uint8_t value, mask_value;
452 453
    Mat mask = Mat::zeros(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1);
    Mat fill_bin_barcode = bin_barcode.clone();
N
Nesterov Alexander 已提交
454 455 456 457 458 459 460 461 462 463
    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)
            {
464 465
                floodFill(fill_bin_barcode, mask, line_iter.pos(), 255,
                          0, Scalar(), Scalar(), FLOODFILL_MASK_ONLY);
N
Nesterov Alexander 已提交
466 467 468
            }
        }
    }
469 470
    vector<Point> locations;
    Mat mask_roi = mask(Range(1, bin_barcode.rows - 1), Range(1, bin_barcode.cols - 1));
N
Nesterov Alexander 已提交
471 472 473 474 475

    cv::findNonZero(mask_roi, locations);

    for (size_t i = 0; i < angle_list.size(); i++)
    {
476 477 478
        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 已提交
479 480
    }

481 482 483 484 485
    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 已提交
486
    {
487 488 489
        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 已提交
490 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 544
            norm(hull[index_hull] - hull[next_index_hull]) >
            norm(angle_list[1] - angle_list[2]) / 10)
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 582
            norm(hull[index_hull] - hull[next_index_hull]) >
            norm(angle_list[0] - angle_list[1]) / 20)
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);
N
Nesterov Alexander 已提交
603 604 605 606
    double min_area = std::numeric_limits<double>::max(), test_area;
    index_hull = start_line[0];
    do
    {
607
        if (bypass_orientation) { next_index_hull = index_hull + 1; }
N
Nesterov Alexander 已提交
608 609 610 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; }

        extra_index_hull = finish_line[1];
        do
        {
616
            if (extra_bypass_orientation) { extra_next_index_hull = extra_index_hull + 1; }
N
Nesterov Alexander 已提交
617 618 619 620 621 622
            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; }

            test_result_angle_list[0]
623 624
            = intersectionLines(result_side_begin[0], result_side_end[0],
                                result_side_begin[1], result_side_end[1]);
N
Nesterov Alexander 已提交
625
            test_result_angle_list[1]
626 627
            = intersectionLines(result_side_begin[1], result_side_end[1],
                                hull[extra_index_hull], hull[extra_next_index_hull]);
N
Nesterov Alexander 已提交
628
            test_result_angle_list[2]
629 630
            = intersectionLines(hull[extra_index_hull], hull[extra_next_index_hull],
                                hull[index_hull], hull[next_index_hull]);
N
Nesterov Alexander 已提交
631
            test_result_angle_list[3]
632 633 634
            = intersectionLines(hull[index_hull], hull[next_index_hull],
                                result_side_begin[0], result_side_end[0]);

N
Nesterov Alexander 已提交
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
            test_area = getQuadrilateralArea(test_result_angle_list[0],
                                             test_result_angle_list[1],
                                             test_result_angle_list[2],
                                             test_result_angle_list[3]);
            if (min_area > test_area)
            {
                min_area = test_area;
                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);

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

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

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

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

    return result_angle_list;
}

//        b __________ c
//        /           |
//       /            |
//      /      S      |
//     /              |
//   a --------------- d

678
double QRDecode::getQuadrilateralArea(Point2f a, Point2f b, Point2f c, Point2f d)
N
Nesterov Alexander 已提交
679 680 681 682 683
{
    double length_sides[4], perimeter = 0.0, result_area = 1.0;
    length_sides[0] = norm(a - b); length_sides[1] = norm(b - c);
    length_sides[2] = norm(c - d); length_sides[3] = norm(d - a);

684
    for (size_t i = 0; i < 4; i++) { perimeter += length_sides[i]; }
N
Nesterov Alexander 已提交
685 686
    perimeter /= 2;

687
    for (size_t i = 0; i < 4; i++)
N
Nesterov Alexander 已提交
688 689 690 691 692 693 694 695 696
    {
        result_area *= (perimeter - length_sides[i]);
    }

    result_area = sqrt(result_area);

    return result_area;
}

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
//          b
//         / |
//        /  |
//       /   |
//      /  S |
//     /     |
//   a ----- c

double QRDecode::getTriangleArea(Point2f a, Point2f b, Point2f c)
{
    double length_sides[3], perimeter = 0.0, triangle_area = 1.0;
    length_sides[0] = norm(a - b);
    length_sides[1] = norm(b - c);
    length_sides[2] = norm(c - a);
    for (size_t i = 0; i < 3; i++) { perimeter += length_sides[i]; }
    perimeter /= 2;
    for (size_t i = 0; i < 3; i++)
    {
        triangle_area *= (perimeter - length_sides[i]);
    }
    triangle_area += sqrt(triangle_area);

    return triangle_area;
}

N
Nesterov Alexander 已提交
722 723 724 725 726
//      / | b
//     /  |
//    /   |
//  a/    | c

727
double QRDecode::getCosVectors(Point2f a, Point2f b, Point2f c)
N
Nesterov Alexander 已提交
728
{
729 730
    return ((a - b).x * (c - b).x + (a - b).y * (c - b).y)
            / (norm(a - b) * norm(c - b));
N
Nesterov Alexander 已提交
731 732
}

733
bool QRDecode::transformation()
N
Nesterov Alexander 已提交
734
{
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
    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] -
                            transformation_points[(i + 1) % transform_size]);
        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 已提交
790
    QRDecode qrdec;
791
    qrdec.init(inarr, p->epsX, p->epsY);
N
Nesterov Alexander 已提交
792 793 794
    qrdec.binarization();
    if (!qrdec.localization()) { return false; }
    if (!qrdec.transformation()) { return false; }
795 796
    vector<Point2f> pnts2f = qrdec.getTransformationPoints();
    Mat(pnts2f).convertTo(points, points.fixedType() ? points.type() : CV_32FC2);
N
Nesterov Alexander 已提交
797 798 799
    return true;
}

800 801 802 803 804 805 806 807 808
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 已提交
809
}