I want to flip an image along the y=x axis as so.
I've made this function to do what I want but I was wondering if there's a more optimised way to do this. The function I made is a bit slow when working with big images
def flipImage(img):
# Get image dimensions
h, w = img.shape[:2]
# Create a image
imgYX = np.zeros((w, h, 3), np.uint8)
for y in range(w):
for x in range(h):
imgYX[y,x,:]=img[x,y,:] #Flip pixels along y=x
return imgYX
Simply swap the first two axes that correspond to the height and width -
img.swapaxes(0,1) # or np.swapaxes(img,0,1)
We can permute axes with transpose as well -
img.transpose(1,0,2) # or np.transpose(img,(1,0,2))
We can also roll axes for the same effect -
np.rollaxis(img,0,-1)
We use the same trick when working with images in MATLAB.
Related
I'm trying to generate a 3D images using stack of 2D grayscale images in python. I currently have the images, mask, and mask output. I tried creating an ndarray by adding an axis to my images but this didn't seems to work.
This is what I wrote:
# load images
images_gray = []
#x, y= images[0].shape
#z= len(frames)
#threeD= np.ndarray([x,y,z]) #3D
threeD=[]
for i in range(len(images)):
frame= cv2.imread(path+'/images/' + str(i))
#convert to grayscale then save
images_gray.append(rgb2gray(frame))
#create a polygon
coordinates=coord[i]
coordinates = [[y,x] for [x,y] in coordinates] #change order for polygon2mask
polygon = np.array(coordinates)
#create a mask
mask= polygon2mask(images_gray[i].shape, polygon)
#apply mask
result=ma.masked_array(images_gray[i], np.invert(mask))
temp=result[... ,np.newaxis]
threeD.append(temp)
The resulted output shape for threeD is (#of frames, image hight, image width, 1). I don't know where the 1 come from, and I also expected the order to be (x, y, z) = (image hight, image width, #of frames). The output is wrong and I wasn't able to view it using plt as I got type error saying invalid shape.
For the z, I thought about setting a value of 0.1 that would represent the thickness, not sure how to set that up.
I'm also not sure if my approach is correct or not; do I have to create a points clouds instead? mesh? any suggestions?
I have to analyze the data given as an image like:
What I do is
Earasing the axises manually.
Convert the image to (x,y) coordinates by imagemagick (collecting the coordinates of black pixels)
Adjusting the (x,y) values (according to the axis values (rather than the pixel coordinates), then y direction: in images, the y coordinate increases from top to bottom).
Sorting the data by x.
Loading the data in a SciPy script.
I wonder if there is a function to do any of the steps 1-4 in the same SciPy script.
Since SciPy has a range of functions for image recognition, I would like to know if there is a function to translate an image into the (x,y) coordinates of the black pixels creating the curve, and so on.
First, load the image with sp.misc.imread or PIL so that it resides in a numpy array that I'll refer to as img below. (If it is a color image convert it to grayscale with img = np.mean(img, axis=-1). Then:
img = img[:-k, :] # Erase axes manually, k is the height of the axis in pixels
y, x = np.where(img == 0) # Find x and y coordinates of all black pixels
y -= y.max() # invert y axis so (0,0) is in the bottom left corner
i = np.argsort(x); x, y = x[i], y[i] # Sort data by x
Assumed imports:
import numpy as np
import scipy as sp
import os, sys
from PIL import Image
im = Image.open("result_bw.png")
l = []
print im.size
pix = im.load()
for x in range(im.size[0]):
for y in range(im.size[1]):
l.append(pix[x,y])
img = Image.new('L', (im.size[1],im.size[0] ))
img.putdata(l)
img.save('image.png')
The above code reads a black and white image and stores the pixel value in a list.
When I am creating a new image from the pixels stored in the list, I get the original image which is rotated anti-clockwise.
Why am I getting a rotated image?
How can I correct it?
Flip the x and y values you read. Computers write images with an origin in the top-left of a screen, y positive axis pointing down, and x positive axis pointing right. Your implementation assumes an origin at the bottom-left corner.
Where there is putdata, there is likely also getdata. Indeed, you could use
l = list(im.getdata())
instead of your for loops. This would also fix the rotation problem.
So I have an image and I have a pixel mask for that image, where the mask is the same size as the image and contains values of 0 and 1, where if it is 0 I don't want to modify the image, and if it is 1 I want to add a transparent color over that pixel of the image.
Basically I want to highlight certain segments of the image but still see what is underneath.
Now I have searched high and low but haven't found a simple way to do this. I used np.where with the mask to get the pixel locations of the 1's to use with the plot functions. I first tried scatter plots with a small marker size and no edge color (small scatter plot markers in matplotlib are always black), but the markers are not one image pixel in size, they seem to be an absolute size and so depending on the size of the figure the transparency is affected and weird patterns are created from the overlapping markers.
Just the regular pyplot plot function created the exact look I desired (where the coloring was smooth and invariant to figure size) but it also colored horizontal connections between disjoint segments in the mask (since it is drawing lines I guess), so I couldn't use that.
What worked the best was patches, which I came across in this question: (How to set a fixed/static size of circle marker on a scatter plot?). I found that rectangular patches with width and height of 1 gave me the exact desired effect, where I could put a transparent color over certain pixels of the image. However this proved to produce a ton (tens of thousands) of rectangles for certain images, and so it was quite slow. Even when using a PatchCollection instead of calling addPatch every time it was still slow.
Now I can probably just join adjacent rectangles to reduce the number of things needing to be drawn, but I was just wondering if there was an easier way to do this?
Thanks.
You can do a semitransparent overlay either using masked arrays or by setting the alpha values in an RGBA image. Here are both worked through (using the example of three semitransparent red squares placed over a circular pattern), and they give similar images (so I'll only show one):
from pylab import *
from numpy import ma
x = y = linspace(-6, 6, 100)
X, Y = meshgrid(x, y)
z3 = X*X + Y*Y # circular pattern
# first, do this with a masked array
figure()
# z4 = 3 diagonal square
# zm = a uniform image (ones), with a mask of squares (~z4)
z4 = np.repeat(np.repeat(eye(3, dtype=bool), 40, axis=0), 40, axis=1)
zm = ma.masked_where(~z4, ones((120,120)))
imshow(z3, cmap=cm.jet)
imshow(zm, cmap=cm.bwr, alpha=.3, vmin=0, vmax=1) #cm.bwr is an easy way to get red
# do this by changing alpha for each pixel
figure()
z5 = zeros((120, 120, 4), dtype=float)
z5[..., 0] = 1
z5[..., 3] = .4*z4.astype(float)
imshow(z3, cmap=cm.jet)
imshow(z5)
show()
I think both approaches can produce the same results for all cases, but:
1. the masked arrays can be a more direct approach if the mask or composition becomes complicated, and masking gives you more flexibility in drawing your overlay image since, for example, you can use colormaps rather than specifying the full RGBA for every pixel, but,
2. the masked array approach doesn't give full pixel-by-pixel control over the alpha value like RGBA does.
z1 = sin(X*Y)
z1 = cos(2*X)
z2 = cos(5*(X+Y))
zm = ma.masked_where( (z2<.5) & (Y>0), z1)
figure()
imshow(z3)
imshow(zm, cmap=cm.gray, alpha=.4, vmin=-2, vmax=2)
show()
It's a bit crazy, but here's what's going on: The primary image is a circular pattern that goes from blue to red (z3). Then there are vertical bars that faintly shade this (z1) but only in half of the figure and in narrow alternate diagonal bands on the other half (due to the mask). Here's a more complicated image using masked arrays:
Just to add on to what tom10 has posted, the masked arrays do work great with colormaps, but I also wrote a small function in the meantime that should work with any RGB color tuple.
def overlayImage(im, mask, col, alpha):
maskRGB = np.tile(mask[..., np.newaxis], 3)
untocuhed = (maskRGB == False) * im
overlayComponent = alpha * np.array(col) * maskRGB
origImageComponent = (1 - alpha) * maskRGB * im
return untocuhed + overlayComponent + origImageComponent
im is the rgb image
mask is a boolean mask of the image, such that mask.shape + (3,) = im.shape
col is just the 3-tuple rgb value you want to mask the image with
alpha is just the alpha value / transparency for the mask
I also needed a clear contour on my areas. Thus, you can easily add a contour plot on top: e.g., create a dummy numpy array and set a different value in each area of interest.
Here's an example build on top of tom10's answer with a different condition:
x = y = linspace(-6, 6, 100)
X, Y = meshgrid(x, y)
z3 = X*X + Y*Y # circular pattern
# first, do this with a masked array
figure()
imshow(z3, cmap=cm.jet, extent = (-6,6,-6,6));
zm = ma.masked_where((z3>=0.7) & (z3<=1.5), ones(np.shape(z3)));
imshow(zm, cmap=cm.bwr, alpha=.4, vmin=0, vmax=1, extent = (-6,6,-6,6)) #cm.bwr is an easy way to get red
# Build dummy array of 1s and 0s (you can play with different values to obtain different contours for different regions):
temp_vector = ones(np.shape(z3));
temp_vector[(z3>=0.7) & (z3<=1.5)] = 0.0;
temp_vector[(z3>8.2)] = 2.0; # etc.
# Create contour. I found only one contour necessary:
contour(X, Y, temp_vector, 1, colors=['r','g']);
show()
Which yields:
I've been making a picture mirroring in horizontal and vertical axes. Now I'm going to make the diagonal.
I had done the hori and verti width two for loops which in the hori scenario loops through all the pixels in the height and only the half of the pixels in the width. Then it gets the color of the pixel and set the same color to the pixel on the other side. Going from the getWidth(pic) to the center.
Then I have my mirror in the middle of the pic. How to do the diagonal way?
Edit:
img_src = makePicture(pickAFile())
W = getWidth(img_src)
H = getHeight(img_src)
for x in range(W):
for y in range(H):
p = getPixel(img_src, x, y)
colorInSrc = getColor( getPixel(img_src, x, y) )
destPixel = getPixel(img_src, H-y-1, W-x-1)
setColor(destPixel, colorInSrc)
Using PIL (the Python Imaging Library) this is a relatively straightforward task. Notice however, that the output image is square -- thus not the same size as the original image.
Here is the code:
from PIL import Image, ImageDraw
# load the image, create the mirrored image, and the result placeholder
img = Image.open('img.png')
mirror = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90)
sz = max(img.size + mirror.size)
result = Image.new(img.mode, (sz,sz))
result.paste(img, (0,0)+img.size)
# now paste the mirrored image, but with a triangular binary mask
mask = Image.new('1', mirror.size)
draw = ImageDraw.Draw(mask)
draw.polygon([0,0,0,sz,sz,sz], outline='white', fill='white')
result.paste(mirror, (0,0)+mirror.size, mask)
# clean up and save the result
del mirror, mask, draw
result.save('result.png')
If I understood correctly what you need is to "flip" the image by a diagonal. Since there are two of them I'll presume that you mean the one that goes from left bottom to right top.
In order to flip by this diagonal you need to transform each row from the source in columns in the destination. The left part of the rows will become the bottom part of the new columns. Also the topmost row will become the rightmost column. You will need to do this pixel by pixel on the whole image. Also keep in mind that the width and height of the image will be swapped.
Edit: A small example. Say you start with an image 5 pixels wide and 3 pixels high (5x3). You will need to create a new blank image 3 pixels wide and 5 pixels high.
If you start pixel numbering from left top corner with (0,0), then this pixel will end up at (2,4) in the new image, pixel (1,0) will end at (2,3) and so on.
If your original width and height are W and H then you should use something like this:
for x in xrange(W):
for y in xrange(H):
p = img_src.getpixel(x, y)
img_dest.setpixel(H-y-1, W-x-1)
This should work, but is not tested.
It's not really an Python question, is it?
The easiest solution would be to first mirror horizontal and then vertical.
Another one would be to switch pixel rows with columns.
Or to do your algorithm but switch the pixels from left-top to bottom-right...
Here's how to mirror diagonally in JES; It only works for a square image though:
def mirrorDiagonal(picture):
for sourceX in range(0,getWidth(picture)):
for sourceY in range (0,getHeight(picture)):
pex=getPixel(picture,sourceY,sourceX)
pix=getPixel(picture, sourceX,sourceY)
color=getColor(pix)
setColor(pex,color)
show(picture)