Issue
I have a list of URLs that contain nature images with different resolutions (JPEG/JPG).
I need to make a PNG image with an empty background as a responsive picture frame (full frame) for those images in Python.
I have tried to use Pillow, but it didn't work as expected, and it seems I also need to alter the Picture Frame resolution for each of those images for the image to fit-in.
import urllib.request
from PIL import Image
urllib.request.urlretrieve('https://images.unsplash.com/photo-1666030910636-6291b581962e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1000&q=80', "nature1.jpg")
nature_image = Image.open("nature1.jpg")
picture_frame = Image.open("pict_frame.png")
picture_frame.paste(nature_image, (50,50))
picture_frame.show()
Is there a way to achieve the result I expected? Should I use OpenCV/other libraries?
Picture Frame
Image
Result
Solution
Assume you want to enlarge the frame to fit the picture. Then here is one way to do that in Python/OpenCV.
- Read the picture
- Read the frame with alpha
- Separate the base (bgr) image from the alpha
- Threshold the alpha channel to binary
- Apply morphology to close up any small gaps
- Get contours
- Find the smallest (interior picture area) contour
- Get its bounding box values
- Draw the contour on a copy of the frame to be sure it is correct
- Compute the scaling between the frame and picture
- Resize the frame to fit the picture in the contour region
- Add an opaque alpha channel to the picture
- Put the picture into the resized frame
- Save results
Image:
Frame:
import cv2
import numpy as np
# load image
img = cv2.imread('mountain_scene.png')
hh, ww = img.shape[:2]
# load frame with alpha channel
frame = cv2.imread('frame.png', cv2.IMREAD_UNCHANGED)
# separate BGR and A channels
bgr = frame[:,:,0:3]
alpha = frame[:,:,3]
# threshold alpha to binary
thresh = cv2.threshold(alpha, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# apply morphology to clean up any small gaps
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# get contours and find smallest (interior picture area)
contours = cv2.findContours(morph, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
small_contour = min(contours, key=cv2.contourArea)
x,y,w,h = cv2.boundingRect(small_contour)
# draw contour on copy of frame in opaque red
contour = frame.copy()
cv2.drawContours(contour, [small_contour], 0, (0,0,255,255), 1)
# scale the frame to fit the picture
scale_x = ww/w
scale_y = hh/h
xoff = int(x*scale_x)
yoff = int(y*scale_y)
new_frame = cv2.resize(frame, (0,0), fx=scale_x, fy=scale_y, interpolation=cv2.INTER_LINEAR)
# add opaque alpha to picture
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
# put picture in new_frame
result = new_frame.copy()
result[yoff:yoff+hh, xoff:xoff+ww] = img2
# save results
cv2.imwrite("frame_contour.png", contour)
cv2.imwrite("frame_with_picture.png", result)
# show result
cv2.imshow("contour", contour)
cv2.imshow("frame_with_picture", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Contour on frame:
Picure in frame:
Answered By - fmw42
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.