Last active
July 21, 2020 20:29
-
-
Save Miladiouss/86bed5c53595116dfe18ee0a5c84f838 to your computer and use it in GitHub Desktop.
Image Histogram Transformation.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
from matplotlib import pyplot as plt | |
import torch | |
class HistogramTransform(object): | |
""" | |
Transforms the distribution of the input tensor to match that | |
of the list of template histograms corresponding to each channel. | |
A template historgram must be set initially. | |
Args: | |
tensor (numpy.ndarray): | |
Image to transform; the histogram is computed over the flattened | |
array | |
noise_range (float): Default is 0. A uniform noise ranged between | |
(-noise_range, +noise_range) will be added to pixels randomly. | |
Returns: | |
histogram transformed tensor: | |
The output tensor type matches the input, either numpy.ndarray or torch.Tensor. | |
""" | |
def __init__(self, template_histograms): | |
''' | |
Args: | |
template_histograms: | |
A list of template histograms. | |
Each template histogram must consist of the tuple | |
(counts (numpy.ndarray), bins (numpy.ndarray)). | |
template_histograms is a list of numpy.histogram outputs, | |
each corresponding to each channel of the input tensor to be transformed. | |
If 1 channel, still feed as a list, i.e. [(counts, bin)]. | |
Example: | |
Assuming img is made by ToTensor(some pil image) and has 3 (RGB) channels, one can get the histogram as such: | |
histR = np.histogram(img[0].numpy().ravel(), bins = 256, range = [0, 1]) | |
histG = np.histogram(img[1].numpy().ravel(), bins = 256, range = [0, 1]) | |
histB = np.histogram(img[2].numpy().ravel(), bins = 256, range = [0, 1]) | |
''' | |
self.template_histograms = template_histograms | |
self.num_channels = len(template_histograms) | |
def __call__(self, tensor, noise_range = 0, dtype = torch.float32): | |
""" | |
Transforms the distribution of the input tensor to match that | |
of the template histogram. If a list of histograms is provided | |
and it maches the number of channels of the input tensor, each | |
channel will be transformed with the corresponding histogram. | |
This funciton utilises histogram_tranform_1D for an easier user interface. | |
Args: | |
tensor (numpy.ndarray): | |
Image to transform; the histogram is computed over the | |
flattened array for each channel. | |
noise_range (float): Default is 0. A uniform noise ranged between | |
(-noise_range, +noise_range) will be added to pixels randomly. | |
Returns: | |
histogram transformed tensor: | |
The output tensor type matches the input, either numpy.ndarray or torch.Tensor. | |
""" | |
tensorType = type(tensor) | |
tensor = np.asanyarray(tensor) | |
channels = [] | |
for c, templateHisto in enumerate(self.template_histograms): | |
channels.append(self.histogram_transform_1D(tensor[c], templateHisto, noise_range = noise_range)) | |
transformed_tensor = np.asanyarray(channels) | |
# Convert to the original type | |
if tensorType == torch.Tensor: | |
transformed_tensor = torch.tensor(transformed_tensor, dtype=dtype) | |
return transformed_tensor | |
# Core of the computation, to be used by histogram_transform method internally | |
def histogram_transform_1D(self, tensor, template_histogram, noise_range = 0): | |
""" | |
Transforms the distribution of the input tensor to match that | |
of the template histogram. | |
Input tensor will be flattened, transformed, and rearranged | |
to the original shape. | |
Mainly intended for call by class functions. | |
Args: | |
tensor (numpy.ndarray): Image to transform; the histogram is computed | |
over the flattened array. | |
template_histogram (tubple of (numpy.ndarray, numpy.ndarray)): | |
The template histogram consisiting of a tuple of (counts, bins). | |
See (the output of) numpy.histogram. | |
noise_range (float): Default is 0. A uniform noise ranged between | |
(-noise_range, +noise_range) will be added to pixels randomly. | |
Returns: | |
histogram transformed array (numpy.ndarray): | |
The transformed output tensor/image that maches the input | |
""" | |
# === Template Histogram === | |
# t_... stands for template_ | |
t_counts, t_bins = template_histogram | |
# t_bin_idx not required | |
# Take the cumsum of the counts and normalize by the number of pixels to | |
# Get the empirical cumulative distribution functions | |
# (maps value --> quantile) | |
t_quantiles = np.cumsum(t_counts).astype(np.float32) | |
t_quantiles /= t_quantiles[-1] | |
# === Input Tensor === | |
# Convert to flattened numpy array | |
tensor = np.asanyarray(tensor) | |
originalShape = tensor.shape | |
tensor = tensor.ravel() | |
# Get counts, bins, and corresponding bin indices for each tensor value | |
counts, bins = np.histogram(tensor, bins = t_bins) | |
bin_idx = np.searchsorted(t_bins[:-2], tensor) | |
# See comments for t_quantiles | |
quantiles = np.cumsum(counts).astype(np.float32) | |
quantiles /= quantiles[-1] | |
# === Histogram Transformation === | |
# interpolate linearly to find the pixel values in the template image | |
# that corresponds most closely to the quantiles for the input tensor | |
interp_t_values = np.interp(quantiles, t_quantiles, t_bins[:-1]) | |
tensor_transformed = interp_t_values[bin_idx] | |
noise = np.random.uniform( | |
low=-noise_range, | |
high=+noise_range, | |
size=(len(tensor_transformed)) | |
) | |
tensor_transformed += noise | |
tensor_transformed = np.maximum(tensor_transformed, min(t_bins)) | |
return tensor_transformed.reshape(originalShape) | |
def histo_plot(img, plt_name = ""): | |
""" | |
Calculates and plots the histogram of the input image. | |
img must be a CHW np.array bounded between 0 and 1. | |
""" | |
img = np.asanyarray(img) | |
histR = np.histogram(img[0].ravel(), bins = 256, range = [0, 1]) | |
histB = np.histogram(img[1].ravel(), bins = 256, range = [0, 1]) | |
histG = np.histogram(img[2].ravel(), bins = 256, range = [0, 1]) | |
fig, (ax_left, ax_right) = plt.subplots( | |
nrows=1, | |
ncols=2, | |
gridspec_kw = {'width_ratios':[1, 2]}, | |
figsize=(14, 6) | |
) | |
fig.set_facecolor('white') | |
imgHWC = img.transpose((1, 2, 0)) | |
ax_left.imshow(imgHWC) | |
ax_left.get_xaxis().set_visible(False) | |
ax_left.get_yaxis().set_visible(False) | |
# ax_right.imshow(img2) | |
Plot = ax_right.scatter; | |
marker = 'o' | |
alpha = 0.15 | |
_ = Plot(histR[-1][:-1], histR[0], marker='o', alpha=alpha, color="r"); | |
_ = Plot(histG[-1][:-1], histG[0], marker='o', alpha=alpha, color="g"); | |
_ = Plot(histB[-1][:-1], histB[0], marker='o', alpha=alpha, color="b"); | |
# ax_right.set_ylim(top=200) | |
ax_right.set_yscale('symlog') | |
fig.suptitle(plt_name, fontsize=16) | |
plt.show() | |
def module_test(): | |
import torchvision.transforms as transforms | |
TT = transforms.ToTensor() | |
TPIL = transforms.ToPILImage() | |
import numpy as np | |
from PIL import Image | |
from urllib.request import urlopen | |
url1 = "data:image/png;base64,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%0AAABJRU5ErkJggg==" | |
url2 = "data:image/png;base64,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%0ArkJggg==" | |
# TT automatically converts a PIL image to a CHW torch.tensor bounded between 0 and 1 | |
img1 = TT(Image.open(urlopen(url1))) | |
img2 = TT(Image.open(urlopen(url2))) | |
from HistogramTransformClass import histo_plot | |
import HistogramTransformClass as HTC | |
# Extract histograms for each channel of the template image | |
histR2 = np.histogram(img2[0].numpy().ravel(), bins = 256, range = [0, 1]) | |
histB2 = np.histogram(img2[1].numpy().ravel(), bins = 256, range = [0, 1]) | |
histG2 = np.histogram(img2[2].numpy().ravel(), bins = 256, range = [0, 1]) | |
# Create a histogram transformation object, initialize with channel histograms | |
HT = HTC.HistogramTransform(template_histograms=(histR2, histG2, histB2)) | |
# Transform the image | |
img3 = HT(img1) | |
# Plot histograms | |
histo_plot(img1, "Original Image") | |
histo_plot(img2, "Template Image") | |
histo_plot(img3, "Transformed Image") | |
if __name__ == "__main__": | |
module_test() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment