Pythonを使用した画像処理の紹介

Pythonでの画像処理の紹介

エピソード2:画像の強化、パート3:ヒストグラム操作

ヒストグラム操作のテクニック。著者による写真

画像処理シリーズの第2エピソードの第3部へようこそ!このシリーズの前の部分では、フーリエ変換とホワイトバランスの技術について説明しましたが、今回は別の興味深い技術であるヒストグラム操作について探求します。

私のような人は、ヒストグラムがどのように操作されるのか疑問に思うかもしれません。ヒストグラムは単に画像のピクセル値の分布を示すグラフではないですか?実は、ヒストグラムを操作することによって、画像のコントラストと明るさを調整することができます。これにより、画像の視覚的な見栄えを大幅に向上させることができます。

では、ヒストグラム操作の世界に飛び込んで、さまざまなヒストグラム操作のテクニックを使用して画像のコントラストと明るさを向上させる方法を発見しましょう。これらのテクニックは、低光画像のオブジェクトの可視性を向上させるために使用したり、画像の詳細を向上させたり、過度または不十分に露出した画像を修正したりするために使用することができます。

まずは、関連するPythonライブラリをインポートしましょう:

ステップ1:ライブラリをインポートし、画像を読み込んで表示する

import numpy as npimport matplotlib.pyplot as pltfrom skimage.color import rgb2grayfrom skimage.exposure import histogram, cumulative_distributionfrom skimage import filtersfrom skimage.color import rgb2hsv, rgb2gray, rgb2yuvfrom skimage import color, exposure, transformfrom skimage.exposure import histogram, cumulative_distribution

# 画像を読み込んでアルファチャンネル(透明度)を削除するdark_image = imread('plasma_ball.png')[:,:,:3]# 画像を表示plt.figure(figsize=(10, 10))plt.title('オリジナル画像:プラズマボール')plt.imshow(dark_image)plt.show()
出力1:著者による写真

ステップ2:チャンネルの統計情報をチェックし、画像のヒストグラムをプロットする

def calc_color_overcast(image):    # 各チャンネルのカラーオーバーキャストを計算    red_channel = image[:, :, 0]    green_channel = image[:, :, 1]    blue_channel = image[:, :, 2]    # 結果を格納するためのデータフレームを作成    channel_stats = pd.DataFrame(columns=['平均', '標準偏差', '最小値', '中央値',                                           'P_80', 'P_90', 'P_99', '最大値'])    # 各カラーチャンネルの統計を計算し、格納する    for channel, name in zip([red_channel, green_channel, blue_channel],                              ['赤', '緑', '青']):        mean = np.mean(channel)        std = np.std(channel)        minimum = np.min(channel)        median = np.median(channel)        p_80 = np.percentile(channel, 80)        p_90 = np.percentile(channel, 90)        p_99 = np.percentile(channel, 99)        maximum = np.max(channel)        channel_stats.loc[name] = [mean, std, minimum, median, p_80, p_90, p_99, maximum]    return channel_stats

# これは前回のエピソードパート2の同じ関数です(チェックしてみてください!)calc_color_overcast(dark_image)
出力2:画像のチャンネル統計のPandasデータフレーム。著者による写真。
# ヒストグラムのプロットdark_image_intensity = img_as_ubyte(rgb2gray(dark_image))freq, bins = histogram(dark_image_intensity)plt.step(bins, freq*1.0/freq.sum())plt.xlabel('強度値')plt.ylabel('ピクセルの割合');
出力 #3: 画像の強度値のヒストグラムプロット。著者撮影。

顕著な曇りは見られませんが、ピクセルの平均強度は非常に低く、画像の暗さと露出不足の可視化が確認されます。ヒストグラムは、ほとんどのピクセルが低い強度値を持っていることを示しています。低いピクセル強度値は、画像のほとんどのピクセルが非常に暗いか黒いことを意味します。画像のコントラストを改善するために、さまざまなヒストグラム操作技術を画像に適用することができます。

ステップ3: さまざまなヒストグラム操作技術の探索

ヒストグラム操作のいくつかの技術に取り組む前に、ヒストグラム均等化という一般的に使用されるヒストグラム操作技術を理解しましょう。

ヒストグラム均等化は、画像のピクセル強度を均等化してヒストグラムをより一様にする技術です。非均等なピクセル強度分布は、低いコントラストと詳細性を持つ画像を引き起こし、画像内のオブジェクトや特徴を区別するのが難しくなります。ピクセル強度分布をより均等にするための一つの方法は、画像の累積分布関数(CDF)を線形にすることです。これは、線形なCDFは画像内で各ピクセル強度値が同じ確率で発生することを意味します。一方、非線形なCDFは、特定のピクセル強度値が他よりも頻繁に発生することを意味し、非均等なピクセル強度分布を引き起こします。CDFを線形にすることで、ピクセル強度分布をより均等にし、画像のコントラストを改善することができます。

def plot_cdf(image):    """    画像の累積分布関数をプロットする。        パラメーター:    image (ndarray): 入力画像。    """        # 必要に応じて画像をグレースケールに変換する    if len(image.shape) == 3:        image = rgb2gray(image[:,:,:3])        # 累積分布関数を計算する    intensity = np.round(image * 255).astype(np.uint8)    freq, bins = cumulative_distribution(intensity)        # 実際のCDFと目標のCDFをプロットする    target_bins = np.arange(256)    target_freq = np.linspace(0, 1, len(target_bins))    plt.step(bins, freq, c='b', label='実際のCDF')    plt.plot(target_bins, target_freq, c='r', label='目標のCDF')        # 例のルックアップをプロットする    example_intensity = 50    example_target = np.interp(freq[example_intensity], target_freq, target_bins)    plt.plot([example_intensity, example_intensity, target_bins[-11], target_bins[-11]],             [0, freq[example_intensity], freq[example_intensity], 0],              'k--',              label=f'例のルックアップ ({example_intensity} -> {example_target:.0f})')        # プロットをカスタマイズする    plt.legend()    plt.xlim(0, 255)    plt.ylim(0, 1)    plt.xlabel('強度値')    plt.ylabel('ピクセルの累積割合')    plt.title('累積分布関数')        return freq, bins, target_freq, target_bins

dark_image = imread('plasma_ball.png')freq, bins, target_freq, target_bins = plot_cdf(dark_image);
出力 #4: CDFプロット

このコードは、暗い画像の累積分布関数(CDF)を計算し、線形分布に基づく目標のCDFを定義します。それから、暗い画像の実際のCDFを青色で、目標のCDF(線形)を赤色でプロットします。また、例のルックアップの強度値もプロットされており、これにより、例では実際のCDFが50であり、それを230にターゲットしたいことが示されます。

# 強度値を実際の値50から目標値230に変換するサンプルdark_image_230 = dark_image_intensity.copy()dark_image_230[dark_image_230==50] = 230plt.figure(figsize=(10,10))plt.imshow(dark_image_230,cmap='gray');
出力#5:実際の強度値(50)を目標値(230)に変換した結果に基づくサンプルのグレー画像。著者撮影。

目標のCDFを取得した後、次のステップは元のピクセルの強度を置き換えるために使用される強度値を計算することです。これは、補間を使用してルックアップテーブルを作成することで行われます。

# 全ての実際の値を目標値に置き換えた結果を表示new_vals = np.interp(freq, target_freq, target_bins)dark_image_eq = img_as_ubyte(new_vals[img_as_ubyte(rgb2gray(dark_image[:,:,:3]))].astype(int))plt.figure(figsize=(10,10))plt.imshow(dark_image_eq, cmap='gray');
出力#6:すべての実際の強度値を目標値に変換した結果に基づくサンプルのグレー画像。著者撮影。

np.interp()関数は、実際のCDFと目標のCDFの間を補間して元のピクセルの強度値を置き換えるための強度値を計算します。その結果得られた強度値は、NumPyのインデックスを使用して元のピクセルの強度を置き換えるために使用されます。最後に、等化された画像はimshow()を使用してgrayで表示されます。

今、私たちは最も基本的なヒストグラムの操作を示しましたので、異なるタイプのCDFとテクニックを試し、どのテクニックが特定の画像に適しているかを見てみましょう:

def custom_rgb_adjustment(image, target_freq):    target_bins = np.arange(256)    freq_bins = [cumulative_distribution(image[:, :, i]) for i in range(3)]    adjusted_channels = []    # 最小周波数で周波数をパッド    padded_freqs = []    for i in range(len(freq_bins)):        if len(freq_bins[i][0]) < 256:            frequencies = list(freq_bins[i][0])            min_pad = [min(frequencies)] * (256 - len(frequencies))            frequencies = min_pad + frequencies        else:            frequencies = freq_bins[i][0]        padded_freqs.append(np.array(frequencies))    for n in range(3):        interpolation = np.interp(padded_freqs[n], target_freq, target_bins)        adjusted_channel = img_as_ubyte(interpolation[image[:, :, n]].astype(int))        adjusted_channels.append([adjusted_channel])    adjusted_image = np.dstack((adjusted_channels[0][0], adjusted_channels[1][0], adjusted_channels[2][0]))    return adjusted_image

# 線形target_bins = np.arange(256)# シグモイドdef sigmoid_cdf(x, a=1): return (1 + np.tanh(a * x)) / 2# 指数関数def exponential_cdf(x, alpha=1): return 1 - np.exp(-alpha * x)# べき関数def power_law_cdf(x, alpha=1): return x ** alpha# その他のテクニックdef adaptive_histogram_equalization(image, clip_limit=0.03, tile_size=(8, 8)): clahe = exposure.equalize_adapthist( image, clip_limit=clip_limit, nbins=256, kernel_size=(tile_size[0], tile_size[1])) return clahedef gamma_correction(image, gamma=1.0): corrected_image = exposure.adjust_gamma(image, gamma) return corrected_imagedef contrast_stretching_percentile(image, lower_percentile=5, upper_percentile=95): in_range = tuple(np.percentile(image, (lower_percentile, upper_percentile))) stretched_image = exposure.rescale_intensity(image, in_range) return stretched_imagedef unsharp_masking(image, radius=5, amount=1.0): blurred_image = filters.gaussian(image, sigma=radius, multichannel=True) sharpened_image = (image + (image - blurred_image) * amount).clip(0, 1) return sharpened_imagedef equalize_hist_rgb(image): equalized_image = exposure.equalize_hist(image) return equalized_imagedef equalize_hist_hsv(image): hsv_image = color.rgb2hsv(image[:,:,:3]) hsv_image[:, :, 2] = exposure.equalize_hist(hsv_image[:, :, 2]) hsv_adjusted = color.hsv2rgb(hsv_image) return hsv_adjusteddef equalize_hist_yuv(image): yuv_image = color.rgb2yuv(image[:,:,:3]) yuv_image[:, :, 0] = exposure.equalize_hist(yuv_image[:, :, 0]) yuv_adjusted = color.yuv2rgb(yuv_image) return yuv_adjusted

# 各テクニックを変数に保存linear = custom_rgb_adjustment(dark_image, np.linspace(0, 1, len(target_bins)))sigmoid = custom_rgb_adjustment(dark_image, sigmoid_cdf((target_bins - 128) / 64, a=1))exponential = custom_rgb_adjustment(dark_image, exponential_cdf(target_bins / 255, alpha=3))power = custom_rgb_adjustment(dark_image, power_law_cdf(target_bins / 255, alpha=2))clahe_image = adaptive_histogram_equalization( dark_image, clip_limit=0.09, tile_size=(50, 50))gamma_corrected_image = gamma_correction(dark_image, gamma=0.4)sharpened_image = unsharp_masking(dark_image, radius=10, amount=-0.98)cs_image = contrast_stretching_percentile(dark_image, 0, 70)equalized_rgb = equalize_hist_rgb(dark_image)equalized_hsv = equalize_hist_hsv(dark_image)equalized_yuv = equalize_hist_yuv(dark_image)

# グラフ作成fig, axes = plt.subplots(3, 4, figsize=(20, 20))# オリジナル画像axes[0, 0].imshow(dark_image)axes[0, 0].set_title('オリジナル画像', fontsize=20)# ヒストグラム平坦化:RGB調整axes[0, 1].imshow(equalized_rgb)axes[0, 1].set_title('RGB調整', fontsize=20)# HSV調整axes[0, 2].imshow(equalized_hsv)axes[0, 2].set_title('HSV調整', fontsize=20)# YUV調整axes[0, 3].imshow(equalized_yuv)axes[0, 3].set_title('YUV調整', fontsize=20)# 線形CDFaxes[1, 0].imshow(linear)axes[1, 0].set_title('線形', fontsize=20)# シグモイドCDFaxes[1, 1].imshow(sigmoid)axes[1, 1].set_title('シグモイド', fontsize=20)# 指数関数CDFaxes[1, 2].imshow(exponential)axes[1, 2].set_title('指数関数', fontsize=20)# べき関数CDFaxes[1, 3].imshow(power)axes[1, 3].set_title('べき関数', fontsize=20)# コントラストストレッチングaxes[2, 0].imshow(cs_image)axes[2, 0].set_title('コントラストストレッチング', fontsize=20)# 適応的ヒストグラム平坦化(CLAHE)axes[2, 1].imshow(clahe_image)axes[2, 1].set_title('適応的ヒストグラム平坦化', fontsize=20)# ガン

Output #7: 各種ヒストグラム操作技術を使用した修正画像のプロット。作者撮影。

RGBで画像を修正するための方法/技術はたくさんありますが、ほとんどの場合、パラメータの手動調整が必要です。Output #7は、さまざまなヒストグラム操作技術を使用して生成された修正画像のプロットを表示しています。

HSVの調整、指数関数、コントラストストレッチ、アンシャープマスキングはすべて満足のいくものと思われます。ただし、結果は使用される元の画像に基づいて異なる場合があります。特定の画像に応じて、さまざまなパラメータの値を試して、目的の画像品質を実現するために実験することができます。

ヒストグラム操作技術は、画像のコントラストと全体的な外観を大幅に向上させることができます。ただし、使用方法には注意が必要です。過度に使用すると、アーティファクトが導入され、不自然な外観になる場合があります。Output #7で使用されるいくつかの技術(例:粒状の背景と過剰強調されたエッジを持つ適応的なヒストグラム均等化)から明らかです。

上記の暗い画像とは対照的に、同じパラメータ値でコードを実行して明るい画像にも試しました。ここで何が起こったかを観察しましょう:

Output #8: Johen Redman氏によるUnsplashのオリジナル写真、他の処理された画像は作者によって実行されました。

お気づきかもしれませんが、暗い画像にうまく機能したほとんどの技術は、明るい画像にはうまく機能しませんでした。HSVの調整、指数関数、アンシャープマスキングなどの技術は、画像にアーティファクトやノイズを追加しました。これは、これらの技術が画像内の既存の明るさを強化または増幅する可能性があるためであり、これにより過曝光やアーティファクトやノイズの追加が生じる可能性があります。

ただし、コントラストストレッチは、予想どおり一部の部分を元の画像よりも明るくするものの、コントラストストレッチは画像内のピクセル値の範囲を拡大して全体的なコントラストを高めるため、明るい画像と暗い画像の両方に使用できる柔軟な解決策を提供します。

結論

このエピソードでは、画像処理の世界に深く入り込み、さまざまな画像の強化技術を探求しました。フーリエ変換(Part 1)、ホワイトバランスアルゴリズム(Part 2)、およびヒストグラム操作技術(Part 3、このPart)と、関連するPythonコードをskimageライブラリを使用してカバーしました。

最終的に、最適な画像の強化技術の選択は、特定の画像と出力品質の要件に依存します。最良の方法として、複数の画像の強化技術を試し、異なるパラメータの値を調整して、目的の画像品質を達成するようにしてください。さまざまな画像の強化技術の影響についてより良い理解を得るのに役立つことを願っています。

画像処理へのこのエキサイティングな旅を続けるにつれ、まだ学びや探求すべきことはたくさんあります。Pythonシリーズのイメージ処理入門の次回のインストールメントでは、さらに高度な技術や応用について説明しますので、お楽しみに!

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

人工知能

「シフトのCEOであるクリス・ナーゲル – インタビューシリーズ」

クリスはSiftの最高経営責任者です彼は、Ping Identityを含むベンチャー支援および公開SaaS企業のシニアリーダーシップポジシ...

人工知能

『ジュリエット・パウエル&アート・クライナー、The AI Dilemma – インタビューシリーズの著者』

『AIのジレンマ』は、ジュリエット・パウエルとアート・クライナーによって書かれましたジュリエット・パウエルは、著者であ...

人工知能

キャルレールの最高製品責任者、ライアン・ジョンソンへのインタビューシリーズ

ライアンは、初期のスタートアップからフォーチュン100の組織まで、多様なテクノロジーと製品開発のリーダーシップ経験を15年...

人工知能

ジョナサン・ダムブロット、Cranium AIのCEO兼共同創設者- インタビューシリーズ

ジョナサン・ダムブロットは、Cranium AIのCEO兼共同創業者ですCranium AIは、サイバーセキュリティおよびデータサイエンスチ...

人工知能

ディープAIの共同創業者兼CEO、ケビン・バラゴナ氏- インタビューシリーズ

ディープAIの創設者であるケビン・バラゴナは、10年以上の経験を持つプロのソフトウェアエンジニア兼製品開発者です彼の目標...

人工知能

ピーター・マッキー、Sonarの開発者担当責任者-インタビューシリーズ

ピーター・マッキーはSonarのDeveloper Relationsの責任者です Sonarは、悪いコードの1兆ドルの課題を解決するプラットフォー...