forked from ustreamer-01647/haisin20f
-
Notifications
You must be signed in to change notification settings - Fork 0
/
extractnumber.cpp
300 lines (284 loc) · 6.68 KB
/
extractnumber.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
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
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
299
300
#include "extractnumber.h"
#include "function.h"
ExtractNumber::ExtractNumber(cv::Mat image)
{
setSource(image);
}
// CV_8UC1を設定する
void ExtractNumber::setSource(cv::Mat image)
{
this->source = image.clone();
}
int ExtractNumber::extract()
{
if ( this->source.empty() )
return 0;
std::vector<cv::Mat> character;
split(character);
int characterCount = character.size();
int value = 0;
for ( int cc = 0; cc < characterCount; cc++ )
{
int n = number(character[cc]);
int keta = std::pow(10, characterCount-cc-1);
value += n*keta;
// value += (std::pow(10, characterCount-cc-1) * number(character[cc]));
}
return value;
}
// 1字ずつ切り出す
void ExtractNumber::split(std::vector<cv::Mat> &character)
{
// 上下の余白を除去する
int width = source.cols;
int height = source.rows;
// 上端を調べる
int w, h, sum = 0;
int startRow = 0;
for ( h = startRow; h < height; h++ )
{
for ( w = 0; w < width; w++ )
{
sum += source.data[h*width+w];
}
// 下へ走査し,最初の輝点があるとき上端
if ( 0 < sum )
{
startRow = h;
break;
}
}
int endRow = height;
// 下端を調べる
sum = 0;
for ( h = height-1 ; 0 <= h; h-- )
{
for ( w = 0; w < width; w++ )
{
sum += source.data[h*width+w];
}
// 上へ走査し,輝点があるとき下端
if ( 0 < sum )
{
endRow = h;
break;
}
}
// 左右端を探索し,1文字ずつ切り出す
std::vector<int> sliceCol; // 文字切り出し位置.始点終点を交互に入力する
bool isSearchStart = true;
// 横方向に探索
sum = 0;
for ( w = 0; w < width; w++ )
{
// 縦方向に探索する
for ( h = startRow; h < endRow; h++ )
{
sum += source.data[h*width+w];
}
if( isSearchStart && sum > 0 )
{
// 輝点があれば始端登録
sliceCol.push_back(w);
isSearchStart = false;
}else if ( !isSearchStart && sum == 0 )
{
// 輝点がなければ終端登録
sliceCol.push_back(w);
isSearchStart = true;
}
sum = 0;
}
// 終端探索中に終了したら終端登録
if ( false == isSearchStart )
{
sliceCol.push_back(w);
}
// 上下左右の下端情報を得て,文字を切り出す
int characterCount = sliceCol.size()/2;
// このへんの#if0は,数字を赤色で囲む
#if 0
cv::Mat kakomu;
std::vector<cv::Mat> kako;
kako.push_back(source);
kako.push_back(source);
kako.push_back(source);
cv::merge(kako, kakomu);
#endif
for ( int cc = 0 ; cc < characterCount; cc++ )
{
character.push_back( source(cv::Range(startRow, endRow),
cv::Range(sliceCol[cc*2], sliceCol[cc*2+1])).clone() );
#if 0
cv::rectangle(kakomu, cv::Point(sliceCol[cc*2], startRow),
cv::Point(sliceCol[cc*2+1], endRow), cv::Scalar(0,0,255) );
#endif
}
#if 0
cv::namedWindow("kakomu");
cv::imshow("kakomu", kakomu);
#endif
}
// 数字1字を認識する
int ExtractNumber::number(cv::Mat character)
{
// 輪郭抽出で分別する
// 12357, 0469, 8
/// @see http://opencv.jp/cookbook/opencv_img.html#id54
std::vector<std::vector<cv::Point> > contours; // 輪郭情報
std::vector<cv::Vec4i> hierarchy; // 階層構造
// 2値画像,輪郭(出力),階層構造(出力),輪郭抽出モード,輪郭の近似手法
cv::findContours(character, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
// 8, 02469, 12357
// 輪郭数で分別する
if ( 3 == hierarchy.size())
{
return 8;
}
else if( 2 == hierarchy.size() )
{
// 6, 049
// 第2輪郭始点が上部3分の1にないとき6
if ( contours[1][0].y > (character.rows/3) )
{
return 6;
}
else
{
// 9, 04
// 中央部の縦の輝点パターン
// 9: 10101
// 0,4: 101
if( 3 == rowBrightPattern(character, character.cols/2) )
{
return 9;
}else
{
// 4, 0
// 右端の上から3分の1に輝点があるとき0
int u = utan(character);
if ( 0 < character.data[character.rows/3*character.cols+u] )
{
return 0;
}else
{
return 4;
}
}
}
}
else if ( 1 == hierarchy.size() )
{
// 17, 235
// 左端の輝点パターン
// 17: 1
// 235: 101
int s = satan(character);
switch (rowBrightPattern(character, s) )
{
case 1:
// 1, 7
// 中央部の縦の輝点パターン
// 1: 1
// 7: 101
switch (rowBrightPattern(character, character.cols/2))
{
case 1:
return 1;
case 2:
return 7;
default:
return 0;
}
case 2:
// 5, 23
// 右端の上から3分の1に暗点があるとき5
int u = utan(character);
if ( 0 == character.data[character.rows/3*character.cols+u] )
{
return 5;
}
// 2, 3
// 右端の上から3分の2に暗点があるとき2
if ( 0 == character.data[character.rows/3*2*character.cols+u] )
{
return 2;
}
return 3;
}
}
return 0;
}
// 指定した列の輝点パターン数を返す
int ExtractNumber::rowBrightPattern(const cv::Mat &bin, const int column)
{
int pattern = 0;
bool isSearchBright = true;
for ( int y = 0; y < bin.rows; y++ )
{
if ( isSearchBright && 0 < bin.data[y*bin.cols+column] )
{
// 輝点を探していて,かつ輝点を見つけた時
pattern++;
isSearchBright = false;
}else if( !isSearchBright && 0 == bin.data[y*bin.cols+column] )
{
// 暗点を探していて,かつ暗点を見つけた時
isSearchBright = true;
}
}
return pattern;
}
// 指定した行の輝点パターン数を返す
int ExtractNumber::colBrightPattern(const cv::Mat &bin, const int row)
{
int pattern = 0;
bool isSearchBright = true;
for ( int x = 0; x < bin.cols; x++ )
{
if ( isSearchBright && 0 < bin.data[row*bin.cols+x] )
{
// 輝点を探していて,かつ輝点を見つけた時
pattern++;
isSearchBright = false;
}else if( !isSearchBright && 0 == bin.data[row*bin.cols+x] )
{
// 暗点を探していて,かつ暗点を見つけた時
isSearchBright = true;
}
}
return pattern;
}
// 右端を探す
int ExtractNumber::utan(const cv::Mat &bin)
{
int sum = 0;
for( int x = bin.cols-1; 0 <= x; x-- )
{
// 縦方向に走査する
for ( int y = 0; y < bin.rows; y++ )
{
sum += bin.data[y*bin.cols+x];
}
if ( sum > 0 )
return x;
}
return 0;
}
// 左端を探す
int ExtractNumber::satan(const cv::Mat &bin)
{
int sum = 0;
for ( int x = 0; x < bin.cols; x++ )
{
for ( int y = 0; y < bin.rows; y++ )
{
sum += bin.data[y*bin.cols+x];
}
if ( sum > 0 )
{
return x;
}
}
return bin.cols-1;
}