1 | /*M/////////////////////////////////////////////////////////////////////////////////////// |
2 | // |
3 | // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. |
4 | // |
5 | // By downloading, copying, installing or using the software you agree to this license. |
6 | // If you do not agree to this license, do not download, install, |
7 | // copy or use the software. |
8 | // |
9 | // |
10 | // Intel License Agreement |
11 | // For Open Source Computer Vision Library |
12 | // |
13 | // Copyright (C) 2000, Intel Corporation, all rights reserved. |
14 | // Third party copyrights are property of their respective owners. |
15 | // |
16 | // Redistribution and use in source and binary forms, with or without modification, |
17 | // are permitted provided that the following conditions are met: |
18 | // |
19 | // * Redistribution's of source code must retain the above copyright notice, |
20 | // this list of conditions and the following disclaimer. |
21 | // |
22 | // * Redistribution's in binary form must reproduce the above copyright notice, |
23 | // this list of conditions and the following disclaimer in the documentation |
24 | // and/or other materials provided with the distribution. |
25 | // |
26 | // * The name of Intel Corporation may not be used to endorse or promote products |
27 | // derived from this software without specific prior written permission. |
28 | // |
29 | // This software is provided by the copyright holders and contributors "as is" and |
30 | // any express or implied warranties, including, but not limited to, the implied |
31 | // warranties of merchantability and fitness for a particular purpose are disclaimed. |
32 | // In no event shall the Intel Corporation or contributors be liable for any direct, |
33 | // indirect, incidental, special, exemplary, or consequential damages |
34 | // (including, but not limited to, procurement of substitute goods or services; |
35 | // loss of use, data, or profits; or business interruption) however caused |
36 | // and on any theory of liability, whether in contract, strict liability, |
37 | // or tort (including negligence or otherwise) arising in any way out of |
38 | // the use of this software, even if advised of the possibility of such damage. |
39 | // |
40 | //M*/ |
41 | |
42 | #include "precomp.hpp" |
43 | |
44 | #include <vector> |
45 | #include <algorithm> |
46 | |
47 | using namespace cv; |
48 | using namespace std; |
49 | |
50 | static void icvGetQuadrangleHypotheses(const std::vector<std::vector< cv::Point > > & contours, const std::vector< cv::Vec4i > & hierarchy, std::vector<std::pair<float, int> >& quads, int class_id) |
51 | { |
52 | const float min_aspect_ratio = 0.3f; |
53 | const float max_aspect_ratio = 3.0f; |
54 | const float min_box_size = 10.0f; |
55 | |
56 | for (size_t i = 0; i < contours.size(); ++i) |
57 | { |
58 | if (hierarchy.at(n: i)[3] != -1) |
59 | continue; // skip holes |
60 | |
61 | const std::vector< cv::Point > & c = contours[i]; |
62 | cv::RotatedRect box = cv::minAreaRect(points: c); |
63 | |
64 | float box_size = MAX(box.size.width, box.size.height); |
65 | if(box_size < min_box_size) |
66 | { |
67 | continue; |
68 | } |
69 | |
70 | float aspect_ratio = box.size.width/MAX(box.size.height, 1); |
71 | if(aspect_ratio < min_aspect_ratio || aspect_ratio > max_aspect_ratio) |
72 | { |
73 | continue; |
74 | } |
75 | |
76 | quads.emplace_back(args&: box_size, args&: class_id); |
77 | } |
78 | } |
79 | |
80 | static void countClasses(const std::vector<std::pair<float, int> >& pairs, size_t idx1, size_t idx2, std::vector<int>& counts) |
81 | { |
82 | counts.assign(n: 2, val: 0); |
83 | for(size_t i = idx1; i != idx2; i++) |
84 | { |
85 | counts[pairs[i].second]++; |
86 | } |
87 | } |
88 | |
89 | inline bool less_pred(const std::pair<float, int>& p1, const std::pair<float, int>& p2) |
90 | { |
91 | return p1.first < p2.first; |
92 | } |
93 | |
94 | static void fillQuads(Mat & white, Mat & black, double white_thresh, double black_thresh, vector<pair<float, int> > & quads) |
95 | { |
96 | Mat thresh; |
97 | { |
98 | vector< vector<Point> > contours; |
99 | vector< Vec4i > hierarchy; |
100 | threshold(src: white, dst: thresh, thresh: white_thresh, maxval: 255, type: THRESH_BINARY); |
101 | findContours(image: thresh, contours, hierarchy, mode: RETR_CCOMP, method: CHAIN_APPROX_SIMPLE); |
102 | icvGetQuadrangleHypotheses(contours, hierarchy, quads, class_id: 1); |
103 | } |
104 | |
105 | { |
106 | vector< vector<Point> > contours; |
107 | vector< Vec4i > hierarchy; |
108 | threshold(src: black, dst: thresh, thresh: black_thresh, maxval: 255, type: THRESH_BINARY_INV); |
109 | findContours(image: thresh, contours, hierarchy, mode: RETR_CCOMP, method: CHAIN_APPROX_SIMPLE); |
110 | icvGetQuadrangleHypotheses(contours, hierarchy, quads, class_id: 0); |
111 | } |
112 | } |
113 | |
114 | static bool checkQuads(vector<pair<float, int> > & quads, const cv::Size & size) |
115 | { |
116 | const size_t min_quads_count = size.width*size.height/2; |
117 | std::sort(first: quads.begin(), last: quads.end(), comp: less_pred); |
118 | |
119 | // now check if there are many hypotheses with similar sizes |
120 | // do this by floodfill-style algorithm |
121 | const float size_rel_dev = 0.4f; |
122 | |
123 | for(size_t i = 0; i < quads.size(); i++) |
124 | { |
125 | size_t j = i + 1; |
126 | for(; j < quads.size(); j++) |
127 | { |
128 | if(quads[j].first/quads[i].first > 1.0f + size_rel_dev) |
129 | { |
130 | break; |
131 | } |
132 | } |
133 | |
134 | if(j + 1 > min_quads_count + i) |
135 | { |
136 | // check the number of black and white squares |
137 | std::vector<int> counts; |
138 | countClasses(pairs: quads, idx1: i, idx2: j, counts); |
139 | const int black_count = cvRound(value: ceil(x: size.width/2.0)*ceil(x: size.height/2.0)); |
140 | const int white_count = cvRound(value: floor(x: size.width/2.0)*floor(x: size.height/2.0)); |
141 | if(counts[0] < black_count*0.75 || |
142 | counts[1] < white_count*0.75) |
143 | { |
144 | continue; |
145 | } |
146 | return true; |
147 | } |
148 | } |
149 | return false; |
150 | } |
151 | |
152 | bool cv::checkChessboard(InputArray _img, Size size) |
153 | { |
154 | Mat img = _img.getMat(); |
155 | CV_Assert(img.channels() == 1 && img.depth() == CV_8U); |
156 | |
157 | const int erosion_count = 1; |
158 | const float black_level = 20.f; |
159 | const float white_level = 130.f; |
160 | const float black_white_gap = 70.f; |
161 | |
162 | Mat white; |
163 | Mat black; |
164 | erode(src: img, dst: white, kernel: Mat(), anchor: Point(-1, -1), iterations: erosion_count); |
165 | dilate(src: img, dst: black, kernel: Mat(), anchor: Point(-1, -1), iterations: erosion_count); |
166 | |
167 | bool result = false; |
168 | for(float thresh_level = black_level; thresh_level < white_level && !result; thresh_level += 20.0f) |
169 | { |
170 | vector<pair<float, int> > quads; |
171 | fillQuads(white, black, white_thresh: thresh_level + black_white_gap, black_thresh: thresh_level, quads); |
172 | if (checkQuads(quads, size)) |
173 | result = true; |
174 | } |
175 | return result; |
176 | } |
177 | |
178 | // does a fast check if a chessboard is in the input image. This is a workaround to |
179 | // a problem of cvFindChessboardCorners being slow on images with no chessboard |
180 | // - src: input binary image |
181 | // - size: chessboard size |
182 | // Returns 1 if a chessboard can be in this image and findChessboardCorners should be called, |
183 | // 0 if there is no chessboard, -1 in case of error |
184 | int checkChessboardBinary(const cv::Mat & img, const cv::Size & size) |
185 | { |
186 | CV_Assert(img.channels() == 1 && img.depth() == CV_8U); |
187 | |
188 | Mat white = img.clone(); |
189 | Mat black = img.clone(); |
190 | |
191 | int result = 0; |
192 | for ( int erosion_count = 0; erosion_count <= 3; erosion_count++ ) |
193 | { |
194 | if ( 1 == result ) |
195 | break; |
196 | |
197 | if ( 0 != erosion_count ) // first iteration keeps original images |
198 | { |
199 | erode(src: white, dst: white, kernel: Mat(), anchor: Point(-1, -1), iterations: 1); |
200 | dilate(src: black, dst: black, kernel: Mat(), anchor: Point(-1, -1), iterations: 1); |
201 | } |
202 | |
203 | vector<pair<float, int> > quads; |
204 | fillQuads(white, black, white_thresh: 128, black_thresh: 128, quads); |
205 | if (checkQuads(quads, size)) |
206 | result = 1; |
207 | } |
208 | return result; |
209 | } |
210 | |