In python I have a function that saturates values stored in an image
def saturateData(minMax, img):
alpB = cal_alpB(minMax)
img[img>minMax[1]] = minMax[1]
img[img<0] = 0
where minMax is a list stores the saturation values to be applied on an image numpy array. I need to do the same operation at a cv::Mat object.
Is there any function that does the same operation. If not how can I achieve this? (since I am working with the images I have to it fast, the function I wrote is O(N^2) which is inefficient that's why I am asking this question!)
Related
I have a list of images that are saved as numpy arrays.
I'm using the following code to change every image to be in grayscale :
def convert_to_grayscale(images:list):
images_in_grayscale = []
rgb_weights = [0.2989, 0.5870, 0.1140]
for image in images :
images_in_grayscale.append(np.dot(image,rgb_weights))
return images_in_grayscale
I was wondering if there is a "pythonic" way to do it. Instead of iterating over the images, can I use np.dot with a matrix with different shapes ?
I tried using np.dot(np.asarray(images),rgb_weights) but I understand that all the vectors inside numpy.array must be in the same size. Is there any pythonic way to do it ?
I am trying to assign provinces to an area for use in a game mod. I have two separate maps for area and provinces.
provinces file,
area file.
Currently I am reading in an image in Python and storing it in an array using PIL like this:
import PIL
land_prov_pic = Image.open(INPUT_FILES_DIR + land_prov_str)
land_prov_array = np.array(land_prov_pic)
image_size = land_prov_pic.size
for x in range(image_size[0]):
if x % 100 == 0:
print(x)
for y in range(image_size[1]):
land_prov_array[x][y] = land_prov_pic.getpixel((x,y))
Where you end up with land_prov_array[x][y] = (R,G,B)
However, this get's really slow, especially for large images. I tried reading it in using opencv like this:
import opencv
land_prov_array = cv2.imread(INPUT_FILES_DIR + land_prov_str)
land_prov_array = cv2.cvtColor(land_prov_array, cv2.COLOR_BGR2RGB) #Convert from BGR to RGB
But now land_prov_array[x][y] = [R G B] which is an ndarray and can't be inserted into a set. But it's way faster than the previous for loop. How do I convert [R G B] to (R,G,B) for every element in the array without for loops or, better yet, read it in that way?
EDIT: Added pictures, more description, and code blocks for readability.
It is best to convert the [R,G,B] array to tuple when you need it to be a tuple, rather than converting the whole image to this form. An array of tuples takes up a lot more memory, and will be a lot slower to process, than a numeric array.
The answer by isCzech shows how to create a NumPy view over a 3D array that presents the data as if it were a 2D array of tuples. This might not require the additional memory of an actual array of tuples, but it is still a lot slower to process.
Most importantly, most NumPy functions (such as np.mean) and operators (such as +) cannot be applied to such an array. Thus, one is obliged to iterate over the array in Python code (or with a #np.vectorize function), which is a lot less efficient than using NumPy functions and operators that work on the array as a whole.
For transformation from a 3D array (data3D) to a 2D array (data2D), I've used this approach:
import numpy as np
dt = np.dtype([('x', 'u1'), ('y', 'u1'), ('z', 'u1')])
data2D = data3D.view(dtype=dt).squeeze()
The .view modifies the data type and returns still a 3D array with the last dimension of size 1 which can be then removed by .squeeze. Alternatively you can use .squeeze(axis=-1) to only squeeze the last dimension (in case some of your other dimensions are of size 1 too).
Please note I've used uint8 ('u1') - your type may be different.
Trying to do this using a loop is very slow, indeed (compared to this approach at least).
Similar question here: Show a 2d numpy array where contents are tuples as an image
So I'm doing this course and there's this exercise where I have to blur an image using a 3x3 area of the array and swapping out all values to the average. So I wrote this function which I know is still does not work completely:
def replace_avg(img, block=3):
x_dim, y_dim = img.shape
for row in range(1,x_dim-block+2,3):
for col in range(1,y_dim-block+2,3):
img[row-(block-2):row+(block-1),col-(block-2):col+(block-1)] = np.average(img[row-(block-2):row+(block-1),col-(block-2):col+(block-1)])
return img
My question is is there a more efficient way of looping through this array with a 3x3 filter using numpy?
The skimage package provides a function that does exactly what you want :
from skimage import transform
img_rescaled = transform.rescale(img,1/block)
Maybe you're looking for a solution specifically using Numpy, in that case you should look how that function is coded inside the skimage module
I have an image I've read from file with shape (m,n,3) (i.e. it has 3 channels). I also have a matrix to convert the color space with dimensions (3,3). I've already arrived at a few different ways of applying this matrix to each vector in the image; for example,
np.einsum('ij,...j',transform,image)
appears to make for the same results as the following (far slower) implementation.
def convert(im: np.array, transform: np.array) -> np.array:
""" Convert an image array to another colorspace """
dimensions = len(im.shape)
axes = im.shape[:dimensions-1]
# Create a new array (respecting mutability)
new_ = np.empty(im.shape)
for coordinate in np.ndindex(axes):
pixel = im[coordinate]
pixel_prime = transform # pixel
new_[coordinate] = pixel_prime
return new_
However, I found that the following is even more efficient while testing on the example image with line_profiler.
np.moveaxis(np.tensordot(transform, X, axes=((-1),(-1))), 0, 2)
The problem I'm having here is using just a np.tensordot, i.e. removing the need for np.moveaxis. I've spent a few hours attempting to find a solution (I'm guessing it resides in choosing the correct axes), so I thought I'd ask others for help.
You can do it concisely with tensordot if you make image the first argument:
np.tensordot(image, transform, axes=(-1, 1))
You can get better performance from einsum by using the argument optimize=True (requires numpy 1.12 or later):
np.einsum('ij,...j', transform, image, optimize=True)
Or (as Paul Panzer pointed out in a comment), you can simply use matrix multiplication:
image # transform.T
They all take about the same time on my computer.
I'm trying to handle a memory problem in my application by using memory mapped arrays. However, as part of my computation I need to set values some values in my array to 0. Unfortunately, the array mask will require additional memory. Is there a way to do the following such that the mask is handled cleanly?
source_array = numpy.memmap(filename, dtype='float32', mode='w+', shape=shape)
#Load data into memory mapped numpy array
band.ReadAsArray(buf_obj = source_array)
#set values == 255 to 0
numpy.putmask(source_array, source_array >= 255.0, 0.0)
I believe the last line with source_array >= 255.0 must make a big array in memory, right? Aside from manually looping through each element, is there a memory efficient mechanism to set all my 255 values in source_array to 0?
Sorry realized that of course memmapping the mask isn't an optimal solution here. Numpy does not really have much to help loop through the array in chunks (which would be the cleanest way), though you can of course do that by hand. You might actually have some success with numexpr, which always does its calculation in chunks for speeding up numpy, but I did not try this.
I guess this wasn't quite what you wanted:
You can always use the out parameters to ufuncs and many other functions to ask numpy to store the result into that array directly (also to generally save memory). This means that if you create an empty memory map array you can do this:
# You could use tempfile.NamedTemporaryFile. But I will leave that to you:
mask = np.memmap(tempfile, shape=source_array.shape, dtype=bool, mode='w+')
np.greater_equal(source_array, 255.0, out=mask)
And then use the mask array in putmask. This should solve the problem.