Sto provando ad abbinare gli istogrammi di due immagini (in MATLAB ciò potrebbe essere fatto usando imhistmatch
). Esiste una funzione equivalente disponibile da una libreria Python standard? Ho esaminato OpenCV, scipy e numpy ma non vedo alcuna funzionalità simile.corrispondenza dell'istogramma di due immagini in Python 2.x?
risposta
Ho scritto in precedenza una risposta here che spiega come eseguire interpolazione lineare a tratti su un istogramma di immagine per imporre particolari rapporti di luci/mezzitoni/ombre.
Gli stessi principi di base sono alla base dello histogram matching tra due immagini. In sostanza si calcola gli istogrammi cumulativi per l'origine e modello di immagini, quindi interpolare linearmente per trovare i valori dei pixel unici l'immagine modello in che corrispondono più da vicino i quantili dei valori dei pixel unici nell'immagine sorgente:
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)
Per esempio:
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)
per un paio di immagini RGB si potrebbe applicare questa funzione separatamente per ciascun canale di colore.
È difficile per me essere sicuro di non avere le immagini di input, ma quello che stai descrivendo sembra un risultato atteso quando c'è meno variazione tonale nell'immagine sorgente rispetto all'obiettivo. Quello che probabilmente sta accadendo all'interno delle aree "solide" nella tua immagine sorgente è che viene amplificata una piccola quantità di variazione casuale per "allungare" l'istogramma in modo che corrisponda a quello del modello. Potrei pensare a un paio di cose che potrebbero essere d'aiuto, ma in generale maggiore è la differenza tra l'istogramma e gli istogrammi del modello, più difficile è ottenere un risultato "bello". –
Per gli altri che leggono questi messaggi, la risposta di Ali ha funzionato bene per me. – ConfusinglyCuriousTheThird
@ali_m: Ho provato questo approccio su un'immagine e una versione ombreggiata di quell'immagine, ma sembra ottenere risultati strani. Qualche idea sul perché questo potrebbe accadere? – Megha
hai provato PIL? – nln
Sì. È un peccato, l'elaborazione delle immagini in Python non è davvero in buona forma. – nln
PIL non sta morendo - il nuovo pacchetto 'pillow' è un'implementazione aggiornata – holdenweb