When rotating an image using
import skimage
result = skimage.transform.rotate(img, angle=some_angle, resize=True)
# the result is the rotated image with black 'margins' that fill the blanks
The algorithm rotates the image but leaves the newly formed background black and there is no way - using the rotate function - to choose the color of the newly formed background.
Do you have any idea how to do choose the color of the background before rotating an image?
Thank you.
Just use cval parameter
img3 = transform.rotate(img20, 45, resize=True, cval=1, mode ='constant')
img4 = img3 * 255
It's a little annoying that the cval parameter to skimage.transform.warp() can only be a scalar float.
You can set it to any value outside of the range of your data (like -1), and use np.where() to fix it afterwards. Here's an example that sets the background of a 3-channel image to red:
output = tf.warp(image, tform, mode='constant', cval=-1,
preserve_range=True, output_shape=(256, 256), order=3)
w = np.where(output == -1)
output[w[0], w[1], :] = [255, 0, 0]
tbh I don't know why everyone set the cval value to a strange value.
I think it's intuitive when you look at the documentation of the function
cval : float, optional
Used in conjunction with mode ‘constant’, the value outside the image boundaries.
Therefore depending on what your input image is, in my case, my picture already used cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), therefore in order to set it to white, I can simply set cval=255.
If it's ndarray(img more than 1 channel), you can either rotate each channel separately, or set it to strange value then use np.where to replace afterward. (idk why but I just don't like this method lol)
if your pic is a gray picture,use cval ;
if pic is rgb ,no good way,blow code works:
path3=r'C:\\Users\forfa\Downloads\1111.jpg'
img20=data.imread(path3)
import numpy as np
img3=transform.rotate(img20,45,resize=True,cval=0.1020544) #0.1020544 can be any unique float,the pixel outside your pic is [0.1020544]*3
c,r,k=img3.shape
for i in range(c):
for j in range(r):
if np.allclose(img3[i][j],[0.1020544]*3):
img3[i][j]=[1,0,0] #[1,0,0] is red pixel,replace pixel [0.1020544]*3 with red pixel [1,0,0]
I don't know how do it, but maybe this help you
http://scikit-image.org/docs/dev/api/skimage.color.html
Good luck! ;)
Related
I have created a black image using np.zeros:
mask = np.zeros((800, 500, 3))
Now I want to edit this variable named mask turn it into an image I like. I know I can access pixel value like this:
for i in range(0,800):
for j in range(0,500):
mask[i,j]=[110,23,245]
but this isn't working all I get as an output is an white image:
If I only use a single value like this. It gives me an blue image:
But I didn't use 255, it shouldn't give me a full dark blue image like 255 pixel value:
for i in range(0,800):
for j in range(0,500):
mask[i,j]=[110,0,0]
I know I can copy image like this:
mask=image
but the thing which I am working on I have values of r,g,b colours in 3, two dimensional arrays, I need to convert them into an image. So I can't just copy paste.
First of all, specify the data type also while creating a black image:
mask = np.zeros((800, 500, 3), dtype=np.uint8)
Then, to color the complete image to another color, you can use slicing methods instead of iterating over all pixels, it will be fast.
Like this:
mask[:, :] = (110,23,245)
The problem in your case is arising because you have not specified the data type while creating the mask image. Thus, it by default uses float as its data type, and then the range of color will be between 0-1. As you are passing value greater than 1, it takes it as the full intensity of that color. Hence, the complete white and blue color images are appearing.
And also, if you want to copy an image, use this:
mask = image.copy()
I've used openCV2 to load a grayscale image, which I then converted to a numpy.array. Now I want to pad that array with a 'frame' around the image. However, I'm having some trouble dissecting what the numpy manual wants me to do exactly. I tried googling and searching for padding examples, none came up that were relevant for my case.
My current code looks like this:
import numpy as np
img = cv2.imread('Lena.png', )
imgArray = np.array((img))
imgArray = np.pad(imgArray, pad_width=1,mode='constant' ,constant_values=0)
cv2.imshow('Padded', imgArray)
Check out the openCV2 documentation here: https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_core/py_basic_ops/py_basic_ops.html
My best guess is to use constant= cv2.copyMakeBorder(img,10,10,10,10,cv2.BORDER_CONSTANT,value=BLUE)
You can do as follows:
import numpy as np
import cv2
img = cv2.imread('Lena.png', 0)
img = np.pad(img, pad_width=4, mode='constant', constant_values=0)
cv2.imshow('Padded', img)
cv2.waitKey(0)
From the documentation of cv2.imread:
cv2.imread(filename[, flags]) → retval
Parameters:
filename – Name of file to be loaded.
flags:
Flags specifying the color type of a loaded image:
CV_LOAD_IMAGE_ANYDEPTH - If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit.
CV_LOAD_IMAGE_COLOR - If set, always convert image to the color one
CV_LOAD_IMAGE_GRAYSCALE - If set, always convert image to the grayscale one
>0 Return a 3-channel color image.
Note In the current implementation the alpha channel, if any, is stripped from the output image. Use negative value if you need the alpha channel.
=0 Return a grayscale image.
<0 Return the loaded image as is (with alpha channel).
With the above code we got the following result:
And another option using np.pad:
As you can see here, you need to supply the axis you want to np.pad. Simply using:
imgArray = np.pad(imgArray, pad_width=1, mode='constant', constant_values=0)
adds only values to the third axis (i.e. the RGB channel), so that you cannot plot the image any more.
As described in the referenced question, you would need to use the following arguments to you code:
imgArray = np.pad(imgArray, pad_width=((1,1), (1,1), (0,0)), mode='constant', constant_values=0)
Also see the np.pad documentation:
Number of values padded to the edges of each axis. ((before_1, after_1), … (before_N, after_N)) unique pad widths for each axis. ((before, after),) yields same before and after pad for each axis. (pad,) or int is a shortcut for before = after = pad width for all axes.
This means the first entry of tuple pads the first axis (in case of an image the upper and lower border) and the second tuple pads the second axis (the left and right borders) with one "0".
You do not want to pad the last dimension, as this is the dimension storing the RGB information.
And as you stated in your question that you want a white border: constant_values should be set to 255 or 1, depending on the range of your image. Using 0 results in a black border.
Whilst I see you already have an answer, I wanted to show the general case where you want to pad with something other than black or white, i.e. you want to add a coloured border. I couldn't get any of the methods suggested in the other answers to do that, so...
Say you have lena.png as follows:
Then you can do:
from PIL import Image, ImageOps
import numpy as np
# Load the image - you could just as well use OpenCV `imread()`
img = Image.open('lena.png')
# Pad 20px to all sides with magenta
padded = ImageOps.expand(img, border=20, fill=(255,0,255))
# Save to disk
padded.save('result.png')
Before anyone decides to downvote because the OP asked how to add white borders, please note you can just as easily add white with this method if you use:
padded = ImageOps.expand(img, border=20, fill=(255,255,255))
If you are using numpy arrays to manipulate your images, you can convert from numpy array to PIL Image with:
pil_image = Image.fromarray(numpy_array)
and the other way with:
numpy_array = np.array(pil_image)
I'm very new to programming, and I am learning more about image processing using PIL.
I have a certain task that requires me to change every specific pixel's color with another color. Since there are more than few pixels I'm required to change, I've created a for loop to access to every pixel. The script "works" at least, however the result is just a black screen with (0, 0, 0) color in each pixel.
from PIL import Image
img = Image.open('/home/usr/convertimage.png')
pixels = img.load()
for i in range(img.size[0]):
for j in range(img.size[1]):
if pixels[i,j] == (225, 225, 225):
pixels[i,j] = (1)
elif pixels[i,j] == (76, 76, 76):
pixels [i,j] = (2)
else: pixels[i,j] = (0)
img.save('example.png')
The image I have is a grayscale image. There are specific colors, and there are gradient colors near the borders. I'm trying to replace each specific color with another color, and then replace the gradient colors with another color.
However for the life of me, I don't understand why my output comes out with a single (0, 0, 0) color at all.
I tried to look for an answer online and friends, but couldn't come up with a solution.
If anyone out there knows what I'm doing wrong, any feedback is highly appreciated. Thanks in advance.
The issue is that your image is, as you said, greyscale, so on this line:
if pixels[i,j] == (225, 225, 225):
no pixel will ever equal the RGB triplet (255,255,255) because the white pixels will be simply the greyscale vale 255 not an RGB triplet.
It works fine if you change your loop to:
if pixels[i,j] == 29:
pixels[i,j] = 1
elif pixels[i,j] == 179:
pixels [i,j] = 2
else:
pixels[i,j] = 0
Here is the contrast-stretched result:
You may like to consider doing the conversion using a "Look Up Table", or LUT, as large numbers of if statements can get unwieldy. Basically, each pixel in the image is replaced with a new one found by looking up its current index in the table. I am doing it with numpy for fun too:
#!/usr/local/bin/python3
import numpy as np
from PIL import Image
# Open the input image
PILimage=Image.open("classified.png")
# Use numpy to convert the PIL image into a numpy array
npImage=np.array(PILimage)
# Make a LUT (Look-Up Table) to translate image values. Default output value is zero.
LUT=np.zeros(256,dtype=np.uint8)
LUT[29]=1 # all pixels with value 29, will become 1
LUT[179]=2 # all pixels with value 179, will become 2
# Transform pixels according to LUT - this line does all the work
pixels=LUT[npImage];
# Save resulting image
result=Image.fromarray(pixels)
result.save('result.png')
Result - after stretching contrast:
I am maybe being a bit verbose above, so if you like more terse code:
import numpy as np
from PIL import Image
# Open the input image as numpy array
npImage=np.array(Image.open("classified.png"))
# Make a LUT (Look-Up Table) to translate image values
LUT=np.zeros(256,dtype=np.uint8)
LUT[29]=1 # all pixels with value 29, will become 1
LUT[179]=2 # all pixels with value 179, will become 2
# Apply LUT and save resulting image
Image.fromarray(LUT[npImage]).save('result.png')
Basically, I have two images. One is comprised of white and black pixels, the black pixels making up a word, and the other image that I'm trying to paste the black pixels on top of. I've pasted the code below, however I'm aware that there's an issue with the "if pixels [x,y] == (0, 0, 0):' being a tuple and not an indice, however I'm uncertain of how to get it to look for black pixels with other means.
So essentially I need to find, and remember the positions of, the black pixels so that I can paste them onto the first image. Any help is very much appreciated!
image_one = Image.open (image_one)
image_two = Image.open (image_two)
pixels = list(image_two.getdata())
for y in xrange(image_two.size[1]):
for x in xrange(image_two.size[0]):
if pixels[x,y] == (0, 0, 0):
pixels = black_pixels
black_pixels.append()
image = Image.open (image_one);
image_one.putdata(pixels)
image.save(image_one+ "_X.bmp")
del image_one, image_two;
You're almost there. I am not too familiar with the PIL class, but instead of calling the getdata method, let's use getpixel directly on the image object, and directly set the results into the output image. That eliminates the need to store the set of pixels to overwrite. However, there may be cases beyond what you've listed here where such an approach would be necessary. I created a random image and then set various pixels to black. For this test I used a different condition - if the R channel of the image is greater than 50. You can comment that out and use the other test, which tests for tuple (R,G,B) == (0,0,0) which will work fine.
imagea = PIL.Image.open('temp.png')
imageb = PIL.Image.open('temp.png')
for y in xrange(imagea.size[1]):
for x in xrange(imagea.size[0]):
currentPixel = imagea.getpixel((x,y))
if currentPixel[0] > 50:
#if currentPixel ==(0,0,0):
#this is a black pixel, you can directly modify image 2 now
imageb.putpixel((x,y),(0,0,0))
imageb.save('outputfile.png')
An alternative way to do this is just to multiply the two images together. Any pixel that's black in the binary image will be black in the result (multiply by zero) and any pixel that's white in the binary image will be unchanged from the other image in the result (multiply by one).
PIL can do this,
from PIL import Image, ImageChops
image_one = Image.open("image_one.bmp")
image_two = Image.open("image_two.bmp")
out = ImageChops.multiply(image_one, image_two)
out.save("output.bmp")
Using the Python Imaging Library PIL how can someone detect if an image has all it's pixels black or white?
~Update~
Condition: Not iterate through each pixel!
if not img.getbbox():
... will test to see whether an image is completely black. (Image.getbbox() returns the falsy None if there are no non-black pixels in the image, otherwise it returns a tuple of points, which is truthy.) To test whether an image is completely white, invert it first:
if not ImageChops.invert(img).getbbox():
You can also use img.getextrema(). This will tell you the highest and lowest values within the image. To work with this most easily you should probably convert the image to grayscale mode first (otherwise the extrema might be an RGB or RGBA tuple, or a single grayscale value, or an index, and you have to deal with all those).
extrema = img.convert("L").getextrema()
if extrema == (0, 0):
# all black
elif extrema == (1, 1):
# all white
The latter method will likely be faster, but not so you'd notice in most applications (both will be quite fast).
A one-line version of the above technique that tests for either black or white:
if sum(img.convert("L").getextrema()) in (0, 2):
# either all black or all white
Expanding on Kindall:
if you look at an image called img with:
extrema = img.convert("L").getextrema()
It gives you a range of the values in the images. So an all black image would be (0,0) and an all white image is (255,255). So you can look at:
if extrema[0] == extrema[1]:
return("This image is one solid color, so I won't use it")
else:
# do something with the image img
pass
Useful to me when I was creating a thumbnail from some data and wanted to make sure it was reading correctly.
from PIL import Image
img = Image.open("test.png")
clrs = img.getcolors()
clrs contains [("num of occurences","color"),...]
By checking for len(clrs) == 1 you can verify if the image contains only one color and by looking at the second element of the first tuple in clrs you can infer the color.
In case the image contains multiple colors, then by taking the number of occurences into account you can also handle almost-completly-single-colored images if 99% of the pixles share the same color.
I tried the Kindall solution ImageChops.invert(img).getbbox() without success, my test images failed.
I had noticed a problem, white should be 255 BUT I have found white images where numeric extrema are (0,0).. why? See the update below.
I have changed Kindall second solution (getextrema), that works right, in a way that doesn't need image conversion, I wrote a function and verified that works with Grayscale and RGB images both:
def is_monochromatic_image(img):
extr = img.getextrema()
a = 0
for i in extr:
if isinstance(i, tuple):
a += abs(i[0] - i[1])
else:
a = abs(extr[0] - extr[1])
break
return a == 0
The img argument is a PIL Image object.
You can also check, with small modifications, if images are black or white.. but you have to decide if "white" is 0 or 255, perhaps you have the definitive answer, I have not. :-)
Hope useful
UPDATE: I suppose that white images with zeros inside.. may be PNG or other image format with transparency.