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 | #include "opencv2/imgproc/imgproc_c.h" |
44 | #include "calib3d_c_api.h" |
45 | |
46 | #include <vector> |
47 | #include <algorithm> |
48 | |
49 | using namespace cv; |
50 | using namespace std; |
51 | |
52 | 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) |
53 | { |
54 | const float min_aspect_ratio = 0.3f; |
55 | const float max_aspect_ratio = 3.0f; |
56 | const float min_box_size = 10.0f; |
57 | |
58 | for (size_t i = 0; i < contours.size(); ++i) |
59 | { |
60 | if (hierarchy.at(n: i)[3] != -1) |
61 | continue; // skip holes |
62 | |
63 | const std::vector< cv::Point > & c = contours[i]; |
64 | cv::RotatedRect box = cv::minAreaRect(points: c); |
65 | |
66 | float box_size = MAX(box.size.width, box.size.height); |
67 | if(box_size < min_box_size) |
68 | { |
69 | continue; |
70 | } |
71 | |
72 | float aspect_ratio = box.size.width/MAX(box.size.height, 1); |
73 | if(aspect_ratio < min_aspect_ratio || aspect_ratio > max_aspect_ratio) |
74 | { |
75 | continue; |
76 | } |
77 | |
78 | quads.emplace_back(args&: box_size, args&: class_id); |
79 | } |
80 | } |
81 | |
82 | static void countClasses(const std::vector<std::pair<float, int> >& pairs, size_t idx1, size_t idx2, std::vector<int>& counts) |
83 | { |
84 | counts.assign(n: 2, val: 0); |
85 | for(size_t i = idx1; i != idx2; i++) |
86 | { |
87 | counts[pairs[i].second]++; |
88 | } |
89 | } |
90 | |
91 | inline bool less_pred(const std::pair<float, int>& p1, const std::pair<float, int>& p2) |
92 | { |
93 | return p1.first < p2.first; |
94 | } |
95 | |
96 | static void fillQuads(Mat & white, Mat & black, double white_thresh, double black_thresh, vector<pair<float, int> > & quads) |
97 | { |
98 | Mat thresh; |
99 | { |
100 | vector< vector<Point> > contours; |
101 | vector< Vec4i > hierarchy; |
102 | threshold(src: white, dst: thresh, thresh: white_thresh, maxval: 255, type: THRESH_BINARY); |
103 | findContours(image: thresh, contours, hierarchy, mode: RETR_CCOMP, method: CHAIN_APPROX_SIMPLE); |
104 | icvGetQuadrangleHypotheses(contours, hierarchy, quads, class_id: 1); |
105 | } |
106 | |
107 | { |
108 | vector< vector<Point> > contours; |
109 | vector< Vec4i > hierarchy; |
110 | threshold(src: black, dst: thresh, thresh: black_thresh, maxval: 255, type: THRESH_BINARY_INV); |
111 | findContours(image: thresh, contours, hierarchy, mode: RETR_CCOMP, method: CHAIN_APPROX_SIMPLE); |
112 | icvGetQuadrangleHypotheses(contours, hierarchy, quads, class_id: 0); |
113 | } |
114 | } |
115 | |
116 | static bool checkQuads(vector<pair<float, int> > & quads, const cv::Size & size) |
117 | { |
118 | const size_t min_quads_count = size.width*size.height/2; |
119 | std::sort(first: quads.begin(), last: quads.end(), comp: less_pred); |
120 | |
121 | // now check if there are many hypotheses with similar sizes |
122 | // do this by floodfill-style algorithm |
123 | const float size_rel_dev = 0.4f; |
124 | |
125 | for(size_t i = 0; i < quads.size(); i++) |
126 | { |
127 | size_t j = i + 1; |
128 | for(; j < quads.size(); j++) |
129 | { |
130 | if(quads[j].first/quads[i].first > 1.0f + size_rel_dev) |
131 | { |
132 | break; |
133 | } |
134 | } |
135 | |
136 | if(j + 1 > min_quads_count + i) |
137 | { |
138 | // check the number of black and white squares |
139 | std::vector<int> counts; |
140 | countClasses(pairs: quads, idx1: i, idx2: j, counts); |
141 | const int black_count = cvRound(value: ceil(x: size.width/2.0)*ceil(x: size.height/2.0)); |
142 | const int white_count = cvRound(value: floor(x: size.width/2.0)*floor(x: size.height/2.0)); |
143 | if(counts[0] < black_count*0.75 || |
144 | counts[1] < white_count*0.75) |
145 | { |
146 | continue; |
147 | } |
148 | return true; |
149 | } |
150 | } |
151 | return false; |
152 | } |
153 | |
154 | // does a fast check if a chessboard is in the input image. This is a workaround to |
155 | // a problem of cvFindChessboardCorners being slow on images with no chessboard |
156 | // - src: input image |
157 | // - size: chessboard size |
158 | // Returns 1 if a chessboard can be in this image and findChessboardCorners should be called, |
159 | // 0 if there is no chessboard, -1 in case of error |
160 | int cvCheckChessboard(IplImage* src, CvSize size) |
161 | { |
162 | cv::Mat img = cv::cvarrToMat(arr: src); |
163 | return (int)cv::checkChessboard(img, size); |
164 | } |
165 | |
166 | bool cv::checkChessboard(InputArray _img, Size size) |
167 | { |
168 | Mat img = _img.getMat(); |
169 | CV_Assert(img.channels() == 1 && img.depth() == CV_8U); |
170 | |
171 | const int erosion_count = 1; |
172 | const float black_level = 20.f; |
173 | const float white_level = 130.f; |
174 | const float black_white_gap = 70.f; |
175 | |
176 | Mat white; |
177 | Mat black; |
178 | erode(src: img, dst: white, kernel: Mat(), anchor: Point(-1, -1), iterations: erosion_count); |
179 | dilate(src: img, dst: black, kernel: Mat(), anchor: Point(-1, -1), iterations: erosion_count); |
180 | |
181 | bool result = false; |
182 | for(float thresh_level = black_level; thresh_level < white_level && !result; thresh_level += 20.0f) |
183 | { |
184 | vector<pair<float, int> > quads; |
185 | fillQuads(white, black, white_thresh: thresh_level + black_white_gap, black_thresh: thresh_level, quads); |
186 | if (checkQuads(quads, size)) |
187 | result = true; |
188 | } |
189 | return result; |
190 | } |
191 | |
192 | // does a fast check if a chessboard is in the input image. This is a workaround to |
193 | // a problem of cvFindChessboardCorners being slow on images with no chessboard |
194 | // - src: input binary image |
195 | // - size: chessboard size |
196 | // Returns 1 if a chessboard can be in this image and findChessboardCorners should be called, |
197 | // 0 if there is no chessboard, -1 in case of error |
198 | int checkChessboardBinary(const cv::Mat & img, const cv::Size & size) |
199 | { |
200 | CV_Assert(img.channels() == 1 && img.depth() == CV_8U); |
201 | |
202 | Mat white = img.clone(); |
203 | Mat black = img.clone(); |
204 | |
205 | int result = 0; |
206 | for ( int erosion_count = 0; erosion_count <= 3; erosion_count++ ) |
207 | { |
208 | if ( 1 == result ) |
209 | break; |
210 | |
211 | if ( 0 != erosion_count ) // first iteration keeps original images |
212 | { |
213 | erode(src: white, dst: white, kernel: Mat(), anchor: Point(-1, -1), iterations: 1); |
214 | dilate(src: black, dst: black, kernel: Mat(), anchor: Point(-1, -1), iterations: 1); |
215 | } |
216 | |
217 | vector<pair<float, int> > quads; |
218 | fillQuads(white, black, white_thresh: 128, black_thresh: 128, quads); |
219 | if (checkQuads(quads, size)) |
220 | result = 1; |
221 | } |
222 | return result; |
223 | } |
224 | |