web-dev-qa-db-ja.com

OpenCVでPythonで画像の平均色を見つける方法は?

私はこのコードを試しました:

import cv2
image = cv2.imread("sample.jpg")
pixel = image[200, 550]
print pixel

しかし、私は次のようにエラーが発生しています:

「Nonetype」属性エラーなしgetitem

このエラーは、コードの3行目を実行した後に表示されます。

19

エラーを修正する方法

このエラーが発生する可能性のある原因は2つあります。

  1. ファイル名のスペルが間違っています。
  2. イメージファイルは現在の作業ディレクトリにありません。

この問題を修正するには、ファイル名のスペルが正しいことを確認し(大文字と小文字を区別して大文字と小文字を区別してチェックします)、画像ファイルが現在の作業ディレクトリにあることを確認する必要があります(ここには2つのオプションがあります:= IDEまたはファイルのフルパスを指定)。

平均色と支配的な色

次に、「平均色」を計算するには、その意味を決定する必要があります。グレースケール画像では、画像全体のグレーレベルの平均にすぎませんが、色では「平均」というものはありません。実際、色は通常3次元ベクトルで表されますが、グレーレベルはスカラーです。スカラーの平均化は問題ありませんが、ベクトルの平均化は意味がありません。

画像を色成分に分離し、各成分の平均を取ることは、可能な方法です。ただし、この方法では意味のない色が生成される場合があります。本当に必要なのは、平均色ではなく、支配的な色です。

実装

コードをゆっくり見ていきましょう。必要なモジュールをインポートし、イメージを読み取ることから始めます。

import cv2
import numpy as np
from skimage import io

img = io.imread('https://i.stack.imgur.com/DNM65.png')[:, :, :-1]

次に、@ Ruan Bによって提案された方法に類似した方法に従って、各有彩色チャネルの平均を計算できます。

average = img.mean(axis=0).mean(axis=0)

次に、 k-means clustering を適用して、イメージの最も代表的な色でパレットを作成します(このおもちゃの例では、n_colors5に設定されていました)。

pixels = np.float32(img.reshape(-1, 3))

n_colors = 5
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 200, .1)
flags = cv2.KMEANS_RANDOM_CENTERS

_, labels, palette = cv2.kmeans(pixels, n_colors, None, criteria, 10, flags)
_, counts = np.unique(labels, return_counts=True)

そして最後に、支配的な色は、量子化された画像で最も頻繁に発生するパレット色です。

dominant = palette[np.argmax(counts)]

結果の比較

両方のアプローチの違いを説明するために、次のサンプル画像を使用しました。

lego

平均色、つまり、3つの有彩色チャネルの平均値を成分とする色、およびk-meansクラスタリングから計算される支配的な色の取得値はかなり異なります。

In [30]: average
Out[30]: array([91.63179156, 69.30190754, 58.11971896])

In [31]: dominant
Out[31]: array([179.3999  ,  27.341282,   2.294441], dtype=float32)

両方のアプローチの違いをよりよく理解するために、これらの色がどのように見えるかを見てみましょう。下の図の左側には、平均色が表示されています。計算された平均色は、元の画像の色の内容を適切に説明していないことが明らかになります。実際、元の画像にはその色のピクセルは1つもありません。図の右側には、重要度(発生頻度)の降順で上から下にソートされた5つの最も代表的な色が示されています。このパレットは、支配的な色が赤であることを明らかにします。これは、元の画像の均一な色の最大領域が赤のレゴ作品に対応するという事実と一致しています。

Results

これは、上の図を生成するために使用されるコードです。

import matplotlib.pyplot as plt

avg_patch = np.ones(shape=img.shape, dtype=np.uint8)*np.uint8(average)

indices = np.argsort(counts)[::-1]   
freqs = np.cumsum(np.hstack([[0], counts[indices]/counts.sum()]))
rows = np.int_(img.shape[0]*freqs)

dom_patch = np.zeros(shape=img.shape, dtype=np.uint8)
for i in range(len(rows) - 1):
    dom_patch[rows[i]:rows[i + 1], :, :] += np.uint8(palette[indices[i]])

fig, (ax0, ax1) = plt.subplots(1, 2, figsize=(12,6))
ax0.imshow(avg_patch)
ax0.set_title('Average color')
ax0.axis('off')
ax1.imshow(dom_patch)
ax1.set_title('Dominant colors')
ax1.axis('off')
plt.show(fig)

TL; DR回答

要約すると、@ Ruan B.の回答で提案されているように、平均色の計算は数学的な観点から技術的に正しいにもかかわらず、得られた結果は画像の色の内容を適切に表していない可能性があります。より賢明なアプローチは、ベクトル量子化(クラスタリング)によって支配的な色を決定することです。

51
Tonechas

次を使用して平均色を取得できました。

import cv2
import numpy
myimg = cv2.imread('image.jpg')
avg_color_per_row = numpy.average(myimg, axis=0)
avg_color = numpy.average(avg_color_per_row, axis=0)
print(avg_color)

結果:

[ 197.53434769  217.88439451  209.63799938]

私が参照した素晴らしいリソース

19
Ruan B.