Converting Image from RGBA to RGB reveals padding, how to crop that? - python

I'm trying to convert an image from RGBA to RGB. But the conversion to RGB is adding padding to the image. How can I convert without the padding? Or how can I remove it?
img = Image.open('path.png')
img.save("img.png")
rgb_im = img.convert('RGB')
rgb_im.save("rgb_im.png")
Thank you. Images below, before and after conversion:

If you open your first image, you'll see that the canvas is larger than the visible image, as you have a transparent frame represented by pixels having rgba=(255,255,255,0). When you remove the alpha channel by converting RGBA to RGB, that transparency disappear, as only rgb=(255,255,255) remains, which turns out to be the white you see in the second image.
So you want to make something similar to what's suggested here
from PIL import Image, ImageChops
def trim_and_convert(im):
bg = Image.new(im.mode, im.size, (255,255,255,0))
diff = ImageChops.difference(im, bg)
diff = ImageChops.add(diff, diff, 2.0, -100)
bbox = diff.getbbox()
if bbox:
return im.crop(bbox).convert('RGB')
im = Image.open("path.png")
rgb_im = trim_and_convert(im)
rgb_im.save("rgb_im.png")

Related

Transparent border of the image Pillow

I have a list of images 512 x 512 pixels. I need to make only 32 pixels of every image transparent (from every side), so I can combine those images together into a mosaic. I've found how to change the opacity of the whole image, but not the border. I would be happy with any help!
Here is my code for changing the opacity
for item in dem_fps:
img = Image.open(item)
img.putalpha(127)
Create a copy of image you need to keep for 100% opacity. Put the opacity to the main image, and at last, paste the copied image to the original image. Opacity at the border done.
from PIL import Image
padding = 32
opacity = 127
img = Image.open("image.png").convert('RGBA')
x, y, w, h = padding, padding, img.width - padding, img.height - padding
img_cropped = img.crop((x, y, w, h))
img.putalpha(127)
img.paste(img_cropped, (x, y))
img.save('image_new.png')
img = Image.open(‘image.png’)
rgba = img.convert(“RGBA”)
data = rgba.load()
data[0,0]=(data[0,0][0],data[0,0][1],data[0,0][2],127)
img.save(filename)
This code first converts the image to RGBA, which allows us to modify the alpha (a) channel, that determines the transparency of an image. The image is loaded into an array, where it is easier to read and modify pixel values. This code only modifies the pixel at (0,0) ,but you can put it in a loop to modify the pixels on the border of your image.
EDIT - This should work -
for y in range(img.height):
for x in range(img.width):
if any([x<32,x>img.width-32,y<32,y>img.height-32]):
lo[x,y]=(lo[x,y][0],lo[x,y][1],lo[x,y][2],127)
Input -
Output -
As you want the same alpha/transparency channel in all images, I would tend to want to make it once up front, outside the loop, then simply push it into each image.
from PIL import Image, ImageOps
# Make a single alpha channel in advance = 512x512 black and 448x448 white square in middle
border = 32
alpha = Image.new('L', (512-2*border,512-2*border), "white")
alpha = ImageOps.expand(alpha, border)
alpha.save('DEBUG-alpha.png')
And your code then becomes very simply:
dem_fps = ["1.png", "2.png", "3.png"]
# Open each image in turn and push in our ready-made alpha channel
for item in dem_fps:
im = Image.open(item).convert('RGB')
im.putalpha(alpha)
im.save(f'RESULT-{item}')
The alpha channel (DEBUG-alpha.png) looks like this:
Of course, you could equally construct the alpha channel by making a 512x512 black square and drawing a white rectangle in the middle if that is conceptually easier for you.

Converting Image to Black and White vs Converting Image to Monochrome(Grey Levels) in Python

I have the image below filename = '1.png':
Whenever, I tried converting it to monochrome using the code below, the image is just the same as the input image.
image_counter = 1
path = 'sample/' + str(image_counter) + '.png'
image = Image.open(path).convert('L') # Convert it into monochrome.
image = Image.fromarray(image)
image.save('monochrome.png')
Monochrome Output:
But when i convert it to a black and white image, the output is different and produces not straight borders.
image_counter = 1
path = 'sample/' + str(image_counter) + '.png'
image = Image.open(path).convert('1') # Convert it into black and white.
image = Image.fromarray(image)
image.save('blackandwhite.png')
When zoomed, you can really observed the not straight borders.
Why is it?
The default method of converting a greyscale (“L”) or “RGB” image into
a bilevel (mode “1”) image uses Floyd-Steinberg dither to approximate
the original image luminosity levels. If dither is NONE, all non-zero
values are set to 255 (white).
Dithering method, used when converting from mode “RGB” to “P” or from
“RGB” or “L” to “1”. Available methods are NONE or FLOYDSTEINBERG
(default).

how to add an alpha channel of particular value in an BGR image

I tried the below code, it doesn't show any error and runs properly, but changing the value of the alpha channel, doesn't show any change in image
img3 = cv2.cvtColor(img2, cv2.COLOR_BGR2BGRA)
img3[:,:,3] = 100
cv2.imshow('img1',img2)
cv2.imshow('img',img3)
cv2.waitKey(0)
works ok, but the output of both images are same and there is no seen-able change after applying alpha channel
i have already tried the below code
Your code is actually correct.
The simple answer is that OpenCV's imshow() ignores transparency, so if you want to see its effect, save your image as a PNG/TIFF (both of which support transparency) and view it with a different viewer - such as GIMP, Photoshop or feh.
As an alternative, I made a wrapper/decorator for OpenCV's imshow() that displays images with transparency overlaid on a chessboard like Photoshop does. So, starting with this RGBA Paddington image and this grey+alpha Paddington image:
#!/usr/bin/env python3
import cv2
import numpy as np
def imshow(title,im):
"""Decorator for OpenCV "imshow()" to handle images with transparency"""
# Check we got np.uint8, 2-channel (grey + alpha) or 4-channel RGBA image
if (im.dtype == np.uint8) and (len(im.shape)==3) and (im.shape[2] in set([2,4])):
# Pick up the alpha channel and delete from original
alpha = im[...,-1]/255.0
im = np.delete(im, -1, -1)
# Promote greyscale image to RGB to make coding simpler
if len(im.shape) == 2:
im = np.stack((im,im,im))
h, w, _ = im.shape
# Make a checkerboard background image same size, dark squares are grey(102), light squares are grey(152)
f = lambda i, j: 102 + 50*((i+j)%2)
bg = np.fromfunction(np.vectorize(f), (16,16)).astype(np.uint8)
# Resize to square same length as longer side (so squares stay square), then trim
if h>w:
longer = h
else:
longer = w
bg = cv2.resize(bg, (longer,longer), interpolation=cv2.INTER_NEAREST)
# Trim to correct size
bg = bg[:h,:w]
# Blend, using result = alpha*overlay + (1-alpha)*background
im = (alpha[...,None] * im + (1.0-alpha[...,None])*bg[...,None]).astype(np.uint8)
cv2.imshow(title,im)
if __name__ == "__main__":
# Open RGBA image
im = cv2.imread('paddington.png',cv2.IMREAD_UNCHANGED)
imshow("Paddington (RGBA)",im)
key = cv2.waitKey(0)
cv2.destroyAllWindows()
# Open Grey + alpha image
im = cv2.imread('paddington-ga.png',cv2.IMREAD_UNCHANGED)
imshow("Paddington (grey + alpha)",im)
key = cv2.waitKey(0)
cv2.destroyAllWindows()
And you will get this:
and this:
Keywords: Image, image processing, Python, alpha channel, transparency, overlay, checkerboard, chessboard, blend, blending. OpenCV, imshow, cv2.imshow.

How to change the color of a pixel using PIL?

I was trying to change pixel of an image in python using this question. If mode is 0, it changes first pixel in top right corner of image to grey(#C8C8C8). But it doesn't change. There is not enough documentation about draw.point(). What is the problem with this code?
import random
from PIL import Image, ImageDraw
mode = 0
image = Image.open("dom.jpg")
draw = ImageDraw.Draw(image)
width = image.size[0]
height = image.size[1]
pix = image.load()
string = "kod"
n = 0
if (mode == 0):
draw.point((0, 0), (200, 200, 200))
if(mode == 1):
print(pix[0,0][0])
image.save("dom.jpg", "JPEG")
del draw
Is using PIL a must in your case? If not then consider using OpenCV (cv2) for altering particular pixels of image.
Code which alter (0,0) pixel to (200,200,200) looks following way in opencv:
import cv2
img = cv2.imread('yourimage.jpg')
height = img.shape[0]
width = img.shape[1]
img[0][0] = [200,200,200]
cv2.imwrite('newimage.bmp',img)
Note that this code saves image in .bmp format - cv2 can also write .jpg images, but as jpg is generally lossy format, some small details might be lost. Keep in mind that in cv2 [0][0] is left upper corner and first value is y-coordinate of pixel, while second is x-coordinate, additionally color are three values from 0 to 255 (inclusive) in BGR order rather than RGB.
For OpenCV tutorials, including installation see this.

Converting 1-layer image to 3-layer image

I'm trying to convert a 1-layer (grey-scale) image to a 3-layer RGB image. Below is the code I'm using. This runs without error but doesn't create the correct result.
from PIL import Image # used for loading images
def convertLToRgb(img):
height = img.size[1]
width = img.size[0]
size = img.size
mode = 'RGB'
data = np.zeros((height, width, 3))
for i in range(height):
for j in range(width):
pixel = img.getpixel((j, i))
data[i][j][0] = pixel
data[i][j][1] = pixel
data[i][j][2] = pixel
img = Image.frombuffer(mode, size, data)
return img
What am I doing wrong here? I'm not expecting a color picture, but I am expecting a black and white picture resembling the input. Below are the input and output images:
Depending on the bit depth of your image, change:
data = np.zeros((height, width, 3))
to:
data = np.zeros((height, width, 3), dtype=np.uint8)
For an 8-bit image, you need to force your Numpy array dtype to an unsigned 8-bit integer, otherwise it defaults to float64. For 16-bit, use np.uint16, etc.
What is your task? black-white image or RGB color image. If you want to convert the gray image to the black-white image. You can directly convert the image into a binary image. As for your code, two things you need to care. Firstly, the location of the pixel is right, the wrong location will make the image all black like your post. Secondly, you only can convert the RGB to grayscale image directly, but you can not convert the grayscale image to RGB directly, because it may be not accurate.
You can do it with the PIL.Image and PIL.ImageOps as shown below. Because of the way it's written, the source image isn't required to be one layer—it will convert it to one if necessary before using it:
from PIL import Image
from PIL.ImageOps import grayscale
def convertLToRgb(src):
src.load()
band = src if Image.getmodebands(src.mode) == 1 else grayscale(src)
return Image.merge('RGB', (band, band, band))
src = 'whale_tail.png'
bw_img = Image.open(src)
rgb_img = convertLToRgb(bw_img)
rgb_img.show()

Categories

Resources