feng2022's picture
Time_TravelRephotography
47c46ea
raw
history blame
5.82 kB
from argparse import (
ArgumentParser,
Namespace,
)
import os
from os.path import join as pjoin
from typing import Optional
import sys
import numpy as np
import cv2
from skimage import exposure
# sys.path.append('Face_Detection')
# from align_warp_back_multiple_dlib import match_histograms
def calculate_cdf(histogram):
"""
This method calculates the cumulative distribution function
:param array histogram: The values of the histogram
:return: normalized_cdf: The normalized cumulative distribution function
:rtype: array
"""
# Get the cumulative sum of the elements
cdf = histogram.cumsum()
# Normalize the cdf
normalized_cdf = cdf / float(cdf.max())
return normalized_cdf
def calculate_lookup(src_cdf, ref_cdf):
"""
This method creates the lookup table
:param array src_cdf: The cdf for the source image
:param array ref_cdf: The cdf for the reference image
:return: lookup_table: The lookup table
:rtype: array
"""
lookup_table = np.zeros(256)
lookup_val = 0
for src_pixel_val in range(len(src_cdf)):
lookup_val
for ref_pixel_val in range(len(ref_cdf)):
if ref_cdf[ref_pixel_val] >= src_cdf[src_pixel_val]:
lookup_val = ref_pixel_val
break
lookup_table[src_pixel_val] = lookup_val
return lookup_table
def match_histograms(src_image, ref_image, src_mask=None, ref_mask=None):
"""
This method matches the source image histogram to the
reference signal
:param image src_image: The original source image
:param image ref_image: The reference image
:return: image_after_matching
:rtype: image (array)
"""
# Split the images into the different color channels
# b means blue, g means green and r means red
src_b, src_g, src_r = cv2.split(src_image)
ref_b, ref_g, ref_r = cv2.split(ref_image)
def rv(im):
if ref_mask is None:
return im.flatten()
return im[ref_mask]
def sv(im):
if src_mask is None:
return im.flatten()
return im[src_mask]
# Compute the b, g, and r histograms separately
# The flatten() Numpy method returns a copy of the array c
# collapsed into one dimension.
src_hist_blue, bin_0 = np.histogram(sv(src_b), 256, [0, 256])
src_hist_green, bin_1 = np.histogram(sv(src_g), 256, [0, 256])
src_hist_red, bin_2 = np.histogram(sv(src_r), 256, [0, 256])
ref_hist_blue, bin_3 = np.histogram(rv(ref_b), 256, [0, 256])
ref_hist_green, bin_4 = np.histogram(rv(ref_g), 256, [0, 256])
ref_hist_red, bin_5 = np.histogram(rv(ref_r), 256, [0, 256])
# Compute the normalized cdf for the source and reference image
src_cdf_blue = calculate_cdf(src_hist_blue)
src_cdf_green = calculate_cdf(src_hist_green)
src_cdf_red = calculate_cdf(src_hist_red)
ref_cdf_blue = calculate_cdf(ref_hist_blue)
ref_cdf_green = calculate_cdf(ref_hist_green)
ref_cdf_red = calculate_cdf(ref_hist_red)
# Make a separate lookup table for each color
blue_lookup_table = calculate_lookup(src_cdf_blue, ref_cdf_blue)
green_lookup_table = calculate_lookup(src_cdf_green, ref_cdf_green)
red_lookup_table = calculate_lookup(src_cdf_red, ref_cdf_red)
# Use the lookup function to transform the colors of the original
# source image
blue_after_transform = cv2.LUT(src_b, blue_lookup_table)
green_after_transform = cv2.LUT(src_g, green_lookup_table)
red_after_transform = cv2.LUT(src_r, red_lookup_table)
# Put the image back together
image_after_matching = cv2.merge([blue_after_transform, green_after_transform, red_after_transform])
image_after_matching = cv2.convertScaleAbs(image_after_matching)
return image_after_matching
def convert_to_BW(im, mode):
if mode == "b":
gray = im[..., 0]
elif mode == "gb":
gray = (im[..., 0].astype(float) + im[..., 1]) / 2.0
else:
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
gray = gray.astype(np.uint8)
return np.stack([gray] * 3, axis=-1)
def parse_args(args=None, namespace: Optional[Namespace] = None):
parser = ArgumentParser('match histogram of src to ref')
parser.add_argument('src')
parser.add_argument('ref')
parser.add_argument('--out', default=None, help="converted src that matches ref")
parser.add_argument('--src_mask', default=None, help="mask on which to match the histogram")
parser.add_argument('--ref_mask', default=None, help="mask on which to match the histogram")
parser.add_argument('--spectral_sensitivity', choices=['b', 'gb', 'g'], help="match the histogram of corresponding sensitive channel(s)")
parser.add_argument('--crop', type=int, default=0, help="crop the boundary to match")
return parser.parse_args(args=args, namespace=namespace)
def main(args):
A = cv2.imread(args.ref)
A = convert_to_BW(A, args.spectral_sensitivity)
B = cv2.imread(args.src, 0)
B = np.stack((B,) * 3, axis=-1)
mask_A = cv2.resize(cv2.imread(args.ref_mask, 0), A.shape[:2][::-1],
interpolation=cv2.INTER_NEAREST) > 0 if args.ref_mask else None
mask_B = cv2.resize(cv2.imread(args.src_mask, 0), B.shape[:2][::-1],
interpolation=cv2.INTER_NEAREST) > 0 if args.src_mask else None
if args.crop > 0:
c = args.crop
bc = int(c / A.shape[0] * B.shape[0] + 0.5)
A = A[c:-c, c:-c]
B = B[bc:-bc, bc:-bc]
B = match_histograms(B, A, src_mask=mask_B, ref_mask=mask_A)
# B = exposure.match_histograms(B, A, multichannel=True)
if args.out:
os.makedirs(os.path.dirname(args.out), exist_ok=True)
cv2.imwrite(args.out, B)
return B
if __name__ == "__main__":
main(parse_args())