第2章 画像処理の基本操作
この章では主に次のことを紹介します。
- 画像の基本的な表現
- ピクセルへのアクセスと操作
- 関心領域の処理
- チャネル処理
知識ポイントを待ちます。OpenCV for Python を使用するには、Numpy ライブラリ、特に Python が画像を処理するための基礎となる Numpy.array ライブラリに習熟している必要があることに注意してください。
まず、画像の基本的な表現:
画像の基本的な表現方法は次のとおりです。
- バイナリ画像
- グレースケール画像
- カラー画像
1. バイナリ画像:
バイナリ イメージは、黒と白の 2 色のみを含むイメージです。
コンピューターでは、画像は行列を通じて表現および処理されます。たとえば、下図の A の画像をコンピュータが処理するとき、まず画像を小さな正方形、つまりピクセルに分割します。各ピクセルは独立した処理単位です。次に、後続の保存および処理操作のために、白のピクセルを 1 に、黒のピクセルを 0 に設定します。
2. グレースケール画像:
2値画像は表現が簡単で便利ですが、色が白と黒の2色しかないため、表現された画像が繊細ではありません。より詳細を表現する必要がある場合は、より多くの色を使用する必要があります。
たとえば、以下のグレースケール画像は次のとおりです。
通常、コンピュータはグレースケールを [0, 255] の間隔で表される 256 のグレースケール レベルに処理します。このうち、255 は純白、0 は純黒を表し、残りの値は純白から純黒までのさまざまなレベルのグレーを表します。
256 グレー レベルを表すために使用される値 0 ~ 255 は、正確に 1 バイト (2 進数 8 桁) で表すことができます。次の図は、いくつかのバイナリ値に対応する 10 進値を示しています。
場合によっては、バイナリ イメージを表すために 8 ビット バイナリも使用されます。この場合、255 は白、0 は黒を意味し、画像には 255 と 0 のみが存在します。
3. カラー画像:
バイナリ イメージやグレースケール イメージと比較して、カラー イメージはより一般的なタイプのイメージです。神経生理学的実験により、網膜には3つの異なる色受容体が存在し、赤、緑、青、つまり三原色の3つの異なる色を知覚できることがわかっています。自然界でよく見られるさまざまな光の色合いは、三原色を一定の割合で混合することによって形成されます。さらに、光学的な観点から、色を主な波長、純度、明度などに分析できます。心理的および視覚的な観点から、色は色相、彩度、明るさなどに解析できます。通常、上記の方法でさまざまな方法で色を表現するモードを、色空間、色空間、またはカラー モードと呼びます。
色空間ごとに表現方法は異なりますが、対応する式に従ってさまざまな色空間を変換できます。
たとえば、RGB 色空間には、R チャネル、G チャネル、B チャネルの 3 つのチャネルがあります。各カラー チャネル値の方向は [0, 255] の間です。したがって、RGB 色空間でカラー画像を表現するには、通常、3 次元配列が使用されます。OpenCV では、チャネルの順序は BGR であることに注意してください。
2. ピクセル処理:
ピクセルは画像構成の基本単位であり、ピクセル処理は画像処理の基本操作であり、画像内の要素はミラーリングされ、アクセスされ、位置インデックスの形式で処理されます。
1. バイナリ画像とグレースケール画像:
OpenCV では、最小のデータ型は符号なし 8 ビット数値です。したがって、OpenCV には実際にはバイナリ イメージ データ型は存在せず、バイナリ イメージは多くの場合、黒を表す 0、白を表す 255 を使用する、処理を通じて得られる特殊なグレースケール イメージです。
これまでの分析により、コンピューター内では画像はピクセルで構成されるマトリックスであることがわかります。OpenCV for Python では、画像は Numpy の配列です。OpenCV グレースケール イメージは、式を使用してピクセル値にアクセスできる 2 次元配列です。たとえば、image[0, 0] を使用すると、イメージ image の行 0、列 0 にある未知のピクセルにアクセスできます。
例:グレースケール画像を読み取り、そのピクセルにアクセスして変更します。
import cv2
img = cv2.imread('lena.bmp', 0)
cv2.imshow('before', img)
for i in range(10, 100):
for j in range(80, 100):
img[i, j] = 255
cv2.imshow('after', img)
cv2.waitKey()
cv2.destroyAllWindows()
2. カラー画像:
RGB モードのカラー画像が OpenCV に読み込まれて処理されると、RGB 画像の B チャネル、G チャネル、R チャネルのピクセルが行方向に順番に読み取られ、行方向にピクセルが ndarray 列に格納されます。ユニットは真ん中。たとえば、サイズ R 行 x C 列の生の RGB イメージがあり、これは OpenCV アンダーウェア BGR モードで 3D 配列の形式で保存されます。
配列内の値には式を使用してアクセスできます。たとえば、image[0, 0, 0] を使用すると、イメージ image の行 0、列 0 にあるピクセルの B チャネルにアクセスできます。
- 最初のインデックスは行 0 を表します
- 2 番目のインデックスは列 0 を表します
- 3 番目のインデックスは 0 番目のカラー チャネルを表します
例:カラー イメージを読み取り、そのピクセルにアクセスして変更します。
import cv2
img = cv2.imread('lena512color.tiff')
cv2.imshow('before', img)
# 白色
for i in range(0, 50):
for j in range(0, 100):
for k in range(0, 3):
img[i, j, k] = 255
# 灰色
for i in range(50, 100):
for j in range(0, 100):
img[i, j] = [128, 128, 128]
# 黑色
for i in range(100, 150):
for j in range(0, 100):
img[i, j] = 0
cv2.imshow('after', img)
cv2.waitKey()
cv2.destroyAllWindows()
3. numpy.array を使用してピクセルにアクセスします。
numpy.array は、ピクセル値にアクセスして変更するための item() 行と itemset() 行を提供し、これら 2 つの関数は処理効率を大幅に向上させるために最適化されています。ピクセルの値にアクセスして変更する場合、numpy.array によって提供される関数を使用する方が、インデックスを直接使用するよりもはるかに高速であり、これら 2 つの関数の可読性も優れています。
-
バイナリ イメージとグレースケール イメージ:
バイナリ イメージは、特殊なグレースケール イメージとして理解できます。- item(): ピクセルにアクセス、構文: item(行、列)
- item(): ピクセル値を変更、構文: itemset(インデックス値、新しい値)
例:グレースケール画像を読み取り、ピクセル値にアクセスして変更します。
import cv2 img = cv2.imread('../lena.bmp', 0) print('读取像素点img.item(3, 2)=', img.item(3, 2)) img.itemset((3, 2), 255) print('修改后像素点img.item(3, 2)=', img.item(3, 2)) cv2.imshow('before', img) for i in range(10, 200): for j in range(80, 300): img.itemset((i, j), 255) cv2.imshow('after', img) cv2.waitKey() cv2.destroyAllWindows()
-
カラー画像:
-
item() 関数と itemset() 関数を使用して、カラー画像のピクセル値にアクセスして変更することもできます。そのプロセスはグレースケール画像の操作と似ています。違いは、チャネル情報を補足する必要があることです。
-
item(): EGB モード画像のピクセル値にアクセスします。構文: item(行、列、チャネル)
-
itemset(): RGB モード画像のピクセル値を変更します。構文: itemset(トリプルインデックス値, 新しい値)
RGB 画像にアクセスするには、img.item(a, b, c) のように、行、列、チャネルを同時に指定する必要があることに注意してください。行と列のみを指定することはできません。
例:カラー画像を読み取り、その画像に対してピクセル アクセスと変更を実行します。
import cv2 img = cv2.imread('lena512color.tiff') cv2.imshow('before', img) print('访问img.item(0, 0, 0) = ', img.item(0, 0, 0)) # 白色 for i in range(0, 50): for j in range(0, 100): for k in range(0, 3): img.itemset((i, j, k), 255) cv2.imshow('after', img) print('修改后端img.item(0, 0, 0) = ', img.item(0, 0, 0)) cv2.waitKey() cv2.destroyAllWindows()
-
ここに画像の説明を挿入
アクセス img.item(0, 0, 0) = 125
バックエンドの変更 img.item(0, 0, 0) = 255
3. 関心領域 (ROI):
画像処理中に、関心領域 (ROI) と呼ばれる画像の特定の領域に注目することがあります。関心領域 ROI を設定した後、その領域に対して全体的な操作を実行できます。たとえば、対象領域 A を変数 B に代入した後、領域 A を領域 C にコピーするという目的を達成するために、変数 B を別の領域 C に代入できます。
例 1:現在のイメージ名が img であると仮定します。図内の数字はそれぞれ行番号と列番号を表します。次に、画像内の黒い ROI は img[200:400, 200:400] と表すことができます。
次のステートメントにより、図の黒い ROI を領域の右側にコピーできます。
a = 画像[200:400, 200:400]
img[200:400, 600:800] = a
例2:画像lenaの顔情報を取得して表示します。
import cv2
a = cv2.imread('lena512color.tiff', cv2.IMREAD_UNCHANGED)
face = a[220: 400, 250: 350]
cv2.imshow('original', a)
cv2.imshow('face', face)
cv2.waitKey()
cv2.destroyAllWindows()
例 3:レナ画像の顔をエンコードする
import cv2
import numpy as np
a = cv2.imread('../lena512color.tiff', cv2.IMREAD_UNCHANGED)
cv2.imshow('original', a)
face = np.random.randint(0, 256, (180, 100, 3))
a[220: 400, 250: 350] = face
cv2.imshow('result', a)
cv2.waitKey()
cv2.destroyAllWindows()
4. チャンネル操作:
RGB 画像では、画像は R チャネル、G チャネル、B チャネルの 3 つのチャネルで構成されます。なお、OpenCVではチャンネルはBチャンネル-Gチャンネル-Rチャンネルの順に格納されます。画像処理中に、必要に応じて画像に対してチャネル分割とチャネル結合を実行できます。
1. チャンネル分割:
RGB 画像の場合、R チャネル、G チャネル、B チャネルを個別に分割できます。OpenCV では、チャネルはインデックスまたは関数ごとに分割できます。
-
インデックスごとにチャネルを分割します。
インデックス作成により、各チャネルを画像から直接抽出できます。例: OpneCV の BGR イメージ img の場合
- b = img[: , : , 0]
- g = img[: , : , 1]
- r = img[: , : , 2]
例:カラー画像に対する画像チャネル分割とチャネル値変換の効果を示すプログラムを作成します。
import cv2 lena = cv2.imread('lena512color.tiff') cv2.imshow('lena1', lena) b = lena[:, :, 0] g = lena[:, :, 1] r = lena[:, :, 2] cv2.imshow('b', b) cv2.imshow('g', g) cv2.imshow('r', r) lena[:, :, 0] = 0 cv2.imshow('lenab0', lena) lena[:, :, 1] = 0 cv2.imshow('lenabogo', lena) cv2.waitKey() cv2.destroyAllWindows()
-
機能ごとにチャンネルを分割します。
関数 cv2.split() は、画像のチャネルを分割できます。たとえば、次のステートメントを使用してカラー BGR イメージ img を分割し、イメージの B、G、および R チャネルを取得できます。
-
b、g、r = cv2.split(img)
b = cv2.split(img)[0]
g = cv2.split(img)[1]
r = cv2.split(img)[2]
例:関数 cv2.split() を使用した画像チャネルの分割
import cv2 lena = cv2.imread('../lena512color.tiff') b, g, r = cv2.split(lena) cv2.imshow('B', b) cv2.imshow('G', g) cv2.imshow('R', r) cv2.waitKey() cv2.destroyAllWindows()
-
2. チャネルの結合:
チャネルの結合は、チャネル分割の逆のプロセスであり、チャネルを結合することにより、3 つのチャネルのグレースケール画像を 1 つのカラー画像に結合できます。画像チャンネルの結合は関数 cv2.merge() で実現でき、B、G、R の 3 チャンネル画像がある場合は、関数 cv2.merge() を使用して BGR 3 チャンネルのカラー画像に結合します。実装ステートメントは次のとおりです: bgr_img = cv2.merge([b, g, r])
例:関数 cv2.merge() を使用してチャネルをマージします。
import cv2
lena = cv2.imread('../lena512color.tiff')
b, g, r = cv2.split(lena)
bgr = cv2.merge([b, g, r])
rgb = cv2.merge([r, g, b])
cv2.imshow('lena', lena)
cv2.imshow('bar', bgr)
cv2.imshow('rgb', rgb)
cv2.waitKey()
cv2.destroyAllWindows()
5. 画像のプロパティを取得します。
画像処理のプロセスでは、画像のサイズや種類などの画像の属性を取得することが必要になることがよくあります。よく使用されるプロパティをいくつか示します
- 形状: カラー イメージの場合は、行、列、チャネルの数を含む配列が返され、バイナリ イメージまたはグレースケール イメージの場合は、行と列の数のみが返されます。このプロパティの戻り値にチャネル数が含まれるかどうかによって、画像がグレースケール画像であるかカラー画像であるかが決まります。
- size: 画像のピクセル数を返します。その値は「行数 × 列数 × チャネル数」であり、グレースケール画像とバイナリ画像のチャネル数は 1 です。
- dtype: 返された画像のデータ型
例:
import cv2
gray = cv2.imread('../lena.bmp', 0)
color = cv2.imread('../lena512color.tiff')
print('图像gray属性')
print('gray.shape=', gray.shape)
print('gray.size=', gray.size)
print('gray.dtype=', gray.dtype)
print('图像color属性')
print('color.shape=', color.shape)
print('color.size=', color.size)
print('color.dtype=', color.dtype)
-
端子出力:
画像のグレー属性
grey.shape= (512, 512)
grey.size= 262144
grey.dtype= uint8
画像の色属性
color.shape= (512, 512, 3)
color.size= 786432
color.dtype= uint8
この記事の内容は書籍『OpenCV かんたん入門』から要約したものですので、詳しくは本書を参照してください。