画素値の配列化
軽く検索した範囲だと Magick::Image#pixel_color でいちいち画素毎にデータを取得してループさせるひとが多いのだけど、それはいくら何でも手間だろう…しかも返されるのはMagick::Pixel クラスなので、RGB 値を取得するのにいちいちメソッドを使わないといけない。
調べてみると Magick::Image#export_pixels という複数画素取得&画素値の成分を配列にして出力してくれる、ずっとマシなメソッドがあることに気付きます。
ただし。このメソッドの出力は RGB しかも16 bit なので、そのまま出すとこうなります。
> img_z.export_pixels
=> [0, 65535, 0, 0, 65535, 0, 0, 65535, 0, 0, 65535, 0, 0, 65535, 0, 0, 65535, 0, 0, 65535, 0, 34695, 42662,
11822, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
65535, 65535, 51143, 51143, 65535, 5654, 5654, 65535, 46260, 46260, 65535, 65535, 65535, 65535, 65535,
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 50372, 50372, 65535, 5911, 5911, 65535, 47031,
47031, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 50115,
50115, 65535, 5911, 5911, 65535, 47288, 47288, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
65535, 65535, 65535, 65535, 65535, 49601, 49601, 65535, 5397, 5397, 65535, 48059, 48059, 65535, 65535,
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 48830, 48830, 65535, 5140,
5140, 65535, 49087, 49087, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
65535, 65535, 48059, 48059, 65535, 5654, 5654, 65535, 49858, 49858, 65535, 65535, 65535, 65535, 65535,
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 23130, 4112, 46517, 0, 0, 65535, 0,
0, 65535, 0, 0, 65535, 0, 0, 65535, 0, 0, 65535, 0, 0, 65535, 11565, 11565, 65535]
…少なくともRGBの3つを区切って、さらに8 bit にしないと分からないですよね。
> img_z.export_pixels.map { |pix| pix/257 }.each_slice(3).to_a
=> [[0, 255, 0],
[0, 255, 0],
[0, 255, 0],
[0, 255, 0],
[0, 255, 0],
[0, 255, 0],
[0, 255, 0],
[135, 166, 46],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 199, 199],
[255, 22, 22],
[255, 180, 180],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 196, 196],
[255, 23, 23],
[255, 183, 183],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 195, 195],
[255, 23, 23],
[255, 184, 184],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 193, 193],
[255, 21, 21],
[255, 187, 187],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 190, 190],
[255, 20, 20],
[255, 191, 191],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 187, 187],
[255, 22, 22],
[255, 194, 194],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[255, 255, 255],
[90, 16, 181],
[0, 0, 255],
[0, 0, 255],
[0, 0, 255],
[0, 0, 255],
[0, 0, 255],
[0, 0, 255],
[45, 45, 255]]
もう一段階、X軸の繰り返しを区切れば理解しやすいですよね。
> img_z.export_pixels.map { |pix| pix/257 }.each_slice(3).each_slice(img_z.columns).to_a
=> [
[[0, 255, 0], [0, 255, 0], [0, 255, 0], [0, 255, 0], [0, 255, 0], [0, 255, 0], [0, 255, 0], [135, 166, 46]],
[[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 199, 199], [255, 22, 22], [255, 180, 180]],
[[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 196, 196], [255, 23, 23], [255, 183, 183], [255, 255, 255]],
[[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 195, 195], [255, 23, 23], [255, 184, 184], [255, 255, 255], [255, 255, 255]],
[[255, 255, 255], [255, 255, 255], [255, 193, 193], [255, 21, 21], [255, 187, 187], [255, 255, 255], [255, 255, 255], [255, 255, 255]],
[[255, 255, 255], [255, 190, 190], [255, 20, 20], [255, 191, 191], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255]],
[[255, 187, 187], [255, 22, 22], [255, 194, 194], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255]],
[[90, 16, 181], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [45, 45, 255]]
]
画素情報を取得するメソッドとして最初に挙げた Magick::Image#pixel_color メソッドは出力が Magick::Pixel クラスなのですが、複数画素を取得出来る Magick::Image#get_pixels メソッドもあります。あとで扱うときに Magick::Pixel クラスが良いのであればこれでも良いでしょう。
グレースケールにする
RMagick 2.12.0: Common Tasks によると Magick::Image#quantize メソッドで2番目の引数(colorspace)に Magick::GRAYColorspace
を渡せば良いようです。
> img_z_gray = img_z.quantize(256, Magick::GRAYColorspace)
なお1つ目の引数は量子化数(幾つ区切りにするか)なので、8bit = 256にしました。
出力するとこうなります(32倍に拡大)↓
ただし、 Magick::Image#export_pixels メソッドで画素値を配列化すると、デフォルトの RGB 出力でやはり 16bit のままなので
> img_z_gray.export_pixels
=> [46868,
46868,
46868,
46868,
46868,
46868,
…
となってしまいます。読めません。なのでやはり 8bit にした上で3つずつに区切りましょう。
> img_z_gray.export_pixels.map { |pix| pix/257 }.each_slice(3).each_slice(img_z_gray.columns).to_a
=> [
[[182, 182, 182], [182, 182, 182], [182, 182, 182], [182, 182, 182], [182, 182, 182], [182, 182, 182], [182, 182, 182], [150, 150, 150]],
[[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [210, 210, 210], [71, 71, 71], [195, 195, 195]],
[[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [208, 208, 208], [71, 71, 71], [198, 198, 198], [255, 255, 255]],
[[255, 255, 255], [255, 255, 255], [255, 255, 255], [207, 207, 207], [71, 71, 71], [199, 199, 199], [255, 255, 255], [255, 255, 255]],
[[255, 255, 255], [255, 255, 255], [206, 206, 206], [70, 70, 70], [201, 201, 201], [255, 255, 255], [255, 255, 255], [255, 255, 255]],
[[255, 255, 255], [203, 203, 203], [69, 69, 69], [204, 204, 204], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255]],
[[201, 201, 201], [71, 71, 71], [206, 206, 206], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255]],
[[43, 43, 43], [18, 18, 18], [18, 18, 18], [18, 18, 18], [18, 18, 18], [18, 18, 18], [18, 18, 18], [60, 60, 60]]
]
でも。RGB がいちいち同じ値で3つ並んでるのはどうかと思いますね。Magick::Image#export_pixels メソッドには map
という出力向けパラメータがありますので、出力するのをRGBじゃなくグレースケール成分だけにしてみましょう。
> img_z_gray.export_pixels(0, 0, img_z_gray.columns, img_z_gray.rows, 'i').map { |pix| pix/257 }.each_slice(img_z_gray.columns).to_a
=> [
[182, 182, 182, 182, 182, 182, 182, 150],
[255, 255, 255, 255, 255, 210, 71, 195],
[255, 255, 255, 255, 208, 71, 198, 255],
[255, 255, 255, 207, 71, 199, 255, 255],
[255, 255, 206, 70, 201, 255, 255, 255],
[255, 203, 69, 204, 255, 255, 255, 255],
[201, 71, 206, 255, 255, 255, 255, 255],
[43, 18, 18, 18, 18, 18, 18, 60]
]
グレースケールの画像が不要なのであれば、上記の手順で元データのグレースケール成分を直接取得することが出来ます。
> img_z.export_pixels(0, 0, img_z.columns, img_z.rows, 'i').map { |pix| pix/257 }.each_slice(img_z.columns).to_a
=> [
[182, 182, 182, 182, 182, 182, 182, 150],
[255, 255, 255, 255, 255, 210, 71, 195],
[255, 255, 255, 255, 208, 72, 198, 255],
[255, 255, 255, 207, 72, 199, 255, 255],
[255, 255, 206, 70, 201, 255, 255, 255],
[255, 203, 69, 204, 255, 255, 255, 255],
[201, 71, 206, 255, 255, 255, 255, 255],
[43, 18, 18, 18, 18, 18, 18, 60]
]
なお map
パラメータに指定できるのはこちらに記載がありました。引数には文字列で与えてください。
-map components
pixel map.
Here are the valid components of a map:
- r
- red pixel component
- g
- green pixel component
- b
- blue pixel component
- a
- alpha pixel component (0 is transparent)
- o
- opacity pixel component (0 is opaque)
- i
- grayscale intensity pixel component
- c
- cyan pixel component
- m
- magenta pixel component
- y
- yellow pixel component
- k
- black pixel component
- p
- pad component (always 0)
You can specify as many of these components as needed in any order (e.g. bgr). The components can repeat as well (e.g. rgbr).