画像処理１︓画像の記録形式
画像がコンピュータ上でどのように記録されているかを学んだ後、簡単な画像処理をやってみましょう。
なお、本資料で使⽤している説明画像の⼀部はウィキペディアから引⽤しています。ここに感謝の意を表します。

1. 画像の記録形式
1.1 グレースケール画像 (Gray-scale image)
グレースケール画像（濃淡画像とも呼ばれる）は、⾚や⻘のような⾊の情報を持たず、⽩・⿊・灰⾊のみからなる画像形式です。
濃淡値は真っ⽩から真っ⿊まで多段階で表現されます。
濃淡レベルを何階調にするかはその画像の⽤途によって変わります。最もよく使われるのは1画素あたり8-bitで濃淡値を表すものです。
この値をビット深度と呼びます（⾳の情報処理でも「ビット深度」が出てきましたね。⾳のほうは、1サンプルを何ビットで表すかを意味しました）。
通常のソフトウェアでサポートされているビット深度は１画素あたり8-bit, 16-bit, 32-bitです。

28 = 256

ビット深度が8-bitのとき、ですので、濃淡レベルは256階調で表現されることになります。
が真っ⿊、最⼤値（ビット深度が8-bitの場合、ビット深度が16-bitの場合は）が真っ⽩に対応します。

0255

東京大学 数理・情報教育研究センター 山肩洋子 2020 CC BY-NC-SA65535

In [19]:% matplotlib inline
import matplotlib.pyplot as plt
import cv2
img_gray = cv2.imread('img/grayscale.bmp', cv2.IMREAD_ANYDEPTH)
plt.imshow(img_gray, 'gray')print('ビット深度：', img_gray.dtype)
print('画像サイズ（画素数）:', img_gray.shape)
print('画像データを数値として書き出し:\n', img_gray)

ビット深度： uint8
画像サイズ（画素数）: (16, 16)
画像データを数値として書き出し:[[255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255][255 255 255 255 255 241 208 198 187 177 222 255 255 255 255 255]
[255 255 255 253 219 182 161 141 120 99 78 118 241 255 255 255]
[255 255 254 230 203 182 161 141 120 99 78 57 77 241 255 255]
[255 255 247 224 203 182 161 141 120 99 78 57 37 98 255 255]
[255 255 244 224 203 182 161 141 120 99 78 57 37 27 212 255]
[255 255 244 224 203 182 161 141 120 99 78 57 37 27 141 255]
[255 255 244 224 203 182 161 141 120 99 78 57 37 27 112 255]
[255 255 244 224 203 182 161 141 120 99 78 57 37 27 141 255]
[255 255 244 224 203 182 161 141 120 99 78 57 37 27 141 255]
[255 255 244 224 203 182 161 141 120 99 78 57 37 27 212 255]
[255 255 247 224 203 182 161 141 120 99 78 57 37 98 255 255]
[255 255 254 230 203 182 161 141 120 99 78 57 77 241 255 255]
[255 255 255 253 219 182 161 141 120 99 78 118 241 255 255 255]
[255 255 255 255 255 241 208 198 187 177 222 255 255 255 255 255]
[255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255]]

1.2 ⼆値画像（Bianry image)
⽂字のみが印刷されている紙⾯をスキャンして画像にする場合、⽂字が⿊、背景が⽩と、⽩か⿊の2⾊しか必要ありません。
このような⽩⿊画像（2階調）をグレースケール（たとえば256階調）として保存すると無駄にファイルサイズが⼤きくなってしまうので、 0か1のみからなる画像形式で保存します。
これを⼆値画像（Binary image)と呼びます。
⼆値画像ではビット深度は1-bitですが、opencvでは読み込んだ時点で8-bitに変換します。
東京大学 数理・情報教育研究センター 山肩洋子 2020 CC BY-NC-SA

In [21]:% matplotlib inline
import matplotlib.pyplot as plt
import cv2
img_binary = cv2.imread('img/binary.bmp', cv2.IMREAD_UNCHANGED)
plt.imshow(img_binary, 'gray')print('ビット深度：', img_binary.dtype)
print('画像サイズ（画素数）:', img_binary.shape)
print('画像データを数値として書き出し:\n', img_binary)
ビット深度： uint8
画像サイズ（画素数）: (16, 16)
画像データを数値として書き出し:[[255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255][255 255 255 255 255 255
0 255 255 255 255 255 255 255 255 255]
[255 255 255 255 255 255
0 255 255 255 255 255 255 255 255 255]
[255 255 00000000 0 0 0 255 255 255]
[255 255 255 255 255
0 255 255 255 255 255 255 255 255 255 255]
[255 255 255 255 255
0 255 255 255
0 255 255 255 255 255 255]
[255 255 255 255 255
00000 0 255 255 255 255 255]
[255 255 255 255
00 255 255 255
0 255 0 255 255 255 255]
[255 255 255
0 255
0 255 255
0 255 255 255 0 255 255 255]
[255 255 0 255 255
0 255 255
0 255 255 255 255 0 255 255]
[2550 255 255 2550 255
0 255 255 255 255 255 0 255 255]
[2550 255 255 255 255
0 255 255 255 255 255 255 0 255 255]
[2550 255 255
000 255 255 255 255 255 0 255 255 255]
[255 255 00 255 255 255 255 255 255 255 0 255 255 255 255]
[255 255 255 255 255 255 255 255
00 0 255 255 255 255 255]
[255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255]]

1.3 カラー画像 (Color image)
⼀般的なカラー画像の表現では、⾚（R)、緑（G）、⻘（B)の3枚の画像の重ね合わせで⾊を表現しています。
この3枚の画像をそれぞれチャネルと呼びます。グレースケール画像や⼆値画像は1チャネル、RGBのカラー画像は3チャネルです。
とにかく読み込んでデータの中⾝を⾒てみましょう。
この画像はサイズが縦横16画素で、1画素あたり8-bit×RGBの3枚の画像からなるカラー画像です。
真っ⿊のときは(0, 0, 0)、真っ⽩のときは(255, 255, 255)ですね。
東京大学 数理・情報教育研究センター 山肩洋子 2020 CC BY-NC-SA

また0チャネル（R)だけが255で残りが0のとき⾚、1チャネル（G)だけが255で残りが0のとき緑、 2チャネル（B)だけが255で残りが0のとき⻘です。
In [24]:% matplotlib inline
import matplotlib.pyplot as plt
import cv2
img_rgb = cv2.imread('img/color.bmp') # 画像の読み込み# opencvでは3つのチャネルは実はRGBの順番ではなくBGRの順番でで読み込んでいます# 一方、plt.imshow()はRGBの順番に並んでいるとみなして画像を表示します# ですから以下の関数でRGBの並びに変換する必要があります
img_rgb= cv2.cvtColor(img_rgb, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)# 1画素あたりのビッド深度 (8-bitなら256快調)# 画像のサイズ# 縦方向に一行抜き出して、3つのprint('ビット深度：', img_rgb.dtype)
print('画像サイズ（画素数）:', img_rgb.shape)
print('画像データを数値として書き出し(縦1行分）:\n', img_rgb[:,1,:])

ビット深度： uint8
画像サイズ（画素数）: (16, 16, 3)
画像データを数値として書き出し(縦1行分）:[[ 00 0][2550 0][ 0 255 0][ 00 255][ 0 255 255]
[2550 255]
[255 255 0][255 255 255]
[1250 0][ 0 125 0][ 00 125][ 0 125 125]
[1250 125]
[125 125 0][125 125 125][ 00 0]]上の配列の書き出しでは、画像の縦1列分のRGBの値を出⼒している点に注意してください（全部書き出す
東京大学 数理・情報教育研究センター 山肩洋子 2020 CC BY-NC-SA

と、16x16x3のデータになります）。

2.様々な⾊空間
⾚・シアン眼鏡で⾒てみましょう。

2.1 RGB⾊空間（光の三原⾊・加法混合）
1.3で⽣成した画像からもわかるように、コンピュータ上ではカラー画像はR(⾚）、G(緑）、B（⻘）の3種類の⾊画像を持ち、その⾊の交わり具合によってさまざまな⾊が表現されています。
⼈間の網膜には特定の波⻑に反応する錐体細胞と呼ばれる細胞が散らばっています。
ほとんどのヒトはS・M・Lの3つの錐体細胞を持つ3⾊型⾊覚です。
このとき、Sは⻘⾊の波⻑、Mは緑⾊の波⻑、Lは⾚⾊の波⻑に強く反応します。
RGB⾊空間は、この⼈間の⽬の仕組みを再現しています。
RGB⾊空間では、⾊は⾜せば⾜すほど⽩に近づきます。
太陽の光は⾊を持たないように⾒えますが、これは様々な波⻑の光が混ざっているためです。
このように、様々な波⻑（＝⾊）の光を加えて⾊を作る⽅法は加法混合と呼ばれます。
また、RGBは光の三原⾊と呼ばれます。
下の図はRGBの光を混ぜるとどのような⾊になるかを表しています。

Wikipedia:File:AdditiveColorMixiing.svg (https://commons.wikimedia.org/wiki/File:AdditiveColorMixiing.svg) より引⽤

2.2 CMYK⾊空間（⾊の三原⾊・減法混合）
⼀⽅、絵の具のようにいろいろな⾊を混ぜれば混ぜるほど⿊に近づく⾊の作り⽅は減法混合と呼ばれます。
⾚⾊の絵の具は、無⾊の光（＝様々な波⻑が混ざった光）のうち、⾚い波⻑以外の光を吸収して熱に変え、⾚い波⻑の光のみを反射する塗料です。だから⾚に⾒えるのですね。
いろんないろの絵の具を混ぜるということは、つまりいろんな波⻑の光を吸収する素材を混ぜるということで、混ぜられた絵の具は様々な波⻑を吸収光をどんどん吸収することになり、ついには⿊に近づくわけです。
光の三原⾊ではRGBでしたが、加法混合ではシアン（Cyan)・マゼンタ（Magenta)・イエロー（Yellow)の3つの⾊が原⾊で、これを⾊の三原⾊と呼びます。 下の図は⾊の三原⾊を混ぜるとどのような⾊になるかを表しています。

東京大学 数理・情報教育研究センター 山肩洋子 2020 CC BY-NC-SAWikipedia:File:SubtractiveColorMixing.png(https://commons.wikimedia.org/wiki/File:SubtractiveColorMixing.png) より引⽤
光の三原⾊と⾊の三原⾊を⽐べてみると、Rに対してCが互いに補⾊（その⾊が混ざっていないときの⾊）になっていることに注⽬してください。
同じくGに対してはMが、Bに対してはYが補⾊です。
RGB⾊空間で表現された画像を、その補⾊画像に変換してみましょう。
この画像、⾒覚えありませんか︖（若い皆さんはないかもしれませんね…。） これはフィルムカメラのいわゆる「ネガフィルム」の⾊です（「ネガ」はnegativeの略です）。

東京大学 数理・情報教育研究センター 山肩洋子 2020 CC BY-NC-SA

In [4]:% matplotlib inline
import matplotlib.pyplot as plt
import cv2
import numpy as np
img_rgb = cv2.imread('img/UTokyoYasudaMini.jpg') # 画像の読み込み
img_rgb= cv2.cvtColor(img_rgb, cv2.COLOR_BGR2RGB) # BGRからRGBに変換
plt.figure(figsize= (10,10))plt.subplot(1,2,1)
plt.imshow(img_rgb) # rgb画像の表示
plt.title('original image')=*

img_cmynp.uint8(255np.ones(img_rgb.shape)
plt.subplot(1,2,2)
plt.imshow(img_cmy)
plt.title('negative image')- img_rgb) # 255からカラー画像を引くとネガ画像になり

Out[4]:
Text(0.5, 1.0, 'negative image')

2.3 HSV⾊空間
RGB⾊空間は⼈間の⽬の仕組みを模倣していますが、⾊や明暗や鮮やかさなどの連続的な変化を表すには不向きです。
例えば同じ⾊の物体でも、陰になればそれだけ暗い⾊に移りますが、同じ⾊として扱いたいですよね。
このような場合は⾊相 (hue)、彩度 (saturation)、明度 (value) の3つの⾊からなるHSV⾊空間が使えます。

東京大学 数理・情報教育研究センター 山肩洋子 2020 CC BY-NC-SAWikipedia:File:HSV cone.jpg (https://commons.wikimedia.org/wiki/File:HSV_cone.jpg) より引⽤
彩度（S)と明度（V）はRGBと同様、0から255（各チャネル8-bitの場合）ですが、 ⾊相は下の図のようにぐるっと⼀周、周ると元に戻る⾊空間ですので、360度の⾓度で表します。
ただし、原画像と同じデータサイズだと、0〜255までしか表現できないので、1度を1階調に割り当てると、256以上の⾓度は表現できません。
そこで、opencvでは cv2.cvtColor([元の画像], cv2.COLOR_RGB2HSV) という関数でHSV⾊空間に変換すると、 Hの画像は1階調あたり2度として、180度まで（0〜179)までの値に変換されます。
ただし、 cv2.cvtColor([元の画像], cv2.COLOR_RGB2HSV_FULL) のように、フラグにFULLをつけると、0〜359までの値に変換します。
安⽥講堂の写真について、⾊相H, 彩度S, 明度V 情報を三枚の画像に分離して、それぞれを表⽰してみましょう。

東京大学 数理・情報教育研究センター 山肩洋子 2020 CC BY-NC-SA

In [27]:% matplotlib inline
import matplotlib.pyplot as plt
import cv2
import numpy as np
img_org = cv2.imread('img/UTokyoYasudaMini.jpg')
img_rgb= cv2.cvtColor(img_org, cv2.COLOR_BGR2RGB) # まずはBGRをRGBに変換
plt.figure(figsize= (10,10))plt.subplot(2,2,1)
plt.imshow(img_rgb)
plt.title('original image')= cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV) # RGBをHSVに変換# img_hsvと同じ大きさの白い画像img_hを用意し、Hのみimg_hsvの値を代入# SとVは255とします（鮮やかに明るく）
img_h = 255* np.ones_like(img_hsv)
img_h[:,:,0] = img_hsv[:,:,0]
img_hsv

plt.subplot(2,2,2)
plt.imshow(cv2.cvtColor(img_h, cv2.COLOR_HSV2RGB))
plt.title('H image')# img_hsvと同じ大きさの白い画像img_sを用意し、Sのみimg_hsvの値を代入# Hは0（どの値でもいいです。0だと赤付近なので赤っぽい画像になります）# Vは255（つまりなるべく明るい画像にします）
img_s = 0* np.ones_like(img_hsv)
img_s[:,:,1] = img_hsv[:,:,1]
img_s[:,:,2] = 255* np.ones_like(img_hsv[:,:,1])
plt.subplot(2,2,3)
plt.imshow(cv2.cvtColor(img_s, cv2.COLOR_HSV2RGB))
plt.title('S image')# img_hsvと同じ大きさの白い画像img_vを用意し、Vのみimg_hsvの値を代入# HとSは0（実はどの値でも同じです。HSVの三角錐で色の関係を見てみてください。）
img_v = np.zeros_like(img_hsv)
img_v[:,:,2] = img_hsv[:,:,2]
plt.subplot(2,2,4)
plt.imshow(cv2.cvtColor(img_v, cv2.COLOR_HSV2RGB))
plt.title('V image')Out[27]:
Text(0.5, 1.0, 'V image')

東京大学 数理・情報教育研究センター 山肩洋子 2020 CC BY-NC-SA⾊相（H)空間をあらわす H imgage を⾒てください。
安⽥講堂部分は⾚レンガなので、⾊相（H)空間で⾒ると⾚になっていることが分かりますね。
また、空は⻘空なので⻘、⽊は緑です。
彩度（S)空間 S image では、値が⼤きいほど「鮮やか」であることを意味します。
S image 画像が⾚っぽいのは、HSVのうちHを0つまり⾚にしているためです。
ここでは⾊ではなく濃淡に注⽬してください（⾚が濃いほSの値が⼤きく、⽩っぽいほどSの値が⼩さいと考えてください）。
空のほうが安⽥講堂よりも⾚が濃いので鮮やかなようです。
⼀⽅、安⽥講堂はRGB画像を⾒ると⾊は濃く⾒えますが、鮮やかさは低いので彩度画像では⽩っぽく（つまり値が⼩さく）⾒えます。
また、⼿前の⽊に注⽬すると、RGBカラー画像では光の当たり具合によって明暗がくっきり分かれていますが、 彩度の画像ではあまり⾊の変化が⾒られません。
光の当たり具合や陰による⾊の変化は主に明度を表すVの空間に現れ、 Sの空間ではあまり影響を及ぼしません。
明度（V）空間は V image は、まさに画像の明るさを表していますね。
カラー画像をグレースケール画像に変換するときは、この明度空間の画像を使うのが⼀般的です。
In [ ]:

東京大学 数理・情報教育研究センター 山肩洋子 2020 CC BY-NC-SA