1

I have two images, content and generated. I want to create a new image with the Y channel of generated and the U and V channels of content. With PIL, I think that I should be able to use .convert('YCbCr') to convert my RGB input images to YUV. Then after I have created the new image, I can convert that to RGB with .convert('RGB').

The images enter the function below in an RGB format:

def original_colors(content, generated):
    generated_y = generated.convert('YCbCr')
    content_uv = content_uv.convert('YCbCr')
    # Combine Y from generated_y with U and V from content_uv
    # and convert the resulting output back to RGB. 
    return output

What is the best/most efficient way to combine the channels into a new image?


Here's the solution I went with:

# Combine the Y channel of the generated image and the UV/CbCr channels of the
# content image to perform color-independent style transfer.
def original_colors(content, generated):
    content_channels = list(content.convert('YCbCr').split())
    generated_channels = list(generated.convert('YCbCr').split())
    content_channels[0] = generated_channels[0]
    return Image.merge('YCbCr', content_channels).convert('RGB') 
ProGamerGov
  • 870
  • 1
  • 10
  • 23

1 Answers1

3

If the alpha_composite() function doesn't do what you want then you can load the images into data arrays, and use slicing to replace the relevant parts of the array. The following appears to work using two images:

from PIL import Image
import numpy

# open images and make the same size
image1 = Image.open("image1.jpg").resize((256, 256))
image2 = Image.open("image2.jpg").resize((256, 256))

# convert tp YCbCr
image1_y = image1.convert('YCbCr')
image2_y = image2.convert('YCbCr')

# load image data into arrays
image1_y_array = numpy.array(image1_y)
image2_y_array = numpy.array(image2_y)

# show shape and size of arrays
print (image1_y_array.shape)
print (image2_y_array.shape)

#print (image1_y_array[:,:,0])    # uncomment to see actual data
#print (image2_y_array[:,:,0])    # uncomment to see actual data

# replace image 1 Y channel with image 2 Y channel
# assume 1st [0] channel is the Y
image1_y_array[:,:,0] = image2_y_array[:,:,0]

# create new image from the updated array data
new_image1_y = Image.fromarray(image1_y_array)

# and show the result
new_image1_y.show()

# can now convert new_image1_y back to jpeg, etc.

When the image data is loaded into the arrays, one can see the 3 channels of data from the array shapes output:

(256, 256, 3)
(256, 256, 3)

I've assumed the 0-index channel is the Y-channel, if not then replace the magic number 0 with 1 or 2 as needed.

NB obviously the images should be the same size. I hope this may help.

Edit:

It's also possible to do the same thing without using numpy:

from PIL import Image

# open images and make the same size
image1 = Image.open("image1.jpg").resize((256, 256))
image2 = Image.open("image2.jpg").resize((256, 256))

# convert tp YCbCr
image1_y = image1.convert('YCbCr')
image2_y = image2.convert('YCbCr')

# split image data
image1_y_data = image1_y.split()
image2_y_data = image2_y.split()

# replace image 1 Y channel with image 2 Y channel
# assume 1st [0] channel is the Y
image1_y_data_list = list(image1_y_data)
image2_y_data_list = list(image2_y_data)

image1_y_data_list[0] = image2_y_data_list[0]

# create new image from the updated data
new_image1_y = Image.merge('YCbCr', image1_y_data_list)

# and show the result
new_image1_y.show()

# can now convert new_image1_y back to jpeg, etc.
AS Mackay
  • 2,831
  • 9
  • 19
  • 25