I'm trying to work out an algorithm that does the following, input is left image, output is right image:
IMPORTANT: Images are dynamically generated and the horizontal lines I need to remove are always in the same position.
In the output I don't really care about the colors, it could be white, and black like this, although that might be a little more complicated:
So far I've came up with this algorithm for modifying the images:
def modify_image(filename):
img = Image.open(filename)
img = img.convert('RGBA')
pixdata = img.load()
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y][0] in range(155, 190): #removes horizontal lines but doesn't change them to the vertical lines color
pixdata[x, y] = (255, 255, 255, 255)
if pixdata[x, y][0] < 135:
pixdata[x, y] = (0, 0, 0, 255)
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y][1] < 195:
pixdata[x, y] = (0, 0, 0, 255)
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y][2] > 0:
pixdata[x, y] = (255, 255, 255, 255)
img = img.resize((1000, 300), Image.NEAREST)
im2 = Image.new('P', img.size, 255)
im = img.convert('P')
img.save(base_path + "current.jpg")
modify_image (base_path + "image.gif")
This turns the images black and white and sometimes succeeds in isolating the middle bar from the background, the thing is, it doesn't remove the horizontal lines and it doesn't certainly change the color to the one of the vertical line either.
Bear in mind that the colors are merely illustrative and dynamic so think about any possible color.
Any suggestions/approaches are greatly appreciated :)
UPDATE: managed to remove horizontal lines but not replace them with the vertical lines color, so that only partially solves the problem (or not at all)
I don't know how accurate this needs to be but, since the positions of the horizontal bars are fixed, could you copy the pixel values from the row immediately preceding each horizontal bar to each row of the horizontal bar.
This will not look perfect because of the gradient background, but it might be enough for your purpose. Looking at the first example image, try this:
import os.path
from PIL import Image
HORIZONTAL_BAND_1_Y = range(37, 64)
HORIZONTAL_BAND_2_Y = range(125, 149)
img = Image.open(filename)
img = img.convert('RGBA')
pixdata = img.load()
for band in HORIZONTAL_BAND_1_Y, HORIZONTAL_BAND_2_Y:
for y in band:
for x in range(img.size[0]):
pixdata[x,y] = pixdata[x, band[0]-1]
new_filename = os.path.join(os.path.dirname(filename),
'new_{}'.format(os.path.basename(filename)))
img.save(new_filename)
Sample ouput
Related
I found a solution to this but the background is not transparent, it's black and I don't know how I should make it transparent.
Here's my code:
from PIL import Image
image = Image.open('pic.jpg')
image.show()
image_data = image.load()
height,width = image.size
for loop1 in range(height):
for loop2 in range(width):
r,g,b = image_data[loop1,loop2]
image_data[loop1,loop2] = r,0,b
image.save('changed.png')
If your background consists of one color, then you can replace it with transparent in the following way:
from PIL import Image
img = Image.open('pic.png')
rgba = img.convert('RGBA')
data = rgba.getdata()
green_rgb = (0, 128, 0) # change it to your exact bg color
new_data = [item if item[:-1] != green_rgb else (255, 255, 255, 0) for item in data]
rgba.putdata(new_data)
rgba.save('changed.png', 'PNG')
However, if your background is of different shades, then you will have to write additional checks and determine the boundaries of acceptable shades of green.
I tried doing this but it takes too long to run. The problem is that the black text in the image is made by a lot of different shades of gray and the color. I also want to remove shades of gray that range between 230 to 255.
How can I do this better?
OLD_PATH = r'C:\Users\avivb\Desktop\Untitled.png'
NEW_PATH = r'C:\Users\avivb\Desktop\test.png'
R_OLD, G_OLD, B_OLD = (range(230,255), range(230,255), range(230,255))
R_NEW, G_NEW, B_NEW = (255, 255, 255)
from PIL import Image
im = Image.open(OLD_PATH)
pixels = im.load()
width, height = im.size
for x in range(width):
for y in range(height):
r, g, b = pixels[x, y]
for i in R_OLD:
for j in G_OLD:
for k in B_OLD:
if (r, g, b) == (i, j, k):
pixels[x, y] = (R_NEW, G_NEW, B_NEW)
im.save(NEW_PATH)
If you are looking for performance, I would avoid as many for statements as I can, as they are slower in python than in other low-level languages (like C or C++).
This is my approach using openCV, should be very fast:
import cv2 as cv
# Set range of color values
lower = np.array([230, 230, 230])
upper = np.array([255, 255, 255])
# Threshold the image to get only selected colors
mask = cv.inRange(img, lower, upper)
# Set the new value to the masked image
img[mask.astype(bool)] = 255
Please note that there are no explicit for in this code!
Hope it helps!
There is no need to iterate over the old ranges like that - use an if on each channel and your speed will greatly improve.
threshold = 230
replacement_pixel = (255, 255, 255)
from PIL import Image
im = Image.open(OLD_PATH)
pixels = im.load()
width, height = im.size
for x in range(width):
for y in range(height):
r, g, b = pixels[x, y]
if r >= threshold and g >= threshold and b >= treshold:
pixels[x, y] = replacement_pixel
im.save(NEW_PATH)
I want to customize the image color to make a similar image with color variants.
Example :
For the above image, I want to replace the red color with other colors like blue, green, yellow, black, etc.
I tried :
from PIL import Image
filename ="./Logo.jpg"
picture = Image.open(filename, 'r')
_colors = [(255, 255, 255), (128, 128, 0), (128, 128, 128), (192, 128, 0), (128, 64, 0), (0, 192, 0), (128, 64, 128), (255, 255, 255)]
width, height = picture.size
for x in range(0, width):
for y in range(0, height):
current_color = picture.getpixel((x,y))
# print (current_color)
if current_color in _colors:
picture.putpixel((x,y), (255,5, 255))
# print ("Y")
picture.save("./test/change.png")
The above code is quite common code which is suggested for most of them.
But it is quite hard to as it replaces the pixel in the list " _colors "
The output image is :
Any solution to the above problem? Any smart way to deal with this using machine learning? Any solution using another programming language?
I'm not familiar with PIL, and I heard that it's slow. So here's a OpenCV version:
# for red color, it's easier to work with negative image
# since hue is in [170, 180] or [0,10]
hsv_inv = cv2.cvtColor(255-img, cv2.COLOR_BGR2HSV)
# these are cyan limit, but we're working on negative image, so...
lower_range = np.array([80,0,0])
upper_range = np.array([100,255,255])
# mask the red
mask = cv2.inRange(hsv_inv, lower_range, upper_range)
# replace red by green
green_hsv = hsv_inv.copy()
green_hsv[np.where(mask)] += np.array([60,0,0], dtype=np.uint8)
green_img = 255 - cv2.cvtColor(green_hsv, cv2.COLOR_HSV2BGR)
purple_hsv = hsv_inv.copy()
purple_hsv[np.where(mask)] -= np.array([30,0,0], dtype=np.uint8)
purple_img = 255 - cv2.cvtColor(purple_hsv, cv2.COLOR_HSV2BGR)
And result, pls ignore the ticks as I showed them by matplotlib.
I'm trying to make a number of white pixels transparent, however I'm doing something wrong.
I can alter the colour of the pixels, however my code seems to ignore any alterations I make to the alpha value. I'm fairly new to PIL, and Python in general, so it's probably a relatively simple mistake.
Here's the code:
image_two = Image.open ("image_two.bmp")
image_two = image_two.convert ("RGBA")
pixels = image_two.load()
for y in xrange (image_two.size[1]):
for x in xrange (image_two.size[0]):
if pixels[x, y] == (0, 0, 0, 255):
pixels[x, y] = (0, 0, 0, 255)
else:
pixels[x, y] = (255, 255, 255, 0)
image_two.save("image_two")
My version of PIL does not support alpha channel in BMP files. I was able to use your code to load a PNG file with alpha. When I try to write that back out to a BMP file, I get a python exception that tells me "IOError: cannot write mode RGBA as BMP".
Your code says:
if pixels[x, y] == (0, 0, 0, 255): #black with alpha of 255
pixels[x, y] = (0, 0, 0, 255) #black with alpha of 255
else:
pixels[x, y] = (255, 255, 255, 0) #white with alpha of 255
A white pixel would have the r,g, and b set to "255". So probably what you want to do is something like this:
if pixels[x,y] == (255,255,255,255):
pixels[x,y] = (pixels[x,y][0], pixels[x,y][1], pixels[x,y][2], 0) #just set this pixel's alpha channel to 0
You may not need an else here, because if the pixels are not white with alpha of 255, you probably don't want to touch them.
I modified your code like this:
import Image
image_two = Image.open ("image_two.png")
image_two = image_two.convert ("RGBA")
pixels = image_two.load()
for y in xrange (image_two.size[1]):
for x in xrange (image_two.size[0]):
if pixels[x, y][3] == 255:
pixels[x, y] = (255, 0, 0, 255)
else:
pixels[x, y] = (255, 255, 255, 255)
image_two.save("image_two2.png")
This code takes my image and writes out a mask - a white pixel where the alpha is 0 and a red pixel where the alpha is 255.
I have a png image as background and I want to add a transparent mesh to this background but this doesn't work as expected. The background image is converted to transparent on places where I apply transparent mesh.
I am doing:
from PIL import Image, ImageDraw
map_background = Image.open(MAP_BACKGROUND_FILE).convert('RGBA')
map_mesh = Image.new('RGBA', (width, height), (0, 0, 0, 0))
draw = ImageDraw.Draw(map_mesh)
# Create mesh using: draw.line([...], fill=(255, 255, 255, 50), width=1)
...
map_background.paste(map_mesh, (0, 0), map_mesh)
But the result is:
You can see a chessboard pattern if you look carefully (used in graphics programs as no background). Transparent lines makes the background layer transparent too in places where both layers met. But I only want the transparent line to be added on top of the background.
I can solve it with:
map_background.paste((255,255,255), (0, 0), map_mesh)
but as I use different colors for different lines, I would have to make for every color this process. If I had 100 colors, then I need 100 layers what is not very good solution.
What you are trying to do is to composite the grid onto the background, and for that you need to use Image.blend or Image.composite. Here's an example using the latter to composite red lines with random alpha values onto a white background:
import Image, ImageDraw, random
background = Image.new('RGB', (100, 100), (255, 255, 255))
foreground = Image.new('RGB', (100, 100), (255, 0, 0))
mask = Image.new('L', (100, 100), 0)
draw = ImageDraw.Draw(mask)
for i in range(5, 100, 10):
draw.line((i, 0, i, 100), fill=random.randrange(256))
draw.line((0, i, 100, i), fill=random.randrange(256))
result = Image.composite(background, foreground, mask)
From left to right:
[background] [mask]
[foreground]
[result]
(If you are happy to write the result back to the background image, then you can use one of the masked versions of Image.paste, as pointed out by Paulo Scardine in a deleted answer.)
I had trouble getting the above examples to work well. Instead, this worked for me:
import numpy as np
import Image
import ImageDraw
def add_craters(image, craterization=20.0, width=256, height=256):
foreground = Image.new('RGBA', (width, height), (0, 0, 0, 0))
draw = ImageDraw.Draw(foreground)
for c in range(0, craterization):
x = np.random.randint(10, width-10)
y = np.random.randint(10, height-10)
radius = np.random.randint(2, 10)
dark_color = (0, 0, 0, 128)
draw.ellipse((x-radius, y-radius, x+radius, y+radius), fill=dark_color)
image_new = Image.composite(foreground, image, foreground)
return image_new