Module: JXL::IO::NPY

Defined in:
lib/jxl/io/npy.rb

Constant Summary collapse

MAGIC =
"\x93NUMPY".b
FORMATS =
{
  "<f4" => "e*",
  "<f8" => "E*",
  "|u1" => "C*",
  "<u2" => "v*",
  "<i4" => "l<*"
}.freeze

Class Method Summary collapse

Class Method Details

.dump(path, shape, values, descriptor: "<f4") ⇒ Object

Raises:

  • (ArgumentError)


41
42
43
44
45
46
47
48
49
50
51
# File 'lib/jxl/io/npy.rb', line 41

def dump(path, shape, values, descriptor: "<f4")
  format = FORMATS[descriptor] || raise(UnsupportedFeatureError, "NPY dtype #{descriptor}")
  expected = shape.inject(1, :*)
  raise ArgumentError, "shape does not match values" unless expected == values.length

  shape_text = shape.length == 1 ? "#{shape.first}," : shape.join(", ")
  dictionary = "{'descr': '#{descriptor}', 'fortran_order': False, 'shape': (#{shape_text}), }"
  padding = 16 - ((MAGIC.bytesize + 4 + dictionary.bytesize + 1) % 16)
  header = "#{dictionary}#{' ' * padding}\n"
  File.binwrite(path, MAGIC + [1, 0, header.bytesize].pack("CCv") + header + values.pack(format))
end

.dump_image(path, image) ⇒ Object



53
54
55
56
57
58
# File 'lib/jxl/io/npy.rb', line 53

def dump_image(path, image)
  frame_planes = image.frames.empty? ? [image.channels] : image.frames.map(&:planes)
  channels = image.num_color_channels + image..extra_channels.length
  values = frame_planes.flat_map { |planes| planes.first(channels).map(&:data).transpose.flatten }
  dump(path, [frame_planes.length, image.height, image.width, channels], values)
end

.load(path) ⇒ Object

Raises:



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/jxl/io/npy.rb', line 17

def load(path)
  data = File.binread(path)
  raise FormatError, "invalid NPY signature" unless data.start_with?(MAGIC)

  major = data.getbyte(6)
  header_offset, header_length = header_location(data, major)
  header = data.byteslice(header_offset, header_length)
  descriptor = header[/['"]descr['"]\s*:\s*['"]([^'"]+)['"]/, 1]
  fortran = header[/['"]fortran_order['"]\s*:\s*(True|False)/, 1]
  raise FormatError, "invalid NPY header" unless descriptor && fortran
  raise UnsupportedFeatureError, "Fortran-order NPY array" if fortran == "True"

  shape = parse_shape(header)
  format = FORMATS[descriptor] || raise(UnsupportedFeatureError, "NPY dtype #{descriptor}")
  values = data.byteslice((header_offset + header_length)..).unpack(format)
  expected = shape.inject(1, :*)
  unless values.length == expected
    raise CorruptError,
          "NPY shape contains #{expected} values, got #{values.length}"
  end

  [shape, values]
end