Aravind
Aravind

Reputation: 543

Making background transparent in JPEG Image by converting to PNG

I'm trying to make the background transparent in the following image. See images below.

Before conversion

Required Image

Using the Opencv and matplotlib, I was able to achieve this.

import cv2
import numpy as np
from matplotlib import pyplot as plt

#== Parameters =======================================================================
BLUR = 21
CANNY_THRESH_1 = 10
CANNY_THRESH_2 = 200
MASK_DILATE_ITER = 10
MASK_ERODE_ITER = 10
MASK_COLOR = (0.0,0.0,1.0) # In BGR format


#== Processing =======================================================================

#-- Read image -----------------------------------------------------------------------
img = cv2.imread('/home/hasher/Documents/30302649.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#-- Edge detection -------------------------------------------------------------------
edges = cv2.Canny(gray, CANNY_THRESH_1, CANNY_THRESH_2)
edges = cv2.dilate(edges, None)
edges = cv2.erode(edges, None)

#-- Find contours in edges, sort by area ---------------------------------------------
contour_info = []
_, contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
for c in contours:
    contour_info.append((
        c,
        cv2.isContourConvex(c),
        cv2.contourArea(c),
    ))
contour_info = sorted(contour_info, key=lambda c: c[2], reverse=True)
max_contour = contour_info[0]

#-- Create empty mask, draw filled polygon on it corresponding to largest contour ----
# Mask is black, polygon is white
mask = np.zeros(edges.shape)
cv2.fillConvexPoly(mask, max_contour[0], (255))



#-- Smooth mask, then blur it --------------------------------------------------------
mask = cv2.dilate(mask, None, iterations=MASK_DILATE_ITER)
mask = cv2.erode(mask, None, iterations=MASK_ERODE_ITER)
mask = cv2.GaussianBlur(mask, (BLUR, BLUR), 0)

mask_stack = np.dstack([mask]*3)    # Create 3-channel alpha mask

#-- Blend masked img into MASK_COLOR background --------------------------------------
mask_stack  = mask_stack.astype('float32') / 255.0          # Use float matrices, 
img         = img.astype('float32') / 255.0                 #  for easy blending

masked = (mask_stack * img) + ((1-mask_stack) * MASK_COLOR) # Blend
masked = (masked * 255).astype('uint8')                     # Convert back to 8-bit 

# plt.imsave('/home/hasher/Documents/girl_blue.png', masked)
# split image into channels
c_red, c_green, c_blue = cv2.split(img)

# merge with mask got on one of a previous steps
img_a = cv2.merge((c_blue, c_green, c_red, mask.astype('float32') / 255.0))

# show on screen (optional in jupiter)
#%matplotlib inline
plt.imshow(img_a)
plt.show()

# save to disk
# cv2.imwrite('/home/hasher/Documents/girl_1.png', img_a*255)

# or the same using plt
plt.imsave('/home/hasher/Documents/transparent.png', img_a)

# cv2.imshow('img', masked)  # Displays red, saves blue

cv2.waitKey()

I was able to convert image to After conversion (See image). But there are minor issues in code. There is some extra details in borders of converted image. I'm not able to figure out. Any help is appreciated.

Samples before conversion. sample-1 sample-2 sample-3 sample-4

Upvotes: 0

Views: 2114

Answers (1)

Kinght 金
Kinght 金

Reputation: 18341

Task: Convert JPEGs with specific color background into transparent PNGs.

(1) JPEGs

enter image description here

(2) For these jpegs, convert them into HSV and split channels. Then We can seperate the target in the V channel because the background is most different with other channels.

enter image description here

(3) Threshold the V channel and do morph-op, then we can get a alpha mask and the png.

enter image description here

enter image description here


The code:

import cv2 
import numpy as np 

fname = "alpha.jpg"
img = cv2.imread(fname)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

v = hsv[:,:,2]
th, threshed = cv2.threshold(v, 100, 255, cv2.THRESH_OTSU|cv2.THRESH_BINARY_INV)
threshed[-1] = 255

cnts = cv2.findContours(threshed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]

mask = np.zeros_like(threshed)
cv2.drawContours(mask, cnts, -1, (255, 0, 0), -1, cv2.LINE_AA)
mask = cv2.erode(mask, np.ones((3,3), np.int32), iterations=1)

png = np.dstack((img, mask))
cv2.imwrite("alpha.png", png)

Upvotes: 1

Related Questions