I am trying to trying to teach myself python from the net. Have no previous prg experience. need help on how to write a function to remove red from an imgae using the 'image' module in python. The code i tried to write is below ...do help
import image
img = image.Image("luther.jpg")
newimg = image.EmptyImage(img.getWidth(),img.getHeight())
win = image.ImageWin()
def no_red():
for col in range(img.getWidth()):
for row in range(img.getHeight()):
p = img.getPixel(col,row)
newred = 0
newgreen = p.getGreen()
newblue = p.getBlue()
newpixel = image.Pixel(newred,newgreen,newblue)
return newimg.setPixel(col,row,newpixel)
print (newimg.getPixel(45,52))
win.exitonclick()
what am i doing wrong? Any guidance will be helpful :)
Regards >>
You have to call newimg.setPixel inside the for loops, otherwise it'll only replace one pixel all the way at the end of the image:
def no_red():
for col in range(img.getWidth()):
for row in range(img.getHeight()):
p = img.getPixel(col,row)
newred = 0
newgreen = p.getGreen()
newblue = p.getBlue()
newpixel = image.Pixel(newred,newgreen,newblue)
newimg.setPixel(col,row,newpixel)
You also aren't calling no_red anywhere. I recommend writing something like this instead:
def no_red(image):
new_image = ... # make a copy of image
# original code that removes red subpixels
return new_image
And then calling:
new_image = no_red(original_image)
for i in range(img.getWidth()):
for j in range(img.getHeight()):
old_pixel = img.getPixel(i, j)
new_pixel = image.Pixel(0, old_pixel.getGreen(), old_pixel.getBlue())
new_image.setPixel(i, j, new_pixel)
This is the simplest way to do it in my opinion
If you don't want to get stuck with the loops, you may find the following lines more useful. The following lines removes RED from the image test.
def removeColour():
img = Image.open('test.png').convert('RGB')
source = img.split()
R, G, B = 0, 1, 2
out = source[R].point(lambda i: i * 0)
source[R].paste(out, None, None)
img = Image.merge(img.mode, source)
img.save('testNoRed.png')
img.show()
Related
I am trying to read all the touching pixels with the same color in a image.
For that I use reccursive functions. When I check one pixel, I look on the right, left, top and bottom if the pixel close to it is the same color. If it is I add it to an array otherwise I don't.
The code is as follow:
vimport tkinter as tk
from PIL import Image
import sys
sys.setrecursionlimit(200000)
## WINDOWS
# to launch in debug mode
imgToDraw = Image.open('assets-test\\smile-face.png')
# to launch normaly
# imgToDraw = Image.open('..\\assets-test\\smile-face.png')
## LINUX
# imgToDraw = Image.open('../assets-test/smile-face.png')
imgPixels = imgToDraw.load()
imgWidth = imgToDraw.size[0]
imgHeight = imgToDraw.size[1]
# an element is a part of the image, it's a bunch of pixels with approximately the same color
# and each pixel touch at least one other pixel of the same element
elements = [];
isPixelChecked = [[ False for y in range( imgWidth ) ] for x in range( imgHeight )]
# min tolerable difference between two colors to consider them the same
# the higher the value is the more colors will be considered the same
COLOR_TOLERANCE = 10
reccursionCount = 0
class Element:
def __init__(self, color):
self.pixels = [];
self.color = color;
def addPixel(self, pixel):
self.pixels.append(pixel);
class Pixel:
def __init__(self, x, y, color):
self.x = x # x position of the pixel
self.y = y # y position of the pixel
self.color = color # color is a tuple (r,g,b)
def cutImageInElements():
global element
completeElement(element.pixels)
def completeElement(elemPixels):
global reccursionCount
global isPixelChecked
reccursionCount += 1
nbPixels = len(elemPixels);
xIndex = elemPixels[nbPixels - 1].x
yIndex = elemPixels[nbPixels - 1].y
xRightIdx = elemPixels[nbPixels - 1].x + 1
xLeftIdx = elemPixels[nbPixels - 1].x - 1
yBottomIdx = elemPixels[nbPixels - 1].y + 1
yTopIdx = elemPixels[nbPixels - 1].y - 1
isPixelChecked[xIndex][yIndex] = True
if((xRightIdx < imgWidth) and isPixelChecked[xRightIdx][yIndex] == False):
if(isColorAlmostSame(imgPixels[elemPixels[0].x, elemPixels[0].y], imgPixels[xRightIdx, yIndex])):
pixelAppended = Pixel(xRightIdx, yIndex, imgPixels[xRightIdx, yIndex])
elemPixels.append(pixelAppended)
completeElement(elemPixels)
if((xLeftIdx >= 0) and isPixelChecked[xLeftIdx][yIndex] == False):
if(isColorAlmostSame(imgPixels[elemPixels[0].x, elemPixels[0].y], imgPixels[xLeftIdx, yIndex])):
pixelAppended = Pixel(xLeftIdx, yIndex, imgPixels[xLeftIdx, yIndex])
elemPixels.append(pixelAppended)
completeElement(elemPixels)
if((yBottomIdx < imgHeight) and isPixelChecked[xIndex][yBottomIdx] == False):
if(isColorAlmostSame(imgPixels[elemPixels[0].x, elemPixels[0].y], imgPixels[xIndex, yBottomIdx])):
pixelAppended = Pixel(xIndex, yBottomIdx, imgPixels[xIndex, yBottomIdx])
elemPixels.append(pixelAppended)
completeElement(elemPixels)
if((yTopIdx >= 0) and isPixelChecked[xIndex][yTopIdx] == False):
if(isColorAlmostSame(imgPixels[elemPixels[0].x, elemPixels[0].y], imgPixels[xIndex, yTopIdx])):
pixelAppended = Pixel(xIndex, yTopIdx, imgPixels[xIndex, yTopIdx])
elemPixels.append(pixelAppended)
completeElement(elemPixels)
def isColorAlmostSame(pixel1, pixel2):
redDiff = abs(pixel1[0] - pixel2[0])
greenDiff = abs(pixel1[1] - pixel2[1])
blueDiff = abs(pixel1[2] - pixel2[2])
if(redDiff < COLOR_TOLERANCE and greenDiff < COLOR_TOLERANCE and blueDiff < COLOR_TOLERANCE):
return True
else:
return False
def printPixelsArr(pixelsArr):
for x in range(0, len(pixelsArr)):
print(pixelsArr[x].x, pixelsArr[x].y, pixelsArr[x].color)
if __name__ == '__main__':
pixel = Pixel(0, 0, imgPixels[0, 0]);
element = Element(pixel.color);
element.addPixel(pixel);
cutImageInElements();
print("NbReccursive call: ", reccursionCount)
This code works for small images of size 100x100 but crashes with an image of 400x400 with the error "terminated by signal SIGSEGV (Address boundary error)" when I launch the program on wsl2. When I run the program on cmd or powershell it just crashes but with no error code/msg.
I cannot understand why it would work with some size of images and not others. I can only think that the memory runs out or something but in the task manager the program uses almost no memory.
Not sure why that's failing, but that much recursion in Python isn't a great idea. I'd suggest reading about tail recursion that other languages use to make some recursive algorithms consume constant stack space. Note that your algorithm is not tail recursive, so this optimisation wouldn't help even if Python supported it.
I hacked together the following flood fill implementation. It uses Numpy so that it's only 10x slower than Pillow's ImageDraw.floodfill.
import numpy as np
def floodfill(im, row, col, threshold):
similar = np.mean(np.abs(im - im[row, col]), 2) < threshold
mask = np.zeros_like(similar)
mask[row, col] = 1
m2 = mask.copy()
while True:
m2[:,:] = mask
m2[1:,:] |= mask[:-1]
m2[:-1,:] |= mask[1:]
m2[:,1:] |= mask[:,:-1]
m2[:,:-1] |= mask[:,1:]
m2 &= similar
if np.all(m2 == mask):
return mask
mask[:,:] = m2
As an example of using this, you could do;
import requests
from io import BytesIO
res = requests.get("https://picsum.photos/300")
res.raise_for_status()
src = Image.open(BytesIO(res.content))
mask = floodfill(np.array(src, int), 10, 10, 40)
where the random image I got and the output mask are:
I am fairly new to Python and have a difficult Problem to solve:
Here is what I am trying to do -->
I have a Folder (path known) with 1 - x picture files (.jpg) in it, whereas x can be as high as 1000
These picture files should be stiched together (one on top of the other)
To do so I want Python to assign a variable to each Picture file in the known folder and then create a loop which stiches these variable-stored pictures together and outputs it as 1 picture (.jpg)
Here is what I have coded so far:
from PIL import Image
import glob
#The following GLOB part doesn't work, I tried to make a List with all the
#files (.jpg) inside the main directory
#image_list = []
#for filename in glob.glob('*.jpg'):
# test = Image.open(filename)
# image_list.append(test)
img1 = Image.open("img1.jpg")
img2 = Image.open("img2.jpg")
def merge_images(img1, img2):
(width1, height1) = img1.size
(width2, height2) = img2.size
result_width = max(width1, width2)
result_height = height1 + height2
result = Image.new('RGB', (result_width, result_height))
result.paste(im=img2, box=(0,0))
result.paste(im=img1, box=(0,height1-2890))
return result
merged = merge_images(img1, img2)
merged.save("test.jpg")
What this does is to assign img1.jpg and img2.jpg to a variable and then stack them on top of oneanother and save this stacked picture as "test.jpg". Where do I go from here if I want to assign many pictures (.jpg) to variables and stack them on top of each other without typing a line of code for each new picture (see description further up?)
Thanks a lot for your help!
Chris
If you start with a 0x0 image, you can stack further images onto it like this:
stacked_img = Image.new('RGB', (0, 0))
for filename in glob.glob('*.jpg'):
stacked_img = merge_images(stacked_img, Image.open(filename))
stacked_img.save('stacked.jpg')
You might also like to change the height1-2890 to height2 in the merge_images() function if you are trying to stack the second image below the first image, i.e.
result.paste(im=img1, box=(0,height2))
Why not use a container such as a list?
images = [Image.open(img_name) for img_name in os.listdir(my_folder) if img_name.endswith('.jpg')
def merge_images(list_images):
result_width = max(img.size[0] for img in images)
result_height = sum(img.size[1] for img in images)
result = Image.new('RGB', (result_width, result_height))
for idx, img in enumerate(list_images):
result.paste(im=img, box=(0,list_images[0].size[1]-idx*2890))
See Data Structures for more information.
If performance is important, consider using map instead of a loop.
this is my code in which I implemented your approach. As a result, I am getting an image where 2 of the 4 images are displayed and the rest of the image is black. In the source directory I have 4 images (img1.jpg - img4.jpg)
from PIL import Image
import glob
stacked_img = Image.new('RGB', (0,0))
def merge_images(img1, img2):
(width1, height1) = img1.size
(width2, height2) = img2.size
result_width = max(width1, width2)
result_height = height1 + height2
result = Image.new('RGB', (result_width, result_height))
result.paste(im=img2, box=(0,0))
result.paste(im=img1, box=(0,height1))
return result
for filename in glob.glob('*.jpg'):
stacked_img = merge_images(stacked_img, Image.open(filename))
stacked_img.save('stacked.jpg')
I'm using following code to add watermark to animated GIF images. My problem is that all GIF frames except the first one have incorrect colors in result. Would you know how to fix the color of frames? Thank you.
def add_watermark(in_file, watermark_file, watermark_position, watermark_ratio, out_file, quality=85):
img = Image.open(in_file)
watermark_layer = Image.new('RGBA', img.size, (0,0,0,0))
watermark_img = Image.open(watermark_file).convert('RGBA')
watermark_img.thumbnail((img.size[0]/watermark_ratio, 1000), Image.ANTIALIAS)
alpha = watermark_img.split()[3]
alpha = ImageEnhance.Brightness(alpha).enhance(0.95)
watermark_img.putalpha(alpha)
watermark_layer.paste(watermark_img, count_watermark_position(img, watermark_img, watermark_position))
frames = images2gif.readGifFromPIL(img, False)
frames_out = []
for frame in frames:
frames_out.append(Image.composite(watermark_layer, frame, watermark_layer))
images2gif.writeGif(out_file, frames_out, duration=0.5)
To complete example, i provide also code of helper function:
def count_watermark_position(img, watermark, position):
if position == 'right_bottom':
return img.size[0] - watermark.size[0], img.size[1] - watermark.size[1]
if position == 'center':
return (img.size[0] - watermark.size[0])/2, (img.size[1] - watermark.size[1])/2
if position == 'left_bottom':
return 0, img.size[1] - watermark.size[1]
if position == 'left_top':
return 0, 0
if position == 'right_top':
return img.size[0] - watermark.size[0], 0
raise AttributeError('Invalid position')
Source code of images2gif I 've used - I modified it a little bit to make it work with pillow. See comment at the begining of source code.
i would like to do some program by capture image from webcam, then cropped it. after crop, i do some image processing and from the process it will run my robots. Here the full program:
import cv2
from cv2 import *
import numpy as np
import pylab
import pymorph
import mahotas
from matplotlib import pyplot
from PIL import Image
# initialize the camera
cam = VideoCapture(0) # 0 -> index of camera
s, img = cam.read()
# frame captured without any errors
if s:
imwrite("img.jpg",img) #save image
#Crop Image
imageFile = "img.jpg"
im1 = Image.open(imageFile)
def imgCrop(im):
box = (0, 199, 640, 200)
region = im.crop(box)
region.save('crop.jpg')
cImg = imgCrop(im1)
#thresholding
def greyImg(im):
gray = im.convert('L')
bw = gray.point(lambda x: 0 if x<128 else 255, '1')
bw.save("bw.jpg")
tImg = greyImg(cImg )
#direction
def find_centroid(im, rez):
width, height = im.size
XX, YY, count = 0, 0, 0
for x in xrange(0, width, rez):
for y in xrange(0, height, rez):
if im.getpixel((x, y)) == 255:
XX += x
YY += y
count += 1
return XX/count, YY/count
print find_centroid(tImg, 1)
def robo_direct():
cen = find_centroid(im, 1)
diff = cen[0] - 320
if diff > 10:
print 'right'
if diff < -10:
print 'left'
else:
print 'straight'
print robo_direct()
The error was come out like this:
File "compile.py", line 32, in greyImg
gray = im.convert('L')
AttributeError: 'NoneType' object has no attribute 'convert'
That is because im is a None object.
Try again the code with:
print im is None
And you'll see. I don't know about threshold, but obviously you are creating the im object the wrong way.
Your function imgCrop(im1) has no return statement and as such returns None. And then your greyImg(im) function also has no return statement and also will return None.
To fix that add return statements to both functions that for the first return region and the second return bw.
Also your robo_direct() function should return and not print the direction so that the call to it in the statement print robo_direct() would print the direction.
I am trying to create an algorithm that blends the pixels of an image and I can bring the image as it was before, but I do not know do this.
I'm using python and pil, but I can use other libraries.
Exemple: to and back to
Thank you.
This should do it. There's no error handling, it doesn't follow pep8 standards, it uses slow PIL operations and it doesn't use an argument parsing library. I'm sure there are other bad things about it also.
It works by seeding python's random number generator with an invariant of the image under scrambling. The hash of the size is used. Since the size doesn't changed, a random sequence built on it will be the same for all images that share the same size. That sequence is used as a one-to-one mapping, therefore it's reversible.
The script may be invoked twice from a shell to create two images, "scrambled.png" and "unscrambled.png". "Qfhe3.png" is the source image.
python scramble.py scramble "./Qfhe3.png"
python scramble.py unscramble "./scrambled.png"
#scramble.py
from PIL import Image
import sys
import os
import random
def openImage():
return Image.open(sys.argv[2])
def operation():
return sys.argv[1]
def seed(img):
random.seed(hash(img.size))
def getPixels(img):
w, h = img.size
pxs = []
for x in range(w):
for y in range(h):
pxs.append(img.getpixel((x, y)))
return pxs
def scrambledIndex(pxs):
idx = list(range(len(pxs)))
random.shuffle(idx)
return idx
def scramblePixels(img):
seed(img)
pxs = getPixels(img)
idx = scrambledIndex(pxs)
out = []
for i in idx:
out.append(pxs[i])
return out
def unScramblePixels(img):
seed(img)
pxs = getPixels(img)
idx = scrambledIndex(pxs)
out = list(range(len(pxs)))
cur = 0
for i in idx:
out[i] = pxs[cur]
cur += 1
return out
def storePixels(name, size, pxs):
outImg = Image.new("RGB", size)
w, h = size
pxIter = iter(pxs)
for x in range(w):
for y in range(h):
outImg.putpixel((x, y), next(pxIter))
outImg.save(name)
def main():
img = openImage()
if operation() == "scramble":
pxs = scramblePixels(img)
storePixels("scrambled.png", img.size, pxs)
elif operation() == "unscramble":
pxs = unScramblePixels(img)
storePixels("unscrambled.png", img.size, pxs)
else:
sys.exit("Unsupported operation: " + operation())
if __name__ == "__main__":
main()