magro 0.1.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.coveralls.yml +1 -0
- data/.gitignore +4 -0
- data/.travis.yml +8 -3
- data/CHANGELOG.md +21 -0
- data/Gemfile +7 -1
- data/LICENSE.txt +1 -1
- data/README.md +17 -12
- data/ext/magro/imgrw.c +411 -0
- data/ext/magro/imgrw.h +18 -0
- data/ext/magro/magro.c +1 -394
- data/ext/magro/magro.h +2 -9
- data/lib/magro.rb +2 -0
- data/lib/magro/filter.rb +118 -0
- data/lib/magro/io.rb +10 -4
- data/lib/magro/transform.rb +77 -0
- data/lib/magro/version.rb +1 -1
- data/magro.gemspec +12 -7
- metadata +22 -66
data/ext/magro/imgrw.h
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#ifndef MAGRO_IO_H
|
2
|
+
#define MAGRO_IO_H 1
|
3
|
+
|
4
|
+
#include <stdio.h>
|
5
|
+
#include <stdlib.h>
|
6
|
+
#include <string.h>
|
7
|
+
|
8
|
+
#include <setjmp.h>
|
9
|
+
#include <png.h>
|
10
|
+
#include <jpeglib.h>
|
11
|
+
|
12
|
+
#include <ruby.h>
|
13
|
+
#include <numo/narray.h>
|
14
|
+
#include <numo/template.h>
|
15
|
+
|
16
|
+
void init_io_module();
|
17
|
+
|
18
|
+
#endif /* MAGRO_IO_H */
|
data/ext/magro/magro.c
CHANGED
@@ -1,403 +1,10 @@
|
|
1
1
|
#include "magro.h"
|
2
2
|
|
3
3
|
VALUE mMagro;
|
4
|
-
VALUE mIO;
|
5
|
-
|
6
|
-
/**
|
7
|
-
* @!visibility private
|
8
|
-
*/
|
9
|
-
static
|
10
|
-
VALUE magro_io_read_png(VALUE self, VALUE filename_)
|
11
|
-
{
|
12
|
-
char* filename = StringValuePtr(filename_);
|
13
|
-
FILE* file_ptr = fopen(filename, "rb");
|
14
|
-
unsigned char header[8];
|
15
|
-
png_structp png_ptr;
|
16
|
-
png_infop info_ptr;
|
17
|
-
png_bytep* row_ptr_ptr;
|
18
|
-
png_bytep row_ptr;
|
19
|
-
png_uint_32 width, height;
|
20
|
-
int color_type;
|
21
|
-
int bit_depth;
|
22
|
-
png_uint_32 y;
|
23
|
-
int n_dims = 0;
|
24
|
-
int n_ch;
|
25
|
-
size_t shape[3] = { 0 };
|
26
|
-
VALUE nary;
|
27
|
-
uint8_t* nary_ptr;
|
28
|
-
|
29
|
-
if (file_ptr == NULL) {
|
30
|
-
return Qnil;
|
31
|
-
}
|
32
|
-
|
33
|
-
if (fread(header, 1, 8, file_ptr) < 8) {
|
34
|
-
fclose(file_ptr);
|
35
|
-
return Qnil;
|
36
|
-
}
|
37
|
-
|
38
|
-
if (png_sig_cmp(header, 0, 8)) {
|
39
|
-
fclose(file_ptr);
|
40
|
-
return Qnil;
|
41
|
-
}
|
42
|
-
|
43
|
-
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
44
|
-
if (png_ptr == NULL) {
|
45
|
-
fclose(file_ptr);
|
46
|
-
return Qnil;
|
47
|
-
}
|
48
|
-
info_ptr = png_create_info_struct(png_ptr);
|
49
|
-
if (info_ptr == NULL) {
|
50
|
-
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
51
|
-
fclose(file_ptr);
|
52
|
-
return Qnil;
|
53
|
-
}
|
54
|
-
if (setjmp(png_jmpbuf(png_ptr))) {
|
55
|
-
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
56
|
-
fclose(file_ptr);
|
57
|
-
return Qnil;
|
58
|
-
}
|
59
|
-
|
60
|
-
png_init_io(png_ptr, file_ptr);
|
61
|
-
png_set_sig_bytes(png_ptr, 8);
|
62
|
-
|
63
|
-
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16, NULL);
|
64
|
-
row_ptr_ptr = png_get_rows(png_ptr, info_ptr);
|
65
|
-
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
|
66
|
-
|
67
|
-
if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
68
|
-
png_set_palette_to_rgb(png_ptr);
|
69
|
-
png_read_update_info(png_ptr, info_ptr);
|
70
|
-
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
|
71
|
-
}
|
72
|
-
|
73
|
-
switch (color_type) {
|
74
|
-
case PNG_COLOR_TYPE_GRAY:
|
75
|
-
n_ch = 1;
|
76
|
-
n_dims = 2;
|
77
|
-
shape[0] = height;
|
78
|
-
shape[1] = width;
|
79
|
-
break;
|
80
|
-
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
81
|
-
n_ch = 2;
|
82
|
-
n_dims = 3;
|
83
|
-
shape[0] = height;
|
84
|
-
shape[1] = width;
|
85
|
-
shape[2] = 2;
|
86
|
-
break;
|
87
|
-
case PNG_COLOR_TYPE_RGB:
|
88
|
-
n_ch = 3;
|
89
|
-
n_dims = 3;
|
90
|
-
shape[0] = height;
|
91
|
-
shape[1] = width;
|
92
|
-
shape[2] = 3;
|
93
|
-
break;
|
94
|
-
case PNG_COLOR_TYPE_RGB_ALPHA:
|
95
|
-
n_ch = 4;
|
96
|
-
n_dims = 3;
|
97
|
-
shape[0] = height;
|
98
|
-
shape[1] = width;
|
99
|
-
shape[2] = 4;
|
100
|
-
break;
|
101
|
-
default:
|
102
|
-
n_dims = 0;
|
103
|
-
break;
|
104
|
-
}
|
105
|
-
|
106
|
-
if (n_dims == 0) {
|
107
|
-
fclose(file_ptr);
|
108
|
-
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
109
|
-
return Qnil;
|
110
|
-
}
|
111
|
-
|
112
|
-
nary = rb_narray_new(numo_cUInt8, n_dims, shape);
|
113
|
-
nary_ptr = (uint8_t*)na_get_pointer_for_write(nary);
|
114
|
-
|
115
|
-
for (y = 0; y < height; y++) {
|
116
|
-
row_ptr = row_ptr_ptr[y];
|
117
|
-
memcpy(nary_ptr + y * width * n_ch, row_ptr, width * n_ch);
|
118
|
-
}
|
119
|
-
|
120
|
-
fclose(file_ptr);
|
121
|
-
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
122
|
-
|
123
|
-
return nary;
|
124
|
-
}
|
125
|
-
|
126
|
-
/**
|
127
|
-
* @!visibility private
|
128
|
-
*/
|
129
|
-
static
|
130
|
-
VALUE magro_io_save_png(VALUE self, VALUE filename_, VALUE image)
|
131
|
-
{
|
132
|
-
char* filename = StringValuePtr(filename_);
|
133
|
-
FILE* file_ptr = fopen(filename, "wb");
|
134
|
-
png_structp png_ptr;
|
135
|
-
png_infop info_ptr;
|
136
|
-
png_bytep* row_ptr_ptr;
|
137
|
-
png_uint_32 width, height;
|
138
|
-
int color_type;
|
139
|
-
int bit_depth = 8;
|
140
|
-
png_uint_32 y;
|
141
|
-
int n_ch;
|
142
|
-
int n_dims;
|
143
|
-
narray_t* image_nary;
|
144
|
-
uint8_t* image_ptr;
|
145
|
-
|
146
|
-
if (CLASS_OF(image) != numo_cUInt8) {
|
147
|
-
image = rb_funcall(numo_cUInt8, rb_intern("cast"), 1, image);
|
148
|
-
}
|
149
|
-
if (!RTEST(nary_check_contiguous(image))) {
|
150
|
-
image = nary_dup(image);
|
151
|
-
}
|
152
|
-
|
153
|
-
GetNArray(image, image_nary);
|
154
|
-
n_dims = NA_NDIM(image_nary);
|
155
|
-
height = (png_uint_32)NA_SHAPE(image_nary)[0];
|
156
|
-
width = (png_uint_32)NA_SHAPE(image_nary)[1];
|
157
|
-
image_ptr = (uint8_t*)na_get_pointer_for_read(image);
|
158
|
-
|
159
|
-
n_ch = 1;
|
160
|
-
if (n_dims == 3) {
|
161
|
-
n_ch = (int)NA_SHAPE(image_nary)[2];
|
162
|
-
}
|
163
|
-
|
164
|
-
switch (n_ch) {
|
165
|
-
case 4:
|
166
|
-
color_type = PNG_COLOR_TYPE_RGBA;
|
167
|
-
break;
|
168
|
-
case 3:
|
169
|
-
color_type = PNG_COLOR_TYPE_RGB;
|
170
|
-
break;
|
171
|
-
case 2:
|
172
|
-
color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
|
173
|
-
break;
|
174
|
-
default:
|
175
|
-
color_type = PNG_COLOR_TYPE_GRAY;
|
176
|
-
break;
|
177
|
-
}
|
178
|
-
|
179
|
-
if (file_ptr == NULL) {
|
180
|
-
return Qfalse;
|
181
|
-
}
|
182
|
-
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
183
|
-
if (png_ptr == NULL) {
|
184
|
-
fclose(file_ptr);
|
185
|
-
return Qfalse;
|
186
|
-
}
|
187
|
-
info_ptr = png_create_info_struct(png_ptr);
|
188
|
-
if (info_ptr == NULL) {
|
189
|
-
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
190
|
-
fclose(file_ptr);
|
191
|
-
return Qfalse;
|
192
|
-
}
|
193
|
-
if (setjmp(png_jmpbuf(png_ptr))) {
|
194
|
-
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
195
|
-
fclose(file_ptr);
|
196
|
-
return Qfalse;
|
197
|
-
}
|
198
|
-
|
199
|
-
png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type,
|
200
|
-
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
201
|
-
|
202
|
-
row_ptr_ptr = png_malloc(png_ptr, height * sizeof(png_bytep));
|
203
|
-
for (y = 0; y < height; y++) {
|
204
|
-
row_ptr_ptr[y] = png_malloc(png_ptr, width * n_ch * sizeof(png_byte));
|
205
|
-
memcpy(row_ptr_ptr[y], image_ptr + y * width * n_ch, width * n_ch);
|
206
|
-
}
|
207
|
-
|
208
|
-
png_init_io(png_ptr, file_ptr);
|
209
|
-
png_set_rows(png_ptr, info_ptr, row_ptr_ptr);
|
210
|
-
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
211
|
-
|
212
|
-
fclose(file_ptr);
|
213
|
-
for (y = 0; y < height; y++) {
|
214
|
-
png_free(png_ptr, row_ptr_ptr[y]);
|
215
|
-
}
|
216
|
-
png_free(png_ptr, row_ptr_ptr);
|
217
|
-
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
218
|
-
|
219
|
-
return Qtrue;
|
220
|
-
}
|
221
|
-
|
222
|
-
struct my_error_mgr {
|
223
|
-
struct jpeg_error_mgr pub;
|
224
|
-
jmp_buf setjmp_buffer;
|
225
|
-
};
|
226
|
-
|
227
|
-
static void
|
228
|
-
my_error_exit(j_common_ptr cinfo)
|
229
|
-
{
|
230
|
-
struct my_error_mgr* my_err = (struct my_error_mgr*)cinfo->err;
|
231
|
-
(*cinfo->err->output_message)(cinfo);
|
232
|
-
longjmp(my_err->setjmp_buffer, 1);
|
233
|
-
}
|
234
|
-
|
235
|
-
/**
|
236
|
-
* @!visibility private
|
237
|
-
*/
|
238
|
-
static
|
239
|
-
VALUE magro_io_read_jpg(VALUE self, VALUE filename_)
|
240
|
-
{
|
241
|
-
char* filename = StringValuePtr(filename_);
|
242
|
-
FILE* file_ptr = fopen(filename, "rb");
|
243
|
-
struct jpeg_decompress_struct jpeg;
|
244
|
-
struct my_error_mgr err;
|
245
|
-
unsigned int width, height;
|
246
|
-
int n_colors;
|
247
|
-
size_t shape[3] = { 0 };
|
248
|
-
int n_dims;
|
249
|
-
unsigned int y;
|
250
|
-
VALUE nary;
|
251
|
-
uint8_t* nary_ptr;
|
252
|
-
JSAMPLE* tmp;
|
253
|
-
|
254
|
-
|
255
|
-
if (file_ptr == NULL) {
|
256
|
-
return Qnil;
|
257
|
-
}
|
258
|
-
|
259
|
-
jpeg.err = jpeg_std_error(&err.pub);
|
260
|
-
err.pub.error_exit = my_error_exit;
|
261
|
-
if (setjmp(err.setjmp_buffer)) {
|
262
|
-
return Qnil;
|
263
|
-
}
|
264
|
-
|
265
|
-
jpeg_create_decompress(&jpeg);
|
266
|
-
jpeg_stdio_src(&jpeg, file_ptr);
|
267
|
-
jpeg_read_header(&jpeg, TRUE);
|
268
|
-
jpeg_start_decompress(&jpeg);
|
269
|
-
|
270
|
-
width = jpeg.output_width;
|
271
|
-
height = jpeg.output_height;
|
272
|
-
n_colors = jpeg.out_color_components;
|
273
|
-
|
274
|
-
n_dims = n_colors == 1 ? 2 : 3;
|
275
|
-
shape[0] = height;
|
276
|
-
shape[1] = width;
|
277
|
-
shape[2] = n_colors;
|
278
|
-
nary = rb_narray_new(numo_cUInt8, n_dims, shape);
|
279
|
-
nary_ptr = (uint8_t*)na_get_pointer_for_write(nary);
|
280
|
-
|
281
|
-
for (y = 0; y < height; y++) {
|
282
|
-
tmp = nary_ptr + y * width * n_colors;
|
283
|
-
jpeg_read_scanlines(&jpeg, &tmp, 1);
|
284
|
-
}
|
285
|
-
|
286
|
-
fclose(file_ptr);
|
287
|
-
jpeg_finish_decompress(&jpeg);
|
288
|
-
jpeg_destroy_decompress(&jpeg);
|
289
|
-
|
290
|
-
return nary;
|
291
|
-
}
|
292
|
-
|
293
|
-
/**
|
294
|
-
* @!visibility private
|
295
|
-
*/
|
296
|
-
static
|
297
|
-
VALUE magro_io_save_jpg(int argc, VALUE* argv, VALUE self)
|
298
|
-
{
|
299
|
-
VALUE filename_;
|
300
|
-
VALUE image;
|
301
|
-
VALUE quality_;
|
302
|
-
char* filename;
|
303
|
-
FILE* file_ptr;
|
304
|
-
struct jpeg_compress_struct jpeg;
|
305
|
-
struct my_error_mgr err;
|
306
|
-
narray_t* image_nary;
|
307
|
-
int quality;
|
308
|
-
int n_dims, n_ch;
|
309
|
-
unsigned int width, height, y;
|
310
|
-
uint8_t* image_ptr;
|
311
|
-
JSAMPLE* tmp;
|
312
|
-
|
313
|
-
rb_scan_args(argc, argv, "21", &filename_, &image, &quality_);
|
314
|
-
|
315
|
-
if (NIL_P(quality_)) {
|
316
|
-
quality = 95;
|
317
|
-
} else {
|
318
|
-
quality = NUM2INT(quality_);
|
319
|
-
}
|
320
|
-
|
321
|
-
filename = StringValuePtr(filename_);
|
322
|
-
|
323
|
-
if (CLASS_OF(image) != numo_cUInt8) {
|
324
|
-
image = rb_funcall(numo_cUInt8, rb_intern("cast"), 1, image);
|
325
|
-
}
|
326
|
-
if (!RTEST(nary_check_contiguous(image))) {
|
327
|
-
image = nary_dup(image);
|
328
|
-
}
|
329
|
-
|
330
|
-
jpeg.err = jpeg_std_error(&err.pub);
|
331
|
-
err.pub.error_exit = my_error_exit;
|
332
|
-
if (setjmp(err.setjmp_buffer)) {
|
333
|
-
return Qfalse;
|
334
|
-
}
|
335
|
-
|
336
|
-
jpeg_create_compress(&jpeg);
|
337
|
-
|
338
|
-
file_ptr = fopen(filename, "wb");
|
339
|
-
if (file_ptr == NULL) {
|
340
|
-
jpeg_destroy_compress(&jpeg);
|
341
|
-
return Qfalse;
|
342
|
-
}
|
343
|
-
|
344
|
-
GetNArray(image, image_nary);
|
345
|
-
n_dims = NA_NDIM(image_nary);
|
346
|
-
height = (unsigned int)NA_SHAPE(image_nary)[0];
|
347
|
-
width = (unsigned int)NA_SHAPE(image_nary)[1];
|
348
|
-
image_ptr = (uint8_t*)na_get_pointer_for_read(image);
|
349
|
-
|
350
|
-
n_ch = 1;
|
351
|
-
if (n_dims == 3) {
|
352
|
-
n_ch = (int)NA_SHAPE(image_nary)[2];
|
353
|
-
}
|
354
|
-
|
355
|
-
jpeg_stdio_dest(&jpeg, file_ptr);
|
356
|
-
|
357
|
-
jpeg.image_height = height;
|
358
|
-
jpeg.image_width = width;
|
359
|
-
jpeg.input_components = n_ch;
|
360
|
-
|
361
|
-
switch (n_ch) {
|
362
|
-
case 3:
|
363
|
-
jpeg.in_color_space = JCS_RGB;
|
364
|
-
break;
|
365
|
-
case 1:
|
366
|
-
jpeg.in_color_space = JCS_GRAYSCALE;
|
367
|
-
break;
|
368
|
-
default:
|
369
|
-
jpeg.in_color_space = JCS_UNKNOWN;
|
370
|
-
break;
|
371
|
-
}
|
372
|
-
|
373
|
-
jpeg_set_defaults(&jpeg);
|
374
|
-
|
375
|
-
jpeg_set_quality(&jpeg, quality, TRUE);
|
376
|
-
|
377
|
-
jpeg_start_compress(&jpeg, TRUE);
|
378
|
-
|
379
|
-
for (y = 0; y < height; y++) {
|
380
|
-
tmp = image_ptr + y * width * n_ch;
|
381
|
-
jpeg_write_scanlines(&jpeg, &tmp, 1);
|
382
|
-
}
|
383
|
-
|
384
|
-
jpeg_finish_compress(&jpeg);
|
385
|
-
jpeg_destroy_compress(&jpeg);
|
386
|
-
|
387
|
-
fclose(file_ptr);
|
388
|
-
|
389
|
-
return Qtrue;
|
390
|
-
}
|
391
4
|
|
392
5
|
void Init_magro()
|
393
6
|
{
|
394
|
-
rb_require("numo/narray");
|
395
|
-
|
396
7
|
mMagro = rb_define_module("Magro");
|
397
8
|
|
398
|
-
|
399
|
-
rb_define_module_function(mIO, "read_png", magro_io_read_png, 1);
|
400
|
-
rb_define_module_function(mIO, "save_png", magro_io_save_png, 2);
|
401
|
-
rb_define_module_function(mIO, "read_jpg", magro_io_read_jpg, 1);
|
402
|
-
rb_define_module_function(mIO, "save_jpg", magro_io_save_jpg, -1);
|
9
|
+
init_io_module();
|
403
10
|
}
|
data/ext/magro/magro.h
CHANGED
@@ -1,15 +1,8 @@
|
|
1
1
|
#ifndef MAGRO_H
|
2
2
|
#define MAGRO_H 1
|
3
3
|
|
4
|
-
#include <math.h>
|
5
|
-
#include <string.h>
|
6
|
-
|
7
|
-
#include <setjmp.h>
|
8
|
-
#include <png.h>
|
9
|
-
#include <jpeglib.h>
|
10
|
-
|
11
4
|
#include <ruby.h>
|
12
|
-
|
13
|
-
#include
|
5
|
+
|
6
|
+
#include "imgrw.h"
|
14
7
|
|
15
8
|
#endif /* MAGRO_H */
|
data/lib/magro.rb
CHANGED
data/lib/magro/filter.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Magro
|
4
|
+
# Filter module provides functions for image filtering.
|
5
|
+
module Filter
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# Applies box filter to image.
|
9
|
+
# This method performs zero padding as a preprocessing.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# image = Magro::IO.imread('foo.png')
|
13
|
+
# kernel = Numo::DFloat[
|
14
|
+
# [1, 1, 1],
|
15
|
+
# [1, 1, 1],
|
16
|
+
# [1, 1, 1]
|
17
|
+
# ]
|
18
|
+
# blured_image = Magro::Filter.filter2d(image, kernel)
|
19
|
+
# Magro::IO.imsave('bar.png', blured_image)
|
20
|
+
#
|
21
|
+
# @param image [Numo::UInt8] (shape: [height, width, n_channels]) Input image to be filtered.
|
22
|
+
# @param kernel [Numo::DFloat] (shape: [kernel_height, kernel_width]) Box filter.
|
23
|
+
# @param scale [Float/Nil] Scale parameter for box filter. If nil is given, the box filter is normalized with sum of filter values.
|
24
|
+
# @param offset [Integer] Offset value of filtered image.
|
25
|
+
# @raise [ArgumentError] This error is raised when class of input image is not Numo::NArray.
|
26
|
+
# @return [Numo::UInt8] (shape: [height, width, n_channels]) Filtered image.
|
27
|
+
def filter2d(image, kernel, scale: nil, offset: 0)
|
28
|
+
raise ArgumentError, 'Expect class of image to be Numo::NArray.' unless image.is_a?(Numo::NArray)
|
29
|
+
filter_h, filter_w = kernel.shape
|
30
|
+
padded = zero_padding(image, filter_h, filter_w)
|
31
|
+
n_channels = image.shape[2]
|
32
|
+
if n_channels.nil?
|
33
|
+
filter1ch(padded, kernel, scale, offset)
|
34
|
+
else
|
35
|
+
image.class.zeros(*image.shape).tap do |filtered|
|
36
|
+
n_channels.times do |c|
|
37
|
+
filtered[true, true, c] = filter1ch(padded[true, true, c], kernel, scale, offset)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Convolve two 2-dimensional arrays.
|
44
|
+
#
|
45
|
+
# @param arr1 [Numo::NArray] (shape: [row1, col1]) First input array.
|
46
|
+
# @param arr2 [Numo::NArray] (shape: [row2, col2]) Second input array.
|
47
|
+
# @raise [ArgumentError] This error is raised when class of input array is not Numo::NArray.
|
48
|
+
# @return [Numo::NArray] (shape: [row1 - row2 + 1, col1 - col2 + 1]) Convolution of arr1 with arr2.
|
49
|
+
def convolve2d(arr1, arr2)
|
50
|
+
raise ArgumentError, 'Expect class of first input array to be Numo::NArray.' unless arr1.is_a?(Numo::NArray)
|
51
|
+
raise ArgumentError, 'Expect class of second input array to be Numo::NArray.' unless arr2.is_a?(Numo::NArray)
|
52
|
+
raise ArgumentError, 'Expect first input array to be 2-dimensional array.' unless arr1.ndim == 2
|
53
|
+
raise ArgumentError, 'Expect second input array to be 2-dimensional array.' unless arr2.ndim == 2
|
54
|
+
row1, col1 = arr1.shape
|
55
|
+
row2, col2 = arr2.shape
|
56
|
+
# FIXME: lib/numo/narray/extra.rb:1098: warning: Using the last argument as keyword parameters is deprecated
|
57
|
+
# convolved = im2col(arr1, row2, col2).dot(arr2.flatten)
|
58
|
+
convolved = arr2.flatten.dot(im2col(arr1, row2, col2).transpose)
|
59
|
+
convolved.reshape(row1 - row2 + 1, col1 - col2 + 1)
|
60
|
+
end
|
61
|
+
|
62
|
+
# private
|
63
|
+
|
64
|
+
def zero_padding(image, filter_h, filter_w)
|
65
|
+
image_h, image_w, n_channels = image.shape
|
66
|
+
pad_h = filter_h / 2
|
67
|
+
pad_w = filter_w / 2
|
68
|
+
out_h = image_h + pad_h * 2
|
69
|
+
out_w = image_w + pad_w * 2
|
70
|
+
if n_channels.nil?
|
71
|
+
image.class.zeros(out_h, out_w).tap do |padded|
|
72
|
+
padded[pad_h...(pad_h + image_h), pad_w...(pad_w + image_w)] = image
|
73
|
+
end
|
74
|
+
else
|
75
|
+
image.class.zeros(out_h, out_w, n_channels).tap do |padded|
|
76
|
+
n_channels.times do |c|
|
77
|
+
padded[pad_h...(pad_h + image_h), pad_w...(pad_w + image_w), c] = image[true, true, c]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def filter1ch(image, kernel, scale, offset)
|
84
|
+
scale ||= kernel.sum
|
85
|
+
kernel *= scale.zero? ? 1.0 : 1.fdiv(scale)
|
86
|
+
filtered = convolve2d(image, kernel)
|
87
|
+
filtered = (filtered + offset).round.clip(image.class::MIN, image.class::MAX) if integer_narray?(image)
|
88
|
+
filtered = image.class.cast(filtered) unless filtered.is_a?(image.class)
|
89
|
+
filtered
|
90
|
+
end
|
91
|
+
|
92
|
+
def im2col(image, filter_h, filter_w)
|
93
|
+
height, width = image.shape
|
94
|
+
rows = height - filter_h + 1
|
95
|
+
cols = width - filter_w + 1
|
96
|
+
mat = image.class.zeros(filter_h, filter_w, rows, cols)
|
97
|
+
filter_h.times do |y|
|
98
|
+
y_end = y + rows
|
99
|
+
filter_w.times do |x|
|
100
|
+
x_end = x + cols
|
101
|
+
mat[y, x, true, true] = image[y...y_end, x...x_end]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
mat.transpose(2, 3, 0, 1).reshape(rows * cols, filter_h * filter_w)
|
105
|
+
end
|
106
|
+
|
107
|
+
INTEGER_NARRAY = %w[Numo::Int8 Numo::Int16 Numo::Int32 Numo::Int64
|
108
|
+
Numo::UInt8 Numo::UInt16 Numo::UInt32 Numo::UInt64].freeze
|
109
|
+
|
110
|
+
private_constant :INTEGER_NARRAY
|
111
|
+
|
112
|
+
def integer_narray?(image)
|
113
|
+
INTEGER_NARRAY.include?(image.class.to_s)
|
114
|
+
end
|
115
|
+
|
116
|
+
private_class_method :zero_padding, :filter1ch, :im2col, :integer_narray?
|
117
|
+
end
|
118
|
+
end
|