Crop foreground of image in Python - python

I have manipulated a jpg image so that I have foreground isolated, and all black pixels everywhere else.
I'd like to be able to crop the image so that there are no full black rows above and below, or full black columns left and right of the foreground.
I'm thinking I could get the 4 indices I need just looping through the Numpy array, but was wondering if there is a more straightforward and/or fast approach.
import numpy as np
import matplotlib.pyplot as plt
im=np.array(
[[0,0,0,0,0,0,0],
[0,0,1,1,1,0,0],
[0,1,1,1,0,0,0],
[0,0,1,1,0,0,0],
[0,0,0,0,0,0,0]])
plt.imshow(im, cmap=plt.cm.gray,interpolation='nearest')
Then something happens here and I get:

im[~np.all(im == 0, axis=1)] can remove the rows with all zero. axis=2 will be the columns deletion. Would that work for you?

Related

How to downscale an image without losing discrete values?

I have an image of a city with discrete colors (Green=meadow, black=buildings, white/yellow=roads). Using Pillow, I import the picture in my (Python) program and convert it to a Numpy array with discrete values for the colors (i.e. green pixels become 1's, black pixels become 2's, etc).
I want to downscale the resolution of the image (for computational purposes) while retaining as much information as possible. However, using Pillow's resize() method, colors deviate from these discrete values. How can I downscale this image while (most importantly) retaining the discrete colors and (also important) with losing as little information as possible?
Here an example of the image: https://i.imgur.com/6Tef55H.png
EDIT: per request, some code:
from PIL import Image
import Numpy as np
picture = Image.open(some_image.png)
width, height = picture.size
pic_array = np.zeros(width,height)
# Turn the image into discrete values
for i in range(0,width):
for j in range(0,height):
red, green, blue = picture.getpixel((i,j))
if red == a and green == b and blue == c:
#An example of how discrete colors are converted to values
pic_array[i][j] = 1
Scaling can be done in two ways:
1) Scaling the original image using Pillow's resize library or
2) rescaling the final array using something like:
scaled_array = pic_array[0:width:5, 0:height,5]
Option 1 is "well" in terms of retaining information but loses discrete values, while option 2 does it the other way around.
I was interested in this question and wrote some code to try out some ideas - specifically the "mode" filter suggested by #jasonharper in the comments. So, I programmed it up.
First of all the input image is not 4 nicely defined classes, but actually has 6,504 different colours, so I made a palette of 4 colours using ImageMagick like this:
magick xc:black xc:white xc:yellow xc:green +append palette.png
Here it is enlarged - in reality is 4x1 pixels:
Then I mapped the colours in the image to the palette of 4 discrete colours:
magick map.png +dither -remap palette.png start.png
Then I tried this code to calculate the median and the mode of each 3x3 window:
#!/usr/bin/env python3
from PIL import Image
import numpy as np
from scipy import stats
from skimage.util import view_as_blocks
# Open image and make into Numpy array
im = Image.open('start.png')
na = np.array(im)
# Make a view as 3x3 blocks - crop anything not a multiple of 3
block_shape=(3,3)
view = view_as_blocks(na[:747,:], block_shape)
flatView = view.reshape(view.shape[0], view.shape[1], -1) # now (249,303,9)
# Get median of each 3x3 block
resMedian = np.median(flatView, axis=2).astype(np.uint8)
Image.fromarray(resMedian*60).save('resMedian.png') # arbitrary scaling by 60 for contrast
# Get mode of each 3x3 block
resMode = stats.mode(flatView, axis=2)[0].reshape((249,303)).astype(np.uint8)
Image.fromarray(resMode*60).save('resMode.png') # arbitrary scaling by 60 for contrast
Here is the result of the median filter:
And here is the result of the "mode" filter which is indeed better IMHO:
Here is animated comparison:
If anyone wants to take the code and adapt it to try new ideas, please feel free!

Python-Why does imshow() yield a blank image for a non-zero array?

My goal is to display a 2D Array as a image in Python. The array doesn't contain zero elements, and therefore I would expect an image in which imshow() automatically sets the color scale according to the array values. However, when I run the code, the image is blank.
The csv file is: https://ufile.io/urk5m
import numpy as np
import matplotlib.pyplot as plt
data_ = np.loadtxt(open("new_file.csv", "rb"), delimiter=",")
plt.imshow(data_)
My result is this: https://imgur.com/jMNnF0h
Always remember, but really always, that images works on 8bit integers. Thats why there is 2^8 shades of gray and why most commmon number of CS colors is (2^8)^3= 16.7 mil. colors. 3 because there are 3 color channels - RGB, each having 256 shades.
Everybody is counting with it and mainly the image processing libraries.
Therefore ALWAYS make sure you pass correct matrix datatype into image processing functions:
image_8bit = np.uint8(data_)
plt.imshow(image_8bit)
plt.show()

How to plot an image from a connection matrix?

I want to write a script to create an image from a connection matrix. Basically, wherever there is a '1' in the matrix, I want that area to be shaded in the image. For eg -
I created this image using Photoshop. But I have a large dataset so I will have to automate the process. It would be really helpful if anyone could point me in the right direction.
EDIT
The image that I am getting after using the script is this. This is due to the fact that the matrix is large (19 x 19). Is there any way I can increase the visibility of this image so the black and white boxes appear more clear?
I would suggest usage of opencv combined with numpy in this case.
Create two-dimensional numpy.array of dtype='uint8' with 0 for black and 255 for white. For example, to get 2x2 array with white left upper, white right lower, black left lower and black right upper, you could use code:
myarray = numpy.array([[255,0],[0,255]],dtype='uint8')
Then you could save that array as image with opencv2 in this way:
cv2.imwrite('image.bmp',myarray)
In which every cell of array is represented by single pixel, however if you want to upscale (so for example every cell is represented by 5x5 square) then you might use numpy.kron function, with following one line:
myarray = numpy.kron(myarray, numpy.ones((5,5)))
before writing image
May be you can try this!
import matplotlib.cm as cm
# Display matrix
plt.imshow(np.random.choice([0, 1], size=100).reshape((10, 10)),cmap=cm.binary)
With a Seaborn heatmap:
import seaborn as sns
np.random.seed(3)
sns.set()
data = np.random.choice([0, 1], size=(16,16), p=[3./4, 1./4])
ax = sns.heatmap(data, square=True, xticklabels=False, yticklabels=False, cbar=False, linewidths=.8, linecolor='lightgray', cmap='gray_r')
Note the reverse colormap gray_r to have black for 1's and white for 0's.

Making image white space transparent, overlay onto imshow()

I have a plot of spatial data that I display with imshow().
I need to be able to overlay the crystal lattice that produced the data. I have a png
file of the lattice that loads as a black and white image.The parts of this image I want to
overlay are the black lines that are the lattice and not see the white background between the lines.
I'm thinking that I need to set the alphas for each background ( white ) pixel to transparent (0 ? ).
I'm so new to this that I don't really know how to ask this question.
EDIT:
import matplotlib.pyplot as plt
import numpy as np
lattice = plt.imread('path')
im = plt.imshow(data[0,:,:],vmin=v_min,vmax=v_max,extent=(0,32,0,32),interpolation='nearest',cmap='jet')
im2 = plt.imshow(lattice,extent=(0,32,0,32),cmap='gray')
#thinking of making a mask for the white background
mask = np.ma.masked_where( lattice < 1,lattice ) #confusion here b/c even tho theimage is gray scale in8, 0-255, the numpy array lattice 0-1.0 floats...?
With out your data, I can't test this, but something like
import matplotlib.pyplot as plt
import numpy as np
import copy
my_cmap = copy.copy(plt.cm.get_cmap('gray')) # get a copy of the gray color map
my_cmap.set_bad(alpha=0) # set how the colormap handles 'bad' values
lattice = plt.imread('path')
im = plt.imshow(data[0,:,:],vmin=v_min,vmax=v_max,extent=(0,32,0,32),interpolation='nearest',cmap='jet')
lattice[lattice< thresh] = np.nan # insert 'bad' values into your lattice (the white)
im2 = plt.imshow(lattice,extent=(0,32,0,32),cmap=my_cmap)
Alternately, you can hand imshow a NxMx4 np.array of RBGA values, that way you don't have to muck with the color map
im2 = np.zeros(lattice.shape + (4,))
im2[:, :, 3] = lattice # assuming lattice is already a bool array
imshow(im2)
The easy way is to simply use your image as a background rather than an overlay. Other than that you will need to use PIL or Python Image Magic bindings to convert the selected colour to transparent.
Don't forget you will probably also need to resize either your plot or your image so that they match in size.
Update:
If you follow the tutorial here with your image and then plot your data over it you should get what you need, note that the tutorial uses PIL so you will need that installed as well.

Flipping a image vertically, relation ship between the original picture and the new one. [python]

I am trying to flip a picture on its vertical axis, I am doing this in python, and using the Media module.
like this:
i try to find the relationship between the original and the flipped. since i can't go to negative coordinates in python, what i decided to do is use the middle of the picture as the reference.
so i split the picture in half,and this is what i am going to do:
[note i create a new blank picture and copy each (x,y) pixel to the corresponding to (-x,y), if the original pixel is after the middle.
if its before the middle, i copy the pixel (-x,y) to (x,y)
so i coded it in python, and this is the result.
Original:
i got this:
import media
pic=media.load_picture(media.choose_file())
height=media.get_height(pic)
width=media.get_width(pic)
new_pic=media.create_picture(width,height)
for pixel in pic:
x_org=media.get_x(pixel)
y_org=media.get_y(pixel)
colour=media.get_color(pixel)
new_pixel_0=media.get_pixel(new_pic,x_org+mid_width,y_org) #replace with suggested
#answer below
media.set_color( new_pixel_0,colour)
media.show(new_pic)
this is not what i wanted, but i am so confused, i try to find the relationship between the original pixel location and its transformed (x,y)->(-x,y). but i think that's wrong. If anyone could help me with this method it would be great full.
at the end of the day i want a picture like this:
http://www.misterteacher.com/alphabetgeometry/transformations.html#Flip
Why not just use Python Imaging Library? Flipping an image horizontally is a one-liner, and much faster to boot.
from PIL import Image
img = Image.open("AFLAC.jpg").transpose(Image.FLIP_LEFT_RIGHT)
Your arithmetic is incorrect. Try this instead...
new_pixel_0 = media.get_pixel(new_pic, width - x_org, y_org)
There is no need to treat the two halves of the image separately.
This is essentially negating the x-co-ordinate, as your first diagram illustrates, but then slides (or translates) the flipped image by width pixels to the right to put it back in the range (0 - width).
Here is a simple function to flip an image using scipy and numpy:
import numpy as np
from scipy.misc import imread, imshow
import matplotlib.pyplot as plt
def flip_image(file_name):
img = imread(file_name)
flipped_img = np.ndarray((img.shape), dtype='uint8')
flipped_img[:,:,0] = np.fliplr(img[:,:,0])
flipped_img[:,:,1] = np.fliplr(img[:,:,1])
flipped_img[:,:,2] = np.fliplr(img[:,:,2])
plt.imshow(flipped_img)
return flipped_img

Categories

Resources