%pylab inline
from __future__ import print_function
from __future__ import division
Populating the interactive namespace from numpy and matplotlib
** The Original Portrait of Marilyn Monroe and the information of this image: **
portrait = imread('Marilyn_Portrait.jpg')
imshow(portrait)
title('Marilyn Portrait Photo Portrait')
portrait.shape
(292L, 283L, 3L)
** The Original Untitled from Marilyn Monroe in MoMA **
AW_MoMA = imread('MoMA.jpg')
imshow(AW_MoMA)
title('Untitled from Marilyn Monroe')
<matplotlib.text.Text at 0xc523cf8>
** 1. Turn the portrait into a one-layer grayscale image by forming a weighted sum of the R, G, and B layers, and binarize the image. **
portrait_gray = 0.2989 * portrait[:, :, 0] + 0.5870 * portrait[:, :, 1] + 0.1140 * portrait[:, :, 2]
imshow(portrait_gray, cmap = cm.gray)
colorbar()
<matplotlib.colorbar.Colorbar at 0xc86f2e8>
med = median(portrait_gray)
thld = 0.65 * med
portrait_bi = where(portrait_gray > thld, 1, 0)
imshow(portrait_bi, cmap = cm.gray)
colorbar()
title('Marilyn Portrait Photo Portrait - Binarized')
<matplotlib.text.Text at 0xcb1e4e0>
** 2. Create patterns to color different parts. The patterns were drew using Paint on the original photo portrait. **
From observation, there are 4 different color patterns used when create the painting:
Background = imread('Background.jpg')
Face = imread('Face and neck.jpg')
Hair = imread('Hair.jpg')
Lip = imread('Lip and shadow.jpg')
title('Patterns')
subplot(141)
imshow(Background)
subplot(142)
imshow(Face)
subplot(143)
imshow(Hair)
subplot(144)
imshow(Lip)
<matplotlib.image.AxesImage at 0xd0353c8>
** 3. Only keep the pattern draw in red and binarize the patterns **
# Start trying from the first pattern
P1 = Background - portrait
P1_g = 0.2989 * P1[:, :, 0] + 0.5870 * P1[:, :, 1] + 0.1140 * P1[:, :, 2]
imshow(P1_g)
colorbar()
<matplotlib.colorbar.Colorbar at 0xe55d6a0>
thld = 30
P1_b = where(P1_g > thld, 1, 0)
imshow(P1_b, cmap = cm.gray)
colorbar()
<matplotlib.colorbar.Colorbar at 0xe96fe80>
As can be seen, the generated pattern contains salt and pepper noise. Tp remove such noise, use a median filter to remove it.
import scipy.ndimage as ndimage
P1_g_m = ndimage.median_filter(P1_g,4)
imshow(P1_g_m, cmap = cm.gray)
colorbar()
<matplotlib.colorbar.Colorbar at 0x1bbe00b8>
thld = 30
P1_b_m = where(P1_g_m > thld, 1, 0)
imshow(P1_b_m, cmap = cm.gray)
colorbar()
<matplotlib.colorbar.Colorbar at 0x1c068780>
Likewise, generate the other three binary patterns follow the same procedure.
def bi_pattern(pattern, original):
P = pattern - original
P_g = 0.2989 * P[:, :, 0] + 0.5870 * P[:, :, 1] + 0.1140 * P[:, :, 2]
P_g_m = ndimage.median_filter(P_g,4)
thld = 30
P_b = where(P_g_m > thld, 1, 0)
return P_b
P1 = P1_b_m
P2 = bi_pattern(Face, portrait)
P3 = bi_pattern(Hair, portrait)
P4 = bi_pattern(Lip, portrait)
subplot(141)
imshow(P1, cmap = cm.gray)
subplot(142)
imshow(P2, cmap = cm.gray)
subplot(143)
imshow(P3, cmap = cm.gray)
subplot(144)
imshow(P4, cmap = cm.gray)
<matplotlib.image.AxesImage at 0xdf0fbe0>
** 4. Fill the patterns with different random colors. **
# Start trying from Pattern 1
from random import randint
color = np.random.randint(0, 255, size=3)
R = color[0]
G = color[1]
B = color[2]
P1_gray = 255 * uint8(P1)
P1_R = P1_gray * R
P1_G = P1_gray * G
P1_B = P1_gray * B
r, c = P1_gray.shape
P1_c = zeros((r, c, 3), 'uint8')
P1_c[:, :, 0] = P1_R
P1_c[:, :, 1] = P1_G
P1_c[:, :, 2] = P1_B
imshow(P1_c)
<matplotlib.image.AxesImage at 0x1d4f4b00>
Follow the same strategy, generated 4 colored patterns
def color_pattern(pattern):
color = np.random.randint(0, 255, size=3)
R = color[0]
G = color[1]
B = color[2]
P_gray = 255 * uint8(pattern)
P_R = P_gray * R
P_G = P_gray * G
P_B = P_gray * B
r, c = P_gray.shape
P_c = zeros((r, c, 3), 'uint8')
P_c[:, :, 0] = P_R
P_c[:, :, 1] = P_G
P_c[:, :, 2] = P_B
return P_c
P1_c = color_pattern(P1)
P2_c = color_pattern(P2)
P3_c = color_pattern(P3)
P4_c = color_pattern(P4)
subplot(141)
imshow(P1_c)
subplot(142)
imshow(P2_c)
subplot(143)
imshow(P3_c)
subplot(144)
imshow(P4_c)
<matplotlib.image.AxesImage at 0x1da2d0b8>
** 5. Add the colored patterns and the binary portrait to generate the painting effect image. **
r, c = portrait_bi.shape
Paint = zeros((r, c, 3), 'uint8')
P_R = P1_c[:,:,0] + P2_c[:, :, 0] + P3_c[:, :, 0] + P4_c[:,:,0]
P_G = P1_c[:,:,1] + P2_c[:, :, 1] + P3_c[:, :, 1] + P4_c[:,:,1]
P_B = P1_c[:,:,2] + P2_c[:, :, 2] + P3_c[:, :, 2] + P4_c[:,:,2]
Paint[:,:,0] = portrait_bi * P_R
Paint[:,:,1] = portrait_bi * P_G
Paint[:,:,2] = portrait_bi * P_B
imshow(Paint)
<matplotlib.image.AxesImage at 0x1def6b38>
Now the painting has the Andy Warhol's style, but the color sections in the above image is not correct because patterns have overlapping portion.
P2_new = P2 - P1 - P4
imshow(P2_new, cmap = cm.gray)
colorbar()
<matplotlib.colorbar.Colorbar at 0x1ef51ba8>
P2_new = where(P2_new > 0, 1, 0)
imshow(P2_new, cmap = cm.gray)
<matplotlib.image.AxesImage at 0x1f2ca6a0>
The new Pattern 2 should be able to solve the overlapping problem
P2_c = color_pattern(P2_new)
r, c = portrait_bi.shape
Paint = zeros((r, c, 3), 'uint8')
P_R = P1_c[:,:,0] + P2_c[:, :, 0] + P3_c[:, :, 0] + P4_c[:,:,0]
P_G = P1_c[:,:,1] + P2_c[:, :, 1] + P3_c[:, :, 1] + P4_c[:,:,1]
P_B = P1_c[:,:,2] + P2_c[:, :, 2] + P3_c[:, :, 2] + P4_c[:,:,2]
Paint[:,:,0] = portrait_bi * P_R
Paint[:,:,1] = portrait_bi * P_G
Paint[:,:,2] = portrait_bi * P_B
imshow(Paint)
<matplotlib.image.AxesImage at 0x1f630828>
It is now really similar to the Andy Warhol's Untitled from Marilyn Monroe.
** 6. Organize the above approach to generate a filter that creates the painting effect. **
def Andy_Warhol_Monroe(portrait, Pattern1, Pattern2, Pattern3, Pattern4):
# Convert the portrait into a binary image
portrait_gray = 0.2989 * portrait[:, :, 0] + 0.5870 * portrait[:, :, 1] + 0.1140 * portrait[:, :, 2]
med = median(portrait_gray)
thld = 0.65 * med
portrait_bi = where(portrait_gray > thld, 1, 0)
# Deal with patterns
P1 = bi_pattern(Pattern1, portrait)
P2 = bi_pattern(Pattern2, portrait)
P3 = bi_pattern(Pattern3, portrait)
P4 = bi_pattern(Pattern4, portrait)
P2 = P2 - P1 - P4
P2 = where(P2 > 0, 1, 0)
P1_c = color_pattern(P1)
P2_c = color_pattern(P2)
P3_c = color_pattern(P3)
P4_c = color_pattern(P4)
r, c = portrait_bi.shape
Paint = zeros((r, c, 3), 'uint8')
P_R = P1_c[:,:,0] + P2_c[:, :, 0] + P3_c[:, :, 0] + P4_c[:,:,0]
P_G = P1_c[:,:,1] + P2_c[:, :, 1] + P3_c[:, :, 1] + P4_c[:,:,1]
P_B = P1_c[:,:,2] + P2_c[:, :, 2] + P3_c[:, :, 2] + P4_c[:,:,2]
Paint[:,:,0] = portrait_bi * P_R
Paint[:,:,1] = portrait_bi * P_G
Paint[:,:,2] = portrait_bi * P_B
return Paint
** 7. Generate a series of results. **
%matplotlib qt
for i in range(0, 20):
Paint = Andy_Warhol_Monroe(portrait, Background, Face, Hair, Lip)
imshow(Paint)
matplotlib.pyplot.pause(0.2)
i = i+1
%matplotlib inline
Apparently, the above method simulates the Andy Warhol's Untitled from Marilyn Monroe pretty well. However, this filter is only valid for the specific portrait because all the patterns used were generated directly on the original portrait.
Reconsidering the Andy Warhol silk screening printing process effect, the most significant feature is that each painting contains 5 color portions. Therefore, an alternative approach to obtain similar effect would be to separate the original grayscale portrait into 5 levels of grayscale value and color each level differently.
** 1. Take the grayscale portrait. **
portrait_gray = 0.2989 * portrait[:, :, 0] + 0.5870 * portrait[:, :, 1] + 0.1140 * portrait[:, :, 2]
imshow(portrait_gray, cmap = cm.gray)
colorbar()
<matplotlib.colorbar.Colorbar at 0x2ef35ba8>
** 2. Seperate the grayscale portrait into 5 grayscale levels by thresholding. **
# Set evenly distributed levels
level = [51, 102, 153, 204]
row, col = portrait_gray.shape
portrait_5 = zeros((row, col), 'uint8')
# Thresholding
for i in range(0, row):
for j in range(0, col):
if portrait_gray[i,j] < level[0]:
portrait_5[i,j] = 0
if ( portrait_gray[i,j] >= level[0] and portrait_gray[i,j] < level[1] ):
portrait_5[i,j] = level[0]
if ( portrait_gray[i,j] >= level[1] and portrait_gray[i,j] < level[2] ):
portrait_5[i,j] = level[1]
if ( portrait_gray[i,j] >= level[2] and portrait_gray[i,j] < level[3] ):
portrait_5[i,j] = level[2]
if portrait_gray[i,j] > level[3]:
portrait_5[i,j] = level[3]
imshow(portrait_5, cmap = cm.gray)
colorbar()
<matplotlib.colorbar.Colorbar at 0x2bf70278>
** 3. Generate 4 random colors, and color the 5 regions with these 4 colors and black. **
color1 = np.random.randint(0, 255, size=3)
R1 = color1[0]
G1 = color1[1]
B1 = color1[2]
color2 = np.random.randint(0, 255, size=3)
R2 = color2[0]
G2 = color2[1]
B2 = color2[2]
color3 = np.random.randint(0, 255, size=3)
R3 = color3[0]
G3 = color3[1]
B3 = color3[2]
color4 = np.random.randint(0, 255, size=3)
R4 = color4[0]
G4 = color4[1]
B4 = color4[2]
Paint = zeros((row, col, 3), 'uint8')
for i in range(0, row):
for j in range(0, col):
if portrait_5[i,j] == 0:
Paint[i, j, 0] = 0
Paint[i, j, 1] = 0
Paint[i, j, 2] = 0
if portrait_5[i,j] == level[0]:
Paint[i, j, 0] = R1
Paint[i, j, 1] = G1
Paint[i, j, 2] = B1
if portrait_5[i,j] == level[1]:
Paint[i, j, 0] = R2
Paint[i, j, 1] = G2
Paint[i, j, 2] = B2
if portrait_5[i,j] == level[2]:
Paint[i, j, 0] = R3
Paint[i, j, 1] = G3
Paint[i, j, 2] = B3
if portrait_5[i,j] == level[3]:
Paint[i, j, 0] = R4
Paint[i, j, 1] = G4
Paint[i, j, 2] = B4
imshow(Paint)
Although this doesn't give a similar Untitled from Marilyn Monroe paiting, this approach can be applied to other portraits.
** 4. Organize the above approach to generate a filter that creates the painting effect. **
def Portrait_threshold(portrait, level):
portrait_gray = 0.2989 * portrait[:, :, 0] + 0.5870 * portrait[:, :, 1] + 0.1140 * portrait[:, :, 2]
row, col = portrait_gray.shape
portrait_5 = zeros((row, col), 'uint8')
for i in range(0, row):
for j in range(0, col):
if portrait_gray[i,j] < level[0]:
portrait_5[i,j] = 0
if ( portrait_gray[i,j] >= level[0] and portrait_gray[i,j] < level[1] ):
portrait_5[i,j] = level[0]
if ( portrait_gray[i,j] >= level[1] and portrait_gray[i,j] < level[2] ):
portrait_5[i,j] = level[1]
if ( portrait_gray[i,j] >= level[2] and portrait_gray[i,j] < level[3] ):
portrait_5[i,j] = level[2]
if portrait_gray[i,j] > level[3]:
portrait_5[i,j] = level[3]
color1 = np.random.randint(0, 255, size=3)
R1 = color1[0]
G1 = color1[1]
B1 = color1[2]
color2 = np.random.randint(0, 255, size=3)
R2 = color2[0]
G2 = color2[1]
B2 = color2[2]
color3 = np.random.randint(0, 255, size=3)
R3 = color3[0]
G3 = color3[1]
B3 = color3[2]
color4 = np.random.randint(0, 255, size=3)
R4 = color4[0]
G4 = color4[1]
B4 = color4[2]
Paint = zeros((row, col, 3), 'uint8')
for i in range(0, row):
for j in range(0, col):
if portrait_5[i,j] == 0:
Paint[i, j, 0] = 0
Paint[i, j, 1] = 0
Paint[i, j, 2] = 0
if portrait_5[i,j] == level[0]:
Paint[i, j, 0] = R1
Paint[i, j, 1] = G1
Paint[i, j, 2] = B1
if portrait_5[i,j] == level[1]:
Paint[i, j, 0] = R2
Paint[i, j, 1] = G2
Paint[i, j, 2] = B2
if portrait_5[i,j] == level[2]:
Paint[i, j, 0] = R3
Paint[i, j, 1] = G3
Paint[i, j, 2] = B3
if portrait_5[i,j] == level[3]:
Paint[i, j, 0] = R4
Paint[i, j, 1] = G4
Paint[i, j, 2] = B4
return Paint
** 5. Try this filter on an arbitrarily picked portrait. **
portrait_new = imread('maxresdefault.jpg')
level = [51, 102, 153, 204]
portrait_paint = Portrait_threshold(portrait_new, level)
imshow(portrait_paint)
savefig("Paint.png")