Replace certain pixels by integers in numpy array - python

I have made myself a numpy array from a picture using
from PIL import Image
import numpy as np
image = Image.open(file)
np.array(image)
its shape is (6000, 6000, 4) and in that array I would like to replace pixel values by one number lets say this green pixel [99,214,104,255] will be 1.
I have only 4 such pixels I want to replace with a number and all other pixels will be 0. Is there a fast and efficient way to do so and what is the best way to minimize the size of the data. Is it better to save it as dict(), where keys will be x,y and values, will be integers? Or is it better to save the whole array as it is with the shape it has? I only need the color values the rest is not important for me.
I need to process such a picture as fast as possible because there is one picture every 5 minutes and lets say i would like to store 1 year of data. That is why I'd like to make it as efficient as possible time and space-wise.

If I understand the question correctly, you can use np.where for this:
>>> arr = np.array(image)
>>> COLOR = [99,214,104,255]
>>> np.where(np.all(arr == COLOR, axis=-1), 1, 0)
This will produce a 6000*6000 array with 1 if the pixel is the selected colour, or 0 if not.

How about just storing in a database: the position and value of the pixels you want to modify, the shape of the image, the dtype of the array and the extension (jpg, etc...). You can use that information to build a new image from an array filled with 0.

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.

Check if cifar10 is converted well

Recently I followed a few tutorials on machine learning, and now I want to test if I can make some image recognition program by myself. For this I want to use the CIFAR 10 dataset, but I think I have a small problem in the conversion of the dataset.
For who is not familiar with this set: the dataset comes as lists of n rows and 3072 columns, in which the first 1024 columns represent the red values, the second 1024 the green values and the last are the blue values. Each row is a single image (size 32x32) and the pixel rows are stacked after each other (first 32 values are the red values for the top-most row of pixels, etc.)
What I wanted to do with this dataset is to transform it to a 4D tensor (with numpy), so I can view the images with matplotlibs .imshow(). the tensor I made has this shape: (n, 32, 32, 3), so the first 'dimension' stores all images, the second stores rows of pixels, the third stores individual pixels and the last represents the rgb values of those pixels. Here is the function I made that should do this:
def rawToRgb(data):
length = data.shape[0]
# convert to flat img array with rgb pixels
newAr = np.zeros([length, 1024, 3])
for img in range(length):
for pixel in range(1024):
newAr[img, pixel, 0] = data[img, pixel]
newAr[img, pixel, 1] = data[img, pixel+1024]
newAr[img, pixel, 2] = data[img, pixel+2048]
# convert to 2D img array
newAr2D = newAr.reshape([length, 32, 32, 3])
# plt.imshow(newAr2D[5998])
# plt.show()
return newAr2D
Which takes a single parameter (a tensor of shape (n, 3072)). I have commented out the pyplot code, as this is only for testing, but when testing, I noticed that everything seems to be ok (I can recognise the shapes of the objects in the images, but I am not sure if the colours are good or not, as I get some oddly-coloured images as well as some pretty normal images... Here are a few examples: purple plane, blue cat, normal horse, blue frog.
Can anyone tell me wether I am making a mistake or not?
The images that appear oddly-coloured are the negative of the actual image, so you need to subtract each pixel value from 255 to get the true value. If you simply want to see what the original images look like, use:
from scipy.misc import imread
import matplotlib.pyplot as plt
img = imread(file_path)
plt.imshow(255 - img)
plt.show()
The original cause of the problem is that the CIFAR-10 data stores the pixel values on a scale of 0-255, but matplotlib's imshow() method (which I assume you are using) expects inputs between 0 and 1. Given an input that is not scaled between 0 and 1, imshow() does some normalization internally, which causes some images to become negatives.

Display the Pixels of Bayer Format Image

I have just started using OpenCv-Python.
I want to see the pixel values of an image which is in Bayer format. Using OpenCv-Python, I have written the following code to display the pixel values :
import numpy as np
import cv2
image = cv2.imread("bayer_small.png")
image_data = np.array(image)
print(image_data[50][50])
#Output is printed as " [ 0 0 102]
My expectation is single pixel value : 102
Why is this happening? Since the image is only in Bayer format, I am expecting one component only per pixel.
I understand that output is showing zero for other two components. But I am expecting the data in the bayer format only, for example : BGBGBGBG for first line.
My goal is to implement an algorithm. Hence trying to do step by step.
Edit 1: Is there any in-built function where I can convert a normal image to Bayer format?
While your picure probably contains the data from the Bayer matrix, it obiously still uses the RGB format for containing it. You can probably assume that two channels contain 0 for each pixel; thus you should apply a vectorized sum to the whole picture along the third axis with:
data = np.sum(image_data, axis=2)
in order to normalize it.
By the way, you can access data in a Numpy array with the more concise syntax: data[50, 50] instead of data[50][50].

Swapping array dimensions with NumPy

When I load an image with PIL and convert it into a NumPy array:
image = Image.open("myimage.png")
pixels = np.asarray(image)
The data is stored as [x][y][channel]. I.e., the value of pixels[3, 5, 0] will be the the (3, 5) pixel, and the red component of that pixel.
However, I am using a library which requires the image to be in the format [channel][x][y]. Therefore, I am wondering how I can do this conversion?
I know that NumPy has a reshape function, but this doesn't actually allow you to "swap" over the dimensions as I want.
Any help? Thanks!
In order to get the dimensions in the order that you want, you could use the transpose method as follows:
image = Image.open("myimage.png")
pixels = np.asarray(image).transpose(2,0,1)

Intersecting Arrays in python

Ok, so here is my code. om is the array I'm comparing im to. I'm hoping the array is in the format [b, g, r]
import cv2
import numpy as np
import time
om=cv2.imread('RGB.png')
om=om.reshape(1,-1,3)
while True:
cam = cv2.VideoCapture(0)
start=time.time()
while(cam.isOpened()):
ret, im = cam.read()
im=cv2.resize(im,(325,240))
im= im.reshape(1,-1,3)
Ok, so I'm hoping the arrays are based off of all the pixel colours in them and that they are 1D (reshape should have done that?). Duplicates aren't necessary but like, if possible I would like to keep them.
I want to intersect om with im and get the value of the number of elements intersecting. I tried the in1d thing, but it would return Trues and Falses. I'm half wondering if it would be easier to count them?/ the trues.
Also, if I do use the in1d function, does that only count corresponding pixels? (like, that the pixel height and row matter) or is it only pixel bgr? because I am only after bgr.
Basically, I wanna see how many pixels have the same colour value as those in the picture I already have saved.
Btw, I tried using sets, but they were fairly slow and difficult to get into the correct order (I seemed to be getting only one element a set).
intersection = [x for x in list_1 if x in list_2]

Categories