appending 3d arrays in numpy - python

I'm trying to find dominant colors in image and then treshold the most dominant one. However I'm having trouble with data types.
My formula gives the most dominant color as:
color=[10,10,10] # type=numpy.ndarray ,uint8
But it gives assertion error when I try to convert it:
color=cv2.cvtColor(color, cv2.COLOR_BGR2HSV) #gives assertion error
What cv2.cvtColor wants as an input is that:
color_ideal=[[[ 10, 10, 10 ]]] #type=numpy.ndarray, uint8
To obtain it, I managed to manipulate color as such:
color=np.uint8(np.atleast_3d(clr).astype(int).reshape(1,1,3))
This seems working, but know I cannot append multiple colors to numpy array.Somehow, after appending the dimension is reduced to 1. My code is:
color=np.uint8([[[]]])
for item in clt.cluster_centers_:
color=np.append(color,(np.uint8(np.atleast_3d(item).astype(int).reshape(1,1,3))))
#returns: color=[10,10,10] somehow its dimension is down to 1
My questions are:
1-How to properly append color data without loosing its dimension?
2-Is there easier way to handle this? I'm suprised how difficult it is to manipulate custom color pixel.
The full code is here in case it helps:
<!-- language: lang-py -->
import cv2
import numpy as np
from sklearn.cluster import KMeans
def find_kmean_colors(img,no_cluster=2):
clt = KMeans(no_cluster).fit(img)
return clt
def initialize(img='people_frontal.jpg'):
img=cv2.imread('people_frontal_close_body.jpg')
img=cv2.bilateralFilter(img,9,75,75)
return img
img=initialize()
img_hsv =cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
img_list= img.reshape((img.shape[0] * img_hsv.shape[1], 3))
clt=(find_kmean_colors(img_list,1))
color=np.uint8([[[]]])
for i in clt.cluster_centers_:
color=np.append(color,(np.uint8(np.atleast_3d(i).astype(int).reshape(1,1,3))))
#color=np.uint8(np.atleast_3d(clt.cluster_centers_).astype(int).reshape(1,1,3))
up=cv2.cvtColor(color,cv2.COLOR_BGR2HSV)

Without the cv2 code, I'm guessing about shapes here. But looks like img is a (n,m,3) array. img_list is (m1,3), and clt as list of m1 items, and clt.cluster_centers_ a list of m1 arrays of shape (3,).
For test sake lets make a list of lists (it could just as well be a list of arrays):
ctrs=[[10,10,10], [3,5,3], [20,10,10], [0,0,0]]
color = np.array(ctrs,dtype=np.uint8) # (4,3) array
color = color.reshape(len(ctrs),1,3)
Just wrap it in np.array, and reshape to 3d.
array([[[10, 10, 10]],
[[ 3, 5, 3]],
[[20, 10, 10]],
[[ 0, 0, 0]]], dtype=uint8)
Or it could be reshaped to (1,4,3) or (2,2,3).
Or closer to what you are trying:
np.concatenate([np.array(i,np.uint8).reshape(1,1,3) for i in ctrs])
You don't want to use atleast_3d here since it reshapes a (N,) array to (1,N,1) (see its docs). np.concatenate joins on the 1st axis, where as np.array adds a 1st dimension and then joins.
You probably could get append to work, but it just does a step by step concatenate, which is slower. In general if you need to append, do it with lists, and then convert to an array at the end.
There are various ways of preserving or restoring dimensions after slicing. If color is 3d and you need the i'th row also as 3d:
color[[i]]
color[i].reshape(1,...)
color[i][np.newaxis,...]
Reshaping operations like this do not add significant time to the processing, so don't be afraid to use them.

Related

Transform a 2x2 array into a 2x2x2 arrays with numpy

I use numpy to do image processing, I wanted to switch the image to black and white and for that I did the calculation in each cell to see the luminosity, but if i want to show it i have to transform a 2d array into 2d array with 3 times the same value
for exemple i have this:
a = np.array([[255,0][0,255]])
#into
b = np.array([[[255,255,255],[0,0,0]],[[0,0,0],[255,255,255]]])
I've been searching for a while but i don't find anything to help
PS: sorry if i have made some mistake with my English.
You'll want to us an explicit broadcast: https://numpy.org/doc/stable/reference/generated/numpy.broadcast_to.html#numpy.broadcast_to
b = np.broadcast_to(a[..., np.newaxis], (2, 2, 3))
Usually you don't need to do it explicitly, maybe try and see if just a[..., np.newaxis] and the standard broadcasting rules are enough.
Another way to do it
np.einsum('ij,k->ijk', a, [1,1,1])
It's a way to create a 3 dimensional array (hence the ijk), from a 2d array (ij) and a 1d array (k). Whose result is for all i,j,k being indices of a and of [1,1,1], the 3d matrix of a[i,j]×[1,1,1][k].

Creating Mask that Applies to Vectors in 3D Array

I could not find a previous post that specifically addressed how to create masks that work against vectors in a 3D array. I have only found previous questions and answers that either address only how masks can be applied to individual elements in a 3D array or vectors in a 2D array. So as the title states, that is exactly what I wish to do here. I want to remove all zero vectors from a 3D (x,y,z) array and the only method I can think of is to create two for loops that run over both x and (y,:) as shown in the code below. However, this does not work either because of the error message I receive when I try to run this.
'list' object cannot be safely interpreted as an integer
Moreover, even if I do get this method to work somehow, I know that using a double for loop will make this masking process very time consuming because eventually I want to apply this to array sizes in the millions. So this develops into my main question; What would be the fastest method to accomplish this?
Code:
import numpy as np
data = np.array([[[0,0,0],[1,2,3],[4,5,6],[0,0,0]],[[7,8,9],[0,0,0],[0,0,0],[10,11,12]]],dtype=float)
datanonzero = np.empty([[],[]],dtype=float)
for maskclear1 in range(0,2):
for maskclear2 in range(0,4):
datanonzero[maskclear1,maskclear2,:] = data[~np.all(data[maskclear1,maskclear2,0:3] == 0, axis=0)
import numpy as np
data = np.array([[[0,0,0],[1,2,3],[4,5,6],[0,0,0]],[[7,8,9],[0,0,0],[0,0,0],[10,11,12]]],dtype=float)
flatten_data = data.reshape(-1, 3)
datanonzero = [ data[~np.all(vec == 0, axis=0)] for vec in flatten_data ]
datanonzero = np.reshape(datanonzero, (2,-1))

How would you reshape a collection of images from numpy arrays into one big image?

I'm having some trouble reshaping a 4D numpy array to a 2D numpy array. Currently the numpy array is follows, (35280L, 1L, 32L, 32L). The format is number of images, channel, width, height. Basically, I have 35280 image blocks that are 32x32 and I want to combine the image blocks (keeping the indices) to create one big image.
Reshaping is not sufficient, you must carefully rearrange your data with swapaxes.
Sample data :
dims=nbim,_,h,w=np.array([6,1,7,6])
data=arange(dims.prod()).reshape(dims)%256
The images :
figure()
for i in range(nbim):
subplot(1,nbim,i+1)
imshow(data[i,0],vmin=0,vmax=255)
and the big image :
#number of images in each dim :
nh = 2 # a choice
nw=nbim // nh
bigim=data.reshape(nh,nw,h,w).swapaxes(1,2).reshape(nh*h,nw*w)
figure()
imshow(bigim)
You have an array like this:
images = np.random.randint(0,256,(35280, 1, 32, 32))
The first thing you need is to figure out (somehow) what the width of the final image is supposed to be. Let's say for this example that it's (441 * 32, 80 * 32).
Then you can do:
image = images.swapaxes(0,2).reshape((441 * 32, -1))
This gives you almost what you need, except the rows are interleaved, so you have:
AAABBBCCC
DDDEEEFFF
GGGHHHIII
AAABBBCCC
DDDEEEFFF
GGGHHHIII
You can then use "fancy indexing" to rearrange the rows:
image[np.array([0,3,1,4,2,5])]
Now you have:
AAABBBCCC
AAABBBCCC
DDDEEEFFF
DDDEEEFFF
GGGHHHIII
GGGHHHIII
I will leave as an exercise the part where you generate the fancy indexing sequence.

assigning different weights to every numpy column

I have the following numpy array:
from sklearn.decomposition import PCA
from sklearn.preprocessing import normalize
import numpy as np
# NumPy array comprising associate metrics
# i.e. Open TA's, Open SR's, Open SE's
associateMetrics = np.array([[11, 28, 21],
[27, 17, 20],
[19, 31, 3],
[17, 24, 17]]).astype(np.float64)
print("raw metrics=", associateMetrics)
Now, I want to assign different weights to every column in the above array & later normalize this. For eg. lets say i want to assign higher weight to 1st column by multiplying by 5, multiple column 2 by 3 and the last column by 2.
How do i do this in python? Sorry a bit new to python and numpy.
I have tried this for just 1 column but it wont work:
# Assign weights to metrics
weightedMetrics = associateMetrics
np.multiply(2, weightedMetrics[:,0])
print("weighted metrics=", weightedMetrics)
You should make use of numpy's array broadcasting. This means that lower-dimensional arrays can be automatically expanded to perform a vectorized operation with an array of higher (but compatible) dimensions. In your specific case, you can multiply your (4,3)-shaped array with a 1d weight array of shape (3,) and obtain what you want:
weightedMetrics = associateMetrics * np.array([5,3,2])
The trick is that you can imagine numpy ndarrays to have leading singleton dimensions, along which broadcasting is automatic. By this I mean that your 1d numpy weight array of shape (3,) can be thought to have a leading singleton dimension (but only from the point of view of broadcasting!). And it's easy to see how the array of shape (4,3) and (1,3) should be multiplied: each element of the latter has to be used for full columns of the former.
In the very general case, you can even use arithmetic operations on, say, an array of shape (3,1,3,1,4) and one of shape (2,3,4,4). What's important that dimensions that meet should either agree, or one of the arrays should have a singleton dimension at that place, and one of the arrays is allowed to be longer (in the front).
i found my answer. This is what i used:
print("weighted metrics=", np.multiply([ 1, 2, 3], associateMetrics))

Fancier Fancy Indexing in NumPy?

I am implementing color interpolation using a look-up-table (LUT) with NumPy. At one point I am using the 4 most significant bits of RGB values to choose corresponding CMYK values from a 17x17x17x4 LUT. Right now it looks something like this:
import numpy as np
rgb = np.random.randint(16, size=(3, 1000, 1000))
lut = np.random.randint(256, size=(17, 17, 17, 4))
cmyk = lut[rgb[0], rgb[1], rgb[2]]
Here comes the first question... Is there no better way? It sort of seems natural that you could tell NumPy that the indices for lut are stored along axis 0 of rgb, without having to actually write it out. So is there anything like cmyk = lut.fancier_take(rgb, axis=0) in NumPy?
Furthermore, I am left with an array of shape (1000, 1000, 4), so to be consistent with the input, I need to rotate it all around using a couple of swapaxes:
cmyk = cmyk.swapaxes(2, 1).swapaxes(1, 0).copy()
And I also need to add the copy statement, because if not the resulting array is not contiguous in memory, and that brings trouble later on.
Right now I am leaning towards rotating the LUT before the fancy indexing and then do something along the lines of:
swapped_lut = lut.swapaxes(2, 1).swapaxes(1, 0)
cmyk = swapped_lut[np.arange(4), rgb[0], rgb[1], rgb[2]]
But again, it just does not seem right... There has to be a more elegant way to do this, right? Something like cmyk = lut.even_fancier_take(rgb, in_axis=0, out_axis=0)...
I'd suggest using tuple to force indexing rowwise, and np.rollaxis or transpose instead of swapaxes:
lut[tuple(rgb)].transpose(2, 0, 1).copy()
or
np.rollaxis(lut[tuple(rgb)], 2).copy()
To roll the axis first, use:
np.rollaxis(lut, -1)[(Ellipsis,) + tuple(rgb)]
You'll need to do the following if you swap lut, np.arange(4) will not work:
swapped_lut = np.rollaxis(lut, -1)
cmyk = swapped_lut[:, rgb[0], rgb[1], rgb[2]].copy()
Or you can replace
cmyk = lut[rgb[0], rgb[1], rgb[2]]
cmyk = cmyk.swapaxes(2, 1).swapaxes(1, 0).copy()
with:
cmyk = lut[tuple(rgb)]
cmyk = np.rollaxis(cmyk, -1).copy()
But to try and do it all in one step, ... Maybe:
rng = np.arange(4).reshape(4, 1, 1)
cmyk = lut[rgb[0], rgb[1], rgb[2], rng]
That's not very readable at all is it?
Take a look at the answer to this question, Numpy multi-dimensional array indexing swaps axis order. It does a good job of explaining how numpy broadcasts multiple arrays to get the output size. Here you want to create indices into lut that broadcast to (4, 1000, 1000). Hope that makes some sense.

Categories

Resources