qrtools 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +6 -0
- data/Manifest.txt +65 -0
- data/README.txt +75 -0
- data/Rakefile +49 -0
- data/bin/qrdecode +14 -0
- data/ext/qrtools/Makefile.in +65 -0
- data/ext/qrtools/bitstream.cpp +147 -0
- data/ext/qrtools/bitstream.h +48 -0
- data/ext/qrtools/codedata.cpp +506 -0
- data/ext/qrtools/codedata.h +95 -0
- data/ext/qrtools/container.cpp +288 -0
- data/ext/qrtools/container.h +175 -0
- data/ext/qrtools/decodeqr.h +286 -0
- data/ext/qrtools/ecidecoder.cpp +341 -0
- data/ext/qrtools/ecidecoder.h +110 -0
- data/ext/qrtools/extconf.rb +24 -0
- data/ext/qrtools/formatinfo.cpp +195 -0
- data/ext/qrtools/formatinfo.h +113 -0
- data/ext/qrtools/galois.cpp +593 -0
- data/ext/qrtools/galois.h +134 -0
- data/ext/qrtools/imagereader.cpp +1099 -0
- data/ext/qrtools/imagereader.h +127 -0
- data/ext/qrtools/libdecodeqr.cpp +195 -0
- data/ext/qrtools/libdecodeqr.dep +80 -0
- data/ext/qrtools/libdecodeqr.dsp +160 -0
- data/ext/qrtools/libdecodeqr.dsw +29 -0
- data/ext/qrtools/libdecodeqr.mak +245 -0
- data/ext/qrtools/qrerror.h +40 -0
- data/ext/qrtools/qrtools.c +17 -0
- data/ext/qrtools/qrtools.h +21 -0
- data/ext/qrtools/qrtools_decoder.c +123 -0
- data/ext/qrtools/qrtools_decoder.h +10 -0
- data/ext/qrtools/qrtools_encoder.c +63 -0
- data/ext/qrtools/qrtools_encoder.h +10 -0
- data/ext/qrtools/qrtools_header.c +51 -0
- data/ext/qrtools/qrtools_header.h +10 -0
- data/ext/qrtools/qrtools_image.c +80 -0
- data/ext/qrtools/qrtools_image.h +11 -0
- data/ext/qrtools/qrtools_qrcode.c +36 -0
- data/ext/qrtools/qrtools_qrcode.h +10 -0
- data/ext/qrtools/qrtools_ui_camera.c +58 -0
- data/ext/qrtools/qrtools_ui_camera.h +10 -0
- data/ext/qrtools/qrtools_ui_window.c +40 -0
- data/ext/qrtools/qrtools_ui_window.h +10 -0
- data/ext/qrtools/qrtypes.h +42 -0
- data/ext/qrtools/version.h +42 -0
- data/lib/qrtools.rb +11 -0
- data/lib/qrtools/decoder.rb +17 -0
- data/lib/qrtools/encoder.rb +14 -0
- data/lib/qrtools/image.rb +43 -0
- data/lib/qrtools/qrcode.rb +54 -0
- data/lib/qrtools/ui/camera.rb +28 -0
- data/lib/qrtools/ui/window.rb +16 -0
- data/lib/qrtools/version.rb +3 -0
- data/qrtools.gemspec +38 -0
- data/test/assets/01-1.jpg +0 -0
- data/test/helper.rb +17 -0
- data/test/test_decoder.rb +67 -0
- data/test/test_encoder.rb +35 -0
- data/test/test_header.rb +14 -0
- data/test/test_image.rb +19 -0
- data/test/test_qrcode.rb +78 -0
- data/test/test_qrdecode.rb +0 -0
- data/test/ui/test_camera.rb +43 -0
- data/test/ui/test_window.rb +34 -0
- metadata +138 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////
|
|
2
|
+
//
|
|
3
|
+
// galois.h --a part of libdecodeqr
|
|
4
|
+
//
|
|
5
|
+
// Copyright(C) 2007 NISHI Takao <zophos@koka-in.org>
|
|
6
|
+
// JMA (Japan Medical Association)
|
|
7
|
+
// NaCl (Network Applied Communication Laboratory Ltd.)
|
|
8
|
+
//
|
|
9
|
+
// This is free software with ABSOLUTELY NO WARRANTY.
|
|
10
|
+
// You can redistribute and/or modify it under the terms of LGPL.
|
|
11
|
+
//
|
|
12
|
+
// $Id: galois.h 36 2007-02-21 23:22:03Z zophos $
|
|
13
|
+
//
|
|
14
|
+
#ifndef __GALOIS__
|
|
15
|
+
#define __GALOIS__
|
|
16
|
+
|
|
17
|
+
#include <memory.h>
|
|
18
|
+
|
|
19
|
+
#ifndef NULL
|
|
20
|
+
#define NULL 0
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
namespace Galois{
|
|
24
|
+
class Nomial{
|
|
25
|
+
public:
|
|
26
|
+
unsigned int val;
|
|
27
|
+
|
|
28
|
+
private:
|
|
29
|
+
void *_gf;
|
|
30
|
+
|
|
31
|
+
public:
|
|
32
|
+
static Nomial *instance(void * gf,unsigned int x);
|
|
33
|
+
Nomial *dup();
|
|
34
|
+
|
|
35
|
+
unsigned int to_exp();
|
|
36
|
+
unsigned int to_vect();
|
|
37
|
+
|
|
38
|
+
inline int m();
|
|
39
|
+
inline int n();
|
|
40
|
+
inline unsigned int exp2vect(unsigned int x);
|
|
41
|
+
inline unsigned int vect2exp(unsigned int x);
|
|
42
|
+
|
|
43
|
+
bool is_zero();
|
|
44
|
+
bool operator==(Nomial x);
|
|
45
|
+
bool operator!=(Nomial x);
|
|
46
|
+
Nomial operator+(Nomial x);
|
|
47
|
+
Nomial operator-(Nomial x);
|
|
48
|
+
Nomial operator*(Nomial x);
|
|
49
|
+
Nomial operator/(Nomial x);
|
|
50
|
+
|
|
51
|
+
protected:
|
|
52
|
+
Nomial(void * gf,unsigned int x);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
class Field{
|
|
56
|
+
public:
|
|
57
|
+
int m;
|
|
58
|
+
int n;
|
|
59
|
+
unsigned int *exp2vect;
|
|
60
|
+
unsigned int *vect2exp;
|
|
61
|
+
Nomial **pool;
|
|
62
|
+
private:
|
|
63
|
+
int _pool_size;
|
|
64
|
+
bool _need_delete;
|
|
65
|
+
|
|
66
|
+
public:
|
|
67
|
+
Field(int m);
|
|
68
|
+
~Field();
|
|
69
|
+
|
|
70
|
+
int pool_size();
|
|
71
|
+
|
|
72
|
+
Nomial *exp2nomial(unsigned int x);
|
|
73
|
+
Nomial *vect2nomial(unsigned int x);
|
|
74
|
+
Nomial *zero();
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Polynomial{
|
|
79
|
+
public:
|
|
80
|
+
int cols;
|
|
81
|
+
int rows;
|
|
82
|
+
Nomial **nomial;
|
|
83
|
+
|
|
84
|
+
Polynomial();
|
|
85
|
+
Polynomial(int rows);
|
|
86
|
+
Polynomial(int cols,int rows);
|
|
87
|
+
~Polynomial();
|
|
88
|
+
|
|
89
|
+
Polynomial *dup();
|
|
90
|
+
Polynomial *dup(int count);
|
|
91
|
+
Polynomial *dup(int start_col,int start_row,int count);
|
|
92
|
+
Polynomial *dup(int start_col,int start_row,
|
|
93
|
+
int col_count,int row_count);
|
|
94
|
+
|
|
95
|
+
Nomial *set(int row,Nomial *val);
|
|
96
|
+
Nomial *set(int col,int row,Nomial *val);
|
|
97
|
+
|
|
98
|
+
Nomial *get(int row);
|
|
99
|
+
Nomial *get(int col,int row);
|
|
100
|
+
|
|
101
|
+
Polynomial *lu();
|
|
102
|
+
Polynomial *lu(int count);
|
|
103
|
+
Polynomial *lu(int start_col,int start_row,int count);
|
|
104
|
+
Polynomial *_lu(Polynomial *buf);
|
|
105
|
+
|
|
106
|
+
Polynomial *solve();
|
|
107
|
+
Polynomial *solve(Polynomial *lu);
|
|
108
|
+
|
|
109
|
+
void swap_col(int i,int j);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
class BCH :public Polynomial{
|
|
113
|
+
public:
|
|
114
|
+
int error_size;
|
|
115
|
+
int *error_pos;
|
|
116
|
+
int syndrome_size;
|
|
117
|
+
Galois::Nomial **syndromes;
|
|
118
|
+
|
|
119
|
+
private:
|
|
120
|
+
Field *_gf;
|
|
121
|
+
int _capability;
|
|
122
|
+
|
|
123
|
+
public:
|
|
124
|
+
BCH(Field *gf,int size,int capability);
|
|
125
|
+
~BCH();
|
|
126
|
+
|
|
127
|
+
int decode(int syndorome_base=0);
|
|
128
|
+
|
|
129
|
+
private:
|
|
130
|
+
Galois::Nomial *_error_syndrome(int d);
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
#endif
|
|
@@ -0,0 +1,1099 @@
|
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////
|
|
2
|
+
//
|
|
3
|
+
// imagereader.cpp --a part of libdecodeqr
|
|
4
|
+
//
|
|
5
|
+
// Copyright(C) 2007 NISHI Takao <zophos@koka-in.org>
|
|
6
|
+
// JMA (Japan Medical Association)
|
|
7
|
+
// NaCl (Network Applied Communication Laboratory Ltd.)
|
|
8
|
+
//
|
|
9
|
+
// This is free software with ABSOLUTELY NO WARRANTY.
|
|
10
|
+
// You can redistribute and/or modify it under the terms of LGPL.
|
|
11
|
+
//
|
|
12
|
+
// $Id: imagereader.cpp 36 2007-02-21 23:22:03Z zophos $
|
|
13
|
+
//
|
|
14
|
+
#include "imagereader.h"
|
|
15
|
+
|
|
16
|
+
namespace Qr{
|
|
17
|
+
|
|
18
|
+
//
|
|
19
|
+
// avoid cvBoundingRect() fxxcin' bug.
|
|
20
|
+
//
|
|
21
|
+
typedef struct{
|
|
22
|
+
CvRect feret;
|
|
23
|
+
CvSeq *contour;
|
|
24
|
+
} ImageReaderCandidate;
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/////////////////////////////////////////////////////////////////////
|
|
28
|
+
//
|
|
29
|
+
//
|
|
30
|
+
//
|
|
31
|
+
ImageReader::ImageReader()
|
|
32
|
+
{
|
|
33
|
+
this->_init();
|
|
34
|
+
}
|
|
35
|
+
ImageReader::ImageReader(int width,int height,int depth,int channel)
|
|
36
|
+
{
|
|
37
|
+
this->_init();
|
|
38
|
+
this->_alloc_image(width,height,depth,channel);
|
|
39
|
+
}
|
|
40
|
+
ImageReader::~ImageReader()
|
|
41
|
+
{
|
|
42
|
+
if(this->qr)
|
|
43
|
+
delete this->qr;
|
|
44
|
+
|
|
45
|
+
if(this->_seq_code_area_contour)
|
|
46
|
+
cvRelease((void **)&this->_seq_code_area_contour);
|
|
47
|
+
|
|
48
|
+
cvRelease((void **)&this->_seq_finder_pattern);
|
|
49
|
+
cvReleaseMemStorage(&this->_stor_tmp);
|
|
50
|
+
cvReleaseMemStorage(&this->_stor);
|
|
51
|
+
|
|
52
|
+
this->release_image();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
void ImageReader::_init()
|
|
56
|
+
{
|
|
57
|
+
memset(this->_coderegion_vertexes,0,sizeof(CvPoint)*4);
|
|
58
|
+
memset(this->_finderpattern_boxes,0,sizeof(CvBox2D)*3);
|
|
59
|
+
|
|
60
|
+
this->_img_src_internal=NULL;
|
|
61
|
+
this->_img_src=NULL;
|
|
62
|
+
this->_img_transformed=NULL;
|
|
63
|
+
this->_img_binarized=NULL;
|
|
64
|
+
this->_img_tmp_1c=NULL;
|
|
65
|
+
|
|
66
|
+
this->_stor=cvCreateMemStorage(0);
|
|
67
|
+
this->_stor_tmp=cvCreateMemStorage(0);
|
|
68
|
+
|
|
69
|
+
this->_seq_finder_pattern=cvCreateSeq(CV_SEQ_ELTYPE_GENERIC,
|
|
70
|
+
sizeof(CvSeq),
|
|
71
|
+
sizeof(CvBox2D),
|
|
72
|
+
this->_stor);
|
|
73
|
+
this->_seq_code_area_contour=NULL;
|
|
74
|
+
|
|
75
|
+
this->status=0;
|
|
76
|
+
this->qr=NULL;
|
|
77
|
+
}
|
|
78
|
+
void ImageReader::_alloc_image(int width,int height,int depth,int channel)
|
|
79
|
+
{
|
|
80
|
+
this->_img_src_internal=cvCreateImage(cvSize(width,height),
|
|
81
|
+
depth,channel);
|
|
82
|
+
cvZero(this->_img_src_internal);
|
|
83
|
+
this->_img_src=this->_img_src_internal;
|
|
84
|
+
|
|
85
|
+
this->_img_transformed=cvCloneImage(this->_img_src);
|
|
86
|
+
this->_img_binarized=cvCreateImage(cvSize(this->_img_src->width,
|
|
87
|
+
this->_img_src->height),
|
|
88
|
+
IPL_DEPTH_8U,1);
|
|
89
|
+
cvZero(this->_img_binarized);
|
|
90
|
+
this->_img_tmp_1c=cvCloneImage(this->_img_binarized);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
IplImage *ImageReader::set_image(IplImage *src)
|
|
94
|
+
{
|
|
95
|
+
this->release_image();
|
|
96
|
+
|
|
97
|
+
this->_img_src=src;
|
|
98
|
+
this->_img_transformed=cvCloneImage(this->_img_src);
|
|
99
|
+
this->_img_binarized=cvCreateImage(cvSize(this->_img_src->width,
|
|
100
|
+
this->_img_src->height),
|
|
101
|
+
IPL_DEPTH_8U,1);
|
|
102
|
+
cvZero(this->_img_binarized);
|
|
103
|
+
this->_img_tmp_1c=cvCloneImage(this->_img_binarized);
|
|
104
|
+
|
|
105
|
+
return(this->_img_src);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
IplImage *ImageReader::set_image(int width,int height,
|
|
109
|
+
int depth,int channel)
|
|
110
|
+
{
|
|
111
|
+
this->release_image();
|
|
112
|
+
this->_alloc_image(width,height,depth,channel);
|
|
113
|
+
|
|
114
|
+
return(this->_img_src);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
void ImageReader::release_image()
|
|
118
|
+
{
|
|
119
|
+
if(this->_img_tmp_1c)
|
|
120
|
+
cvReleaseImage(&this->_img_tmp_1c);
|
|
121
|
+
if(this->_img_binarized)
|
|
122
|
+
cvReleaseImage(&this->_img_binarized);
|
|
123
|
+
if(this->_img_transformed)
|
|
124
|
+
cvReleaseImage(&this->_img_transformed);
|
|
125
|
+
if(this->_img_src_internal)
|
|
126
|
+
cvReleaseImage(&this->_img_src_internal);
|
|
127
|
+
|
|
128
|
+
this->_img_tmp_1c=NULL;
|
|
129
|
+
this->_img_binarized=NULL;
|
|
130
|
+
this->_img_transformed=NULL;
|
|
131
|
+
this->_img_src_internal=NULL;
|
|
132
|
+
this->_img_src=NULL;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
IplImage *ImageReader::src_buffer()
|
|
137
|
+
{
|
|
138
|
+
return(this->_img_src);
|
|
139
|
+
}
|
|
140
|
+
IplImage *ImageReader::transformed_buffer()
|
|
141
|
+
{
|
|
142
|
+
return(this->_img_transformed);
|
|
143
|
+
}
|
|
144
|
+
IplImage *ImageReader::binarized_buffer()
|
|
145
|
+
{
|
|
146
|
+
return(this->_img_binarized);
|
|
147
|
+
}
|
|
148
|
+
IplImage *ImageReader::tmp_buffer()
|
|
149
|
+
{
|
|
150
|
+
return(this->_img_tmp_1c);
|
|
151
|
+
}
|
|
152
|
+
CvPoint *ImageReader::coderegion_vertexes()
|
|
153
|
+
{
|
|
154
|
+
return(this->_coderegion_vertexes);
|
|
155
|
+
}
|
|
156
|
+
CvBox2D *ImageReader::finderpattern_boxes()
|
|
157
|
+
{
|
|
158
|
+
return(this->_finderpattern_boxes);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
Qr *ImageReader::decode(int adaptive_th_size,
|
|
163
|
+
int adaptive_th_delta)
|
|
164
|
+
{
|
|
165
|
+
if(this->status&QR_IMAGEREADER_WORKING)
|
|
166
|
+
return(NULL);
|
|
167
|
+
|
|
168
|
+
if(!this->_img_src){
|
|
169
|
+
this->status=(QR_IMAGEREADER_ERROR|
|
|
170
|
+
QR_IMAGEREADER_NOT_INVALID_SRC_IMAGE);
|
|
171
|
+
return(NULL);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
this->status=QR_IMAGEREADER_WORKING;
|
|
175
|
+
|
|
176
|
+
cvResetImageROI(this->_img_transformed);
|
|
177
|
+
cvResetImageROI(this->_img_binarized);
|
|
178
|
+
cvResetImageROI(this->_img_tmp_1c);
|
|
179
|
+
|
|
180
|
+
Qr *ret=this->_decode(adaptive_th_size,adaptive_th_delta);
|
|
181
|
+
|
|
182
|
+
this->status^=QR_IMAGEREADER_WORKING;
|
|
183
|
+
return(ret);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
Qr *ImageReader::decode(IplImage *src,
|
|
187
|
+
int adaptive_th_size,
|
|
188
|
+
int adaptive_th_delta)
|
|
189
|
+
{
|
|
190
|
+
if(this->status&QR_IMAGEREADER_WORKING)
|
|
191
|
+
return(NULL);
|
|
192
|
+
|
|
193
|
+
this->status=QR_IMAGEREADER_WORKING;
|
|
194
|
+
|
|
195
|
+
this->set_image(src);
|
|
196
|
+
Qr *ret=this->_decode(adaptive_th_size,adaptive_th_delta);
|
|
197
|
+
|
|
198
|
+
this->_img_src=NULL;
|
|
199
|
+
|
|
200
|
+
this->status^=QR_IMAGEREADER_WORKING;
|
|
201
|
+
return(ret);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
Qr *ImageReader::_decode(int adaptive_th_size,int adaptive_th_delta)
|
|
206
|
+
{
|
|
207
|
+
if(this->qr){
|
|
208
|
+
delete this->qr;
|
|
209
|
+
this->qr=NULL;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
memset(this->_coderegion_vertexes,0,sizeof(CvPoint)*4);
|
|
213
|
+
memset(this->_finderpattern_boxes,0,sizeof(CvBox2D)*3);
|
|
214
|
+
|
|
215
|
+
//
|
|
216
|
+
// binarize
|
|
217
|
+
//
|
|
218
|
+
if(this->_img_src->nChannels>1)
|
|
219
|
+
cvCvtColor(this->_img_src,this->_img_tmp_1c,CV_BGR2GRAY);
|
|
220
|
+
else
|
|
221
|
+
cvCopy(this->_img_src,this->_img_tmp_1c);
|
|
222
|
+
|
|
223
|
+
cvSmooth(this->_img_tmp_1c,this->_img_binarized,CV_MEDIAN,3);
|
|
224
|
+
cvCopy(this->_img_binarized,this->_img_tmp_1c);
|
|
225
|
+
if(adaptive_th_size>0)
|
|
226
|
+
cvAdaptiveThreshold(this->_img_tmp_1c,
|
|
227
|
+
this->_img_binarized,
|
|
228
|
+
128,CV_ADAPTIVE_THRESH_MEAN_C,
|
|
229
|
+
CV_THRESH_BINARY_INV,
|
|
230
|
+
adaptive_th_size,
|
|
231
|
+
adaptive_th_delta);
|
|
232
|
+
else
|
|
233
|
+
cvThreshold(this->_img_tmp_1c,
|
|
234
|
+
this->_img_binarized,
|
|
235
|
+
adaptive_th_delta,
|
|
236
|
+
255,CV_THRESH_BINARY_INV);
|
|
237
|
+
|
|
238
|
+
//
|
|
239
|
+
// find finder patterns from binarized image
|
|
240
|
+
//
|
|
241
|
+
this->_find_finder_pattern();
|
|
242
|
+
if(this->_seq_finder_pattern->total!=3){
|
|
243
|
+
this->status|=(QR_IMAGEREADER_ERROR|
|
|
244
|
+
QR_IMAGEREADER_NOT_FOUND_FINDER_PATTERN);
|
|
245
|
+
return(NULL);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
//
|
|
249
|
+
// find code area from binarized image using finder patterns
|
|
250
|
+
//
|
|
251
|
+
this->_find_code_area_contour(FIND_CODE_AREA_POLY_APPROX_TH);
|
|
252
|
+
|
|
253
|
+
if(!this->_seq_code_area_contour||
|
|
254
|
+
this->_seq_code_area_contour->total!=4){
|
|
255
|
+
this->status|=(QR_IMAGEREADER_ERROR|
|
|
256
|
+
QR_IMAGEREADER_NOT_FOUND_CODE_AREA);
|
|
257
|
+
return(NULL);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
//
|
|
261
|
+
// perspective transform from source image
|
|
262
|
+
//
|
|
263
|
+
this->_transform_image();
|
|
264
|
+
IplImage *code_matrix=NULL;
|
|
265
|
+
for(int th=POSTERIZED_TH_LOW;th<=POSTERIZED_TH_HI&&(!code_matrix);
|
|
266
|
+
th+=POSTERIZED_TH_STEP){
|
|
267
|
+
|
|
268
|
+
this->_create_posterized_image(adaptive_th_size,
|
|
269
|
+
adaptive_th_delta*3,
|
|
270
|
+
th,
|
|
271
|
+
POSTERIZED_TH_HI);
|
|
272
|
+
code_matrix=this->_get_code_matrix();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if(!code_matrix){
|
|
276
|
+
this->status|=(QR_IMAGEREADER_ERROR|
|
|
277
|
+
QR_IMAGEREADER_NOT_DETERMINABLE_CODE_AREA);
|
|
278
|
+
return(NULL);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
this->qr=new Qr();
|
|
282
|
+
this->qr->set_version((code_matrix->width-17)/4);
|
|
283
|
+
if(this->qr->version>=7){
|
|
284
|
+
//FIXME this->_verify_code_version();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if(this->_get_format_info(code_matrix,0)<0){
|
|
288
|
+
if((this->_get_format_info(code_matrix,1)<0)&&
|
|
289
|
+
(this->qr->formatinfo->status&QR_FORMATINFO_INVALID_LEVEL)){
|
|
290
|
+
this->_get_format_info(code_matrix,0);
|
|
291
|
+
if(this->qr->formatinfo->status&QR_FORMATINFO_INVALID_LEVEL){
|
|
292
|
+
cvReleaseImage(&code_matrix);
|
|
293
|
+
this->status|=(QR_IMAGEREADER_ERROR|
|
|
294
|
+
this->qr->formatinfo->status);
|
|
295
|
+
return(NULL);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
IplImage *function_patterns=this->_get_function_patterns();
|
|
301
|
+
|
|
302
|
+
this->_unmask_code_matrix(code_matrix,function_patterns);
|
|
303
|
+
this->_read_code_word(code_matrix,function_patterns);
|
|
304
|
+
|
|
305
|
+
cvReleaseImage(&function_patterns);
|
|
306
|
+
cvReleaseImage(&code_matrix);
|
|
307
|
+
|
|
308
|
+
int errors=this->qr->decode_codedata();
|
|
309
|
+
|
|
310
|
+
#ifdef _DEBUG
|
|
311
|
+
fprintf(stderr,"errors=%d\n",errors);
|
|
312
|
+
#endif
|
|
313
|
+
this->status|=(QR_IMAGEREADER_DECODED|this->qr->status);
|
|
314
|
+
|
|
315
|
+
return(this->qr);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
CvSeq *ImageReader::_find_finder_pattern()
|
|
319
|
+
{
|
|
320
|
+
IplImage *src=this->_img_binarized;
|
|
321
|
+
cvClearSeq(this->_seq_finder_pattern);
|
|
322
|
+
|
|
323
|
+
// for contour list
|
|
324
|
+
cvClearMemStorage(this->_stor_tmp);
|
|
325
|
+
CvSeq *cont=cvCreateSeq(CV_SEQ_ELTYPE_POINT,
|
|
326
|
+
sizeof(CvSeq),sizeof(CvPoint),
|
|
327
|
+
this->_stor_tmp);
|
|
328
|
+
|
|
329
|
+
//
|
|
330
|
+
// Find all contours.
|
|
331
|
+
//
|
|
332
|
+
// cvFindContours() spoil source image.
|
|
333
|
+
//
|
|
334
|
+
CvRect roi=cvGetImageROI(src);
|
|
335
|
+
cvSetImageROI(this->_img_tmp_1c,roi);
|
|
336
|
+
cvCopy(src,this->_img_tmp_1c);
|
|
337
|
+
|
|
338
|
+
cvFindContours(this->_img_tmp_1c,
|
|
339
|
+
this->_stor_tmp,&cont,sizeof(CvContour),
|
|
340
|
+
CV_RETR_LIST,CV_CHAIN_APPROX_NONE,cvPoint(0,0));
|
|
341
|
+
|
|
342
|
+
cvResetImageROI(this->_img_tmp_1c);
|
|
343
|
+
|
|
344
|
+
// for marker candidates list
|
|
345
|
+
CvSeq *candidates=cvCreateSeq(CV_SEQ_ELTYPE_GENERIC,
|
|
346
|
+
sizeof(CvSeq),sizeof(ImageReaderCandidate),
|
|
347
|
+
this->_stor_tmp);
|
|
348
|
+
//
|
|
349
|
+
// check each block
|
|
350
|
+
//
|
|
351
|
+
CvSeq *cont_head=cont;
|
|
352
|
+
for(;cont;cont=cont->h_next){
|
|
353
|
+
CvRect feret=cvBoundingRect(cont);
|
|
354
|
+
double area=fabs(cvContourArea(cont));
|
|
355
|
+
double area_ratio=area/(double)(feret.width*feret.height);
|
|
356
|
+
double feret_ratio=((feret.width<feret.height)?
|
|
357
|
+
(double)feret.width/(double)feret.height:
|
|
358
|
+
(double)feret.height/(double)feret.width);
|
|
359
|
+
|
|
360
|
+
//
|
|
361
|
+
// search square
|
|
362
|
+
//
|
|
363
|
+
if(area>=MIN_AREA &&
|
|
364
|
+
area_ratio>=MIN_AREA_RATIO &&
|
|
365
|
+
feret_ratio>=MIN_FERET_RATIO){
|
|
366
|
+
ImageReaderCandidate c;
|
|
367
|
+
c.feret.x=feret.x;
|
|
368
|
+
c.feret.y=feret.y;
|
|
369
|
+
c.feret.width=feret.width;
|
|
370
|
+
c.feret.height=feret.height;
|
|
371
|
+
c.contour=cont;
|
|
372
|
+
cvSeqPush(candidates,&c);
|
|
373
|
+
}
|
|
374
|
+
else{
|
|
375
|
+
cvClearSeq(cont);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
cvRelease((void **)&cont_head);
|
|
379
|
+
|
|
380
|
+
//
|
|
381
|
+
// check each sqare has inner squire
|
|
382
|
+
//
|
|
383
|
+
int i;
|
|
384
|
+
for(i=0;i<candidates->total;i++){
|
|
385
|
+
ImageReaderCandidate *cand1=
|
|
386
|
+
(ImageReaderCandidate *)cvGetSeqElem(candidates,i);
|
|
387
|
+
|
|
388
|
+
int inner_contour=0;
|
|
389
|
+
int j;
|
|
390
|
+
for(j=0;j<candidates->total;j++){
|
|
391
|
+
if(i==j)
|
|
392
|
+
continue;
|
|
393
|
+
|
|
394
|
+
ImageReaderCandidate *cand2=
|
|
395
|
+
(ImageReaderCandidate *)cvGetSeqElem(candidates,j);
|
|
396
|
+
CvRect max_rect=cvMaxRect(&(cand1->feret),&(cand2->feret));
|
|
397
|
+
if(cand1->feret.x==max_rect.x&&
|
|
398
|
+
cand1->feret.y==max_rect.y&&
|
|
399
|
+
cand1->feret.width==max_rect.width&&
|
|
400
|
+
cand1->feret.height==max_rect.height)
|
|
401
|
+
inner_contour++;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
//
|
|
405
|
+
// There were 2 squires (white and black) inside a squire,
|
|
406
|
+
// the most outer squire assumed as position marker.
|
|
407
|
+
//
|
|
408
|
+
if(inner_contour==2){
|
|
409
|
+
CvBox2D box=cvMinAreaRect2(cand1->contour);
|
|
410
|
+
cvSeqPush(this->_seq_finder_pattern,&box);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
//
|
|
415
|
+
// clear buffers
|
|
416
|
+
//
|
|
417
|
+
for(i=0;i<candidates->total;i++){
|
|
418
|
+
ImageReaderCandidate *cand1=
|
|
419
|
+
(ImageReaderCandidate *)cvGetSeqElem(candidates,i);
|
|
420
|
+
cvClearSeq(cand1->contour);
|
|
421
|
+
}
|
|
422
|
+
//cvClearSeq(candidates);
|
|
423
|
+
cvRelease((void **)&candidates);
|
|
424
|
+
|
|
425
|
+
cvClearMemStorage(this->_stor_tmp);
|
|
426
|
+
|
|
427
|
+
return(this->_seq_finder_pattern);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/////////////////////////////////////////////////////////////////////////
|
|
431
|
+
//
|
|
432
|
+
//
|
|
433
|
+
//
|
|
434
|
+
CvSeq *ImageReader::_find_code_area_contour(double th)
|
|
435
|
+
{
|
|
436
|
+
IplImage *src=this->_img_binarized;
|
|
437
|
+
IplImage *mask=this->_img_tmp_1c;
|
|
438
|
+
cvZero(mask);
|
|
439
|
+
|
|
440
|
+
cvClearMemStorage(this->_stor_tmp);
|
|
441
|
+
|
|
442
|
+
//
|
|
443
|
+
// create position maker mask
|
|
444
|
+
//
|
|
445
|
+
CvBox2D box;
|
|
446
|
+
CvPoint2D32f pt_32f[4];
|
|
447
|
+
CvSeq *markers_vertex=cvCreateSeq(CV_SEQ_ELTYPE_POINT,
|
|
448
|
+
sizeof(CvSeq),
|
|
449
|
+
sizeof(CvPoint),
|
|
450
|
+
this->_stor_tmp);
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
int c=this->_seq_finder_pattern->total,i;
|
|
454
|
+
for(i=0;i<c;i++){
|
|
455
|
+
box=*(CvBox2D *)cvGetSeqElem(this->_seq_finder_pattern,i);
|
|
456
|
+
this->_finderpattern_boxes[i]=box;
|
|
457
|
+
|
|
458
|
+
//
|
|
459
|
+
// set 2-cells margin for fale-safe
|
|
460
|
+
//
|
|
461
|
+
box.size.width+=box.size.width/7.0F*4.0F;
|
|
462
|
+
box.size.height+=box.size.height/7.0F*4.0F;
|
|
463
|
+
|
|
464
|
+
//
|
|
465
|
+
// get each position maker's vertex
|
|
466
|
+
//
|
|
467
|
+
cvBoxPoints(box,pt_32f);
|
|
468
|
+
for(int j=0;j<4;j++){
|
|
469
|
+
CvPoint p=cvPointFrom32f(pt_32f[j]);
|
|
470
|
+
cvSeqPush(markers_vertex,&p);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
//
|
|
475
|
+
// create Minimal-area bounding rectangle which condist
|
|
476
|
+
// every position makers
|
|
477
|
+
//
|
|
478
|
+
box=cvMinAreaRect2(markers_vertex);
|
|
479
|
+
cvRelease((void **)&markers_vertex);
|
|
480
|
+
|
|
481
|
+
//
|
|
482
|
+
// create code area mask
|
|
483
|
+
//
|
|
484
|
+
cvBoxPoints(box,pt_32f);
|
|
485
|
+
CvPoint *points=new CvPoint[4];
|
|
486
|
+
for(i=0;i<4;i++){
|
|
487
|
+
points[i]=cvPointFrom32f(pt_32f[i]);
|
|
488
|
+
}
|
|
489
|
+
i=4;
|
|
490
|
+
cvFillPoly(mask,&points,&i,1,cvScalarAll(255));
|
|
491
|
+
delete points;
|
|
492
|
+
|
|
493
|
+
//
|
|
494
|
+
// apply mask to src image and reduce noise using opening
|
|
495
|
+
//
|
|
496
|
+
cvAnd(src,mask,mask);
|
|
497
|
+
cvErode(mask,mask,NULL,1);
|
|
498
|
+
cvDilate(mask,mask,NULL,1);
|
|
499
|
+
|
|
500
|
+
//
|
|
501
|
+
// get contours of masked image
|
|
502
|
+
//
|
|
503
|
+
CvSeq *cont=cvCreateSeq(CV_SEQ_ELTYPE_POINT,
|
|
504
|
+
sizeof(CvSeq),sizeof(CvPoint),
|
|
505
|
+
this->_stor_tmp);
|
|
506
|
+
cvFindContours(mask,cont->storage,&cont,sizeof(CvContour),
|
|
507
|
+
CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE,cvPoint(0,0));
|
|
508
|
+
|
|
509
|
+
//
|
|
510
|
+
// calcurate convex hull that assumed as code area
|
|
511
|
+
//
|
|
512
|
+
CvSeq *pts=cvCreateSeq(CV_SEQ_ELTYPE_POINT,
|
|
513
|
+
sizeof(CvSeq),sizeof(CvPoint),
|
|
514
|
+
this->_stor_tmp);
|
|
515
|
+
CvSeq *cont_head=cont;
|
|
516
|
+
for(;cont;cont=cont->h_next){
|
|
517
|
+
int c=cont->total;
|
|
518
|
+
for(i=0;i<c;i++){
|
|
519
|
+
CvPoint pt=*((CvPoint *)cvGetSeqElem(cont,i));
|
|
520
|
+
cvSeqPush(pts,&pt);
|
|
521
|
+
}
|
|
522
|
+
cvClearSeq(cont);
|
|
523
|
+
}
|
|
524
|
+
cvRelease((void **)&cont_head);
|
|
525
|
+
|
|
526
|
+
CvSeq *hull=cvConvexHull2(pts);
|
|
527
|
+
|
|
528
|
+
//
|
|
529
|
+
// polygonal approximation to reduce noise
|
|
530
|
+
//
|
|
531
|
+
if(this->_seq_code_area_contour){
|
|
532
|
+
cvClearSeq(this->_seq_code_area_contour);
|
|
533
|
+
cvRelease((void **)&this->_seq_code_area_contour);
|
|
534
|
+
this->_seq_code_area_contour=NULL;
|
|
535
|
+
}
|
|
536
|
+
if(hull){
|
|
537
|
+
int hullcount=hull->total;
|
|
538
|
+
CvMat* vector=cvCreateMat(1,hullcount,CV_32SC2);
|
|
539
|
+
for(i=0;i<hullcount;i++ ){
|
|
540
|
+
CV_MAT_ELEM(*vector,CvPoint,0,i)=
|
|
541
|
+
**CV_GET_SEQ_ELEM(CvPoint*,hull,i);
|
|
542
|
+
}
|
|
543
|
+
CvContour header;
|
|
544
|
+
CvSeqBlock block;
|
|
545
|
+
this->_seq_code_area_contour=cvApproxPoly(
|
|
546
|
+
cvPointSeqFromMat(CV_SEQ_KIND_CURVE+CV_SEQ_FLAG_CLOSED,
|
|
547
|
+
vector, &header, &block),
|
|
548
|
+
sizeof(CvContour),
|
|
549
|
+
this->_stor,
|
|
550
|
+
CV_POLY_APPROX_DP,th,0);
|
|
551
|
+
|
|
552
|
+
cvRelease((void **)&hull);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return(this->_seq_code_area_contour);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
CvRect ImageReader::_transform_image()
|
|
560
|
+
{
|
|
561
|
+
IplImage *src=this->_img_src;
|
|
562
|
+
IplImage *dst=this->_img_transformed;
|
|
563
|
+
cvResetImageROI(dst);
|
|
564
|
+
|
|
565
|
+
//
|
|
566
|
+
// get code area's Centor of Gravity
|
|
567
|
+
//
|
|
568
|
+
int i;
|
|
569
|
+
int c=this->_seq_code_area_contour->total;
|
|
570
|
+
CvPoint2D32f cog;
|
|
571
|
+
cog.x=0.0F;
|
|
572
|
+
cog.y=0.0F;
|
|
573
|
+
for(i=0;i<c;i++){
|
|
574
|
+
CvPoint2D32f p=cvPointTo32f(
|
|
575
|
+
*(CvPoint *)cvGetSeqElem(this->_seq_code_area_contour,i));
|
|
576
|
+
cog.x+=p.x;
|
|
577
|
+
cog.y+=p.y;
|
|
578
|
+
}
|
|
579
|
+
cog.x/=(float)c;
|
|
580
|
+
cog.y/=(float)c;
|
|
581
|
+
|
|
582
|
+
//
|
|
583
|
+
// sort code_area_contour by clock-wise
|
|
584
|
+
//
|
|
585
|
+
cvSeqSort(this->_seq_code_area_contour,seq_cmp_by_clockwise,&cog);
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
//
|
|
589
|
+
// calculates matrix of perspective transform
|
|
590
|
+
//
|
|
591
|
+
// The src rectangle transform to a square which left-top vertex
|
|
592
|
+
// is same as src rectangle and each side length is same as top side
|
|
593
|
+
// length of src.
|
|
594
|
+
//
|
|
595
|
+
CvPoint2D32f spts[4];
|
|
596
|
+
CvPoint2D32f dpts[4];
|
|
597
|
+
float max_d=0.0F;
|
|
598
|
+
int offset=0;
|
|
599
|
+
|
|
600
|
+
#ifdef _DEBUG
|
|
601
|
+
fprintf(stderr,"target region: ");
|
|
602
|
+
#endif
|
|
603
|
+
for(i=0;i<c;i++){
|
|
604
|
+
this->_coderegion_vertexes[i]=*(CvPoint *)cvGetSeqElem(
|
|
605
|
+
this->_seq_code_area_contour,i);
|
|
606
|
+
spts[i]=cvPointTo32f(this->_coderegion_vertexes[i]);
|
|
607
|
+
|
|
608
|
+
//
|
|
609
|
+
// find nearest finder pattern
|
|
610
|
+
//
|
|
611
|
+
int j=0;
|
|
612
|
+
float tmp_d=0.0F;
|
|
613
|
+
float min_d=(this->_finderpattern_boxes[j].center.x-spts[i].x)*
|
|
614
|
+
(this->_finderpattern_boxes[j].center.x-spts[i].x)+
|
|
615
|
+
(this->_finderpattern_boxes[j].center.y-spts[i].y)*
|
|
616
|
+
(this->_finderpattern_boxes[j].center.y-spts[i].y);
|
|
617
|
+
|
|
618
|
+
for(j=1;j<3;j++){
|
|
619
|
+
tmp_d=(this->_finderpattern_boxes[j].center.x-spts[i].x)*
|
|
620
|
+
(this->_finderpattern_boxes[j].center.x-spts[i].x)+
|
|
621
|
+
(this->_finderpattern_boxes[j].center.y-spts[i].y)*
|
|
622
|
+
(this->_finderpattern_boxes[j].center.y-spts[i].y);
|
|
623
|
+
|
|
624
|
+
if(min_d>tmp_d)
|
|
625
|
+
min_d=tmp_d;
|
|
626
|
+
}
|
|
627
|
+
if(max_d<min_d){
|
|
628
|
+
max_d=min_d;
|
|
629
|
+
offset=i;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
#ifdef _DEBUG
|
|
633
|
+
fprintf(stderr,"(%.0lf,%.0lf) ",spts[i].x,spts[i].y);
|
|
634
|
+
#endif
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
offset=(offset+2)%4;
|
|
638
|
+
|
|
639
|
+
#ifdef _DEBUG
|
|
640
|
+
fprintf(stderr,", rotation offset=%d\n",offset);
|
|
641
|
+
#endif
|
|
642
|
+
|
|
643
|
+
float side_len=cvSqrt((spts[0].x-spts[1].x)*(spts[0].x-spts[1].x)+
|
|
644
|
+
(spts[0].y-spts[1].y)*(spts[0].y-spts[1].y));
|
|
645
|
+
for(i=0;i<4;i++)
|
|
646
|
+
dpts[i]=cvPoint2D32f(0.0,0.0);
|
|
647
|
+
|
|
648
|
+
dpts[(offset+1)%4].x+=side_len;
|
|
649
|
+
dpts[(offset+2)%4].x+=side_len;
|
|
650
|
+
dpts[(offset+2)%4].y+=side_len;
|
|
651
|
+
dpts[(offset+3)%4].y+=side_len;
|
|
652
|
+
|
|
653
|
+
CvMat *map=cvCreateMat(3,3,CV_64FC1);
|
|
654
|
+
|
|
655
|
+
cvWarpPerspectiveQMatrix(spts,dpts,map);
|
|
656
|
+
|
|
657
|
+
//
|
|
658
|
+
// perspective transform
|
|
659
|
+
//
|
|
660
|
+
cvWarpPerspective(src,dst,map);
|
|
661
|
+
cvReleaseMat(&map);
|
|
662
|
+
|
|
663
|
+
//
|
|
664
|
+
// set ROI as code area
|
|
665
|
+
//
|
|
666
|
+
CvRect roi=cvRect((int)dpts[offset].x,(int)dpts[offset].y,
|
|
667
|
+
(int)side_len+1,(int)side_len+1);
|
|
668
|
+
cvSetImageROI(dst,roi);
|
|
669
|
+
|
|
670
|
+
return(roi);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
void ImageReader::_create_posterized_image(int block_size,
|
|
675
|
+
double delta,
|
|
676
|
+
int low_th,
|
|
677
|
+
int hi_th)
|
|
678
|
+
{
|
|
679
|
+
IplImage *src=this->_img_transformed;
|
|
680
|
+
IplImage *dst=this->_img_binarized;
|
|
681
|
+
IplImage *buf=this->_img_tmp_1c;
|
|
682
|
+
|
|
683
|
+
CvRect roi=cvGetImageROI(src);
|
|
684
|
+
cvSetImageROI(buf,roi);
|
|
685
|
+
cvSetImageROI(dst,roi);
|
|
686
|
+
|
|
687
|
+
if(this->_img_src->nChannels>1)
|
|
688
|
+
cvCvtColor(src,buf,CV_BGR2GRAY);
|
|
689
|
+
else
|
|
690
|
+
cvCopy(src,buf);
|
|
691
|
+
|
|
692
|
+
if(block_size>0)
|
|
693
|
+
apaptive_white_leveling(buf,dst,128,
|
|
694
|
+
CV_ADAPTIVE_THRESH_MEAN_C,
|
|
695
|
+
CV_THRESH_BINARY_INV,
|
|
696
|
+
block_size,delta);
|
|
697
|
+
else
|
|
698
|
+
cvThreshold(buf,dst,delta,
|
|
699
|
+
255,CV_THRESH_BINARY_INV);
|
|
700
|
+
|
|
701
|
+
int a=0;
|
|
702
|
+
int b=0;
|
|
703
|
+
if(hi_th>low_th){
|
|
704
|
+
a=128/(hi_th-low_th);
|
|
705
|
+
b=-a*low_th;
|
|
706
|
+
}
|
|
707
|
+
else{
|
|
708
|
+
hi_th=low_th;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
uchar lut_data[256];
|
|
712
|
+
int i;
|
|
713
|
+
for(i=0;i<low_th;i++)
|
|
714
|
+
lut_data[i]=0;
|
|
715
|
+
for(i=low_th;i<hi_th;i++)
|
|
716
|
+
lut_data[i]=a*i+b;
|
|
717
|
+
for(i=hi_th;i<256;i++)
|
|
718
|
+
lut_data[i]=255;
|
|
719
|
+
CvMat lut=cvMat(1,256,CV_8UC1,lut_data);
|
|
720
|
+
cvLUT(dst,buf,&lut);
|
|
721
|
+
cvCopy(buf,dst);
|
|
722
|
+
|
|
723
|
+
CvRect roi_mask=roi;
|
|
724
|
+
cvDilate(buf,buf,NULL,1);
|
|
725
|
+
cvErode(buf,buf,NULL,1);
|
|
726
|
+
|
|
727
|
+
roi_mask.x+=1;
|
|
728
|
+
roi_mask.y+=1;
|
|
729
|
+
roi_mask.width-=2;
|
|
730
|
+
roi_mask.height-=2;
|
|
731
|
+
cvSetImageROI(buf,roi_mask);
|
|
732
|
+
cvSet(buf,cvScalarAll(0));
|
|
733
|
+
cvSetImageROI(buf,roi);
|
|
734
|
+
cvOr(dst,buf,dst);
|
|
735
|
+
|
|
736
|
+
cvResetImageROI(buf);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
IplImage *ImageReader::_get_code_matrix()
|
|
741
|
+
{
|
|
742
|
+
IplImage *src=this->_img_binarized;
|
|
743
|
+
|
|
744
|
+
double cell_size=this->_get_cell_size();
|
|
745
|
+
if(cell_size<=0.0)
|
|
746
|
+
return(NULL);
|
|
747
|
+
|
|
748
|
+
CvRect roi=cvGetImageROI(src);
|
|
749
|
+
int version=(int)(((double)roi.width/cell_size-17.0)/4.0);
|
|
750
|
+
int w=4*version+17;
|
|
751
|
+
|
|
752
|
+
IplImage *dst=cvCreateImage(cvSize(w,w),IPL_DEPTH_8U,1);
|
|
753
|
+
cvResize(src,dst);
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
#ifdef _DEBUG
|
|
757
|
+
fprintf(stderr,"version=%d (%dx%d); %lfpixel/module\n",
|
|
758
|
+
version,w,w,cell_size*cell_size);
|
|
759
|
+
#endif
|
|
760
|
+
return(dst);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
int ImageReader::_get_format_info(IplImage *src,int pos)
|
|
764
|
+
{
|
|
765
|
+
unsigned int raw_data=0;
|
|
766
|
+
this->qr->init_each_formatinfo_pattern_pixel();
|
|
767
|
+
int x,y;
|
|
768
|
+
while(this->qr->each_formatinfo_pattern_pixel(pos,&x,&y)){
|
|
769
|
+
raw_data<<=1;
|
|
770
|
+
if(CV_IMAGE_ELEM(src,uchar,y,x)==255)
|
|
771
|
+
raw_data|=0x01;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
#ifdef _DEBUG
|
|
775
|
+
fprintf(stderr,"format Info=%04x, ",raw_data);
|
|
776
|
+
#endif
|
|
777
|
+
|
|
778
|
+
int ret=this->qr->decode_formatinfo(raw_data);
|
|
779
|
+
|
|
780
|
+
#ifdef _DEBUG
|
|
781
|
+
fprintf(stderr,"corrected %d errors ",ret);
|
|
782
|
+
|
|
783
|
+
char l[2]={0,0};
|
|
784
|
+
switch(this->qr->formatinfo->level){
|
|
785
|
+
case 0:
|
|
786
|
+
l[0]='M';
|
|
787
|
+
break;
|
|
788
|
+
case 1:
|
|
789
|
+
l[0]='L';
|
|
790
|
+
break;
|
|
791
|
+
case 2:
|
|
792
|
+
l[0]='H';
|
|
793
|
+
break;
|
|
794
|
+
case 3:
|
|
795
|
+
l[0]='Q';
|
|
796
|
+
}
|
|
797
|
+
int m=0;
|
|
798
|
+
if(this->qr->formatinfo->mask_pattern&0x4)
|
|
799
|
+
m|=0x100;
|
|
800
|
+
if(this->qr->formatinfo->mask_pattern&0x2)
|
|
801
|
+
m|=0x10;
|
|
802
|
+
if(this->qr->formatinfo->mask_pattern&0x1)
|
|
803
|
+
m|=0x1;
|
|
804
|
+
|
|
805
|
+
fprintf(stderr," =>level %s, mask %03x\n",l,m);
|
|
806
|
+
#endif
|
|
807
|
+
|
|
808
|
+
return(ret);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
IplImage *ImageReader::_get_function_patterns()
|
|
812
|
+
{
|
|
813
|
+
IplImage *buf=cvCreateImage(cvSize(this->qr->cells_par_side,
|
|
814
|
+
this->qr->cells_par_side),
|
|
815
|
+
IPL_DEPTH_8U,1);
|
|
816
|
+
cvZero(buf);
|
|
817
|
+
|
|
818
|
+
int x,y;
|
|
819
|
+
this->qr->init_each_function_pattern_pixel();
|
|
820
|
+
while(this->qr->each_function_pattern_pixel(&x,&y)){
|
|
821
|
+
CV_IMAGE_ELEM(buf,uchar,y,x)=255;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
cvNot(buf,buf);
|
|
825
|
+
|
|
826
|
+
return(buf);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
void ImageReader::_unmask_code_matrix(IplImage *src,
|
|
830
|
+
IplImage *function_patterns)
|
|
831
|
+
{
|
|
832
|
+
IplImage *mask=this->_get_mask_pattern();
|
|
833
|
+
IplImage *unproc=cvCloneImage(mask);
|
|
834
|
+
|
|
835
|
+
cvAnd(function_patterns,unproc,unproc);
|
|
836
|
+
cvXor(src,mask,src,unproc);
|
|
837
|
+
|
|
838
|
+
cvReleaseImage(&unproc);
|
|
839
|
+
cvReleaseImage(&mask);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
int ImageReader::_read_code_word(IplImage *src,IplImage *mask)
|
|
843
|
+
{
|
|
844
|
+
//
|
|
845
|
+
// erace timing pattern
|
|
846
|
+
//
|
|
847
|
+
IplImage *codes=cvCreateImage(cvSize(src->width-1,src->height-1),
|
|
848
|
+
IPL_DEPTH_8U,1);
|
|
849
|
+
cvZero(codes);
|
|
850
|
+
IplImage *funcs=cvCloneImage(codes);
|
|
851
|
+
|
|
852
|
+
CvRect roi=cvGetImageROI(src);
|
|
853
|
+
|
|
854
|
+
CvRect r=cvRect(7,0,roi.width-7,6);
|
|
855
|
+
cvSetImageROI(src,r);
|
|
856
|
+
cvSetImageROI(mask,r);
|
|
857
|
+
r.x-=1;
|
|
858
|
+
cvSetImageROI(codes,r);
|
|
859
|
+
cvSetImageROI(funcs,r);
|
|
860
|
+
cvCopy(src,codes);
|
|
861
|
+
cvCopy(mask,funcs);
|
|
862
|
+
|
|
863
|
+
r=cvRect(0,7,6,roi.height-6);
|
|
864
|
+
cvSetImageROI(src,r);
|
|
865
|
+
cvSetImageROI(mask,r);
|
|
866
|
+
r.y-=1;
|
|
867
|
+
cvSetImageROI(codes,r);
|
|
868
|
+
cvSetImageROI(funcs,r);
|
|
869
|
+
cvCopy(src,codes);
|
|
870
|
+
cvCopy(mask,funcs);
|
|
871
|
+
|
|
872
|
+
r=cvRect(7,7,roi.width-7,roi.height-7);
|
|
873
|
+
cvSetImageROI(src,r);
|
|
874
|
+
cvSetImageROI(mask,r);
|
|
875
|
+
r.x-=1;
|
|
876
|
+
r.y-=1;
|
|
877
|
+
cvSetImageROI(codes,r);
|
|
878
|
+
cvSetImageROI(funcs,r);
|
|
879
|
+
cvCopy(src,codes);
|
|
880
|
+
cvCopy(mask,funcs);
|
|
881
|
+
|
|
882
|
+
cvSetImageROI(src,roi);
|
|
883
|
+
cvSetImageROI(mask,roi);
|
|
884
|
+
|
|
885
|
+
cvResetImageROI(codes);
|
|
886
|
+
cvResetImageROI(funcs);
|
|
887
|
+
|
|
888
|
+
//
|
|
889
|
+
// read code from matrix
|
|
890
|
+
//
|
|
891
|
+
uchar data=0;
|
|
892
|
+
int bit_count=0,word_count=0;
|
|
893
|
+
int dir=-1;
|
|
894
|
+
int x=codes->width-1;
|
|
895
|
+
int y=codes->height-1;
|
|
896
|
+
|
|
897
|
+
do{
|
|
898
|
+
data<<=1;
|
|
899
|
+
bit_count++;
|
|
900
|
+
if(CV_IMAGE_ELEM(codes,uchar,y,x)>=128)
|
|
901
|
+
data|=1;
|
|
902
|
+
if(!(bit_count%8)){
|
|
903
|
+
if(this->qr->push_codedata(data))
|
|
904
|
+
word_count++;
|
|
905
|
+
data=0;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
if(x&0x1){
|
|
909
|
+
if(CV_IMAGE_ELEM(funcs,uchar,y,x-1))
|
|
910
|
+
x--;
|
|
911
|
+
else{
|
|
912
|
+
do{
|
|
913
|
+
y+=dir;
|
|
914
|
+
if(y<0||y>=codes->height){
|
|
915
|
+
x-=2;
|
|
916
|
+
dir*=-1;
|
|
917
|
+
y+=dir;
|
|
918
|
+
if(x<0)
|
|
919
|
+
break;
|
|
920
|
+
}
|
|
921
|
+
}while(!CV_IMAGE_ELEM(funcs,uchar,y,x));
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
else{
|
|
925
|
+
x++;
|
|
926
|
+
do{
|
|
927
|
+
y+=dir;
|
|
928
|
+
if(y>=0&&y<codes->height){
|
|
929
|
+
if(CV_IMAGE_ELEM(funcs,uchar,y,x)){
|
|
930
|
+
break;
|
|
931
|
+
}
|
|
932
|
+
else if(CV_IMAGE_ELEM(funcs,uchar,y,x-1)){
|
|
933
|
+
x--;
|
|
934
|
+
break;
|
|
935
|
+
}
|
|
936
|
+
else{
|
|
937
|
+
y+=dir;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
else{
|
|
941
|
+
x-=2;
|
|
942
|
+
dir*=-1;
|
|
943
|
+
y+=dir;
|
|
944
|
+
if(x<0)
|
|
945
|
+
break;
|
|
946
|
+
}
|
|
947
|
+
}while(!CV_IMAGE_ELEM(funcs,uchar,y,x));
|
|
948
|
+
}
|
|
949
|
+
}while(x>=0);
|
|
950
|
+
|
|
951
|
+
if(data){
|
|
952
|
+
if(this->qr->push_codedata(data))
|
|
953
|
+
word_count++;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
cvReleaseImage(&funcs);
|
|
957
|
+
cvReleaseImage(&codes);
|
|
958
|
+
|
|
959
|
+
#ifdef _DEBUG
|
|
960
|
+
fprintf(stderr,"%d words in %d blocks <= %d bits, %d words were read.\n",
|
|
961
|
+
this->qr->codedata->total_words,
|
|
962
|
+
this->qr->codedata->data_blocks,
|
|
963
|
+
bit_count,word_count);
|
|
964
|
+
#endif
|
|
965
|
+
|
|
966
|
+
return(bit_count);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
/////////////////////////////////////////////////////////////////////////
|
|
971
|
+
//
|
|
972
|
+
//
|
|
973
|
+
//
|
|
974
|
+
double ImageReader::_get_cell_size()
|
|
975
|
+
{
|
|
976
|
+
IplImage *src=this->_img_binarized;
|
|
977
|
+
this->_find_finder_pattern();
|
|
978
|
+
|
|
979
|
+
int c=this->_seq_finder_pattern->total;
|
|
980
|
+
if(c!=3)
|
|
981
|
+
return(-1.0);
|
|
982
|
+
|
|
983
|
+
double cell_size=0.0;
|
|
984
|
+
for(int i=0;i<c;i++){
|
|
985
|
+
CvBox2D box=*(CvBox2D *)cvGetSeqElem(this->_seq_finder_pattern,
|
|
986
|
+
i);
|
|
987
|
+
cell_size+=box.size.width+box.size.height;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
cell_size/=42.0;
|
|
991
|
+
|
|
992
|
+
return(cell_size);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/////////////////////////////////////////////////////////////////////////
|
|
996
|
+
//
|
|
997
|
+
//
|
|
998
|
+
//
|
|
999
|
+
IplImage *ImageReader::_get_mask_pattern()
|
|
1000
|
+
{
|
|
1001
|
+
IplImage *mask=cvCreateImage(cvSize(this->qr->cells_par_side,
|
|
1002
|
+
this->qr->cells_par_side),
|
|
1003
|
+
IPL_DEPTH_8U,1);
|
|
1004
|
+
cvZero(mask);
|
|
1005
|
+
|
|
1006
|
+
CvRect roi=cvGetImageROI(mask);
|
|
1007
|
+
roi.width+=roi.x;
|
|
1008
|
+
roi.height+=roi.y;
|
|
1009
|
+
|
|
1010
|
+
for(int y=roi.y;y<roi.height;y++){
|
|
1011
|
+
for(int x=roi.x;x<roi.width;x++){
|
|
1012
|
+
CV_IMAGE_ELEM(mask,uchar,y,x)=this->qr->
|
|
1013
|
+
formatinfo->mask_pixel(y,x);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
return(mask);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
/////////////////////////////////////////////////////////////////////////
|
|
1022
|
+
//
|
|
1023
|
+
// sort by clock-wise
|
|
1024
|
+
//
|
|
1025
|
+
static int seq_cmp_by_clockwise(const void *_a,const void *_b,void *_cog)
|
|
1026
|
+
{
|
|
1027
|
+
CvPoint* a = (CvPoint*)_a;
|
|
1028
|
+
CvPoint* b = (CvPoint*)_b;
|
|
1029
|
+
CvPoint2D32f *cog=(CvPoint2D32f *)_cog;
|
|
1030
|
+
|
|
1031
|
+
float aa=cvFastArctan((float)(a->y)-cog->y,cog->x-(float)(a->x));
|
|
1032
|
+
float ba=cvFastArctan((float)(b->y)-cog->y,cog->x-(float)(b->x));
|
|
1033
|
+
|
|
1034
|
+
return(aa < ba ? 1 : aa > ba ? -1 : 0);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
/////////////////////////////////////////////////////////////////////////
|
|
1038
|
+
//
|
|
1039
|
+
// following code originates in OpenCV/cv/src/cvadapthresh.cpp
|
|
1040
|
+
//
|
|
1041
|
+
void apaptive_white_leveling(const CvArr* src,
|
|
1042
|
+
CvArr* dst,
|
|
1043
|
+
double middle_value,
|
|
1044
|
+
int adaptive_method,
|
|
1045
|
+
int threshold_type,
|
|
1046
|
+
int block_size,
|
|
1047
|
+
double param1)
|
|
1048
|
+
{
|
|
1049
|
+
CV_FUNCNAME( "apaptive_white_leveling" );
|
|
1050
|
+
CvMat src_stub, dst_stub;
|
|
1051
|
+
CvMat *srcMat,*dstMat,*mean,*mask;
|
|
1052
|
+
|
|
1053
|
+
__BEGIN__
|
|
1054
|
+
if( adaptive_method != CV_ADAPTIVE_THRESH_MEAN_C &&
|
|
1055
|
+
adaptive_method != CV_ADAPTIVE_THRESH_GAUSSIAN_C )
|
|
1056
|
+
CV_ERROR( CV_StsBadArg,
|
|
1057
|
+
"Only CV_ADAPTIVE_THRESH_MEAN_C and CV_ADAPTIVE_THRESH_GAUSSIAN_C "
|
|
1058
|
+
"adaptive method are acceptable" );
|
|
1059
|
+
|
|
1060
|
+
if( threshold_type != CV_THRESH_BINARY &&
|
|
1061
|
+
threshold_type != CV_THRESH_BINARY_INV )
|
|
1062
|
+
CV_ERROR( CV_StsBadArg, "Only CV_TRESH_BINARY and CV_THRESH_BINARY_INV "
|
|
1063
|
+
"threshold types are acceptable" );
|
|
1064
|
+
|
|
1065
|
+
srcMat=cvGetMat(src, &src_stub );
|
|
1066
|
+
dstMat=cvGetMat(dst, &dst_stub );
|
|
1067
|
+
|
|
1068
|
+
if( !CV_ARE_CNS_EQ( srcMat, dstMat ))
|
|
1069
|
+
CV_ERROR( CV_StsUnmatchedFormats, "" );
|
|
1070
|
+
|
|
1071
|
+
if( CV_MAT_TYPE(dstMat->type) != CV_8UC1 )
|
|
1072
|
+
CV_ERROR( CV_StsUnsupportedFormat, "" );
|
|
1073
|
+
|
|
1074
|
+
if( !CV_ARE_SIZES_EQ( srcMat, dstMat ) )
|
|
1075
|
+
CV_ERROR( CV_StsUnmatchedSizes, "" );
|
|
1076
|
+
|
|
1077
|
+
mean=cvCreateMat(srcMat->rows,srcMat->cols,CV_8UC1);
|
|
1078
|
+
mask=cvCreateMat(srcMat->rows,srcMat->cols,CV_8UC1);
|
|
1079
|
+
|
|
1080
|
+
cvSmooth( srcMat, mean, adaptive_method == CV_ADAPTIVE_THRESH_MEAN_C ?
|
|
1081
|
+
CV_BLUR : CV_GAUSSIAN, block_size, block_size );
|
|
1082
|
+
cvSubS(mean,cvRealScalar(param1),mean);
|
|
1083
|
+
cvAbsDiff(srcMat,mean,dstMat);
|
|
1084
|
+
cvCmp(srcMat,mean,mask,CV_CMP_GT);
|
|
1085
|
+
cvAddS(dstMat,cvRealScalar(middle_value),dstMat,mask);
|
|
1086
|
+
cvNot(mask,mask);
|
|
1087
|
+
cvSubRS(dstMat,cvRealScalar(middle_value),dstMat,mask);
|
|
1088
|
+
|
|
1089
|
+
if(threshold_type!=CV_THRESH_BINARY)
|
|
1090
|
+
cvNot(dstMat,dstMat);
|
|
1091
|
+
|
|
1092
|
+
cvReleaseMat( &mask );
|
|
1093
|
+
cvReleaseMat( &mean );
|
|
1094
|
+
|
|
1095
|
+
__END__
|
|
1096
|
+
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
}
|