I am trying to create a radial gradient that fades to the clear background. My goal is to create this gradient and paste it as the background to another image.
So far, i have been able to create a circular gradient, but it i not transparent. i found the code below in stackoverflow :
imgsize=(650,650)
image = Image.new('RGBA', imgsize)
innerColor = [153,0,0]
for y in range(imgsize[1]):
for x in range(imgsize[0]):
distanceToCenter = math.sqrt((x - imgsize[0]/2) ** 2 + (y - imgsize[1]/2) ** 2)
distanceToCenter = float(distanceToCenter) / (math.sqrt(2) * imgsize[0]/2)
r = distanceToCenter + innerColor[0] * (1 - distanceToCenter)
g = distanceToCenter + innerColor[1] * (1 - distanceToCenter)
b = distanceToCenter + innerColor[2] * (1 - distanceToCenter)
image.putpixel((x, y), (int(r), int(g), int(b)))
this is the image that is produced
i would like to not have it fade to black, but to clear instead.
thank you for your help :)
I think I would just draw your solid red and calculate a transparency layer with Numpy and push it in:
#!/usr/bin/env python3
import numpy as np
from PIL import Image
# Define width and height of image
W, H = 650, 650
# Create solid red image
im = Image.new(mode='RGB', size=(W,H), color=(153,0,0))
# Create radial alpha/transparency layer. 255 in centre, 0 at edge
Y = np.linspace(-1, 1, H)[None, :]*255
X = np.linspace(-1, 1, W)[:, None]*255
alpha = np.sqrt(X**2 + Y**2)
alpha = 255 - np.clip(0,255,alpha)
# Push that radial gradient transparency onto red image and save
im.putalpha(Image.fromarray(alpha.astype(np.uint8)))
im.save('result.png')
Keywords: Python, image processing, radial gradient, alpha, transparency.
It's simple math: white/opaque is 255, so you need i-th dimension to be innerColor[i] + distanceToCenter * (255 - innerColor[i]) and add transparency parameter to pixel tuple int((1 - distanceToCenter) * 255):
imgsize=(650,650)
image = Image.new('RGBA', imgsize)
innerColor = [153,0,0]
for y in range(imgsize[1]):
for x in range(imgsize[0]):
distanceToCenter = math.sqrt((x - imgsize[0]/2) ** 2 + (y - imgsize[1]/2) ** 2)
distanceToCenter = float(distanceToCenter) / (math.sqrt(2) * imgsize[0]/2)
r = innerColor[0] + distanceToCenter * (255 - innerColor[0])
g = innerColor[1] + distanceToCenter * (255 - innerColor[1])
b = innerColor[2] + distanceToCenter * (255 - innerColor[2])
# Or just
# r = innerColor[0]
# g = innerColor[1]
# b = innerColor[2]
# if not blending with white as you get farther away from the center.
image.putpixel((x, y), (int(r), int(g), int(b), int((1 - distanceToCenter) * 255)))
Result is:
I wanted to detect and delete watermarks from the images using python. I have tried opencv but couldn't make it. Below is the sample image. Here time is watermark on all images.
Image attached here
Sample code attached
#coding:utf-8
import os
import os.path
#import Image
def hasBlackAround(x, y, distance, img):
w, h = img.size
startX = 0 if x - distance < 0 else x - distance
startY = 0 if y - distance < 0 else y - distance
endX = w - 1 if x + distance > w - 1 else x + distance
endY = h - 1 if y + distance > h - 1 else y + distance
hasBlackAround = False
for j in range(startX, endX):
for k in range(startY, endY):
r, g, b = img.getpixel((j, k))
#if r < 130 and g < 130 and b < 130:
if r >= 255 and g >= 255 and b >= 255:
return True
return False
from PIL import Image
img = Image.open("E:/Projects/Analysis/unnamed.png")
w, h = img.size
rgb_im = img.convert('RGB')
for x in range(0, w - 1):
for y in range(0, h - 1):
if not hasBlackAround(x, y, 1, rgb_im):
rgb_im.putpixel((x, y), (255,255,255))
rgb_im.save("E:/Projects/Analysis/1_output_v1.jpg")
Additional Information about Images:
1. It is always bottom left corner
2. It is transparent
3. Color is always the same
Inpainting technique reconstructs the image by filling any gaps in the image.
Please try the inpainting method in opencv:
cv2.inpaint()
http://docs.opencv.org/3.0-beta/modules/photo/doc/inpainting.html
I am experimenting with JPEG compression using python. I load in a tiff image and store it as numpy uint8 RGB array. I was doing this for color mapping.
def rgb2ycbcr(im):
cbcr = np.empty_like(im)
r = im[:,:,0]
g = im[:,:,1]
b = im[:,:,2]
# Y
cbcr[:,:,0] = .299 * r + .587 * g + .114 * b
# Cb
cbcr[:,:,1] = 128 - .169 * r - .331 * g + .5 * b
# Cr
cbcr[:,:,2] = 128 + .5 * r - .419 * g - .081 * b
return np.uint8(cbcr)
def ycbcr2rgb(im):
rgb = np.empty_like(im)
y = im[:,:,0]
cb = im[:,:,1] - 128
cr = im[:,:,2] - 128
# R
rgb[:,:,0] = y + 1.402 * cr
# G
rgb[:,:,1] = y - .34414 * cb - .71414 * cr
# B
rgb[:,:,2] = y + 1.772 * cb
return np.uint8(rgb)
I did a simple RGB to YCbCr transformation followed with a inverse transformation.
img = rgb2ycbcr(img)
imshow(img)
img = ycbcr2rgb(img)
imshow(img)
I got these two output image as YCbCr and RGB output after the color space transformation.
It seems that something is wrong with my color conversion and I cannot figure out what is wrong. I was using the JPEG color space conversion provided by
Wikipedia. Thanks you for the help.
You have to do your intermediate calculations in floating point. The posterization should tip you off; you have a lot of "hot" (saturated) pixels.
def rgb2ycbcr(im):
xform = np.array([[.299, .587, .114], [-.1687, -.3313, .5], [.5, -.4187, -.0813]])
ycbcr = im.dot(xform.T)
ycbcr[:,:,[1,2]] += 128
return np.uint8(ycbcr)
def ycbcr2rgb(im):
xform = np.array([[1, 0, 1.402], [1, -0.34414, -.71414], [1, 1.772, 0]])
rgb = im.astype(np.float)
rgb[:,:,[1,2]] -= 128
rgb = rgb.dot(xform.T)
np.putmask(rgb, rgb > 255, 255)
np.putmask(rgb, rgb < 0, 0)
return np.uint8(rgb)
Hi I am creating a program that replaces a face in a image with someone else's face. However, I am stuck on trying to insert the new face into the original, larger image. I have researched ROI and addWeight(needs the images to be the same size) but I haven't found a way to do this in python. Any advise is great. I am new to opencv.
I am using the following test images:
smaller_image:
larger_image:
Here is my Code so far... a mixer of other samples:
import cv2
import cv2.cv as cv
import sys
import numpy
def detect(img, cascade):
rects = cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors=3, minSize=(10, 10), flags = cv.CV_HAAR_SCALE_IMAGE)
if len(rects) == 0:
return []
rects[:,2:] += rects[:,:2]
return rects
def draw_rects(img, rects, color):
for x1, y1, x2, y2 in rects:
cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
if __name__ == '__main__':
if len(sys.argv) != 2: ## Check for error in usage syntax
print "Usage : python faces.py <image_file>"
else:
img = cv2.imread(sys.argv[1],cv2.CV_LOAD_IMAGE_COLOR) ## Read image file
if (img == None):
print "Could not open or find the image"
else:
cascade = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml")
gray = cv2.cvtColor(img, cv.CV_BGR2GRAY)
gray = cv2.equalizeHist(gray)
rects = detect(gray, cascade)
## Extract face coordinates
x1 = rects[0][3]
y1 = rects[0][0]
x2 = rects[0][4]
y2 = rects[0][5]
y=y2-y1
x=x2-x1
## Extract face ROI
faceROI = gray[x1:x2, y1:y2]
## Show face ROI
cv2.imshow('Display face ROI', faceROI)
small = cv2.imread("average_face.png",cv2.CV_LOAD_IMAGE_COLOR)
print "here"
small=cv2.resize(small, (x, y))
cv2.namedWindow('Display image') ## create window for display
cv2.imshow('Display image', small) ## Show image in the window
print "size of image: ", img.shape ## print size of image
cv2.waitKey(1000)
A simple way to achieve what you want:
import cv2
s_img = cv2.imread("smaller_image.png")
l_img = cv2.imread("larger_image.jpg")
x_offset=y_offset=50
l_img[y_offset:y_offset+s_img.shape[0], x_offset:x_offset+s_img.shape[1]] = s_img
Update
I suppose you want to take care of the alpha channel too. Here is a quick and dirty way of doing so:
s_img = cv2.imread("smaller_image.png", -1)
y1, y2 = y_offset, y_offset + s_img.shape[0]
x1, x2 = x_offset, x_offset + s_img.shape[1]
alpha_s = s_img[:, :, 3] / 255.0
alpha_l = 1.0 - alpha_s
for c in range(0, 3):
l_img[y1:y2, x1:x2, c] = (alpha_s * s_img[:, :, c] +
alpha_l * l_img[y1:y2, x1:x2, c])
Using #fireant's idea, I wrote up a function to handle overlays. This works well for any position argument (including negative positions).
def overlay_image_alpha(img, img_overlay, x, y, alpha_mask):
"""Overlay `img_overlay` onto `img` at (x, y) and blend using `alpha_mask`.
`alpha_mask` must have same HxW as `img_overlay` and values in range [0, 1].
"""
# Image ranges
y1, y2 = max(0, y), min(img.shape[0], y + img_overlay.shape[0])
x1, x2 = max(0, x), min(img.shape[1], x + img_overlay.shape[1])
# Overlay ranges
y1o, y2o = max(0, -y), min(img_overlay.shape[0], img.shape[0] - y)
x1o, x2o = max(0, -x), min(img_overlay.shape[1], img.shape[1] - x)
# Exit if nothing to do
if y1 >= y2 or x1 >= x2 or y1o >= y2o or x1o >= x2o:
return
# Blend overlay within the determined ranges
img_crop = img[y1:y2, x1:x2]
img_overlay_crop = img_overlay[y1o:y2o, x1o:x2o]
alpha = alpha_mask[y1o:y2o, x1o:x2o, np.newaxis]
alpha_inv = 1.0 - alpha
img_crop[:] = alpha * img_overlay_crop + alpha_inv * img_crop
Example usage:
import numpy as np
from PIL import Image
# Prepare inputs
x, y = 50, 0
img = np.array(Image.open("img_large.jpg"))
img_overlay_rgba = np.array(Image.open("img_small.png"))
# Perform blending
alpha_mask = img_overlay_rgba[:, :, 3] / 255.0
img_result = img[:, :, :3].copy()
img_overlay = img_overlay_rgba[:, :, :3]
overlay_image_alpha(img_result, img_overlay, x, y, alpha_mask)
# Save result
Image.fromarray(img_result).save("img_result.jpg")
Result:
If you encounter errors or unusual outputs, please ensure:
img should not contain an alpha channel. (e.g. If it is RGBA, convert to RGB first.)
img_overlay has the same number of channels as img.
Based on fireant's excellent answer above, here is the alpha blending but a bit more human legible. You may need to swap 1.0-alpha and alpha depending on which direction you're merging (mine is swapped from fireant's answer).
o* == s_img.*
b* == b_img.*
for c in range(0,3):
alpha = s_img[oy:oy+height, ox:ox+width, 3] / 255.0
color = s_img[oy:oy+height, ox:ox+width, c] * (1.0-alpha)
beta = l_img[by:by+height, bx:bx+width, c] * (alpha)
l_img[by:by+height, bx:bx+width, c] = color + beta
Here it is:
def put4ChannelImageOn4ChannelImage(back, fore, x, y):
rows, cols, channels = fore.shape
trans_indices = fore[...,3] != 0 # Where not transparent
overlay_copy = back[y:y+rows, x:x+cols]
overlay_copy[trans_indices] = fore[trans_indices]
back[y:y+rows, x:x+cols] = overlay_copy
#test
background = np.zeros((1000, 1000, 4), np.uint8)
background[:] = (127, 127, 127, 1)
overlay = cv2.imread('imagee.png', cv2.IMREAD_UNCHANGED)
put4ChannelImageOn4ChannelImage(background, overlay, 5, 5)
A simple function that blits an image front onto an image back and returns the result. It works with both 3 and 4-channel images and deals with the alpha channel. Overlaps are handled as well.
The output image has the same size as back, but always 4 channels.
The output alpha channel is given by (u+v)/(1+uv) where u,v are the alpha channels of the front and back image and -1 <= u,v <= 1. Where there is no overlap with front, the alpha value from back is taken.
import cv2
def merge_image(back, front, x,y):
# convert to rgba
if back.shape[2] == 3:
back = cv2.cvtColor(back, cv2.COLOR_BGR2BGRA)
if front.shape[2] == 3:
front = cv2.cvtColor(front, cv2.COLOR_BGR2BGRA)
# crop the overlay from both images
bh,bw = back.shape[:2]
fh,fw = front.shape[:2]
x1, x2 = max(x, 0), min(x+fw, bw)
y1, y2 = max(y, 0), min(y+fh, bh)
front_cropped = front[y1-y:y2-y, x1-x:x2-x]
back_cropped = back[y1:y2, x1:x2]
alpha_front = front_cropped[:,:,3:4] / 255
alpha_back = back_cropped[:,:,3:4] / 255
# replace an area in result with overlay
result = back.copy()
print(f'af: {alpha_front.shape}\nab: {alpha_back.shape}\nfront_cropped: {front_cropped.shape}\nback_cropped: {back_cropped.shape}')
result[y1:y2, x1:x2, :3] = alpha_front * front_cropped[:,:,:3] + (1-alpha_front) * back_cropped[:,:,:3]
result[y1:y2, x1:x2, 3:4] = (alpha_front + alpha_back) / (1 + alpha_front*alpha_back) * 255
return result
For just add an alpha channel to s_img I just use cv2.addWeighted before the line
l_img[y_offset:y_offset+s_img.shape[0], x_offset:x_offset+s_img.shape[1]] = s_img
as following:
s_img=cv2.addWeighted(l_img[y_offset:y_offset+s_img.shape[0], x_offset:x_offset+s_img.shape[1]],0.5,s_img,0.5,0)
When attempting to write to the destination image using any of these answers above and you get the following error:
ValueError: assignment destination is read-only
A quick potential fix is to set the WRITEABLE flag to true.
img.setflags(write=1)
A simple 4on4 pasting function that works-
def paste(background,foreground,pos=(0,0)):
#get position and crop pasting area if needed
x = pos[0]
y = pos[1]
bgWidth = background.shape[0]
bgHeight = background.shape[1]
frWidth = foreground.shape[0]
frHeight = foreground.shape[1]
width = bgWidth-x
height = bgHeight-y
if frWidth<width:
width = frWidth
if frHeight<height:
height = frHeight
# normalize alpha channels from 0-255 to 0-1
alpha_background = background[x:x+width,y:y+height,3] / 255.0
alpha_foreground = foreground[:width,:height,3] / 255.0
# set adjusted colors
for color in range(0, 3):
fr = alpha_foreground * foreground[:width,:height,color]
bg = alpha_background * background[x:x+width,y:y+height,color] * (1 - alpha_foreground)
background[x:x+width,y:y+height,color] = fr+bg
# set adjusted alpha and denormalize back to 0-255
background[x:x+width,y:y+height,3] = (1 - (1 - alpha_foreground) * (1 - alpha_background)) * 255
return background
I reworked #fireant's concept to allow for optional alpha masks and allow any x or y, including values outside of the bounds of the image. It will crop to the bounds.
def overlay_image_alpha(img, img_overlay, x, y, alpha_mask=None):
"""Overlay `img_overlay` onto `img` at (x, y) and blend using optional `alpha_mask`.
`alpha_mask` must have same HxW as `img_overlay` and values in range [0, 1].
"""
if y < 0 or y + img_overlay.shape[0] > img.shape[0] or x < 0 or x + img_overlay.shape[1] > img.shape[1]:
y_origin = 0 if y > 0 else -y
y_end = img_overlay.shape[0] if y < 0 else min(img.shape[0] - y, img_overlay.shape[0])
x_origin = 0 if x > 0 else -x
x_end = img_overlay.shape[1] if x < 0 else min(img.shape[1] - x, img_overlay.shape[1])
img_overlay_crop = img_overlay[y_origin:y_end, x_origin:x_end]
alpha = alpha_mask[y_origin:y_end, x_origin:x_end] if alpha_mask is not None else None
else:
img_overlay_crop = img_overlay
alpha = alpha_mask
y1 = max(y, 0)
y2 = min(img.shape[0], y1 + img_overlay_crop.shape[0])
x1 = max(x, 0)
x2 = min(img.shape[1], x1 + img_overlay_crop.shape[1])
img_crop = img[y1:y2, x1:x2]
img_crop[:] = alpha * img_overlay_crop + (1.0 - alpha) * img_crop if alpha is not None else img_overlay_crop