Adjusting images in a pandas dataframe - python

I'm trying to ajust values of images in a pandas dataframe
Each row of the dataframe (images) holds an image of shape (7,7,3), 7x7 pixels and 3 colours.
So when I try to adjust the top left pixel of the first image like so:
All other images (rows) are affected as well.
print(images.loc[0,'image'][0][0], images.loc[1,'image'][0][0])
images.loc[0,'image'][0][0]=[1,2,3]
print(images.loc[0,'image'][0][0], images.loc[1,'image'][0][0])
[0,0,0] [0,0,0]
[1,2,3] [1,2,3]
This only happens when I adjust a single pixel.
If I edit the image in its entirety, the other images/rows are not affected.
images[0,'image']=[image]
does work properly
added mvce:
import numpy as np
import pandas as pd
images = pd.DataFrame(columns=['image'])
image = np.zeros([2, 2, 2])
images.loc[0, 'image'] = image
images = pd.concat([images] * 2)
images = images.reset_index(drop=True)
print(images.loc[0, 'image'][0][0], '\n')
images.loc[0, 'image'][0][0] = [1, 1]
print(images.loc[0, 'image'][0][0], images.loc[1, 'image'][0][0])

The problem is in the lines
image=np.zeros([2,2,2])
and
images=pd.concat([images]*2)
You create a single numpy object. This object is referenced twice in the final dataframe. To illustrate, if you explicitly make a copy of the object, the problem disappears:
import copy
images=pd.DataFrame(columns=['image'])
image=np.zeros([2,2,2])
images.loc[0,'image']=image
images=pd.concat([copy.deepcopy(images), copy.deepcopy(images)]) # explicitly duplicate the object to avoid reference to the same object
images=images.reset_index(drop=True)
print(images.loc[0,'image'][0][0],'\n')
images.loc[0,'image'][0][0]=[1,1]
print(images.loc[0,'image'][0][0],images.loc[1,'image'][0][0])
edit: to adress your comment, how to create many copies, you could try:
images = [np.zeros([2,2,2]) for lv in range(10000)] # create list containing independent instances of numpy arrays
images = pd.Series(images, index = range(10000))
images = images.to_frame('images')
images # should now be a dataframe containing independent numpy arrays in its 'image' column.

Related

How do I filter by area or eccentricity using skimage.measure.regionprops on a binary image in Python

I have a binary image of a road surface and I am trying to isolate the pothole only. Using skimage.measure.regionprops and skimage.measure.label I can produce a table of properties for different labels within the image.
How do I then filter using those values? - for instance using area or axis length or eccentricity to turn off certain labels.
Input, labled Image and properties table
using python 3
I would use pandas together with skimage.measure.regionprops_table to get what you want:
import pandas as pd
import imageio as iio
from skimage.measure import regionprops_table, label
image = np.asarray(iio.imread('path/to/image.png'))
labeled = label(image > 0) # ensure input is binary
data = regionprops_table(
labeled,
properties=('label', 'eccentricity'),
)
table = pd.DataFrame(data)
table_sorted_by_ecc = table.sort_values(
by='eccentricity', ascending=False
)
# print e.g. the 10 most eccentric labels
print(table_sorted.iloc[:10])
If you then want to e.g. produce the label image with only the most eccentric label, you can do:
eccentric_label = table['labels'].iloc[np.argmax(table['eccentricity'])]
labeled_ecc = np.where(labeled == eccentric_label, eccentric_label, 0)
You can also do more sophisticated things, e.g. make a label image with only labels above a certain eccentricity. Below, we use NumPy elementwise multiplication to produce an array that is the original label if that label has high eccentricity, or 0 otherwise. We then use the skimage.util.map_array function to map the original labels to either themselves or 0, again, depending on the eccentricity.
from skimage.util import map_array
ecc_threshold = 0.3
eccentric_labels = table['labels'] * (table['eccentricity'] > ecc_threshold)
new_labels = map_array(
labeled,
np.asarray(table['labels']),
np.asarray(eccentric_labels),
)

cant save an 4d array int .txt file

I am using the sliding window technic to an image and i am extracting the mean values of pixels of each one window. So the results are someting like this [[[[215.015625][123.55036272][111.66057478]]]].now the question is how could i save all these values for every one window into a txt file or at a CSV because i want to use them for further compare similarities? whatever i tried the error is same..that it is a 4D array and not an 1D or 2D. I ll appreciate any help really.! Thank you in advance
import cv2
import matplotlib.pyplot as plt
import numpy as np
# read the image and define the stepSize and window size
# (width,height)
image2 = cv2.imread("bird.jpg")# your image path
image = cv2.resize(image2, (224, 224))
tmp = image # for drawing a rectangle
stepSize = 10
(w_width, w_height) = (60, 60 ) # window size
for x in range(0, image.shape[1] - w_width, stepSize):
for y in range(0, image.shape[0] - w_height, stepSize):
window = image[x:x + w_width, y:y + w_height, :]
# classify content of the window with your classifier and
# determine if the window includes an object (cell) or not
# draw window on image
cv2.rectangle(tmp, (x, y), (x + w_width, y + w_height), (255, 0, 0), 2) # draw rectangle on image
plt.imshow(np.array(tmp).astype('uint8'))
# show all windows
plt.show()
mean_values=[]
mean_val, std_dev = cv2.meanStdDev(image)
mean_val = mean_val[:3]
mean_values.append([mean_val])
mean_values = np.asarray(mean_values)
print(mean_values)
Human Readable Option
Assuming that you want the data to be human readable, saving the data takes a little bit more work. My search showed me that there's this solution for saving 3D data to a text file. However, it's pretty simple to extend this example to 4D for your use case. This code is taken and adapted from that post, thank you Joe Kington and David Cheung.
import numpy as np
data = np.arange(2*3*4*5).reshape((2,3,4,5))
with open('test.csv', 'w') as outfile:
# We write this header for readable, the pound symbol
# will cause numpy to ignore it
outfile.write('# Array shape: {0}\n'.format(data.shape))
# Iterating through a ndimensional array produces slices along
# the last axis. This is equivalent to data[i,:,:] in this case.
# Because we are dealing with 4D data instead of 3D data,
# we need to add another for loop that's nested inside of the
# previous one.
for threeD_data_slice in data:
for twoD_data_slice in threeD_data_slice:
# The formatting string indicates that I'm writing out
# the values in left-justified columns 7 characters in width
# with 2 decimal places.
np.savetxt(outfile, twoD_data_slice, fmt='%-7.2f')
# Writing out a break to indicate different slices...
outfile.write('# New slice\n')
And then once the data has been saved all you need to do is load it and reshape it (np.load()) will default to reading in the data as a 2D array but np.reshape() will allow us to recover the structure. Again, this code is adapted from the previous post.
new_data = np.loadtxt('test.csv')
# Note that this returned a 2D array!
print(new_data.shape)
# However, going back to 3D is easy if we know the
# original shape of the array
new_data = new_data.reshape((2,3,4,5))
# Just to check that they're the same...
assert np.all(new_data == data)
Binary Option
Assuming that human readability is not necessary, I would recommend using the built-in *.npy format which is described here. This stores the data in a binary format.
You can save the array by doing np.save('NAME_OF_ARRAY.npy', ARRAY_TO_BE_SAVED) and then load it with SAVED_ARRAY = np.load('NAME_OF_ARRAY.npy').
You can also save several numpy array in a single zip file with the np.savez() function like so np.savez('MANY_ARRAYS.npz', ARRAY_ONE, ARRAY_TWO). And you load the zipped arrays in a similar fashion SEVERAL_ARRAYS = np.load('MANY_ARRAYS.npz').

How to do alpha compositing with a list of RGBA data in numpy arrays?

Following this formula for alpha blending two color values, I wish to apply this to n numpy arrays of rgba image data (though the expected use-case will, in practice, have a very low upper bound of arrays, probably > 5). In context, this process will be constrained to arrays of identical shape.
I could in theory achieve this through iteration, but expect that this would be computationally intensive and terribly inefficient.
What is the most efficient way to apply a function between two elements in the same position between two arrays across the entire array?
A loose example:
# in context, the numpy arrays come from here, as either numpy data in the
# first place or a path
def import_data(source):
# first test for an extant numpy array
try:
assert(type(source) is np.ndarray)
data = source
except AssertionError:
try:
exists(source)
data = add_alpha_channel(np.array(Image.open(source)))
except IOError:
raise IOError("Cannot identify image data in file '{0}'".format(source))
except TypeError:
raise TypeError("Cannot identify image data from source.")
return data
# and here is the in-progress method that will, in theory composite the stack of
# arrays; it context this is a bit more elaborate; self.width & height are just what
# they appear to be—-the final size of the composited output of all layers
def render(self):
render_surface = np.zeros((self.height, self.width, 4))
for l in self.__layers:
foreground = l.render() # basically this just returns an np array
# the next four lines just find the regions between two layers to
# be composited
l_x1, l_y1 = l.origin
l_x2 = l_x1 + foreground.shape[1]
l_y2 = l_y1 + foreground.shape[0]
background = render_surface[l_y1: l_y2, l_x1: l_x2]
# at this point, foreground & background contain two identically shaped
# arrays to be composited; next line is where the function i'm seeking
# ought to go
render_surface[l_y1: l_y2, l_x1: l_x2] = ?
Starting with these two RGBA images:
I implemented the formula you linked to and came up with this:
#!/usr/local/bin/python3
from PIL import Image
import numpy as np
# Open input images, and make Numpy array versions
src = Image.open("a.png")
dst = Image.open("b.png")
nsrc = np.array(src, dtype=np.float)
ndst = np.array(dst, dtype=np.float)
# Extract the RGB channels
srcRGB = nsrc[...,:3]
dstRGB = ndst[...,:3]
# Extract the alpha channels and normalise to range 0..1
srcA = nsrc[...,3]/255.0
dstA = ndst[...,3]/255.0
# Work out resultant alpha channel
outA = srcA + dstA*(1-srcA)
# Work out resultant RGB
outRGB = (srcRGB*srcA[...,np.newaxis] + dstRGB*dstA[...,np.newaxis]*(1-srcA[...,np.newaxis])) / outA[...,np.newaxis]
# Merge RGB and alpha (scaled back up to 0..255) back into single image
outRGBA = np.dstack((outRGB,outA*255)).astype(np.uint8)
# Make into a PIL Image, just to save it
Image.fromarray(outRGBA).save('result.png')
Output image

How to concatenate three or more images vertically?

I try to concatenate three images vertically and I need some assistance with the code/function. So far I imported one image and cropped 3 smaller images with the same size. Now I want to concatenate them in one image that will be long, but narrow. However, I can't find an appropriate function or even if I find one I get an error message when I apply it to my code.
I already tried to make a collection from my three pictures and then use the function skimage.io.concatenate_images(sf_collection), but this results in a 4-dimensional picture that cannot be visualized.
sf_collection = (img1,img2,img3)
concat_page = skimage.io.concatenate_images(sf_collection)
My expected output is the three images to be concatenated vertically in one image (very long and narrow).
Ive never used skimage.io.concatenate, but I think you are looking for np.concatenate. It defaults to axis=0, but you can specify axis=1 for a horizontal stack. This also assumes you have already loaded the images into their array from.
from scipy.misc import face
import numpy as np
import matplotlib.pyplot as plt
face1 = face()
face2 = face()
face3 = face()
merge = np.concatenate((face1,face2,face3))
plt.gray()
plt.imshow(merge)
which returns:
If you look at the skimage.io.concatenate_images docs, it's using np.concatenate too. It seems like that function provides a data structure to hold collections of images, but not merge into a single image.
Like this:
import numpy as np
h, w = 100, 400
yellow = np.zeros((h,w,3),dtype=np.uint8) + np.array([255,255,0],dtype=np.uint8)
red = np.zeros((h,w,3),dtype=np.uint8) + np.array([255,0,0],dtype=np.uint8)
blue = np.zeros((h,w,3),dtype=np.uint8) + np.array([0,0,255],dtype=np.uint8)
# Stack vertically
result = np.vstack((yellow,red,blue))
Use the following to stack side-by-side (horizontally):
result = np.hstack((yellow,red,blue))

How to create a new dicom image with annotations from other?

I would like to create two pydicom file from one. But I can't save file in *.dcm format with annotations.
import pydicom
from pydicom.data import get_testdata_files
# read the dicom file
ds = pydicom.dcmread(test_image_fps[0])
# find the shape of your pixel data
shape = ds.pixel_array.shape
# get the half of the x dimension. For the y dimension use shape[0]
half_x = int(shape[1] / 2)
# slice the halves
# [first_axis, second_axis] so [:,:half_x] means slice all from first axis, slice 0 to half_x from second axis
data = ds.pixel_array[:, :half_x]
print('The image has {} x {}'.format(data.shape[0],
data.shape[1]))
# print the image information given in the dataset
print(data)
data.save_as("/my/path/after.dcm")
'numpy.ndarray' object has no attribute 'save_as
Info on this can be foud in the pydicom documentation.
Remark on "your" ;) code: data = ds.pixel_array[:, :half_x] assigns a view of the numpy.ndarray that is ds.pixel_array to data. Calling data.save_as() expectedly fails because that is an attribute of ds not data. As per the documentation you need to write to the ds.PixelData attribute like so:
ds.PixelData = data.tobytes() # where data is a numpy.ndarray or a view of an numpy.ndarray
# if the shape of your pixel data changes ds.Rows and ds.Columns must be updated,
# otherwise calls to ds.pixel_array.shape will fail
ds.Rows = 512 # update with correct number of rows
ds.Columns = 512 # update with the correct number of columns
ds.save_as("/my/path/after.dcm")

Categories