Fixes RCT and sets it as the color transfer algorithm

Fixes a bug in RCT where uint8 (1-255) images were passed in, but cv.cvtColor expects a float32 (0-1)

Changes the Sample Processor to use RCT for it's random color transfer feature
This commit is contained in:
Jeremy Hummel 2019-08-10 07:09:22 -07:00
commit 36b623d5ef
3 changed files with 44 additions and 21 deletions

View file

@ -327,10 +327,9 @@ class ConverterMasked(Converter):
cv2.BORDER_TRANSPARENT), 0, 1.0)]
prd_face_bgr = imagelib.reinhard_color_transfer(
np.clip((prd_face_bgr * 255).astype(np.uint8), 0, 255),
np.clip((dst_face_bgr * 255).astype(np.uint8), 0, 255),
prd_face_bgr,
dst_face_bgr,
source_mask=prd_face_mask_a, target_mask=prd_face_mask_a)
prd_face_bgr = np.clip(prd_face_bgr.astype(np.float32) / 255.0, 0.0, 1.0)
if debug:
debugs += [np.clip(cv2.warpAffine(prd_face_bgr, face_output_mat, img_size,
@ -444,10 +443,9 @@ class ConverterMasked(Converter):
cv2.BORDER_TRANSPARENT), 0, 1.0)]
new_out_face_bgr = imagelib.reinhard_color_transfer(
np.clip((out_face_bgr * 255).astype(np.uint8), 0, 255),
np.clip((dst_face_bgr * 255).astype(np.uint8), 0, 255),
out_face_bgr,
dst_face_bgr,
source_mask=face_mask_blurry_aaa, target_mask=face_mask_blurry_aaa)
new_out_face_bgr = np.clip(new_out_face_bgr.astype(np.float32) / 255.0, 0.0, 1.0)
if debug:
debugs += [np.clip(cv2.warpAffine(new_out_face_bgr, face_output_mat, img_size,

View file

@ -11,16 +11,21 @@ def reinhard_color_transfer(target, source, clip=False, preserve_paper=False, so
This implementation is (loosely) based on to the "Color Transfer
between Images" paper by Reinhard et al., 2001.
Title: "Super fast color transfer between images"
Author: Adrian Rosebrock
Date: June 30. 2014
Url: https://www.pyimagesearch.com/2014/06/30/super-fast-color-transfer-images/
Parameters:
-------
source: NumPy array
OpenCV image in BGR color space (the source image)
target: NumPy array
OpenCV image in BGR color space (the target image)
OpenCV image (w, h, 3) in BGR color space (float32) 0-1
target: NumPy array (float32)
OpenCV image (w, h, 3) in BGR color space (float32), 0-1
clip: Should components of L*a*b* image be scaled by np.clip before
converting back to BGR color space?
If False then components will be min-max scaled appropriately.
Clipping will keep target image brightness truer to the input.
Clipping will keep target image brightness truer to the input.30
Scaling will adjust image brightness to avoid washed out portions
in the resulting color transfer that can be caused by clipping.
preserve_paper: Should color transfer strictly follow methodology
@ -33,14 +38,17 @@ def reinhard_color_transfer(target, source, clip=False, preserve_paper=False, so
Returns:
-------
transfer: NumPy array
OpenCV image (w, h, 3) NumPy array (uint8)
OpenCV image (w, h, 3) NumPy array (float32)
"""
np.clip(source, 0, 1, out=source)
np.clip(target, 0, 1, out=target)
# convert the images from the RGB to L*ab* color space, being
# sure to utilizing the floating point data type (note: OpenCV
# expects floats to be 32-bit, so use that instead of 64-bit)
source = cv2.cvtColor(source, cv2.COLOR_BGR2LAB).astype(np.float32)
target = cv2.cvtColor(target, cv2.COLOR_BGR2LAB).astype(np.float32)
source = cv2.cvtColor(source.astype(np.float32), cv2.COLOR_BGR2LAB)
target = cv2.cvtColor(target.astype(np.float32), cv2.COLOR_BGR2LAB)
# compute color statistics for the source and target images
src_input = source if source_mask is None else source * source_mask
@ -77,10 +85,10 @@ def reinhard_color_transfer(target, source, clip=False, preserve_paper=False, so
b = _scale_array(b, clip=clip)
# merge the channels together and convert back to the RGB color
# space, being sure to utilize the 8-bit unsigned integer data
# type
# space
transfer = cv2.merge([l, a, b])
transfer = cv2.cvtColor(transfer.astype(np.uint8), cv2.COLOR_LAB2BGR)
transfer = cv2.cvtColor(transfer, cv2.COLOR_LAB2BGR)
np.clip(transfer, 0, 1, out=transfer)
# return the color transferred image
return transfer
@ -92,6 +100,11 @@ def linear_color_transfer(target_img, source_img, mode='pca', eps=1e-5):
using a linear transform.
Images are expected to be of form (w,h,c) and float in [0,1].
Modes are chol, pca or sym for different choices of basis.
Title: "NeuralImageSynthesis / ExampleNotebooks / ScaleControl.ipynb"
Author: Leon Gatys
Date: December 14, 2016
Url: https://github.com/leongatys/NeuralImageSynthesis/blob/master/ExampleNotebooks/ScaleControl.ipynb
"""
mu_t = target_img.mean(0).mean(0)
t = target_img - mu_t
@ -125,6 +138,22 @@ def linear_color_transfer(target_img, source_img, mode='pca', eps=1e-5):
return matched_img
def lab_linear_color_transform(target_img, source_img, eps=1e-5, mode='pca'):
np.clip(source_img, 0, 1, out=source_img)
np.clip(target_img, 0, 1, out=target_img)
# convert the images from the RGB to L*ab* color space, being
# sure to utilizing the floating point data type (note: OpenCV
# expects floats to be 32-bit, so use that instead of 64-bit)
source_img = cv2.cvtColor(source_img.astype(np.float32), cv2.COLOR_BGR2LAB)
target_img = cv2.cvtColor(target_img.astype(np.float32), cv2.COLOR_BGR2LAB)
target_img = linear_color_transfer(target_img, source_img, mode=mode, eps=eps)
target_img = cv2.cvtColor(target_img, cv2.COLOR_LAB2BGR)
np.clip(target_img, 0, 1, out=target_img)
return target_img
def lab_image_stats(image):
# compute the mean and standard deviation of each channel
(l, a, b) = cv2.split(image)

View file

@ -223,11 +223,7 @@ class SampleProcessor(object):
if apply_ct and ct_sample is not None:
if ct_sample_bgr is None:
ct_sample_bgr = ct_sample.load_bgr()
# ct_sample_bgr_resized = cv2.resize(ct_sample_bgr, (resolution, resolution), cv2.INTER_LINEAR)
img_bgr = imagelib.reinhard_color_transfer(img_bgr, ct_sample_bgr[..., 0:3])
img_bgr = np.clip(img_bgr, 0.0, 1.0)
img_bgr = imagelib.reinhard_color_transfer(img_bgr, ct_sample_bgr, clip=True)
if normalize_std_dev:
img_bgr = (img_bgr - img_bgr.mean((0, 1))) / img_bgr.std((0, 1))