Python - matplotlib - imshow - How to influence displayed value of unzoomed image - python

I need to search outliers in more or less homogeneous images representing some physical array. The images have a resolution which is much higher than the screen resolution. Thus every pixel on screen originates from a block of image pixels. Is there the possibility to customize the algorithm which calculates the displayed value for such a block? Especially the possibility to either use the lowest or the highest value would be helpful.
Thanks in advance

Scipy provides several such filters. To get a new image (new) whose pixels are the maximum/minimum over a w*w block of an original image (img), you can use:
new = scipy.ndimage.filters.maximum_filter(img, w)
new = scipy.ndimage.filters.minimum_filter(img, w)
scipy.ndimage.filters has several other filters available.
If the standard filters don't fit your requirements, you can roll your own. To get you started here is an example that shows how to get the minimum in each block in the image. This function reduces the size of the full image (img) by a factor of w in each direction. It returns a smaller image (new) in which each pixel is the minimum pixel in a w*w block of pixels from the original image. The function assumes the image is in a numpy array:
import numpy as np
def condense(img, w):
new = np.zeros((img.shape[0]/w, img.shape[1]/w))
for i in range(0, img.shape[1]//w):
col1 = i * w
new[:, i] = img[:, col1:col1+w].reshape(-1, w*w).min(1)
return new
If you wanted the maximum, replace min with max.
For the condense function to work well, the size of the full image must be a multiple of w in each direction. The handling of non-square blocks or images that don't divide exactly is left as an exercise for the reader.

Related

Convert an image of a signal into a python list - by specifying no of points in 1D

I'm struggling to convert an image of a signal back to a python list (it was plotted a long time ago and I have lost the data I have only the images).
I've searched on the internet but I find answers about how to convert a 2d image into a 1d and I want to get the signal back.
Long story short:
I have this image of a signal:
and I want to convert this to a python list with a size of 65535 so my list should be looking like this:
list = [0.14, 0.144, 0.12 ...... ]
Thanks!
As a first plan, you could load the image using PIL/Pillow, or OpenCV, greyscale it and resize it to 65536 pixels wide by 100 pixels tall.
Then you will have a Numpy array with dimensions (100,65536). You can then run np.argmin() to find the index (y-value) of the darkest pixel in each column.
Or, find the indices of all the low valued pixels and take their median instead of the second step above.
The code starts off like this:
#!/usr/bin/env python3
from PIL import Image
import numpy as np
# Load image and convert to greyscale
im = Image.open('signal.png').convert('L')
# Resize to match required output
big = im.resize((65536,100), resample=Image.NEAREST)
# Make Numpy array
na = np.array(big)
# This looks about right, I think
print(np.argmin(na,axis=0))
If you trim the image so that the signal touches the edges all the way around, then the first black pixel on the left comes out as list element 0, the last pixel on the right comes out as the last element of your list and the lowest black pixel comes out with y-value of 0 and the peak comes out with y-value of 100.
Trimming would look like this:
from PIL import Image, ImageOps
import numpy as np
# Load image and convert to greyscale
im = Image.open('signal.png').convert('L')
# Get bounding box
bbox = ImageOps.invert(im).getbbox()
# Trim original image so that signal touches edge on all sides
im = im.crop(bbox)
... continue as before ...
Essentially, you'll have to "scan" the images left to right and identify the correct signal value at each "time step." As the image you presented doesn't have scale / units, you'll probably want to normalize all signal values from 0 to 1, as you've implied in your answer. Later you can adjust the scale of the signal if that's not the right range.
It looks like your images have some anti-aliasing at each step of the signal, which means that you won't have columns of all zeros except for one "signal" value. You'll have a cluster of signal values at each time step, some of which are weaker, because the image compression has blurred the signal slightly. This shouldn't be a problem, since you'll just find the max at each time step.
Assuming these images are in grayscale (if not, convert to grayscale), you'd want to find the maximum (or minimum, if the signal is drawn in black) color value at each column of pixels in the images (representing timesteps of the signal).
Mark Setchell's suggestion of PIL/Pillow seems like a great first step.
numpy's amax takes a matrix and flattens it to the max across an entire axis.

How to get border pixels of an image in python?

I have an image, using steganography I want to save the data in border pixels only.
In other words, I want to save data only in the least significant bits(LSB) of border pixels of an image.
Is there any way to get border pixels to store data( max 15 characters text) in the border pixels?
Plz, help me out...
OBTAINING BORDER PIXELS:
Masking operations are one of many ways to obtain the border pixels of an image. The code would be as follows:
a= cv2.imread('cal1.jpg')
bw = 20 //width of border required
mask = np.ones(a.shape[:2], dtype = "uint8")
cv2.rectangle(mask, (bw,bw),(a.shape[1]-bw,a.shape[0]-bw), 0, -1)
output = cv2.bitwise_and(a, a, mask = mask)
cv2.imshow('out', output)
cv2.waitKey(5000)
After I get an array of ones with the same dimension as the input image, I use cv2.rectangle function to draw a rectangle of zeros. The first argument is the image you want to draw on, second argument is start (x,y) point and the third argument is the end (x,y) point. Fourth argument is the color and '-1' represents the thickness of rectangle drawn (-1 fills the rectangle). You can find the documentation for the function here.
Now that we have our mask, you can use 'cv2.bitwise_and' (documentation) function to perform AND operation on the pixels. Basically what happens is, the pixels that are AND with '1' pixels in the mask, retain their pixel values. Pixels that are AND with '0' pixels in the mask are made 0. This way you will have the output as follows:
.
The input image was :
You have the border pixels now!
Using LSB planes to store your info is not a good idea. It makes sense when you think about it. A simple lossy compression would affect most of your hidden data. Saving your image as JPEG would result in loss of info or severe affected info. If you want to still try LSB, look into bit-plane slicing. Through bit-plane slicing, you basically obtain bit planes (from MSB to LSB) of the image. (image from researchgate.net)
I have done it in Matlab and not quite sure about doing it in python. In Matlab,
the function, 'bitget(image, 1)', returns the LSB of the image. I found a question on bit-plane slicing using python here. Though unanswered, you might want to look into the posted code.
To access border pixel and enter data into it.
A shape of an image is accessed by t= img.shape. It returns a tuple of the number of rows, columns, and channels.A component is RGB which 1,2,3 respectively.int(r[0]) is variable in which a value is stored.
import cv2
img = cv2.imread('xyz.png')
t = img.shape
print(t)
component = 2
img.itemset((0,0,component),int(r[0]))
img.itemset((0,t[1]-1,component),int(r[1]))
img.itemset((t[0]-1,0,component),int(r[2]))
img.itemset((t[0]-1,t[1]-1,component),int(r[3]))
print(img.item(0,0,component))
print(img.item(0,t[1]-1,component))
print(img.item(t[0]-1,0,component))
print(img.item(t[0]-1,t[1]-1,component))
cv2.imwrite('output.png',img)

Using OpenCV remap function crops image

I am trying to warp an 640x360 image via the OpenCV remap function (in python 2.7). The steps executed are the following
Generate a curve and store its x and y coordinates in two seperate arrays, curve_x and curve_y.I am attaching the generated curve as an image(using pyplot):
Load image via the opencv imread function
original = cv2.imread('C:\\Users\\User\\Desktop\\alaskan-landscaps3.jpg')
Execute a nested for loop so that each pixel is shifted upwards in proportion to the height of the curve at that point.For each pixel I calculate a warping factor by dividing the distance between the curve's y coordinate and the "ceiling" (360) by the height of the image. The factor is then multiplied with the distance between the pixel's y-coordinate and the "ceiling" in order to find the new distance that the pixel must have from the "ceiling" (it will be shorter since we have an upward shift). Finally I subtract this new distance from the "ceiling" to obtain the new y-coordinate for the pixel. I thought of this formula in order to ensure that all entries in the map_y array used in the remap function will be within the area of the original image.
for i in range(0, y_size):
for j in range(0,x_size):
map_y[i][j]= y_size-((y_size - i) * ((y_size - curve_y[j]) / y_size))
map_x[i][j]=j`
Then using the remap function
warped=cv2.remap(original,map_x,map_y,cv2.INTER_LINEAR)
The resulting image appears to be warped somewhat along the curve's path but it is cropped - I am attaching both the original and resulting image
I know I must be missing something but I can't figure out where the mistake is in my code - I don't understand why since all y-coordinates in map_y are between 0-360 the top third of the image has disappeared following the remapping
Any pointers or help will be appreciated. Thanks
[EDIT:] I have edited my function as follows:
#array to store previous y-coordinate, used as a counter during mapping process
floor_y=np.zeros((x_size),np.float32)
#for each row and column of picture
for i in range(0, y_size):
for j in range(0,x_size):
#calculate distance between top of the curve at given x coordinate and top
height_above_curve = (y_size-1) - curve_y_points[j]
#calculated a mapping factor, using total height of picture and distance above curve
mapping_factor = (y_size-1)/height_above_curve
# if there was no curve at given x-coordinate then do not change the pixel coordinate
if(curve_y_points[j]==0):
map_y[i][j]=j
#if this is the first time the column is traversed, save the curve y-coordinate
elif (floor_y[j]==0):
#the pixel is translated upwards according to the height of the curve at that point
floor_y[j]=i+curve_y_points[j]
map_y[i][j]=i+curve_y_points[j] # new coordinate saved
# use a modulo operation to only translate each nth pixel where n is the mapping factor.
# the idea is that in order to fit all pixels from the original picture into a new smaller space
#(because the curve squashes the picture upwards) a number of pixels must be removed
elif ((math.floor(i % mapping_factor))==0):
#increment the "floor" counter so that the next group of pixels from the original image
#are mapped 1 pixel higher up than the previous group in the new picture
floor_y[j]=floor_y[j]+1
map_y[i][j]=floor_y[j]
else:
#for pixels that must be skipped map them all to the last pixel actually translated to the new image
map_y[i][j]=floor_y[j]
#all x-coordinates remain unchanges as we only translate pixels upwards
map_x[i][j] = j
#printout function to test mappings at x=383
for j in range(0, 360):
print('At x=383,y='+str(j)+'for curve_y_points[383]='+str(curve_y_points[383])+' and floor_y[383]='+str(floor_y[383])+' mapping is:'+str(map_y[j][383]))
The bottom line is that now the higher part of the image should not receive mappings from the lowest part so overwriting of pixels should not take place. Yet i am still getting a hugely exaggerated upwards warping effect in the picture which I cannot explain. (see new image below).The top of the curved part is at around y=140 in the original picture yet now is very close to the top i.e y around 300. There is also the question of why I am not getting a blank space at the bottom for the pixels below the curve.
I'm thinking that maybe there is also something going on with the order of rows and columns in the map_y array?
I don't think the image is being cropped. Rather, the values are "crowded" in the top-middle pixels, so that they get overwritten. Consider the following example with a simple function on a checkerboard.
import numpy as np
import cv2
import pickle
y_size=200
x_size=200
x=np.linspace(0,x_size,x_size+1)
y=(-(x-x_size/2)*(x-x_size/2))/x_size+x_size
plt.plot(x,y)
The function looks like this:
Then let's produce an image with a regular pattern.
test=np.zeros((x_size,y_size),dtype=np.float32)
for i in range(0, y_size):
for j in range(0,x_size):
if i%2 and j%2:
test[i][j]=255
cv2.imwrite('checker.png',test)
Now let's apply your shift function to that pattern:
map_y=np.zeros((x_size,y_size),dtype=np.float32)
map_x=np.zeros((x_size,y_size),dtype=np.float32)
for i in range(0, y_size):
for j in range(0,x_size):
map_y[i][j]= y_size-((y_size - i) * ((y_size - y[j]) / y_size))
map_x[i][j]=j
warped=cv2.remap(test,map_x,map_y,cv2.INTER_LINEAR)
cv2.imwrite('warped.png',warped)
If you notice, because of the shift, more than one value corresponds to the top-middle areas, which makes it look like it is cropped. But if you check to the top left and right corners of the image, notice that the values are sparser, thus the "cropping" effect does not occur much. I hope the simple example helps better to understand what is going on.

Scipy Binary Closing - Edge Pixels lose value

I am attempting to fill holes in a binary image. The image is rather large so I have broken it into chunks for processing.
When I use the scipy.ndimage.morphology.binary_fill_holes functions, it fills larger holes that belong in the image. So I tried using scipy.ndimage.morphology.binary_closing, which gave the desired results of filling small holes in the image. However, when I put the chunks back together, to create the entire image, I end up with seamlines because the binary_closing function removes any values from the border pixels of each chunk.
Is there any way to avoid this effect?
Yes.
Label your image using ndimage.label (first invert the image, holes=black).
Find the hole object slices with ndimage.find_objects
Filter the list of object slices based on your size criteria
Invert back your image and perform binary_fill_holes on the slices that meet your criteria.
That should do it, without needing to chop the image up. For example:
Input image:
Output image (Middle size holes are gone):
Here is the code (inequality is set to remove the middle size blobs):
import scipy
from scipy import ndimage
import numpy as np
im = scipy.misc.imread('cheese.png',flatten=1)
invert_im = np.where(im == 0, 1, 0)
label_im, num = ndimage.label(invert_im)
holes = ndimage.find_objects(label_im)
small_holes = [hole for hole in holes if 500 < im[hole].size < 1000]
for hole in small_holes:
a,b,c,d = (max(hole[0].start-1,0),
min(hole[0].stop+1,im.shape[0]-1),
max(hole[1].start-1,0),
min(hole[1].stop+1,im.shape[1]-1))
im[a:b,c:d] = scipy.ndimage.morphology.binary_fill_holes(im[a:b,c:d]).astype(int)*255
Also note that I had to increase the size of the slices so that the holes would have border all the way around.
Operations that involve information from neighboring pixels, such as closing will always have trouble at the edges. In your case, this is very easy to get around: just process subimages that are slightly larger than your tiling, and keep the good parts when stitching together.

Finding coordinates of brightest pixel in an image and entering them into an array

I have been asked to write a program to find 'stars' in an image by converting the image file to a numpy array and generating an array of the coordinates of the brightest pixels in the image above a specified threshold (representing background interference).
Once I have located the brightest pixel in the image I must record its x,y coordinates, and set the value of that pixel and surrounding 10X10 pixel area to zero, effectively removing the star from the image.
I already have a helper code which converts the image to an array, and have attempted to tackle the problem as follows;
I have defined a variable
Max = array.max()
and used a while loop;
while Max >= threshold
coordinates = numpy.where(array == Max) # find the maximum value
however I want this to loop over the whole array for all of the coordinates,not just find the first maximum, and also remove each maximum when found and setting the surrounding 10X10 area to zero. I have thought about using a for loop to do this but am unsure how I should use it since I am new to Python.
I would appreciate any suggestions,
Thanks
There are a number of different ways to do it with just numpy, etc.
There's the "brute force" way:
import Image
import numpy as np
im = Image.open('test.bmp')
data = np.array(im)
threshold = 200
window = 5 # This is the "half" window...
ni, nj = data.shape
new_value = 0
for i, j in zip(*np.where(data > threshold)):
istart, istop = max(0, i-window), min(ni, i+window+1)
jstart, jstop = max(0, j-window), min(nj, j+window+1)
data[istart:istop, jstart:jstop] = new_value
Or the faster approach...
import Image
import numpy as np
import scipy.ndimage
im = Image.open('test.bmp')
data = np.array(im)
threshold = 200
window = 10 # This is the "full" window...
new_value = 0
mask = data > threshold
mask = scipy.ndimage.uniform_filter(mask.astype(np.float), size=window)
mask = mask > 0
data[mask] = new_value
Astronomy.net will do this for you:
If you have astronomical imaging of the sky with celestial coordinates
you do not know—or do not trust—then Astrometry.net is for you. Input
an image and we'll give you back astrometric calibration meta-data,
plus lists of known objects falling inside the field of view.
We have built this astrometric calibration service to create correct,
standards-compliant astrometric meta-data for every useful
astronomical image ever taken, past and future, in any state of
archival disarray. We hope this will help organize, annotate and make
searchable all the world's astronomical information.
You don't even have to upload the images to their website. You can download the source. It is licensed under the GPL and uses NumPy, so you can muck around with it if you need to.
Note that you will need to first convert your bitmap to one of the following: JPEG, GIF, PNG, or FITS image.

Categories