farm-recolor / recolorOTAlgo.py
vettorazi's picture
copied local files. docker initial setup
52cbb9c
raw
history blame
3.69 kB
import numpy as np
import cv2
import ot
from PIL import Image
def transfer_channel(source_channel, target_channel):
source_hist, _ = np.histogram(source_channel, bins=256, range=(0, 256))
target_hist, _ = np.histogram(target_channel, bins=256, range=(0, 256))
source_hist = source_hist.astype(np.float64) / source_hist.sum()
target_hist = target_hist.astype(np.float64) / target_hist.sum()
r = np.arange(256).reshape((-1, 1))
c = np.arange(256).reshape((1, -1))
M = (r - c) ** 2
M = M / M.max()
P = ot.emd(source_hist, target_hist, M)
transferred_channel = np.zeros_like(source_channel)
for i in range(256):
transferred_channel[source_channel == i] = P[i].argmax()
return transferred_channel
def optimal_transport_color_transfer(source, target):
source_lab = cv2.cvtColor(source, cv2.COLOR_BGR2Lab).astype(np.float64)
target_lab = cv2.cvtColor(target, cv2.COLOR_BGR2Lab).astype(np.float64)
# Transfer all channels L, a, and b
for ch in range(3):
source_lab[:, :, ch] = transfer_channel(source_lab[:, :, ch], target_lab[:, :, ch])
transferred_rgb = cv2.cvtColor(source_lab.astype(np.uint8), cv2.COLOR_Lab2BGR)
return transferred_rgb
def rgb_to_hex(rgb):
return '#{:02x}{:02x}{:02x}'.format(rgb[0], rgb[1], rgb[2])
def hex_to_rgb(hex):
hex = hex.lstrip('#')
return tuple(int(hex[i:i+2], 16) for i in (0, 2, 4))
def create_color_palette(colors, palette_width=800, palette_height=200):
"""
Receives a list of colors in hex format and creates a palette image
"""
pixels = []
n_colors = len(colors)
for i in range(n_colors):
color = hex_to_rgb(colors[i])
for j in range(palette_width//n_colors * palette_height):
pixels.append(color)
img = Image.new('RGB', (palette_height, palette_width))
img.putdata(pixels)
# img.show()
return img
# if __name__ == "__main__":
# source = cv2.imread("estampa-test.png")
# target = cv2.imread("color.png")
# transferred = optimal_transport_color_transfer(source, target)
# smooth = False#test with true to have a different result.
# if smooth:
# # Apply bilateral filtering
# diameter = 30 # diameter of each pixel neighborhood, adjust based on your image size
# sigma_color = 25 # larger value means colors farther to each other will mix together
# sigma_space = 25 # larger values means farther pixels will influence each other if their colors are close enough
# smoothed = cv2.bilateralFilter(transferred, diameter, sigma_color, sigma_space)
# cv2.imwrite("result_OTA.jpg", smoothed)
# else:
# cv2.imwrite("result_OTA.jpg", transferred)
def recolor(source, colors):
pallete_img = create_color_palette(colors)
palette_bgr = cv2.cvtColor(np.array(pallete_img), cv2.COLOR_RGB2BGR)
recolored = optimal_transport_color_transfer(source, palette_bgr)
smooth = True#test with true for different results.
if smooth:
# Apply bilateral filtering
diameter = 10 # diameter of each pixel neighborhood, adjust based on your image size
sigma_color = 25 # larger value means colors farther to each other will mix together
sigma_space = 15 # larger values means farther pixels will influence each other if their colors are close enough
smoothed = cv2.bilateralFilter(recolored, diameter, sigma_color, sigma_space)
recoloredFile = cv2.imwrite("result.jpg", smoothed, [cv2.IMWRITE_JPEG_QUALITY, 100])
return recoloredFile
else:
recoloredFile = cv2.imwrite("result.jpg", recolored)
return recoloredFile