qrcode.cpp 27.0 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
    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 getTriangleArea(Point2f a, Point2f b, Point2f c);
39
    double getPolygonArea(vector<Point2f> points);
40 41 42 43
    double getCosVectors(Point2f a, Point2f b, Point2f c);

    Mat barcode, bin_barcode, straight_barcode;
    vector<Point2f> localization_points, transformation_points;
44
    double 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

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

114
                CV_Assert(length > 0);
N
Nesterov Alexander 已提交
115 116
                for (size_t i = 0; i < test_lines.size(); i++)
                {
117 118
                    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 已提交
119 120 121 122 123 124 125 126 127 128 129 130 131 132
                }

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

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

    for (size_t pnt = 0; pnt < list_lines.size(); pnt++)
    {
142 143
        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 已提交
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 181

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

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

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

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

        }
    }

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

211
void QRDecode::fixationPoints(vector<Point2f> &local_point)
N
Nesterov Alexander 已提交
212 213 214 215 216 217 218 219
{
    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))
220
    / (2 * norm_triangl[1] * norm_triangl[2]);
N
Nesterov Alexander 已提交
221
    cos_angles[1] = (pow(norm_triangl[0], 2) + pow(norm_triangl[2], 2) - pow(norm_triangl[1], 2))
222
    / (2 * norm_triangl[0] * norm_triangl[2]);
N
Nesterov Alexander 已提交
223
    cos_angles[2] = (pow(norm_triangl[0], 2) + pow(norm_triangl[1], 2) - pow(norm_triangl[2], 2))
224 225 226 227 228 229 230 231 232 233 234
    / (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 已提交
235
    {
236
        std::swap(local_point[1], local_point[2]);
N
Nesterov Alexander 已提交
237 238 239
    }
}

240
bool QRDecode::localization()
N
Nesterov Alexander 已提交
241
{
242 243 244 245 246 247 248 249
    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;
250
    if (list_lines_y.size() < 3) { return false; }
251 252 253 254 255
    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 已提交
256 257
    if (localization_points.size() != 3) { return false; }

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

272 273 274 275 276 277 278 279 280 281
    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 已提交
282
    return true;
283

N
Nesterov Alexander 已提交
284 285
}

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

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

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

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

    return true;
N
Nesterov Alexander 已提交
400 401
}

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

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

    cv::findNonZero(mask_roi, locations);

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

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

488 489
    const double experimental_area = getPolygonArea(hull);

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

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

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

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

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

533 534
        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 已提交
535 536

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


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

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

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

571 572
        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 已提交
573 574

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

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

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

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

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

614 615 616
        if (norm(hull[index_hull] - hull[next_index_hull]) < standart_norm / 10.0)
        { index_hull = next_index_hull; continue; }

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

626 627 628
            if (norm(hull[extra_index_hull] - hull[extra_next_index_hull]) < standart_norm / 10.0)
            { extra_index_hull = extra_next_index_hull; continue; }

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

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

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

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

662 663 664 665 666 667 668 669 670 671
//          b
//         / |
//        /  |
//       /   |
//      /  S |
//     /     |
//   a ----- c

double QRDecode::getTriangleArea(Point2f a, Point2f b, Point2f c)
{
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
    double norm_sides[] = { norm(a - b), norm(b - c), norm(c - a) };
    double half_perimeter = (norm_sides[0] + norm_sides[1] + norm_sides[2]) / 2.0;
    double triangle_area = sqrt(half_perimeter *
                               (half_perimeter - norm_sides[0]) *
                               (half_perimeter - norm_sides[1]) *
                               (half_perimeter - norm_sides[2]));
    return triangle_area;
}

double QRDecode::getPolygonArea(vector<Point2f> points)
{
    CV_Assert(points.size() >= 3);
    if (points.size() == 3)
    { return getTriangleArea(points[0], points[1], points[2]); }
    else
687
    {
688 689 690 691 692 693
        double result_area = 0.0;
        for (size_t i = 1; i < points.size() - 1; i++)
        {
            result_area += getTriangleArea(points[0], points[i], points[i + 1]);
        }
        return result_area;
694 695 696
    }
}

N
Nesterov Alexander 已提交
697 698 699 700 701
//      / | b
//     /  |
//    /   |
//  a/    | c

702
double QRDecode::getCosVectors(Point2f a, Point2f b, Point2f c)
N
Nesterov Alexander 已提交
703
{
704 705
    return ((a - b).x * (c - b).x + (a - b).y * (c - b).y)
            / (norm(a - b) * norm(c - b));
N
Nesterov Alexander 已提交
706 707
}

708
bool QRDecode::transformation()
N
Nesterov Alexander 已提交
709
{
710 711 712 713 714 715 716
    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] -
717
                               transformation_points[(i + 1) % transform_size]);
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 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
        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 已提交
765
    QRDecode qrdec;
766
    qrdec.init(inarr, p->epsX, p->epsY);
N
Nesterov Alexander 已提交
767 768 769
    qrdec.binarization();
    if (!qrdec.localization()) { return false; }
    if (!qrdec.transformation()) { return false; }
770 771
    vector<Point2f> pnts2f = qrdec.getTransformationPoints();
    Mat(pnts2f).convertTo(points, points.fixedType() ? points.type() : CV_32FC2);
N
Nesterov Alexander 已提交
772 773 774
    return true;
}

775 776 777 778 779 780 781 782 783
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 已提交
784
}