2015-09-18 13 views
9

2つの画像のヒストグラムを一致させようとしています(これは、imhistmatchを使って行うことができます)。同等の関数が標準のPythonライブラリから利用できますか?私はOpenCV、scipy、numpyを見てきましたが、同様の機能はありません。Python 2.xでの2つの画像のヒストグラムマッチング?

+0

PILを試しましたか? – nln

+0

うん。残念ですが、pythonでの画像処理は実際には良い形ではありません。 – nln

+1

PILが死んでいない - 最新の 'pillow'パッケージは更新された実装です – holdenweb

答えて

29

私は以前にハイライト/中間色/シャドウの特定の比率を強制するために画像ヒストグラムで区分線形補間を行う方法を説明する回答hereを書きました。

同じ基本原理は、2つの画像の間にhistogram matchingの基礎となります。基本的にあなたが最も密接に元画像で一意の画素値の分位に一致するテンプレート画像にユニークなピクセル値を見つけるために、直線的に補間し、その後、ソースおよびテンプレート画像の累積ヒストグラムを計算するための:

import numpy as np 

def hist_match(source, template): 
    """ 
    Adjust the pixel values of a grayscale image such that its histogram 
    matches that of a target image 

    Arguments: 
    ----------- 
     source: np.ndarray 
      Image to transform; the histogram is computed over the flattened 
      array 
     template: np.ndarray 
      Template image; can have different dimensions to source 
    Returns: 
    ----------- 
     matched: np.ndarray 
      The transformed output image 
    """ 

    oldshape = source.shape 
    source = source.ravel() 
    template = template.ravel() 

    # get the set of unique pixel values and their corresponding indices and 
    # counts 
    s_values, bin_idx, s_counts = np.unique(source, return_inverse=True, 
              return_counts=True) 
    t_values, t_counts = np.unique(template, return_counts=True) 

    # take the cumsum of the counts and normalize by the number of pixels to 
    # get the empirical cumulative distribution functions for the source and 
    # template images (maps pixel value --> quantile) 
    s_quantiles = np.cumsum(s_counts).astype(np.float64) 
    s_quantiles /= s_quantiles[-1] 
    t_quantiles = np.cumsum(t_counts).astype(np.float64) 
    t_quantiles /= t_quantiles[-1] 

    # interpolate linearly to find the pixel values in the template image 
    # that correspond most closely to the quantiles in the source image 
    interp_t_values = np.interp(s_quantiles, t_quantiles, t_values) 

    return interp_t_values[bin_idx].reshape(oldshape) 

を例:

from matplotlib import pyplot as plt 
from scipy.misc import lena, ascent 

source = lena() 
template = ascent() 
matched = hist_match(source, template) 

def ecdf(x): 
    """convenience function for computing the empirical CDF""" 
    vals, counts = np.unique(x, return_counts=True) 
    ecdf = np.cumsum(counts).astype(np.float64) 
    ecdf /= ecdf[-1] 
    return vals, ecdf 

x1, y1 = ecdf(source.ravel()) 
x2, y2 = ecdf(template.ravel()) 
x3, y3 = ecdf(matched.ravel()) 

fig = plt.figure() 
gs = plt.GridSpec(2, 3) 
ax1 = fig.add_subplot(gs[0, 0]) 
ax2 = fig.add_subplot(gs[0, 1], sharex=ax1, sharey=ax1) 
ax3 = fig.add_subplot(gs[0, 2], sharex=ax1, sharey=ax1) 
ax4 = fig.add_subplot(gs[1, :]) 
for aa in (ax1, ax2, ax3): 
    aa.set_axis_off() 

ax1.imshow(source, cmap=plt.cm.gray) 
ax1.set_title('Source') 
ax2.imshow(template, cmap=plt.cm.gray) 
ax2.set_title('template') 
ax3.imshow(matched, cmap=plt.cm.gray) 
ax3.set_title('Matched') 

ax4.plot(x1, y1 * 100, '-r', lw=3, label='Source') 
ax4.plot(x2, y2 * 100, '-k', lw=3, label='Template') 
ax4.plot(x3, y3 * 100, '--r', lw=3, label='Matched') 
ax4.set_xlim(x1[0], x1[-1]) 
ax4.set_xlabel('Pixel value') 
ax4.set_ylabel('Cumulative %') 
ax4.legend(loc=5) 

enter image description here

RGB画像のペアのためには、あなたは、各カラーチャンネルに別々にこの機能を適用することができます。

+0

入力画像を持たないと確信するのは難しいですが、ターゲットと比較してソース画像に色調の変化がはるかに少ない場合には、期待される結果のように表現しています。ソース画像の「ソリッド」領域内で起こっているのは、ヒストグラムをテンプレートのテンプレートに合わせて伸ばすために、少量のランダムなバリエーションが増幅されているということです。私は助けてくれるものがいくつかあると思うかもしれませんが、ソースとテンプレートのヒストグラムの違いが一般に大きくなればなるほど、見栄えのよい結果を得ることが難しくなります。 –

+0

これらの討論会を読む他の人にとって、アリの答えは私にとってはうまくいった。 – ConfusinglyCuriousTheThird

+0

@ali_m:この方法を画像とその画像の影付きのバージョンで試しましたが、奇妙な結果が出るようです。なぜこのようなことが起こるのか? – Megha

関連する問題