http://i.stack.imgur.com/AAtUD.jpg
http://i.stack.imgur.com/eouLY.jpg
images to use for code.
the end result i am trying to do is combine the vignette picture and the CGI picture because the vignette images RGB values are darker towards the edges i need to multiply the original images corresponding pixels by the smaller numbers towards the edges, should make the picture have a darker frame around the edges of the original picture.
here's the code so far:
def addVignette(inputPic, vignette):
#create empty canvas to combine images correctly
canvas = makeEmptyPicture(getWidth(inputPic), getHeight(inputPic))
for x in range(0, getWidth(inputPic)):
for y in range(0, getHeight(inputPic)):
px = getPixel(canvas, x, y)
inputPx = getPixel(inputPic, x, y)
vignettePx = getPixel(vignette, x, y)
#make a new color from these values
newColour = getNewColorValues(vignettePx,inputPx)
#then assign this new color to the current pixel of the input image
setColor(px, newColour)
explore(canvas)
def getNewColourValues(inputPx, vignettePx):
inputRed = getRed(inputPx)
vignetteRed = getRed(vignettePx)
inputGreen = getGreen(inputPx)
vignetteGreen = getGreen(vignettePx)
inputBlue = getBlue(inputPx)
vignetteBlue = getBlue(vignettePx)
newRGB= setColor(inputPx,inputRed,inputGreen,inputBlue)*(vignettePx,vignetteRed,vignetteGreen,vignetteBlue)
newColour = makeColor(newRGB)
return newColour
def newPicture(newColour):
folder = pickAFolder()
filename = requestString("enter file name: ")
path = folder+filename+".jpg"
writePictureTo(inputPic, path)
when testing use vignette_profile image first then CGI image also the saving image doesnt work even though i've been trying to get it to work any help will be appreciated.
Saving the image
Let me start with saving the image. What I can see from the code you posted, you never actually call the newPicture() function which is why it's not saving the image. Also I noticed in the newPicture function that you don't pass a reference of the new image to the function.
Please my solution to this below. I have changed the function name from newPicture to saveNewImage()
Adding the Vignette
Please see the comments for the code block denote by ******* in the getNewColorValues() function.
You run the Main() function for this script to work
# Main function.
# *** THIS FUNCTION NEEDS TO BE CALLED IN THE CONSOLE ***
# i.e >>> main()
def main():
# Choose the files you wish to use
inputFile = pickAFile()
vignetteFile = pickAFile()
# Turn both files into picture objects
inputPic = makePicture(inputFile)
vignette = makePicture(vignetteFile)
# addVignette() function combines the input picture and vignette together
# and returns the result as a new picture object
newImage = addVignette(inputPic, vignette)
# saveNewImage() function stores the new image as file
saveNewImage(newImage)
# Main() calls this function to add input picture and vignette together
def addVignette(inputPic, vignette):
# Create empty canvas
canvas = makeEmptyPicture(getWidth(inputPic), getHeight(inputPic))
# Iterate through all the pixels of the input image. x and y are
# used as the current coordinates of the pixel
for x in range(0, getWidth(inputPic)):
for y in range(0, getHeight(inputPic)):
# Get the current pixels of inputPic and vignette
inputPixel = getPixel(inputPic, x, y)
vignettePixel = getPixel(vignette, x, y)
# The getNewColorValues() function, makes a new color from those
# values
newColor = getNewColorValues(inputPixel, vignettePixel)
# Assign this new color to the current pixel of the canvas
px = getPixel(canvas, x, y)
setColor(px, newColor)
# Show the result of combiming the input picture with the vignette
explore(canvas)
# return the new image to main() function.
return canvas
# Called from the addVignette() function to add the color values from
# the input picture and vignette together. It returns a new color
# object
def getNewColorValues(inputPixel, vignettePixel):
# Get the individual colour values
inputRed = getRed(inputPixel)
vignetteRed = getRed(vignettePixel)
inputGreen = getGreen(inputPixel)
vignetteGreen = getGreen(vignettePixel)
inputBlue = getBlue(inputPixel)
vignetteBlue = getBlue(vignettePixel)
# ***********************************************************
# Most important part. This will determine if the pixel is darkent
# and by how much. How it works is the darker the vignette pixel the less that will
# be taken away from 255. This means the result of `255 - vignetteRed` will be a higher
# value which means more will be taken away from the input colour.
# The light the vignette pixel the less that will be taken away from input pixel
newR = inputRed - (255 - vignetteRed)
newG = inputGreen - (255 - vignetteGreen)
newB = inputBlue - (255 - vignetteBlue)
# ***********************************************************
newC = makeColor(newR, newG, newB)
return newC
# Called from the main() function in order to save the new image
def saveNewImage(newImage):
folder = pickAFolder()
filename = requestString("Please enter file name: ")
path = folder + filename + ".jpg"
writePictureTo(newImage, path)
You can also try doing this in CV. Single pixel manipulation and file I/O are pretty straight forward.
img = cv2.imread('test.jpg')
pixel = img[10,10]
Ive never had any issues with file I/O in CV. Chances are its a permission error or excess white space.
cv2.imwrite('messigray.png',img)
You can also do some easy image previewing which in this case would let you experiment with the output a little more.
Related
I think i've conquered 95% of this short script to make images out of individual pixels, but I'd like help with using a variable as part of a filename. the line in question is as follows (i've used {} to denote the place I want to insert the variable):
img.save("new\\{i, j}.png")
The full code is
# Importing Image from PIL package
from PIL import Image
# Creating image object
im = Image.open("C:\\Users\\joeco\\Desktop\\Python-test-image\\image.jpg")
px = im.load()
# Defining image width and height
imageSizeW, imageSizeH = im.size
# Running through each pixel coord by column then row
for i in range(1, imageSizeW):
for j in range(1, imageSizeH):
# Removing non white pixels, then saving a single pixel of the colour to a new file
if px != (255, 255, 255):
img = Image.new('RGB', (1, 1), color = (px[i, j]))
img.save("new\\{i, j}.png")
You have to use the character f before a string if you want to include variables in that manner. In your example, you would use img.save(f"new\\{i}, {j}.png")
You could also use %i to save the filename like so: img.save("new\\%i, %i.png" % (i, j))
I am trying to take a set of 256x256px 8-bit grayscale .pngs (with transparency) and convert the grayscale .png to a color .png of the same size, still retaining transparency. The palette I want to use is Zissou1 from the R wesanderson package, which I have gotten into a Python dictionary where each key corresponds to a greyscale value and each value a HEX color.
import os
from PIL import Image, ImageColor
### dic = the dictionary containing the palette in format {grayscale pixel value: "HEX COLOR"},
### created earlier in the script
with Image.open("3.png") as im:
fin = Image.new("RGBA", im.size)
px = im.load()
px1 = fin.load()
for x in range(0,256):
for y in range(0,256):
px1.putpixel(x,y,ImageColor.getcolor(dic[px.getpixel(x,y)[1]],"RGBA"))
fin.show()
I am getting the error:
px1.putpixel(x,y,ImageColor.getcolor(dic[px.getpixel(x,y)[1]],"RGBA"))
AttributeError: 'PixelAccess' object has no attribute 'putpixel'
To extend on Jason's answer:
The lookup as given by PIL
With Image.point(lookup_table, mode = 'L') you can lookup and transpose the colors of your image.
lookup_table = ...
with Image.open("3.png") as orig:
image = orig.point(lookup_table, mode = 'L')
image.show()
To see an example for using the Image.point method with the lookup_table:
Using the Image.point() method in PIL to manipulate pixel data
Your own implementation (fixed with improved naming)
or implement the lookup against your_dic yourself:
your_dic = ...
with Image.open("3.png") as orig:
image = colored_from_map(orig, your_dic)
image.show()
with this alternative function (you almost did):
def colored_from_map(orig, map_to_color):
image_in = orig.load()
image = Image.new("RGBA", im.size)
image_out = image.load()
for x in range(0,256):
for y in range(0,256):
coords = (x,y)
greyscale = image_in.getpixel(x,y)[1]
color_name = map_to_color[greyscale]
image_out.putpixel(coords, ImageColor.getcolor(color_name,"RGBA"))
return image
Preserving the alpha-channel (transparency)
See the source-code of ImageColor.getColor()
at the begin and end of its method body:
color, alpha = getrgb(color), 255 # default to no-transparency
if len(color) == 4: # if your mapped color has 4th part as alpha-channel
color, alpha = color[0:3], color[3] # then use it
# omitted lines
else:
if mode[-1] == "A": # if the last char of `RGBA` is `A`
return color + (alpha,) # then return with added alpha-channel
return color
(comments mine)
So you could simply set the fourth element of the returned color-tuple to the previous value of the original gray-scale image:
greyscale = image_in.getpixel(x,y)[1] # containing original alpha-channel
color_name = map_to_color[greyscale] # name or hex
mapped_color = ImageColor.getcolor(color_name,"RGB") # removed the A
transposed_color = mapped_color[:2] + (greyscale[3],) # first 3 for RGB + original 4th for alpha-channel (transparency)
image_out.putpixel(coords, transposed_color)
Note: because the A(lpha-channel) is provided from original image, I removed the A from the getColor invocation's last argument. Technically, you can also remove the slicing from mapped_color[:2] to result in mapped_color + (greyscale[3],).
The first parameter to PIL's PixelAccess.putpixel method expects the pixel's coordinates to be passed as a (x,y) tuple:
px1.putpixel((x,y),ImageColor.getcolor(dic[px.getpixel(x,y)[1]],"RGBA"))
Alternatively, consider using the Image.point method which takes a look up table similar to the one you already created to map an image based on pixel values. See the answer at Using the Image.point() method in PIL to manipulate pixel data for more details
I recently started learning programming and have finally written my first program. This program input all the images in a folder and crops them to a certain size. Then these images are are stacked vertically to produce a new image.
So I have been running test images through my program and it appeared to work fine. But I have only been using 10 images at a time. When I decided to finally try the program for the purpose I intended, which requires over 500 images, the program crashes. I get the MemoryError.
Since I just started, I wrote together whatever I thought would work, whether it was the most efficient method or not. If anyone has time, are there any blatant things in my code that just consume way too much resources.
from PIL import Image
from natsort import natsorted
import glob
# Convert coordinate list into variables
print("Type in the coordinates for the upper left (x1,y1) and bottom right (x2,y2) points")
coordinates = list(map(int, input("Separate values with a space (x1 y1 x2 y2): ").strip().split()))[:4]
x1, y1, x2, y2 = coordinates
print("Generating image...")
# creating lists to hold the initial files and the edited files
image_list = []
cropped_images = []
# Accessing all the files using the glob module
# The function natsorted sorts values the way Windows does
# Opens all the files with the variable img in folder and adds them one by one to a list named image_list
for filename in natsorted(glob.glob("C:\\Users\\Alex\\Desktop\\One Dimensional Reaction Diffusion Program\\data\\*.tiff")):
img = Image.open(filename)
image_list.append(img)
# for each image in the image_list
# selected_region from previous user input
# cropped_region applied the crop function for the images in the selected region
# Cropping function
selected_region = (x1, y1, x2, y2)
cropped_region = image.crop(selected_region) # unsure if this is correct
# If cropped area is vertical, rotate into horizontal position
if (y2 - y1) > (x2 - x1):
rotated_image = cropped_region.rotate(90, expand=1) # Expand 1 ensures image isn't cut off while rotating
else:
rotated_image = cropped_region # Does nothing if image is horizontal
cropped_images.append(rotated_image) # appends all rotated_images to the list cropped_images
# Size of each individual image
widths, heights = zip(*(i.size for i in cropped_images))
# Final image dimensions
max_width = max(widths)
total_height = sum(heights)
# Create a new colored image (RGB)
new_img = Image.new('RGB', (max_width, total_height))
# Stacking function
# to offset horizontally, need to change final image dimensions to sum(widths) and max(heights)
# and change new_im.paste(img, (x_offset,0))
y_offset = 0 # Initial position is 0
for img in cropped_images: # Not sure if img will cause conflict because img used in above for loop
new_img.paste(img, (0, y_offset)) # (x,y) indicates which direction to offset in
y_offset += img.size[1] # location of new image is y_offset (which had position 0) + the height of the image
new_img.save("C:\\Users\\Alex\\Desktop\\One Dimensional Reaction Diffusion Program\\output\\stacked.tiff", quality=95)
I'm trying to write a code to merge 2 photos side by side onto a new image, and I found this script online--however, I have no idea how it works. Where do I input the image files that I want to merge? Can someone please explain this code to me? Thanks!!
from PIL import Image
import sys
if not len(sys.argv) > 3:
raise SystemExit("Usage: %s src1 [src2] .. dest" % sys.argv[0])
images = map(Image.open, sys.argv[1:-1])
w = sum(i.size[0] for i in images)
mh = max(i.size[1] for i in images)
result = Image.new("RGBA", (w, mh))
x = 0
for i in images:
result.paste(i, (x, 0))
x += i.size[0]
result.save(sys.argv[-1])
It is very easy. You open the images with names provided in sys.argv (arguments to the program):
images = map(Image.open, sys.argv[1:-1])
You calculate the new width (sum of all width for opened images, which is i.size[0])
w = sum(i.size[0] for i in images)
The height, which should be equal to the height of the highest image (this way it can fit each)
mh = max(i.size[1] for i in images)
Create an image with calculated dumentions
result = Image.new("RGBA", (w, mh))
For each image opened, insert it (with paste function) to the point of x pixels from the left and 0 from the top and add the width of inserted image to x so that the next one is adjacent, not overlapping
x = 0
for i in images:
result.paste(i, (x, 0))
x += i.size[0]
Save the image
result.save(sys.argv[-1])
The error processing you see at the top has nothing to do with the process of merging images, but rather asserts that there is a correct number of arguments to the program
I am probably looking for the wrong thing in the handbook, but I am looking to take an image object and expand it without resizing (stretching/squishing) the original image.
Toy example: imagine a blue rectangle, 200 x 100, then I perform some operation and I have a new image object, 400 x 300, consisting of a white background upon which a 200 x 100 blue rectangle rests. Bonus if I can control in which direction this expands, or the new background color, etc.
Essentially, I have an image to which I will be adding iteratively, and I do not know what size it will be at the outset.
I suppose it would be possible for me to grab the original object, make a new, slightly larger object, paste the original on there, draw a little more, then repeat. It seems like it might be computationally expensive. However, I thought there would be a function for this, as I assume it is a common operation. Perhaps I assumed wrong.
The ImageOps.expand function will expand the image, but it adds the same amount of pixels in each direction.
The best way is simply to make a new image and paste:
newImage = Image.new(mode, (newWidth,newHeight))
newImage.paste(srcImage, (x1,y1,x1+oldWidth,y1+oldHeight))
If performance is an issue, make your original image bigger than needed and crop it after the drawing is done.
Based on interjays answer:
#!/usr/bin/env python
from PIL import Image
import math
def resize_canvas(old_image_path="314.jpg", new_image_path="save.jpg",
canvas_width=500, canvas_height=500):
"""
Resize the canvas of old_image_path.
Store the new image in new_image_path. Center the image on the new canvas.
Parameters
----------
old_image_path : str
new_image_path : str
canvas_width : int
canvas_height : int
"""
im = Image.open(old_image_path)
old_width, old_height = im.size
# Center the image
x1 = int(math.floor((canvas_width - old_width) / 2))
y1 = int(math.floor((canvas_height - old_height) / 2))
mode = im.mode
if len(mode) == 1: # L, 1
new_background = (255)
if len(mode) == 3: # RGB
new_background = (255, 255, 255)
if len(mode) == 4: # RGBA, CMYK
new_background = (255, 255, 255, 255)
newImage = Image.new(mode, (canvas_width, canvas_height), new_background)
newImage.paste(im, (x1, y1, x1 + old_width, y1 + old_height))
newImage.save(new_image_path)
resize_canvas()
You might consider a rather different approach to your image... build it out of tiles of a fixed size. That way, as you need to expand, you just add new image tiles. When you have completed all of your computation, you can determine the final size of the image, create a blank image of that size, and paste the tiles into it. That should reduce the amount of copying you're looking at for completing the task.
(You'd likely want to encapsulate such a tiled image into an object that hid the tiling aspects from the other layers of code, of course.)
This code will enlarge a smaller image, preserving aspect ratio, then center it on a standard sized canvas. Also preserves transparency, or defaults to gray background.
Tested with P mode PNG files.
Coded debug final.show() and break for testing. Remove lines and hashtag on final.save(...) to loop and save.
Could parameterize canvas ratio and improve flexibility, but it served my purpose.
"""
Resize ... and reconfigures. images in a specified directory
Use case: Images of varying size, need to be enlarged to exaxtly 1200 x 1200
"""
import os
import glob
from PIL import Image
# Source directory plus Glob file reference (Windows)
source_path = os.path.join('C:', os.sep, 'path', 'to', 'source', '*.png')
# List of UNC Image File paths
images = glob.glob(source_path)
# Destination directory of modified image (Windows)
destination_path = os.path.join('C:', os.sep, 'path', 'to', 'destination')
for image in images:
original = Image.open(image)
# Retain original attributes (ancillary chunks)
info = original.info
# Retain original mode
mode = original.mode
# Retain original palette
if original.palette is not None:
palette = original.palette.getdata()[1]
else:
palette = False
# Match original aspect ratio
dimensions = original.getbbox()
# Identify destination image background color
if 'transparency' in info.keys():
background = original.info['transparency']
else:
# Image does not have transparency set
print(image)
background = (64)
# Get base filename and extension for destination
filename, extension = os.path.basename(image).split('.')
# Calculate matched aspect ratio
if dimensions[2] > dimensions[3]:
width = int(1200)
modifier = width / dimensions[2]
length = int(dimensions[3] * modifier)
elif dimensions[3] > dimensions[2]:
length = int(1200)
modifier = length / dimensions[3]
width = int(dimensions[2] * modifier)
else:
width, length = (1200, 1200)
size = (width, length)
# Set desired final image size
canvas = (1200, 1200)
# Calculate center position
position = (
int((1200 - width)/2),
int((1200 - length)/2),
int((1200 - width)/2) + width,
int((1200 - length)/2) + length
)
# Enlarge original image proportionally
resized = original.resize(size, Image.LANCZOS)
# Then create sized canvas
final = Image.new(mode, canvas, background)
# Replicate original properties
final.info = info
# Replicate original palatte
if palette:
final.putpalette(palette)
# Cemter paste resized image to final canvas
final.paste(resized, position)
# Save final image to destination directory
final.show()
#final.save("{}\\{}.{}".format(destination_path, filename, extension))
break