magro 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/lib/magro.rb +1 -0
- data/lib/magro/filter.rb +118 -0
- data/lib/magro/transform.rb +1 -1
- data/lib/magro/version.rb +1 -1
- data/magro.gemspec +5 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50da8b3b6adbd0f21ad6ae09373a4659be55806775a8c136f3d432616868b0f6
|
4
|
+
data.tar.gz: 860678b90cd8010f6968cf91b94e7572f98d4124aa310314aff41a4dc5861d03
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43c79eac7ae51192d1a24d3fa17492d00507a2b6092ab10b62c5fdba83d24f589ff8fd7655d1bc0ea15746f4512e0c02903748d0be12355076c6953661461c85
|
7
|
+
data.tar.gz: 75e279fd89956ad161f1e5a188f3ffa272d1f881f350823d4de33abcca9d71bc6120e097d21a32ffb2e25f9cd7497db7cc375c06c755027880581d67b522f20e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# 0.3.0
|
2
|
+
- Add [filter module](https://yoshoku.github.io/magro/doc/Magro/Filter.html) consists of image filtering methods.
|
3
|
+
- Change to use round instead of ceil in [quantization of resize method](https://github.com/yoshoku/magro/commit/1b3308ddfb98a650889483af3cd2045aaf6b8837) when given integer image.
|
4
|
+
|
1
5
|
# 0.2.0
|
2
6
|
- Add [transform module](https://yoshoku.github.io/magro/doc/Magro/Transform.html) and resize method.
|
3
7
|
- Fix some configulation files.
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
[](https://travis-ci.org/yoshoku/magro)
|
4
4
|
[](https://badge.fury.io/rb/magro)
|
5
5
|
[](https://github.com/yoshoku/numo-liblinear/blob/master/LICENSE.txt)
|
6
|
-
[](https://yoshoku.github.io/magro/doc/)
|
7
7
|
|
8
8
|
Magro is an image processing library in Ruby.
|
9
9
|
Magro uses [Numo::NArray](https://github.com/ruby-numo/numo-narray) arrays as image objects.
|
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
|
data/lib/magro/transform.rb
CHANGED
@@ -58,7 +58,7 @@ module Magro
|
|
58
58
|
|
59
59
|
resized = a * (1 - x_d) * (1 - y_d) + b * x_d * (1 - y_d) + c * (1 - x_d) * y_d + d * x_d * y_d
|
60
60
|
|
61
|
-
resized = resized.
|
61
|
+
resized = resized.round.clip(image.class::MIN, image.class::MAX) if integer_narray?(image)
|
62
62
|
resized = image.class.cast(resized) unless resized.is_a?(image.class)
|
63
63
|
resized.reshape(new_height, new_width)
|
64
64
|
end
|
data/lib/magro/version.rb
CHANGED
data/magro.gemspec
CHANGED
@@ -14,6 +14,11 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.homepage = 'https://github.com/yoshoku/magro'
|
15
15
|
spec.license = 'BSD-3-Clause'
|
16
16
|
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/yoshoku/magro'
|
19
|
+
spec.metadata['changelog_uri'] = 'https://github.com/yoshoku/magro/blob/master/CHANGELOG.md'
|
20
|
+
spec.metadata['documentation_uri'] = 'https://yoshoku.github.io/magro/doc/'
|
21
|
+
|
17
22
|
# Specify which files should be added to the gem when it is released.
|
18
23
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
24
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: magro
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yoshoku
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05-
|
11
|
+
date: 2020-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: numo-narray
|
@@ -49,6 +49,7 @@ files:
|
|
49
49
|
- ext/magro/magro.c
|
50
50
|
- ext/magro/magro.h
|
51
51
|
- lib/magro.rb
|
52
|
+
- lib/magro/filter.rb
|
52
53
|
- lib/magro/io.rb
|
53
54
|
- lib/magro/transform.rb
|
54
55
|
- lib/magro/version.rb
|
@@ -56,7 +57,11 @@ files:
|
|
56
57
|
homepage: https://github.com/yoshoku/magro
|
57
58
|
licenses:
|
58
59
|
- BSD-3-Clause
|
59
|
-
metadata:
|
60
|
+
metadata:
|
61
|
+
homepage_uri: https://github.com/yoshoku/magro
|
62
|
+
source_code_uri: https://github.com/yoshoku/magro
|
63
|
+
changelog_uri: https://github.com/yoshoku/magro/blob/master/CHANGELOG.md
|
64
|
+
documentation_uri: https://yoshoku.github.io/magro/doc/
|
60
65
|
post_install_message:
|
61
66
|
rdoc_options: []
|
62
67
|
require_paths:
|