简体   繁体   中英

Overlaying just black pixels from an image over another using Python Imaging Library

H as my name suggests I am new to code, am working with Python and would really appreciate some help with using the Python Imaging Library to complete a task.

I have 2 bitmap images. The first image is of a color painting and the second contains just black and white pixels... I would like to develop a function that accepts two images as parameters to create a new image that is essentially the first image (the painting), with only the black pixels from the second image, overwritten on top of it. The second image is smaller than the first and the overwritten pixels need to be positioned in the center of the new image.

I have installed the PIL and have been able to use the following code to successfully display the first image using the following code:

import PIL
from PIL import Image

painting = Image.open("painting.bmp")
painting.show()

I have ruled out using .blend and .composite functions or the .multiply function from the ImageChops module, as the images are not the same size.

I believe I'll need to use either .getpixel / .putpixel or .getdata / .putdata to find the black pixels which are identified by tuple (0,0,0). Also PIL's crop and paste functions I'm thinking should help work out the 'region' from the painting to overwrite with the black pixels, and could help center the black pixels over the painting?

So i'm looking at something like this maybe...

def overwrite_black(image1, image2): 

image_1 = Image.open('painting.bmp') 
image_2 = Image.open('black_white.bmp') 
pixels = list(image_2.getdata())

for y in xrange(image_2.size[1]):
    for x in xrange(image_2.size[0]):
        if pixels ==(o,o,o):
          image_2.putdata(pixels((x,y),(0,0,0)))

image_2.save('painted.bmp')

Again, i'm new so go easy on me and any help would be greatly appreciated.

Cheers.

So I assume you just want to multiply two images on top of each other, the catch being that they're different sizes? Forgive me if I misunderstood, but here is some code that puts the black pixels of one image onto another, even if the black and white image is smaller than the other one.

Sourse Images ( painting.bmp and mask.bmp )

绘画面具

Output:

结果

"...a new image that is essentially the first image (the painting), with only the black pixels from the second image, overwritten on top of it." — is this what you want?


Essentially, the code just makes the smaller image as big as the first one, by putting it in the center of a blank image the same size as the first one.

import PIL
from PIL import Image
from PIL import ImageChops # used for multiplying images

# open images
painting = Image.open("painting.bmp")
mask     = Image.open("mask.bmp")


def black_onto(img1, img2):  
    # create blank white canvas to put img2 onto
    resized = Image.new("RGB", img1.size, "white")

    # define where to paste mask onto canvas
    img1_w, img1_h = img1.size
    img2_w, img2_h = img2.size
    box = (img1_w/2-img2_w/2, img1_h/2-img2_h/2, img1_w/2-img2_w/2+img2_w, img1_h/2-img2_h/2+img2_h)

    # multiply new mask onto image
    resized.paste(img2, box)
    return ImageChops.multiply(img1, resized)


out = black_onto(painting, mask)
out.show() # this gives the output image shown above

Explanation for: (img1_w/2-img2_w/2, img1_h/2-img2_h/2, img1_w/2-img2_w/2+img2_w, img1_h/2-img2_h/2+img2_h)

Ok, so this is the ugly bit, but it's really quite simple: box defines the region on the canvas that we want to place the mask onto, which is the center. box is a 4-value tuple that defines the x and y of the top-left and bottom-right corners of the region, like this: (x1, y1, x2, y2) . Not the x, y and width, height, which would be more handy. Anyway, to define the region so that the image is centered, that code is what we get.

It goes like this: The x-value of the top-left corner is equal to half the width of the large image minus half the width of the mask image. (I find pen and paper helpful here.) The same goes for the y-value of the top-left corner. That's the first two values.

Now, if the tuple accepted (x, y, width, height) , then the second two values would just be the dimensions of the mask image. But they're not, they're more x and y positions. So we have to calculate them manually, by taking same code as first two values (top-left position) and adding the width and height of the image to it, which we know from some variables ( img2_w and img2_h ). Hence the second two values of the tuple are the same as the first and second, but with the width or height of the mask ( img2 ) added on.

Well, I hope that makes enough sense, and good luck with you're projects!

Ok, here's an alternate solution that's far more general and extendable. There's two parts to it; firstly there's resizing (but not scaling) the smaller image to the dimensions of the larger one, and then there's taking that image and making it into an image that only consists of black or transparent pixels, which can be simply pasted onto the larger one, which then leaves us with what you want.

Resizing:

1: 绘画 2: 原始面具 3: 调整

  1. Reference image
  2. Input mask
  3. Resized mask

That can be done with this center_resize() function:

def center_resize(smaller, (width, height)):
    resized = Image.new("RGB", (width, height), "white")
    smaller_w, smaller_h = smaller.size
    box = (width/2-smaller_w/2,
           height/2-smaller_h/2,
           width/2-smaller_w/2+smaller_w,
           height/2-smaller_h/2+smaller_h)
    resized.paste(smaller, box)
    return resized

Filtering:

This function is made to iterate through all the pixels in an image and set them to a specified colour, if they are a specified colour. (Otherwise they get set to a third specified color.) In this case we want to leave all black pixels black, but set all other to a fully transparent black. To do that you would input this:

pixel_filter(image, condition=(0, 0, 0), true_colour=(0, 0, 0, 255), false_colour=(0, 0, 0, 0))

... and it would return a filtered image.

def pixel_filter(image, condition, true_colour, false_colour):
    filtered = Image.new("RGBA", image.size)
    pixels = list(image.getdata())
    for index, colour in enumerate(pixels):
        if colour == condition:
            filtered.putpixel((index%image.size[1],index/image.size[1]), true_colour)
        else:
            filtered.putpixel((index%image.size[1],index/image.size[1]), false_colour)
    return filtered

Now in your case, we can apply these to the B&W image, and simply paste it onto the main image.

mask = center_resize(mask, painting.size)
mask = pixel_filter(mask, (0, 0, 0), (0, 0, 0, 255), (0, 0, 0, 0))

painting.paste(mask, (0, 0), mask)
painting.show()

Output:

产量

See if this code is useful—pull it apart and use what you want!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM