ndav-magick-image 0.0.1 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf489f7ae5981b5e552d896e8ce0f0696784d914572af0678790464f409f9b16
4
- data.tar.gz: '09e621d161ed19c832229e535a9f9ff3a6864b1f05756ab91c0c2e5a1e3e800b'
3
+ metadata.gz: 96acb84dd6f8a244f0951db50db36caf76cdf29d87804a45cac2fb356b699e28
4
+ data.tar.gz: 5a5c8fce397292d41afa18d60cbdbd6e8948c40a014143ce3a003b88aea66444
5
5
  SHA512:
6
- metadata.gz: 955db5a60c525d1be3a242c424428a93a71cdec036724684f14c1876741bb56f3de86d0e3be75ced7980f44038573332a7a508b0918df524017f3b3e17c4e666
7
- data.tar.gz: 85d97b041b9153ff193b5348fee2b55257458f76450019307b2a4ff529b726c68d388fb3f1944328aaba12deaf09422d4b6d525c41c5658364b6c7146b30faea
6
+ metadata.gz: d2dcc5754ad77b221630fde7deb4a6f8b36067bc938e3b0d3a2f3d5623c675d44cfba2c41527cade9e7eb4e5a2acda4dbf7cf53dbfb8c5ee9a62aec5df4825a9
7
+ data.tar.gz: 2b76f5e90698374a5788c212837a6cb466977479e18514606b10061be1da6e43047e712530824a6ca67f51e1e9060ba3a7e170f3e78a404d4a0238ea503cd451
data/README.md CHANGED
@@ -60,9 +60,9 @@ The shape is `[height, width, channels]`. Channels vary based on the image attri
60
60
 
61
61
  ### format and item size ###
62
62
 
63
- Currently, this bridge supports only 8-bit depth expression of pixels, which means all channels including grayscale intencity, RGB and alpha are 0-255 values. Therefore, the MemoryView format is always `"C"` and the item size is 1-byte. Currently, HDR images are converted to standard dynamic-range images.
63
+ Currently, this bridge exports `Magick::Image`'s MemoryView and imports from `NDAV` using `Magick::QuantumPixel`. Therefore, the MemoryView's format and item_size depend on the build options of ImageMagick. You can check the format with `NDAV::Magick::Image::MemoryViewable::FORMAT`.
64
64
 
65
- Other formats such as float might be supported in the future.
65
+ It's under consideration whether other formats should be supported or not.
66
66
 
67
67
  EXAMPLE
68
68
  -------
@@ -82,25 +82,25 @@ image = image_list[0]
82
82
  numo = NumoNArray(image)
83
83
  abort "Use RGB image" unless numo.shape[2] == 3
84
84
 
85
- noise = Numo::Int8.new(numo.shape).rand(Numo::Int8::MIN, Numo::Int8::MAX)
86
- noised = Numo::Int16.cast(numo)
87
- noised.inplace + noise
88
- noised.inplace.clip(Numo::UInt8::MIN, Numo::UInt8::MAX)
85
+ # Assumes numo is Numo::SFloat here. It depends on your environment.
86
+ noise = Numo::SFloat.new(numo.shape).rand(0, -Magick::QuantumRange / 2)
87
+ noised = numo + noise
88
+ noised.inplace.clip(0, Magick::QuantumRange)
89
89
 
90
- noised_image = MagickImage(Numo::UInt8.cast(noised))
90
+ noised_image = MagickImage(noised)
91
91
  noised_image.write "noised.png"
92
+
92
93
  ```
93
94
 
94
95
  | Before | After |
95
96
  |--------|-------|
96
- | [![Original image](https://i.gyazo.com/905f541f15c0d57db5fc7ea3f6edaf07.png)](https://gyazo.com/905f541f15c0d57db5fc7ea3f6edaf07) | [![Noized image](https://i.gyazo.com/7ad51507e7a2d2c8c39c15ce71a5175a.png)](https://gyazo.com/7ad51507e7a2d2c8c39c15ce71a5175a) |
97
+ | [![Original image](https://i.gyazo.com/905f541f15c0d57db5fc7ea3f6edaf07.png)](https://gyazo.com/905f541f15c0d57db5fc7ea3f6edaf07) | [![Noised image](https://i.gyazo.com/c8c7191713d97fffaa2509e6b0874aab.png)](https://gyazo.com/c8c7191713d97fffaa2509e6b0874aab) |
97
98
 
98
99
  TODO
99
100
  ----
100
101
 
101
- * Support HDRI
102
102
  * Support zero-copy
103
- * Support float value pixels
103
+ * Support export in non-ImageMagick-native format
104
104
 
105
105
  LICENSE
106
106
  -------
@@ -4,6 +4,25 @@ require "rmagick"
4
4
  class NDAV
5
5
  module Magick
6
6
  module Image
7
+ IS_HDRI_SUPPORTED = ::Magick::Magick_features.match?(/\bHDRI\b/)
8
+ is_little_endian = IO::Buffer::HOST_ENDIAN == IO::Buffer::LITTLE_ENDIAN
9
+ SUPPORTED_FORMATS =
10
+ case [::Magick::MAGICKCORE_QUANTUM_DEPTH, IS_HDRI_SUPPORTED]
11
+ in [ 8, false] then ["C"]
12
+ in [16, false] then ["S", is_little_endian ? "v" : "n"]
13
+ in [32, false] then ["L", is_little_endian ? "V" : "N"]
14
+ in [ 8, true ] | [16, true] | [32, true] then ["f", is_little_endian ? "e" : "g"]
15
+ in [64, true ] then ["d", is_little_endian ? "E" : "G"]
16
+ end
17
+ COLORSPACE_TO_MAP = {
18
+ ::Magick::GRAYColorspace => "I",
19
+ ::Magick::LinearGRAYColorspace => "I",
20
+ ::Magick::RGBColorspace => "RGB",
21
+ ::Magick::ScRGBColorspace => "RGB",
22
+ ::Magick::SRGBColorspace => "RGB",
23
+ ::Magick::CMYColorspace => "CMY",
24
+ ::Magick::CMYKColorspace => "CMYK"
25
+ }
7
26
  CHANNELS_TO_MAP = {
8
27
  nil => "I",
9
28
  1 => "I",
@@ -14,48 +33,50 @@ class NDAV
14
33
 
15
34
  module FromNDAV
16
35
  # TODO: Support more formats
36
+ # TODO: Support CMYK
17
37
  def from_ndav(ndav)
18
- raise %|Currently, supports only format "C", but is "#{ndav.format}"| unless ndav.format == "C"
19
- raise "Currently, supports only row-major contiguous NDAV" unless ndav.row_major_contiguous?
20
- raise "ndim must be 2 or 3 but is #{ndav.ndim}" unless [2, 3].include? ndav.ndim
38
+ Kernel.raise "ndim must be 2 or 3 but is #{ndav.ndim}" unless [2, 3].include? ndav.ndim
39
+ Kernel.raise "Currently, supports only row-major contiguous NDAV" unless ndav.row_major_contiguous?
40
+
41
+ item_desc = NDAV::ITEM_DESCS[ndav.format]
42
+ Kernel.raise "Unsupported format: #{ndav.format}. Must be single character" unless item_desc.length == 1
43
+ component = item_desc[0]
44
+ Kernel.raise "Unsupported format: #{ndav.format}. Must not be repeated" unless component.repeat == 1
45
+ Kernel.raise "Unsupported format: #{ndav.format}. Supported chars: #{SUPPORTED_FORMATS.join(', ')}" unless SUPPORTED_FORMATS.include? component.format
21
46
 
22
47
  height, width, channels = ndav.shape
23
48
  map = CHANNELS_TO_MAP[channels]
24
- raise "Unsupported channels: #{channels}" unless map
49
+ Kernel.raise "Unsupported channels: #{channels}" unless map
25
50
 
26
51
  image = ::Magick::Image.new(width, height)
27
- image.import_pixels 0, 0, width, height, map, ndav.to_s
52
+ image.import_pixels 0, 0, width, height, map, ndav.to_s, ::Magick::QuantumPixel
28
53
  image
29
54
  end
30
55
  end
31
-
56
+
32
57
  module MemoryViewable
58
+ FORMAT = SUPPORTED_FORMATS.first
59
+ ITEM_SIZE = NDAV::ITEM_SIZES[FORMAT]
60
+
33
61
  private
34
62
 
35
- # TODO: Support HDRI
36
63
  def ndav_descriptor(**)
37
- map =
38
- case [gray?, alpha?]
39
- when [true, true] then "IA"
40
- when [true, false] then "I"
41
- when [false, true] then "RGBA"
42
- else "RGB"
43
- end
64
+ map = COLORSPACE_TO_MAP[colorspace]
65
+ Kernel.raise "Unsupported color space: #{colorspace}" unless map
66
+ map += "A" if alpha?
44
67
  shape = [rows, columns, map.length]
45
- format = "C"
46
- item_size = ::NDAV::ITEM_SIZES[format]
47
68
 
48
69
  {
49
- data: export_pixels_to_str(0, 0, columns, rows, map, ::Magick::CharPixel),
70
+ data: export_pixels_to_str(0, 0, columns, rows, map, ::Magick::QuantumPixel),
50
71
  shape:,
51
- strides: ::NDAV.default_strides(shape:, item_size:),
52
- format:,
53
- byte_size: shape.reduce(item_size, :*),
72
+ strides: NDAV.default_strides(shape:, item_size: ITEM_SIZE),
73
+ format: FORMAT,
74
+ byte_size: shape.reduce(ITEM_SIZE, :*),
54
75
  readonly?: true
55
76
  }
56
77
  end
57
78
  end
58
-
79
+
59
80
  module Converter
60
81
  def MagickImage(array, *, **)
61
82
  if array.kind_of? ::Magick::Image
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "ndav-magick-image"
3
- s.version = "0.0.1"
3
+ s.version = "0.0.3"
4
4
  s.authors = ["Kitaiti Makoto"]
5
5
  s.summary = "N-Dimensional Array View for RMagick"
6
6
  s.licenses = ["MIT"]
data/test/helper.rb CHANGED
@@ -3,3 +3,19 @@ require "test/unit"
3
3
  require "test/unit/notify"
4
4
  require "fiddle"
5
5
  require "tmpdir"
6
+
7
+ module Helper
8
+ FORMAT = NDAV::Magick::Image::MemoryViewable::FORMAT
9
+
10
+ private
11
+
12
+ def make_image(width, height, former = [255, 255, 255], latter = [0, 255, 0])
13
+ ratio = (2 ** ::Magick::MAGICKCORE_QUANTUM_DEPTH - 1) / 255
14
+ [
15
+ [former * width] * (height / 2),
16
+ [latter * width] * (height / 2)
17
+ ].flatten.collect {|item| item * ratio}
18
+ end
19
+ end
20
+
21
+ Test::Unit::TestCase.include Helper
@@ -18,12 +18,9 @@ class TestConverter < Test::Unit::TestCase
18
18
  end
19
19
 
20
20
  def test_magick_image_from_ndav
21
- array = [
22
- [[255, 255, 255] * @width] * (@height / 2),
23
- [[ 0, 255, 0] * @width] * (@height / 2)
24
- ]
25
- ptr = Fiddle::Pointer[array.flatten.pack("C*")]
26
- ndav = ::NDAV.new(ptr, shape: [@height, @width, 3])
21
+ array = make_image(@width, @height)
22
+ ptr = Fiddle::Pointer[array.flatten.pack("#{FORMAT}*")]
23
+ ndav = ::NDAV.new(ptr, format: FORMAT, shape: [@height, @width, 3])
27
24
 
28
25
  assert_instance_of ::Magick::Image, ::NDAV::Converter::MagickImage(ndav)
29
26
  end
@@ -7,66 +7,58 @@ class TestFromNDAV < Test::Unit::TestCase
7
7
  end
8
8
 
9
9
  def test_basic
10
- array = [
11
- [[255, 255, 255] * @width] * (@height / 2),
12
- [[ 0, 255, 0] * @width] * (@height / 2)
13
- ]
14
- ptr = Fiddle::Pointer[array.flatten.pack("C*")]
15
- ndav = ::NDAV.new(ptr, shape: [@height, @width, 3])
10
+ array = make_image(@width, @height)
11
+ ptr = Fiddle::Pointer[array.flatten.pack("#{FORMAT}*")]
12
+ ndav = ::NDAV.new(ptr, format: FORMAT, shape: [@height, @width, 3])
16
13
  image = ::Magick::Image.from_ndav(ndav)
17
14
 
18
15
  assert_equal @width, image.columns
19
16
  assert_equal @height, image.rows
20
17
  assert_false image.gray?
21
18
  assert_false image.alpha?
22
- assert_equal array.flatten, image.export_pixels_to_str.unpack("C*")
19
+ assert_equal array.flatten,
20
+ image.export_pixels_to_str(0, 0, @width, @height, "RGB", ::Magick::QuantumPixel).unpack("#{FORMAT}*")
23
21
  end
24
22
 
25
23
  def test_alpha
26
- array = [
27
- [[255, 255, 255, 128] * @width] * (@height / 2),
28
- [[ 0, 255, 0, 128] * @width] * (@height / 2)
29
- ]
30
- ptr = Fiddle::Pointer[array.flatten.pack("C*")]
31
- ndav = ::NDAV.new(ptr, shape: [@height, @width, 4])
24
+ array = make_image(@width, @height, [255, 255, 255, 128], [0, 255, 0, 128])
25
+ ptr = Fiddle::Pointer[array.flatten.pack("#{FORMAT}*")]
26
+ ndav = ::NDAV.new(ptr, format: FORMAT, shape: [@height, @width, 4])
32
27
  image = ::Magick::Image.from_ndav(ndav)
33
28
 
34
29
  assert_equal @width, image.columns
35
30
  assert_equal @height, image.rows
36
31
  assert_false image.gray?
37
32
  assert_true image.alpha?
38
- assert_equal array.flatten, image.export_pixels_to_str(0, 0, @width, @height, "RGBA").unpack("C*")
33
+ assert_equal array.flatten,
34
+ image.export_pixels_to_str(0, 0, @width, @height, "RGBA", ::Magick::QuantumPixel).unpack("#{FORMAT}*")
39
35
  end
40
36
 
41
37
  def test_grayscale
42
- array = [
43
- [[255] * @width] * (@height / 2),
44
- [[128] * @width] * (@height / 2)
45
- ]
46
- ptr = Fiddle::Pointer[array.flatten.pack("C*")]
47
- ndav = ::NDAV.new(ptr, shape: [@height, @width, 1])
38
+ array = make_image(@width, @height, [255], [128])
39
+ ptr = Fiddle::Pointer[array.flatten.pack("#{FORMAT}*")]
40
+ ndav = ::NDAV.new(ptr, format: FORMAT, shape: [@height, @width, 1])
48
41
  image = ::Magick::Image.from_ndav(ndav)
49
42
 
50
43
  assert_equal @width, image.columns
51
44
  assert_equal @height, image.rows
52
45
  assert_true image.gray?
53
46
  assert_false image.alpha?
54
- assert_equal array.flatten, image.export_pixels_to_str(0, 0, @width, @height, "R").unpack("C*")
47
+ assert_equal array.flatten,
48
+ image.export_pixels_to_str(0, 0, @width, @height, "R", ::Magick::QuantumPixel).unpack("#{FORMAT}*")
55
49
  end
56
50
 
57
51
  def test_gray_alpha
58
- array = [
59
- [[255, 128] * @width] * (@height / 2),
60
- [[128, 128] * @width] * (@height / 2)
61
- ]
62
- ptr = Fiddle::Pointer[array.flatten.pack("C*")]
63
- ndav = ::NDAV.new(ptr, shape: [@height, @width, 2])
52
+ array = make_image(@width, @height, [255, 128], [128, 128])
53
+ ptr = Fiddle::Pointer[array.flatten.pack("#{FORMAT}*")]
54
+ ndav = ::NDAV.new(ptr, format: FORMAT, shape: [@height, @width, 2])
64
55
  image = ::Magick::Image.from_ndav(ndav)
65
56
 
66
57
  assert_equal @width, image.columns
67
58
  assert_equal @height, image.rows
68
59
  assert_true image.gray?
69
60
  assert_true image.alpha?
70
- assert_equal array.flatten, image.export_pixels_to_str(0, 0, @width, @height, "RA").unpack("C*")
61
+ assert_equal array.flatten,
62
+ image.export_pixels_to_str(0, 0, @width, @height, "RA", ::Magick::QuantumPixel).unpack("#{FORMAT}*")
71
63
  end
72
64
  end
@@ -1,6 +1,8 @@
1
1
  require_relative "helper"
2
2
 
3
3
  class TestMemoryViewable < Test::Unit::TestCase
4
+ ITEM_SIZE = NDAV::ITEM_SIZES[FORMAT]
5
+
4
6
  def setup
5
7
  @width = 3
6
8
  @height = 2
@@ -18,14 +20,10 @@ class TestMemoryViewable < Test::Unit::TestCase
18
20
  Fiddle::MemoryView.export @image do |view|
19
21
  assert_equal 3, view.ndim
20
22
  assert_equal [@height, @width, 3], view.shape
21
- assert_equal 1, view.item_size
22
- assert_equal "C", view.format
23
- assert_equal @width * @height * 3, view.byte_size
24
- assert_equal [
25
- [[255, 255, 255] * @width] * (@height / 2),
26
- [[ 0, 255, 0] * @width] * (@height / 2)
27
- ].flatten,
28
- view.to_s.unpack("C*")
23
+ assert_equal ITEM_SIZE, view.item_size
24
+ assert_equal FORMAT, view.format
25
+ assert_equal @width * @height * 3 * ITEM_SIZE, view.byte_size
26
+ assert_equal make_image(@width, @height), view.to_s.unpack("#{FORMAT}*")
29
27
  end
30
28
  end
31
29
 
@@ -35,44 +33,38 @@ class TestMemoryViewable < Test::Unit::TestCase
35
33
  Fiddle::MemoryView.export @image do |view|
36
34
  assert_equal 3, view.ndim
37
35
  assert_equal [@height, @width, 4], view.shape
38
- assert_equal 1, view.item_size
39
- assert_equal "C", view.format
40
- assert_equal @width * @height * 4, view.byte_size
36
+ assert_equal ITEM_SIZE, view.item_size
37
+ assert_equal FORMAT, view.format
38
+ assert_equal @width * @height * 4* ITEM_SIZE, view.byte_size
41
39
  end
42
40
  end
43
41
 
44
42
  def test_grayscale
45
- image = @image.quantize(256, Magick::GRAYColorspace)
43
+ @image.colorspace = Magick::GRAYColorspace
46
44
 
47
- Fiddle::MemoryView.export image do |view|
45
+ Fiddle::MemoryView.export @image do |view|
48
46
  assert_equal 3, view.ndim
49
47
  assert_equal [@height, @width, 1], view.shape
50
- assert_equal 1, view.item_size
51
- assert_equal "C", view.format
52
- assert_equal @width * @height * 1, view.byte_size
53
- assert_equal [
54
- [[255] * @width] * (@height / 2),
55
- [[182] * @width] * (@height / 2)
56
- ].flatten,
57
- view.to_s.unpack("C*")
48
+ assert_equal ITEM_SIZE, view.item_size
49
+ assert_equal FORMAT, view.format
50
+ assert_equal @width * @height * 1 * ITEM_SIZE, view.byte_size
51
+ assert_equal @image.export_pixels_to_str(0, 0, @width, @height, "I", Magick::QuantumPixel).unpack("#{FORMAT}*"),
52
+ view.to_s.unpack("#{FORMAT}*")
58
53
  end
59
54
  end
60
55
 
61
56
  def test_gray_alpha
62
- image = @image.quantize(256, Magick::GRAYColorspace)
63
- image.alpha Magick::OnAlphaChannel
57
+ @image.colorspace = Magick::GRAYColorspace
58
+ @image.alpha Magick::OnAlphaChannel
64
59
 
65
- Fiddle::MemoryView.export image do |view|
60
+ Fiddle::MemoryView.export @image do |view|
66
61
  assert_equal 3, view.ndim
67
62
  assert_equal [@height, @width, 2], view.shape
68
- assert_equal 1, view.item_size
69
- assert_equal "C", view.format
70
- assert_equal @width * @height * 2, view.byte_size
71
- assert_equal [
72
- [[255, 255] * @width] * (@height / 2),
73
- [[182, 255] * @width] * (@height / 2)
74
- ].flatten,
75
- view.to_s.unpack("C*")
63
+ assert_equal ITEM_SIZE, view.item_size
64
+ assert_equal FORMAT, view.format
65
+ assert_equal @width * @height * 2 * ITEM_SIZE, view.byte_size
66
+ assert_equal @image.export_pixels_to_str(0, 0, @width, @height, "IA", Magick::QuantumPixel).unpack("#{FORMAT}*"),
67
+ view.to_s.unpack("#{FORMAT}*")
76
68
  end
77
69
  end
78
70
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ndav-magick-image
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kitaiti Makoto