5-4 画像の調整

画像修正:あなたのプログラムを画像デザイナーに!

アプリケーションで画像を「見る」だけでなく、デザイナーのように画像を「着飾る」ことができたらいいと思いませんか?そこで NumPyの 出番だ!


NumPy: 画像のためのビルディング・ブロックとツールボックス!

Pythonで数値やデータを扱うための素晴らしいツールだ。 NumPyの 最大の特徴である "多次元配列"(これは数字の巨大なグリッドのようなものです)。

OpenCVには 画像を処理するための関数がたくさんあるが、その裏では NumPyが よく使われている。imread()を使って画像を読み込むと、得られる画像データは実際には NumPyの配列です!つまり、 NumPyの使い方を覚えれば、画像の構成要素を操作し、画像を好きなように修正することができるのです!


使用前のちょっとした準備

あなたのアプリケーションが画像を "編集 "できるようにするためには、 OpenCVと NumPyの 両方を招待する必要がある:

import numpy as np # NumPy モジュールをインポートし、短い名前「np」を付けます

このコードでは、"Hello NumPy, come in!写真の編集を始める準備ができました。


修正:画像に直接 "命令 "する!

画像が NumPyの配列になったので、それを修正するのは、超大型のエクセルの表を修正するようなものだ!座標 "から直接ピクセルを見つけて、その色を変更することができます:

# 画像がカラーの場合(OpenCV のデフォルトは BGR 順:青、緑、赤)
# (50, 50) の位置にある画素(ピクセル)を、明るい青色に変更します!

# [255, 0, 0] は:青が最大 (255)、緑がなし (0)、赤がなし (0) という意味です
img[50, 50] = [255, 0, 0] # 50行目、50列目の画素を青色に書き換えます

また、一度に広い範囲を変更することもできます。例えば、写真の隅を黒く塗りつぶすこともできます:

# 画像の左上、100x100 ピクセルのエリアをすべて黒色 (0, 0, 0) にします
# img[yの開始:yの終了, xの開始:xの終了] の順で指定します
img[0:100, 0:100] = [0, 0, 0]

ここで0 : 100とは、0番目のピクセル(つまり一番端)から99番目のピクセルまで、合計100ピクセルを意味します。


例:画像を反転させてネガのように見せる!

この例では、クールな画像修正効果である色の反転について説明します!昔のネガのように、白は黒に、黒は白に、色はコントラストカラーになります。

import cv2 as cv
import numpy as np
# Set the file path for the original image.
# Don't forget to replace 'Pandora.png' with your own image!
file_path = 'Pandora.png'
# Use OpenCV's imread function to read the image.
# The image data is stored as number bricks in the 'source_picture' variable.
source_picture = cv.imread(file_path)
# Check if the image was successfully loaded. If not, print a warning!
if source_picture is None:
    print(f"Warning: Oops! Could not load the image.")
    print(f"Please check if the file path is correct: {file_path}")
else:
    # We'll create a "copy" of the original image called 'modification_img'.
    # This way, our modifications won't affect the original picture, 
    # and you can see a side-by-side comparison!
    modification_img = source_picture.copy()
    # Now, we'll start messing with every single small brick (pixel) of the image!
    # 'modification_img.shape[0]' is the height of the image (how many rows of pixels).
    for y in range(modification_img.shape[0]):
        # 'modification_img.shape[1]' is the width of the image 
        # (how many columns of pixels).
        for x in range(modification_img.shape[1]):
            # Get the color values for the pixel at the current (y, x) coordinates.
            # 'pixel' will be a list containing three numbers for blue, green, and red.
            pixel = modification_img[y, x]
            # For each color (blue, green, red), we subtract its original value from 255.
            # Why 255? Because the color values range from 0 to 255.
            # This turns it into its "complementary color"!
            pixel[0] = 255 - pixel[0] # Blue (B)
            pixel[1] = 255 - pixel[1] # Green (G)
            pixel[2] = 255 - pixel[2] # Red (R)
    # Start an infinite loop to display the modified image window.
    while True:
        # Use OpenCV's imshow function to display both the original and modified images.
        cv.imshow('Original Picture', source_picture)
        cv.imshow('Modification Picture (Inverted)', modification_img) 
        # Changed the window name to be clearer
        # The cv.waitKey(1) function "pauses" your program 
        # for 1 millisecond to check for a key press.
        key = cv.waitKey(1)
        # Check if the pressed key is the ESC key (the key code for ESC is 27).
        if key & 0xFF == 27:
            break # If you press the ESC key, exit the loop and close the display.
    # Finally, call cv.destroyAllWindows() to close all image windows created 
    # by OpenCV and free up computer resources.
    cv.destroyAllWindows()

プログラムの結果

Graphic confirming that OpenCV is successfully installed on the system

上のコードを実行すると、2つのウィンドウがポップアップします。1つは元の写真で、もう1つは色が反転した写真です!古い写真のネガ効果のように見えませんか?


NumPyを使って 画像のピクセル値を変更する方法を学んだら、もっと面白いバリエーションを試してみましょう:

  1. 局所的な色の変更: 画像の一部分を好みの色に変更する。
  2. 白黒: カラー画像を白黒写真に変えてみる。
  3. 明るさ/コントラストの調整: ピクセルの値を足したり引いたりすることで、写真の明るさを変えることができます。

NumPyには他にも素晴らしい機能がたくさんあり、今後も様々な分野で役に立つことでしょう!


イメージズーム:画像を簡単に大きくしたり小さくしたりできます!

トランスフォーマーのように、ぼやけたり歪んだりすることなく、画像を拡大したり縮小したりしたいと思いませんか?OpenCVの リサイズ 機能はそんなあなたの強い味方です!この関数を使うと,サムネイルを作ったり,局所的な細部を拡大したり,様々な状況で画像のサイズを簡単に調整することができます.

関数のプロトタイプとパラメータ:リサイズの「変形コマンド」は次のようになります!

以下は リサイズ 関数の "プロトタイプ "です:

def resize(src: cv2.typing.MatLike, 
                # 変換したい「元の画像」です。
           dsize: cv2.typing.Size | None, 
                  # 変換後の「ターゲットサイズ」を(幅, 高さ)で指定します。
           dst: cv2.typing.MatLike | None = ..., 
                  # 変換後の画像データが格納されます(通常は戻り値を使うので指定不要です)。
           fx: float = ..., 
                    # 横方向(水平)の「倍率」です(例:0.5 なら半分、2.0 なら2倍)。
           fy: float = ..., 
                  # 縦方向(垂直)の「倍率」です。
           interpolation: int =...) 
                  # 「補間(ほかん)アルゴリズム」です。サイズ変更後の画質を決定します。
-> cv2.typing.MatLike: ... # 変換された画像データを返します。

戻り値:resizeは変形された画像を返します!

resizeが 画像の変形に成功したら、新しい画像データを返します!


例:画像を "一瞬で大きく "する!

では、写真のサイズを2倍にするプログラムを書いてみましょう!

import cv2 as cv # OpenCV モジュールをインポートします

# 読み込みたい元の画像ファイルのパスを指定します。
# 'Pandora.png' は自分の画像ファイル名に合わせて変更してくださいね!
file_path = 'Pandora.png'

# OpenCV の imread 関数を使って画像を読み込みます。
source_picture = cv.imread(file_path)

# 画像が正しく読み込めたか確認します。
if source_picture is None:
    print(f"警告:おっと!画像が読み込めませんでした。パスを確認してください:{file_path}")
else:
    # 画像の「幅」と「高さ」をそれぞれ 2 倍に拡大します!
    # source_picture.shape[1] は元の画像の幅(横)です。
    # source_picture.shape[0] は元の画像の高さ(縦)です。
    
    # 「<< 1」はビット演算で、数値を 2 倍にするのと等しい意味です!
    # つまり、(幅 << 1, 高さ << 1) で、2 倍の新しいサイズを指定しています。
    resized_image = cv.resize(source_picture,
                           (source_picture.shape[1] << 1, source_picture.shape[0] << 1))

    # --- 画像表示ループ ---
    # 無限ループに入り、ウィンドウを閉じない限り表示し続けます。
    while True:
        # 元の画像をウィンドウに表示します。
        cv.imshow('Original Picture', source_picture)

        # 2 倍にサイズ調整された画像を別のウィンドウで表示します。
        cv.imshow('Scaled Picture (2x)', resized_image)

        # キーボード入力を 1 ミリ秒待機します。
        key = cv.waitKey(1)

        # ESC キー(コード 27)が押されたらループを抜けて終了します。
        if key & 0xFF == 27:
            break

    # --- リソースの解放 ---
    # すべてのウィンドウを閉じ、メモリリソースを解放します。
    cv.destroyAllWindows()

プログラムの結果:

上のコードを実行すると、2つのウィンドウがポップアップします:1つは元のサイズの写真、もう1つは2倍に拡大された写真です!拡大した画像がまだ鮮明かどうかを比較することができます。


リサイズ 機能を覚えたら、次のような様々なリサイズができます:

  1. サムネイルを作る: ウェブサイトやアプリケーションの読み込みを速くするために、大きな画像を小さくする。
  2. ズームイン: 写真の細部を見たいですか?拡大するだけです!
  3. サイズの統一: 異なるサイズの画像を同じサイズにすることで、取り扱いが簡単になります。

画質を最適化するために異なる補間方法を選択するなど、リサイズには さらに高度な使い方がたくさんあります。興味のある方は、補間パラメータを変更して違いを試してみてください!

画像のトリミング:お気に入りの画像だけを残す!

写真を撮ったとき、気に入らない部分があったり、特定の部分だけ強調したいことはありませんか?そんな時に便利なのが画像のトリミングです!このアプリでは、ハサミを使って元の写真から好きな部分を「切り取り」、良い部分だけを残すことができます。


画像トリミングの「魔法のハサミ」:NumPyのスライス!

画像は、コンピュータ上に整然と並んだ数字の束(NumPyの配列)に過ぎないと言ったのを覚えているだろうか?写真を切り抜くには、 NumPyの "スライス "関数を使えばよい!

 


変更点:切り抜きたい場所をプログラムに伝える!

画像の切り抜きとは、元の画像から長方形の領域を抽出することです。必要なのは、切り抜き開始位置と終了位置をプログラムに指示するだけです:

# 画像を座標系として考えます。[yの開始:yの終了, xの開始:xの終了] が切り抜く範囲です。
# y は「行」(高さ)、x は「列」(幅)を表します。

# 例えば、画像上の「50行目から199行目まで」、
# そして「100列目から299列目まで」のエリアを切り抜きます。
# (注意:NumPy のスライスは「終了値を含まない」ルールです!)
roi = img[50:200, 100:300]

注意:NumPyの配列の切り出し順序は、[ y軸の開始:y軸の終了、x軸の開始:x軸の終了 ]であり、これは[ 高さの開始:高さの終了、幅の開始:幅の終了 ]に相当する。


注:トリミングの前に知っておくべき小さな秘密!

画像をトリミングする際に知っておかなければならない重要な概念や「ちょっとした裏技」がいくつかあります:

  1. 画像データは "共有 "か "コピー "か?NumPyのスライシングを使って画像を切り抜くと、通常は"コピー "ではなく"ビュー "**が作成されます。この2つの違いは何でしょうか?

    (1) ビュー

    • 元の画像に "窓 "があるようなもので、そこから画像のある部分を見ることができる。
    • このウィンドウを通して画像の色(ピクセル値)を変えると、元の画像の対応する部分の色も変わります!これは、両者が同じデータを共有しているからです。

    (2) コピー

    • 切り取った画像を独立させ、それを変更しても元の画像に影響を与えないようにしたい場合は、.copy()メソッドを明示的に使用して「完全に独立したコピー」を作成する必要があります。

    • 例えば

      roi = img[50:200, 100:300].copy()

 

2. 画像の外側を切り取らない

(1) 切り抜き範囲を定義するときは、設定した開始座標と終了座標が画像の実際のサイズ内であることを確認してください。

(2) 画像の境界を越えて切り抜くと、通常NumPyはエラーを報告しないが、"空の "画像が表示されたり、画像の端だけが切り抜かれたりすることがある。

(3) この "無効な "切り抜き領域で作業を続けると、プログラムがエラーを出したり、期待した結果とは違う結果を出したりすることがある。

(4) 良い習慣: 切り抜く前に、y_start、y_end、x_start、x_endがすべて画像の高さと幅の範囲内であることを確認してください!

3. メモリ管理のヒント

(1) Pythonでは通常、メモリはプログラムによって自動的に管理されます(自動掃除機のようなものです)。画像(またはそのビュー)がどの変数からも "ポイント "されなくなると、それが占めていたメモリは自動的に解放されます。

(2) 重要! ビュー」を作成しただけでは、ビュー自体には画像をコピーするデータはありません。この「ビュー」変数を削除しても、元の絵が残っている限り、そのメモリは解放されません。元画像も消去されて初めて、実際に占有していたピクセルデータメモリが解放される。


例:画像の好きな部分を切り取る!

では、写真のある部分を切り取るプログラムを書いて、その結果を見てみましょう!

import cv2 as cv # Import the OpenCV module
# Set the file path for the original image.
# Don't forget to replace 'Pandora.png' with your own image!
file_path = 'Pandora.png'
# Use OpenCV's imread function to read the image.
source_picture = cv.imread(file_path)
# Check if the image was successfully loaded.
if source_picture is None:
    print(f"Warning: Oops! Could not load the image.")
    print(f'Please check if the path is correct: {file_path}')
else:
    # Now, we'll "cut" a region from the original 'source_picture'.
    # We're cropping the area:
    # Height (y-axis): From row 100 to row 100 + 200 = 300 (not including row 300).
    # Width (x-axis): From column 100 to column 100 + 300 = 400 
    # (not including column 400).
    # This will give us a new image with 
    # a height of 200 pixels and a width of 300 pixels.
    crop_image = source_picture[100 : 100 + 200, 100 : 100 + 300]
    # --- Image Display Loop ---
    # Enter an infinite loop to keep the image windows open.
    while True:
        # Display the original image in a window named 'Original Picture'.
        cv.imshow('Original Picture', source_picture)
        # Display the cropped image in a window named 'Cropped Picture'.
        cv.imshow('Cropped Picture', crop_image)
        # The cv.waitKey(1) function pauses the program for 
        # 1 millisecond to check for a key press.
        key = cv.waitKey(1)
        # If the ESC key is pressed (number 27), break the loop and end the display.
        if key & 0xFF == 27:
            break
    # --- Release Resources ---
    # Finally, close all image windows created by OpenCV to free up computer resources.
    cv.destroyAllWindows()

プログラムの結果:

上のコードを実行すると、2つのウィンドウがポップアップします。1つは元の写真で、もう1つはあなたが「切り取った」写真の領域です!面白いでしょう?


写真の切り抜き方を覚えたら、こんなことができます:

  1. ハイライトだけを残す: 写真から無関係な人や物を切り取る。
  2. アバターの作成: 大きな顔写真からアバターを正確に切り抜く。
  3. 局所的に分析する: 画像処理や機械学習では、写真の特定の領域に焦点を当てて分析する。

ビュー」と「コピー」の違いを覚えておこう!切り取られた画像に変更を加えたいが、元の画像に影響を与えたくない場合は、必ず.copy()を使ってください!

画像のパン: 画像を「スライド」させましょう!

画像を画面上の別の位置に「移動」させたいと思ったことはありませんか?携帯電話で写真をドラッグ・アンド・ドロップするようなものです! OpenCVでは 、 warpAffineと 呼ばれる関数を使って、この "スライディング "マジックを行うことができます。


warpAffine: 画像の「移動コマンド」!

warpAffine 関数は OpenCVで"アフィン変換 "を行うために使われます。複雑に聞こえますが、要するに高倍率画像の幾何学的変換です。

画像を、伸ばしたり、圧縮したり、回転させたり、移動させたりできる輪ゴムのように想像してみてください。アフィン変換を使うと、平行線は平行のままですが、画像の大きさ、形、角度が変わることがあります。今日は、最も単純な形の「パンニング」(画面上で画像を移動させること)を実現するためにこれを使う!


関数のプロトタイプとパラメータ: warpAffineに移動方法を指示する!

warpAffine関数は、画像をどのように動かすかを知るために、いくつかの "指示 "を必要とする:

def warpAffine(src: cv2.typing.MatLike,         
	       # This is the "original image" you want to move.
               M: cv2.typing.MatLike,           
               # This is a crucial "transformation matrix,"
               # which contains the "secret formula" for how the image moves.
               dsize: cv2.typing.Size,          
               # This is the "new image's size" after the movement (width, height).
               dst: cv2.typing.MatLike | None = ...,   
               # The moved image will be stored here 
               # (usually you don't set this yourself; the function returns it).
               flags: int = ...,                
               # Some extra flags, like the interpolation method, 
               # which affects image quality.
               borderMode: int = ...,           
               # How to handle the image's edges when they go out of bounds 
               # (e.g., what color to fill them with).
               borderValue: cv2.typing.Scalar = ...) 
               # The color value to fill the border with.
-> cv2.typing.MatLike: ...

戻り値:warpAffineは、シフトされた画像を返します!

warpAffineが画像の移動に成功すると、新しい画像データを返します!


重要:画像移動の「小さなステップ」!

画像をスムーズかつ正確に移動させるために重要な "ちょっとした工夫 "をご紹介します:

  1. Mマトリックス:動きの「秘密の公式」!

    (1) warpAffineの 最も重要な部分はMパラメータで、これは2x3の "変換行列 "です。これは2x3の "変換マトリックス "で、画像を "移動、回転、ズーム "するための数式を含んでいます。

    (2) ご心配なく!OpenCV には **cv2.getAffineTransform(src_points, dst_points)**という素晴らしい関数があり,このM行列を自動的に計算することができます.

  2. この関数は,「3つの点」を使って移動方法を決定する.

    (1) cv2.getAffineTransform は非常に賢い関数で,元画像上の「3つの点」(src_points)と,その3つの点の移動先(dst_points)を指定するだけで,移動のための「秘密の公式」を計算してくれます.

    (2)重要: 正確に3組の点を指定しなければなりません。また、それらは同じ線上にあることはできません(そうでなければ、どのように変形すればよいのかわかりません)。

    (3) src_pointsは3つの「元の点の座標」のリストになり、dst_pointsはそれら3点の「対応する新しい座標」のリストになります。

  3. 点の順番を間違えることはない!

    src_pointsに与える最初の点はdst_pointsの最初の点に対応し、src_pointsの2番目の点はdst_pointsの2番目の点に対応する...といった具合です。 などなど。そうしないと、画像はおかしな場所に移動してしまいます!


例:画像を右下に移動させる!

では、画像を少し "スライド "させるプログラムを書いてみよう!

import cv2 as cv
import numpy as np # We need NumPy to handle the point coordinates
# Set the file path for the original image.
# Don't forget to replace 'Pandora.png' with your own image!
file_path = 'Pandora.png'
# Use OpenCV's imread function to read the image.
source_picture = cv.imread(file_path)
# Check if the image was successfully loaded.
if source_picture is None:
    print(f"Warning: Oops! Could not load the image.")
    print(f'Please check if the path is correct: {file_path}')
else:
    # --- 1. Define the "three reference points" on the original image ---
    # We'll choose the top-left, top-right, and bottom-left corners as reference points.
    # The coordinates must be of type float32 (np.float32).
    src_points = np.float32([
        [0.0, 0.0],                          
        # Point 1: Top-left corner of the original image (x=0, y=0)
        [source_picture.shape[1] - 1.0, 0.0], 
        # Point 2: Top-right corner (x=far right, y=0)
        [0.0, source_picture.shape[0] - 1.0]  
        # Point 3: Bottom-left corner (x=0, y=far bottom)
    ])
    # --- 2. Define where these reference points should go "after the move" ---
    # Here, we'll make the whole image move slightly to the bottom right, 
    # with a bit of a squeeze and tilt.
    dst_points = np.float32([
        [0.0, source_picture.shape[0] * 0.1],  
        # Point 1 moves: y-axis moves down by 10% of the image's height
        [source_picture.shape[1] * 0.9, 0.0],  
        # Point 2 moves: x-axis moves left by 10% of the image's width
        [source_picture.shape[1] * 0.2, source_picture.shape[0] * 0.9]
        # Point 3 moves: x-axis moves right by 20%, y-axis moves down by 10%
    ])
    # --- 3. Calculate the image's "movement formula" (Transformation Matrix M) ---
    # cv.getAffineTransform will automatically calculate a 2x3 transformation matrix 
    # (warp_matrix)
    # for us based on src_points and dst_points, 
    # which contains the commands for how to
    # translate, rotate, and scale the image.
    warp_matrix = cv.getAffineTransform(src_points, dst_points)
    # --- 4. Apply the "movement formula" to make the image really move! ---
    # The cv.warpAffine function will take the original image (source_picture)
    # and move it to a new position based on the warp_matrix we calculated.
    # (source_picture.shape[1], source_picture.shape[0]) 
    # specifies the size of the new image after the move.
    # Here, we'll keep it the same size as the original.
    modified_image = cv.warpAffine(source_picture, warp_matrix,
                                  (source_picture.shape[1], source_picture.shape[0]))
    # --- Image Display Loop ---
    # Enter an infinite loop to keep the image windows open.
    while True:
        # Display the original image.
        cv.imshow('Original Picture', source_picture)
        # Display the moved image.
        cv.imshow('Moved Picture', modified_image) 
        # Changed the window name to be clearer!
        # The cv.waitKey(1) function pauses the program for 
        # 1 millisecond to check for a key press.
        key = cv.waitKey(1)
        # If the ESC key is pressed (number 27), break the loop and end the display.
        if key & 0xFF == 27:
            break
    # --- Release Resources ---
    # Finally, close all image windows created by OpenCV to free up computer resources.
    cv.destroyAllWindows()

プログラムの結果:

上のコードを実行すると、2つのウィンドウが表示されます。1つは元の画像で、もう1つは画像が右下に少し「スライド」して移動しています!


単純なパンだけでなく、warpAffineはもっと面白い画像変換もできます:

  1. 画像の回転: 画像を別の方向に回転させます。
  2. 傾き: 画像が風にあおられているように見せます。
  3. ズーム: もちろん、ズームインやズームアウトもできます。

src_pointsとdst_pointsの位置を調整するだけで、アフィン変換のさまざまな効果で遊ぶことができます!

イメージ・ミラー: あなたの写真を鏡に映してみましょう!

鏡を見て逆さまの自分を見たことがありますか?あるいは、写真を撮った後、画像を水平に反転させて、違う感じを見たいと思ったことはありませんか?画像処理では、このような画像の鏡映しを「画像の鏡映し」または「画像の反転」と呼びますが、OpenCVにはこれを手助けする flipという超シンプルな関数があります!


flip: 画像の「ミラーコマンド」!

flip 関数は、画像を「反転」させるための OpenCVの 特別なツールです。画像を水平(左右逆さま)、垂直(上下逆さま)、あるいは水平と垂直の両方を同時に反転させることができます!


関数のプロトタイプとパラメータ: flipにフリップの方法を指示します!

flip関数は、画像をミラーリングする方法を知るために、いくつかの簡単な「命令」を必要とします:

def flip(src: cv2.typing.MatLike,       
				 # This is the "original image" you want to flip.
         flipCode: int,                 
         # This number determines how the image will be flipped!
         dst: cv2.typing.MatLike | None = ...) 
         # The flipped image will be stored here (usually you don't set this yourself; 
         # the function returns it).
-> cv2.typing.MatLike: ...

flipCode: フリップの「秘密の番号」!

flipCodeは、flip関数に画像の反転方法を指示するためのキーです:

  1. flipCode = 0:垂直に (上下逆に)反転します。水平方向の中心線を中心に画像が反転するイメージです。
  2. flipCode = 1:水平方向 (上下逆)に反転します。画像が縦の中心線を中心に反転していると想像してください。
  3. flipCode = -1:水平+垂直に反転 します(左右上下同時に反転します)。


戻り値: flipで反転された画像が得られます!

flipは画像の反転に成功すると、新しい画像データを返します!


例: 画像を上下反転させる

では、画像を水平に(つまり上下逆に)「反転」させるプログラムを書いてみましょう!

import cv2 as cv
# Set the file path for the original image.
# Don't forget to replace 'Pandora.png' with your own image!
file_path = 'Pandora.png'
# Use OpenCV's imread function to read the image.
source_picture = cv.imread(file_path)
# Check if the image was successfully loaded.
if source_picture is None:
    print(f"Warning: Oops! Could not load the image.") 
    print(f"Please check if the path is correct: {file_path}")
else:
    # Here, we'll directly call the cv.flip function to flip the image.
    # source_picture is the image we want to flip.
    # 1 stands for a horizontal flip (left-to-right).
    # modified_image will receive the flipped image data.
    # Note: cv.flip returns a new image by default, so you don't need to use .copy()!
    modified_image = cv.flip(source_picture, 1) # **Horizontal** flip (flipCode=1)
    # --- Image Display Loop ---
    # Enter an infinite loop to keep the image windows open.
    while True:
        # Display the original image.
        cv.imshow('Original Picture', source_picture)
        # Display the flipped image.
        cv.imshow("Flipped Picture (Horizontal)", modified_image) 
        # Changed the window name to be clearer!
        # The cv.waitKey(1) function pauses the program for 
        # 1 millisecond to check for a key press.
        key = cv.waitKey(1)
        # If the ESC key is pressed (number 27), break the loop and end the display.
        if key & 0xFF == 27:
            break
    # --- Release Resources ---
    # Finally, close all image windows created by OpenCV to free up computer resources.
    cv.destroyAllWindows()

プログラムの結果:

上のコードを実行すると、2つのウィンドウが表示されます:1つは元の写真、もう1つは逆の写真です!


flip 関数を覚えたら、flipCode のパラメータを変えて、画像がどのように変化するかを試してみてください:

  1. **cv.flip(source_picture, 1):**鏡を見たときのように,画像が左右反転します.
  2. **cv.flip(source_picture, -1):**画像は,水平方向と垂直方向に同時に反転し,非常に特殊な効果を生み出します.

flip 関数は,面白い視覚効果だけでなく,画像処理におけるデータ拡張にも利用できます.

画像グレースケール:カラー画像を「白黒」にします!

味わい深い昔のモノクロ写真を見たことがありますか?あるいは、写真の形や影に注目するために、カラー写真をグレースケールにすることもあります。 OpenCV には cvtColorという便利な関数があり,白黒の衣装を着るように,簡単に写真をカラーからグレースケールに変換することができます.


cvtColor: 写真の「カラーチェンジャー」.

cvtColor 関数は,画像をあるカラーモードから別のカラーモードに変換する OpenCV の特別なツールです.画像の「カラーモード」(あるいは「色空間」)には,よく知られたカラー画像(RGB モードや BGR モード)や,今日学習する「グレースケールモード」など,さまざまなものがあります.


関数のプロトタイプとパラメータ:cvtColor に色の変更方法を伝えます!

cvtColor 関数には,画像の色を変更する方法を知るための簡単な「命令」が必要です.

def cvtColor(src: cv2.typing.MatLike,    
						 # This is the "original image" you want to change colors for.
             code: int,                  
	           # This "conversion code" determines which 
	           # color mode to convert from and to!
             dst: cv2.typing.MatLike | None = ...,   
             # The color-changed image will be stored here 
             # (usually you don't set this yourself; the function returns it).
             dstCn: int = ...,           
             # The number of channels for the output image 
             # (usually you don't set this; the function figures it out).
             hint: AlgorithmHint = ...)    
             # A hint for the conversion algorithm (usually the default is fine).
-> cv2.typing.MatLike: ...

戻り値: cvtColor は,変更された画像の色を返します.

cvtColor は、画像の色を変更した後、新しい画像データを返します。


注意: 画像の色を変更する前に、ちょっとした注意があります!

画像の色を正しく自然に変えたいのであれば、これらの「ちょっとした注意」はとても重要です:

  1. BGRかRGBか:OpenCVの "癖"

    (1) RGB(Red, Green, Blue) という言葉を聞いたことがあるかもしれませんが、これは多くの画像処理ソフトウェアで使われている一般的な色の順序です。しかし OpenCVには 独自の "ちょっとした癖 "があり、カラー画像のデフォルトの色順は BGR(Blue, Green, Red)です!

    (2) そのため、色を変換するときは、色の「混乱」を避けるために、入力画像の順序と出力画像の順序に特別な注意を払う必要があります!

    (3) 例えば, BGR から灰色に変換する場合, cv.COLOR_BGR2GRAY を利用します.

  2. 色の「値の範囲」に注意してください!

    (1) 画像の色の値は,通常 0 から 255 の間にあります.しかし、いくつかの特殊な色変換(例えば、科学計算のためのいくつかのカラーモード)では、関数が色の値を0と1の間の10進数であることを要求する場合があります。

    (2) 値の範囲が正しくない場合、変換された色が「暴走」したり、奇妙に見えたりすることがあります。しかし、カラーからグレースケールへの一般的な変換では、この心配はありません。

  3. 画像の「データ型

    (1) 最も一般的な画像データ型は uint8 (8ビット符号なし整数)で、これは各色値が0~255の整数であることを意味します。この形式は最もメモリ効率がよく、ほとんどのアプリケーションで十分です。

    (2) より高度な計算では浮動小数点数のようなデータ型を使うこともありますが、グレイスケールの入門的な学習にはuint8で十分です!


実例:写真をクラシックな白黒写真にしてみましょう!

では、カラー写真をノスタルジックなグレースケール写真にするプログラムを書いてみよう!

import cv2 as cv
# Set the file path for the original image.
# Don't forget to replace 'Pandora.png' with your own image!
file_path = 'Pandora.png'
# Use OpenCV's imread function to read the image.
source_picture = cv.imread(file_path)
# Check if the image was successfully loaded.
if source_picture is None:
    print(f"Warning: Oops! Could not load the image.")
    print(f" Please check if the path is correct: {file_path}")
else:
    # --- Image Grayscaling! ---
    # Use the cv.cvtColor function to convert the original color image (source_picture) 
    # to a grayscale image.
    # cv.COLOR_BGR2GRAY is the conversion code, 
    # which means "convert from BGR color mode to grayscale mode."
    # The grayscale image will only have one color channel (representing brightness), 
    # no more red, green, and blue channels!
    modified_image = cv.cvtColor(source_picture, cv.COLOR_BGR2GRAY)
    # --- Image Display Loop ---
    # Enter an infinite loop to keep the image windows open.
    while True:
        # Display the original color image.
        cv.imshow('Original Picture (Color)', source_picture)
        # Display the converted grayscale image.
        cv.imshow("Modified Picture (Grayscale)", modified_image) 
        # Changed the window name to be clearer!
        # The cv.waitKey(1) function pauses the program for 1 millisecond to check for 
        # a key press.
        key = cv.waitKey(1)
        # If the ESC key is pressed (number 27), break the loop and end the display.
        if key & 0xFF == 27:
            break
    # --- Release Resources ---
    # Finally, close all image windows created by OpenCV to free up computer resources.
    cv.destroyAllWindows()

プログラムの結果:

上のコードを実行すると、2つのウィンドウが表示されます:1つは元のカラー写真、もう1つは今プログラムで変換したグレースケール写真です!クールでしょう?


画像のグレースケールは、画像処理において最も基本的でよく使われる操作のひとつです。芸術的な感覚を生み出すだけでなく、多くの高度な画像処理や機械学習タスクでは、画像をグレースケールにすることで計算が簡略化され、プログラムが画像の内容を分析しやすくなります!

画像の2値化:画像を「白黒のシルエット」に変える!

画像をシルエットのようにシンプルな白黒にする方法を考えたことはありますか?コンピュータビジョンの世界では、これを「画像の2値化」と呼びます。これは、画像に「光の閾値」を設定するようなもので、閾値より明るいものは白く、閾値より暗いものは黒くなります。OpenCVの 閾値 関数は、このマジックを実現するためのツールです!


しきい値:画像の「光の門番」!

OpenCVでは ,グレイスケールの画像を2値画像(つまり,白黒画像)に変換したり,画像のピクセルを設定したしきい値に従って分類したりするために,しきい値 関数を利用します.


関数のプロトタイプとパラメータ: threshold に "光の閾値" を指定します.

threshold関数は、画像の白黒ラインをどのように設定するかを知るために、いくつかの指示を必要とします:

def threshold(src: cv2.typing.MatLike,      
							# This is the "grayscale image" you want to process 
							# (Note: it must be a grayscale image!).
              thresh: float,                
              # This is your "threshold value." Pixels brighter than this value will 
	            # be processed.
              maxval: float,                
              # This is the "maximum brightness value" for pixels that 
              # exceed the threshold (usually 255 for white).
              type: int,                    
              # This is the "threshold type," which tells the function 
              # how to classify pixels based on the threshold.
              dst: cv2.typing.MatLike | None = ...)
-> tuple[float, cv2.typing.MatLike]: ...

戻り値: thresholdは閾値と白黒画像を返します!

threshold は画像の2値化に成功すると、2つのものを返します:

  1. float: 実際に使用する閾値。

    (1) もしあなたが自分でしきい値を設定した場合(例では200)、返される値はあなたが設定したものになります。

    (2) ただし,( cv.THRESH_OTSU や cv.THRESH_TRIANGLE を利用するなどして) OpenCV が 自動的に最適な閾値を求めるようにした場合は, OpenCV が計算した値が返されます.

  2. cv2.typing.MatLike:2 値の「白黒画像」.

    これは,0(黒)か 255(白)ピクセルのみを含む NumPy の配列となります.


例:あなたの画像をクールな白黒シルエットに!

では、カラー画像をグレースケールにするプログラムを書いて、2値化して見てみましょう!

import cv2 as cv
# Set the file path for the original image.
# Don't forget to replace 'Pandora.png' with your own image!
file_path = 'Pandora.png'
# Use OpenCV's imread function to read the image.
source_picture = cv.imread(file_path)
# Check if the image was successfully loaded.
if source_picture is None:
    print(f"Warning: Oops! Could not load the image. ")
    print(f"lease check if the path is correct: {file_path}")
else:
    # --- 1. First, turn the color image into a grayscale image! ---
    # cvtColor is our "color-changing magician," converting BGR color to grayscale.
    convert_img = cv.cvtColor(source_picture, cv.COLOR_BGR2GRAY)
    # --- 2. Perform image binarization! ---
    # Use the cv.threshold function to binarize the grayscale image.
    # convert_img: The input grayscale image.
    # 200: This is the "threshold value" we set. Pixels with brightness higher than 200.
    # 255: Pixels that exceed the threshold will become 255 (white).
    # cv.THRESH_BINARY: This is the threshold type. It means "if pixel brightness > 200,
    #                   it becomes 255; otherwise, it becomes 0."
    # ret: The returned actual threshold value (here, it's 200).
    # modified_image: This is the binarized black and white image. 
    # The pixels inside will only have values of 0 or 255.
    ret, modified_image = cv.threshold(convert_img, 200, 255, cv.THRESH_BINARY)
    # --- Image Display Loop ---
    # Enter an infinite loop to keep the image windows open.
    while True:
        # Display the original color image.
        cv.imshow('Original Picture (Color)', source_picture)
        # Display the binarized black and white image.
        cv.imshow("Modified Picture (Binary)", modified_image) 
        # Changed the window name to be clearer!
        # The cv.waitKey(1) function pauses the program for 1 millisecond to check for 
        # a key press.
        key = cv.waitKey(1)
        # If the ESC key is pressed (number 27), break the loop and end the display.
        if key & 0xFF == 27:
            break
    # --- Release Resources ---
    # Finally, close all image windows created by OpenCV to free up computer resources.
    cv.destroyAllWindows()

プログラムの結果:

上のコードを実行すると、2つのウィンドウが表示されます。1つは元のカラー画像で、もう1つはプログラムで変換した白黒画像です!シルエットやプリントのように見えませんか?


画像の2値化は多くのアプリケーションで重要です:

  1. **テキスト認識(OCR):**文書の画像を2値化することで、テキストをより鮮明にし、コンピュータが認識しやすくします。
  2. 物体検出:画像内の物体を背景から分離します。
  3. バーコードスキャン: バーコード画像を単純化して、スキャナで読み取りやすくします。

しきい値機能のしきい値200を調整し、異なる値でどのような白黒効果が得られるか試してみることができます!

画像エッジ検出: 画像の「エッジをなぞる」ことができます!

コンピュータがどうやって写真に写っているものを「知っている」のか不思議に思ったことはありませんか?実は、オブジェクトの「エッジ」が非常に重要な手がかりなのです!漫画家がエッジをなぞって絵を描くように、画像のエッジ検出は、コンピュータが画像内のすべての線を見つけることができる技術です。OpenCVには 、画像に正確な「エッジ線」を描くのに役立つ Cannyという強力な関数があります!


Canny:写真の「スケッチマスター」!

Canny 関数は、 OpenCVで 写真のエッジを検出するために使用されます。単なるランダムな描画ではなく、Cannyエッジ検出は古典的で非常に効果的なアルゴリズムであり、画像の中で明るさの変化が最も顕著な場所(通常はオブジェクトのエッジ)を見つけようとします。


関数のプロトタイプとパラメータ: Cannyに「エッジをトレース」する方法を指示します!

Canny関数は、画像のエッジ線をどのように描画するかを知るために、いくつかの指示を必要とします:

def Canny(image: cv2.typing.MatLike,        
					# This is the "grayscale image" you want to detect edges on 
					# (Note: it must be a grayscale image!).
          threshold1: float,               
          # This is the first "low threshold value."
          threshold2: float,               
          # This is the second "high threshold value."
          edges: cv2.typing.MatLike | None = ...,   
          # The detected edge image will be stored here 
          # (you usually don't set this yourself).
          apertureSize: int = ...,           
          # A parameter that affects the fineness of the edge detection 
          # (usually the default is fine).
          L2gradient: bool = ...)            
          # The method for calculating the gradient (usually the default is fine).
-> cv2.typing.MatLike: ...

戻り値: Cannyは「エッジマップ」を返す!

Cannyが写真のエッジをうまく検出したら、新しい写真データを送り返します。


注:Cannyのエッジ検出の「小さな秘密」!

Cannyのエッジ検出には、知っておくべきいくつかの "小さな角 "があり、それらはエッジ検出結果に影響します:

  1. "ラグしきい値":スマートエッジリンキング!

    (1) Cannyエッジ検出の最大の特徴は、「2つのしきい値(しきい値1としきい値2)」を使ってエッジを処理することです。

    (2) **高いしきい値(threshold2):「**非常に目立つ」エッジ(強いエッジ)を見つけるために使う。

    (3) 低いしきい値(threshold1):「あまり目立たない」エッジ(弱いエッジ)を見つける。

    (4) 弱いエッジのピクセルが強いエッジのピクセルに「接続」できる場合、それも真のエッジとみなされる!この巧妙なメカニズムにより、ノイズによる偽エッジをフィルタリングしながら、真のエッジを保つことができる。

    (5) 通常、閾値2は閾値1の2~3倍が推奨される。

  2. ソーベル演算子:「明るさの変化」を見つける!

    (1) Cannyは**「ソーベル演算子**」というツールを使って、画像内の各場所の「明るさの変化」を計算します。明るさの変化が大きいほど、エッジである可能性が高い。

    (2) apertureSize このパラメータは、ソーベル演算子が「観察」するピクセルの範囲の大きさに影響します。値が大きいほど検出されるエッジは滑らかになりますが、小さなエッジが失われることもあります。

  3. 8ビットグレースケール: Cannyの「お気に入り」!

    Canny関数は通常、 8ビット(0~255)のグレースケール画像しか入力として受け付けません。したがって,カラー画像の場合は,まず cv.cvtColor でグレースケールに変換することを忘れないでください.

  4. 適切な閾値を選ぶことが重要です.

    (1) threshold1 と threshold2 は,非常に重要な2つの閾値です.これらは,どのエッジが検出されるかに直接影響します.

    (2)しきい値の組み合わせに "一長一短 "はありません。 一番良い方法は、あなたの画像に対して様々な値を試して、あなたに最も適したものを見つけることです。


例:画像に輪郭線を描かせる

では、カラー画像をグレースケールにするプログラムを書いて、Canny関数を使って輪郭を調べてみよう!

import cv2 as cv
# Set the file path for the original image.
# Don't forget to replace 'Pandora.png' with your own image!
file_path = 'Pandora.png'
# Use OpenCV's imread function to read the image.
source_picture = cv.imread(file_path)
# Check if the image was successfully loaded.
if source_picture is None:
    print(f"Warning: Oops! Could not load the image.")
    print(f" Please check if the path is correct: {file_path}")
else:
    # --- 1. First, turn the color image into a grayscale image! ---
    # cvtColor is our "color-changing magician," converting BGR color to grayscale.
    convert_img = cv.cvtColor(source_picture, cv.COLOR_BGR2GRAY)
    # --- 2. Perform Canny edge detection! ---
    # Use the cv.Canny function to find the edges of the grayscale image.
    # convert_img: The input grayscale image.
    # 50: This is the "low threshold value."
    # 150: This is the "high threshold value."
    # modified_image: This is the detected edge image, 
    # where edges are displayed as white lines.
    modified_image = cv.Canny(convert_img, 50, 150)
    # --- Image Display Loop ---
    # Enter an infinite loop to keep the image windows open.
    while True:
        # Display the original color image.
        cv.imshow('Original Picture (Color)', source_picture)
        # Display the Canny edge-detected image.
        cv.imshow('Edge Detected Picture', modified_image) 
        # Changed the window name to be clearer!
        # The cv.waitKey(1) function pauses the program for 
        # 1 millisecond to check for a key press.
        key = cv.waitKey(1)
        # If the ESC key is pressed (number 27), break the loop and end the display.
        if key & 0xFF == 27:
            break
    # --- Release Resources ---
    # Finally, close all image windows created by OpenCV to free up computer resources.
    cv.destroyAllWindows()

結果は?

上のコードを実行すると、2つのウィンドウが表示されます。1つは元のカラー写真で、もう1つはCanny関数で「エッジをトレース」した後の写真です!絵の中のオブジェクトの輪郭が白い線としてレンダリングされ、スケッチや漫画のスケッチのように見えませんか?


キャニーのエッジ検出は、次のような多くの高度なコンピュータビジョンタスクで重要な役割を果たしています:

  1. 物体認識:物体の輪郭を見つけ、その形状に基づいてそれが何であるかを判断する。
  2. 画像のつなぎ合わせ:複数の画像をつなぎ合わせる際に、エッジを利用して位置合わせを行う。
  3. ロボットナビゲーション:ロボットが障害物の境界を識別できるようにします。

Canny機能で50と150のしきい値を調整し、異なる値によってエッジ線がどのように太くなるか、細くなるか、または詳細がより多く/より少なく検出されるかを試してみることができます!

 

Copyright © 2026 YUAN High-Tech Development Co., Ltd.
All rights reserved.