Related
I have an image saved as numpy array of shape [Height, Width, 3] and I want to replace every pixel with another value based on the color of pixel, so the final array will have a shape [Height, Weight].
My solution with for loop works but it's pretty slow. How can I use Numpy vectorization to make it more efficient?
image = cv2.imread("myimage.png")
result = np.zeros(shape=(image.shape[0], image.shape[1],))
for h in range(0, result.shape[0]):
for w in range(0, result.shape[1]):
result[h, w] = get_new_value(image[h, w])
Here is get_new_value function:
def get_new_value(array: np.ndarray) -> int:
mapping = {
(0, 0, 0): 0,
(0, 0, 255): 5,
(0, 100, 200): 8,
# ...
}
return mapping[tuple(array)]
you can use np.select() as shown below:
img=np.array(
[[[123 123 123]
[130 130 130]]
[[129 128 128]
[162 162 162]]])
condlist = [img==[123,123,123], img==[130, 130, 130], img==[129, 129, 129], img==[162, 162, 162]]
choicelist = [0, 5, 8, 9]
img_replaced = np.select(condlist, choicelist)
final = img_replaced[:, :, 0]
print('img_replaced')
print(img_replaced)
print('final')
print(final)
condlist is your list of colour values and choicelist is the list of replacements.
np.select then returns three channels and you just need to take one channel from that to give the array 'final' which is the format you want I believe
output is:
img_replaced
[[[0 0 0]
[5 5 5]]
[[0 0 0]
[9 9 9]]]
final
[[0 5]
[0 9]]
so code specific to your example and shown colour mappings would be:
image = cv2.imread("myimage.png")
condlist = [image==[0, 0, 0], image==[0, 0, 255], image==[0, 100, 200]]
choicelist = [0, 5, 8]
img_replaced = np.select(condlist, choicelist)
result = img_replaced[:, :, 0]
I want to ask you about calculating the histogram in Python using OpenCV. I used this code:
hist = cv2.calcHist(im, [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256])
The result gave me the histogram of each color channel with 8 bins, but what I want to get is:
1st bin (R=0-32,G=0-32,B=0-32),
2nd bin (R=33-64,G=0-32,B=0-32),
and so on,
so I will have 512 bins in total.
From my point of view, your cv2.calcHist call isn't correct:
hist = cv2.calcHist(im, [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256])
The first parameter should be a list of images:
hist = cv2.calcHist([im], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256])
Let's see this small example:
import cv2
import numpy as np
# Red blue square of size [4, 4], i.e. eight pixels (255, 0, 0) and eight pixels (0, 0, 255); Attention: BGR ordering!
image = np.zeros((4, 4, 3), dtype=np.uint8)
image[:, 0:2, 2] = 255
image[:, 2:4, 0] = 255
# Calculate histogram with two bins [0 - 127] and [128 - 255] per channel:
# Result should be hist["bin 0", "bin 0", "bin 1"] = 8 (red) and hist["bin 1", "bin 0", "bin 0"] = 8 (blue)
# Original cv2.calcHist call with two bins [0 - 127] and [128 - 255]
hist = cv2.calcHist(image, [0, 1, 2], None, [2, 2, 2], [0, 256, 0, 256, 0, 256])
print(hist, '\n') # Not correct
# Correct cv2.calcHist call
hist = cv2.calcHist([image], [0, 1, 2], None, [2, 2, 2], [0, 256, 0, 256, 0, 256])
print(hist, '\n') # Correct
[[[8. 0.]
[0. 0.]]
[[0. 0.]
[0. 4.]]]
[[[0. 8.]
[0. 0.]]
[[8. 0.]
[0. 0.]]]
As you can, your version only has 12 values in total, whereas there are 16 pixels in the image! Also, it's not clear, what "bins" (if at all) are represented.
So, having the proper cv2.calcHist call, your general idea/approach is correct! Maybe, you just need a little hint, "how to read" the resuling hist:
import cv2
import numpy as np
# Colored rectangle of size [32, 16] with one "color" per bin for eight bins per channel,
# i.e. 512 pixels, such that each of the resulting 512 bins has value 1
x = np.linspace(16, 240, 8, dtype=np.uint8)
image = np.reshape(np.moveaxis(np.array(np.meshgrid(x, x, x)), [0, 1, 2, 3], [3, 0, 1, 2]), (32, 16, 3))
# Correct cv2.calcHist call
hist = cv2.calcHist([image], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256])
# Lengthy output of each histogram bin
for B in np.arange(hist.shape[0]):
for G in np.arange(hist.shape[1]):
for R in np.arange(hist.shape[2]):
r = 'R=' + str(R*32).zfill(3) + '-' + str((R+1)*32-1).zfill(3)
g = 'G=' + str(G*32).zfill(3) + '-' + str((G+1)*32-1).zfill(3)
b = 'B=' + str(B*32).zfill(3) + '-' + str((B+1)*32-1).zfill(3)
print('(' + r + ', ' + g + ', ' + b + '): ', int(hist[B, G, R]))
(R=000-031, G=000-031, B=000-031): 1
(R=032-063, G=000-031, B=000-031): 1
(R=064-095, G=000-031, B=000-031): 1
[... 506 more lines ...]
(R=160-191, G=224-255, B=224-255): 1
(R=192-223, G=224-255, B=224-255): 1
(R=224-255, G=224-255, B=224-255): 1
Hope that helps!
I have a sparse tensor (the tensor was generated using tf.Transform on a categorical value) which I convert it into a dense representation using the following command
bow_indecies = tf.sparse_tensor_to_dense(sparse_bow_indecies, default_value=0)
which results in a matrix of size batch_size x max_seq_length. The array looks like this
[[ 597 1157 60 0 0 0]
[ 939 1212 169 10 0 0]
[ 242 719 215 520 57 6]]
I would like to reverse the zero padding from trailing to leading in order to look like this
[[ 0 0 0 597 1157 60]
[ 0 0 939 1212 169 10]
[ 242 719 215 520 57 6]]
Any idea on how to do this?
There is one rude way to do that, if you can specify the indices of SparseTesor.
I mean you have to tell your SparseTesor object (sparse_bow_indecies) the indices of nonzero values.
The documentation says "Indices not in sp_input are assigned default_value."
https://www.tensorflow.org/api_docs/python/tf/sparse_tensor_to_dense
So in your case
May be the indices in your SparseTesor object (sparse_bow_indecies) should be some what like below for the result you are expecting.
SparseTensor(indices=[[0, 3], [0, 4],[0, 5],[1, 2],[1, 3],.....], values=[............], dense_shape=[3, 6])
or try overriding indices, if it the SparseTensor object is already with you.
sparse_bow_indecies.indices = =[[0, 3], [0, 4],[0, 5],[1, 2],[1, 3],.....] #Kept dots for continuation.
The output from the method detect_keypoints(imagename, threshold) returns an ndarray, which looks like this:
[[131 326 1 0]
[135 281 1 0]
[159 405 1 0]]
Basically there's more data, but I'll keep it simple for this question. Just by this array, I'm not able to get certain columns, what is my actual goal. It keeps tellung me, that the above ndarray a tuple is and hence I can't access any value of it. So, I'm trying to convert the ndarray into a 2D array to read all integers of each array.
My implementation so far:
After research I implemented this one for converting:
def keypoints2D(imagename, threshold):
keypoints = detect_keypoints(imagename, threshold)
keypoints = np.array(keypoints)
lst = [list(i) for i in keypoints]
print(lst)
keypoints_lst = lst[:, 0]
print(keypoints_lst)
return lst
Doing so, I'll get
[[array([131, 326, 1, 0]), array([135, 281, 1, 0]), array([159, 405, 1, 0])]]
Even though, I'd like to get by converting something like this:
[[131, 326, 1, 0]
[135, 281, 1, 0]
[159, 405, 1, 0]]
If I try to get a certain column, it keeps telling me there's a exception in keypoints_lst = lst[:, 0]:
TypeError: list indices must be integers or slices, not tuple
In terms of the exception the return value is still a tuple. How can I change/convert it into a proper 2D array? Nevertheless the output of the ndarray should stay like this.
From the looks of it, the output
[[array([131, 326, 1, 0]), array([135, 281, 1, 0]), array([159, 405, 1, 0])]]
looks like a list of list of 1D-numpy arrays.
So a working but a bad way would be to convert lst to numpy array or maybe dont convert it into a list in the first place.
If you do plan to convert lst to numpy,here it goes.
lst = np.array(lst)
print(lst[:,:,0])#shall get you the first column
[[131 326 1 0]
[135 281 1 0]
[159 405 1 0]]
looks like a 2d array, which can simply be indexed with arr[:,0] for the first column, arr[0,:] the first row. arr[0,1] is actually arr[(0,1)]. Arrays take a tuple of indices.
Arrays display with the commas, lists with.
Your conversion looks like a nested list with array elements. Lists don't accept the multidimensional indexing (tuples).
alist[0] # first element
alist[0][1] # element of the first sublist
keypoints = np.array(keypoints)
lst = [list(i) for i in keypoints]
I don't know what the original keypoints (from detect_keypoints). But the lst line is creating a list of lists.
If I start with the nested list:
In [183]: target=[[131, 326, 1, 0],
...: [135, 281, 1, 0],
...: [159, 405, 1, 0]]
...:
In [184]: target
Out[184]: [[131, 326, 1, 0], [135, 281, 1, 0], [159, 405, 1, 0]]
I can make a 2d array, which displays as your first item:
In [185]: arr = np.array(target)
In [186]: arr
Out[186]:
array([[131, 326, 1, 0],
[135, 281, 1, 0],
[159, 405, 1, 0]])
In [187]: print(arr)
[[131 326 1 0]
[135 281 1 0]
[159 405 1 0]]
That is easily indexed by column
Simply passing that through your list comprehension produces:
In [190]: [list(i) for i in arr]
Out[190]: [[131, 326, 1, 0], [135, 281, 1, 0], [159, 405, 1, 0]]
tolist does the same thing:
In [191]: arr.tolist()
Out[191]: [[131, 326, 1, 0], [135, 281, 1, 0], [159, 405, 1, 0]]
The fact that you get a nested list of lists of arrays indicates that there's a another level of nesting in the source.
Just take out the comma in keypoints_lst = lst[:, 0]. When you include the comma, Python thinks you're giving a tuple, which you can't. Now, that will get rid of the error, but keypoints_lst = lst[:0] will just return an empty list, which I don't think you want. Python list slicing follows the format of mylist[start:stop:step]. When the start is not provided, it defaults to 0. So with the stop also being 0, you're going to get an empty list. Change the stop index to being the index of the last element you want to look at, plus one.
The following octave code shows a sample 3D matrix using Octave/Matlab
octave:1> A=zeros(3,3,3);
octave:2>
octave:2> A(:,:,1)= [[1 2 3];[4 5 6];[7 8 9]];
octave:3>
octave:3> A(:,:,2)= [[11 22 33];[44 55 66];[77 88 99]];
octave:4>
octave:4> A(:,:,3)= [[111 222 333];[444 555 666];[777 888 999]];
octave:5>
octave:5>
octave:5> A
A =
ans(:,:,1) =
1 2 3
4 5 6
7 8 9
ans(:,:,2) =
11 22 33
44 55 66
77 88 99
ans(:,:,3) =
111 222 333
444 555 666
777 888 999
octave:6> A(1,3,2)
ans = 33
And I need to convert the same matrix using numpy ... unfortunately When I'm trying to access the same index using array in numpy I get different values as shown below!!
import numpy as np
array = np.array([[[1 ,2 ,3],[4 ,5 ,6],[7 ,8 ,9]], [[11 ,22 ,33],[44 ,55 ,66],[77 ,88 ,99]], [[111 ,222 ,333],[444 ,555 ,666],[777 ,888 ,999]]])
>>> array[0,2,1]
8
Also I read the following document that shows the difference between matrix implementation in Matlab and in Python numpy Numpy for Matlab users but I didn't find a sample 3d array and the mapping of it into Matlab and vice versa!
the answer is different for example accessing the element(1,3,2) in Matlab doesn't match the same index using numpy (0,2,1)
Octave/Matlab
octave:6> A(1,3,2)
ans = 33
Python
>>> array[0,2,1]
8
The way your array is constructed in numpy is different than it is in MATLAB.
Where your MATLAB array is (y, x, z), your numpy array is (z, y, x). Your 3d numpy array is a series of 'stacked' 2d arrays, so you're indexing "outside->inside" (for lack of a better term). Here's your array definition expanded so this (hopefully) makes a little more sense:
[[[1, 2, 3],
[4, 5, 6], # Z = 0
[7 ,8 ,9]],
[[11 ,22 ,33],
[44 ,55 ,66], # Z = 1
[77 ,88 ,99]],
[[111 ,222 ,333],
[444 ,555 ,666], # Z = 2
[777 ,888 ,999]]
]
So with:
import numpy as np
A = np.array([[[1 ,2 ,3],[4 ,5 ,6],[7 ,8 ,9]], [[11 ,22 ,33],[44 ,55 ,66],[77 ,88 ,99]], [[111 ,222 ,333],[444 ,555 ,666],[777 ,888 ,999]]])
B = A[1, 0, 2]
B returns 33, as expected.
If you want a less mind-bending way to indexing your array, consider generating it as you did in MATLAB.
MATLAB and Python index differently. To investigate this, lets create a linear array of number 1 to 8 and then reshape the result to be a 2-by-2-by-2 matrix in each language:
MATLAB:
M_flat = 1:8
M = reshape(M_flat, [2,2,2])
which returns
M =
ans(:,:,1) =
1 3
2 4
ans(:,:,2) =
5 7
6 8
Python:
import numpy as np
P_flat = np.array(range(1,9))
P = np.reshape(P, [2,2,2])
which returns
array([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]])
The first thing you should notice is that the first two dimensions have switched. This is because MATLAB uses column-major indexing which means we count down the columns first whereas Python use row-major indexing and hence it counts across the rows first.
Now let's try indexing them. So let's try slicing along the different dimensions. In MATLAB, I know to get a slice out of the third dimension I can do
M(:,:,1)
ans =
1 3
2 4
Now let's try the same in Python
P[:,:,0]
array([[1, 3],
[5, 7]])
So that's completely different. To get the MATLAB 'equivalent' we need to go
P[0,:,:]
array([[1, 2],
[3, 4]])
Now this returns the transpose of the MATLAB version which is to be expected due the the row-major vs column-major difference.
So what does this mean for indexing? It looks like Python puts the major index at the end which is the reverse of MALTAB.
Let's say I index as follows in MATLAB
M(1,2,2)
ans =
7
now to get the 7 from Python we should go
P(1,1,0)
which is the MATLAB syntax reversed. Note that is is reversed because we created the Python matrix with a row-major ordering in mind. If you create it as you did in your code you would have to swap the last 2 indices so rather create the matrix correctly in the first place as Ander has suggested in the comments.
I think better than just calling the difference "row major" or "column major" is numpy's way of describing them:
āCā means to read / write the elements using C-like index order, with the last axis index changing fastest, back to the first axis index changing slowest. āFā means to read / write the elements using Fortran-like index order, with the first index changing fastest, and the last index changing slowest.
Some gifs to illustrate the difference: The first is row-major (python / c), second is column-major (MATLAB/ Fortran)
I think that the problem is the way you create the matrix in numpy and also the different representation of matlab and numpy, why you don't use the same system in matlab and numpy
>>> A = np.zeros((3,3,3),dtype=int)
>>> A
array([[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]],
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]],
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]])
>>> A[:,:,0] = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> A[:,:,1] = np.array([[11,22,33],[44,55,66],[77,88,99]])
>>> A[:,:,2] = np.array([[111,222,333],[444,555,666],[777,888,999]])
>>> A
array([[[ 1, 11, 111],
[ 2, 22, 222],
[ 3, 33, 333]],
[[ 4, 44, 444],
[ 5, 55, 555],
[ 6, 66, 666]],
[[ 7, 77, 777],
[ 8, 88, 888],
[ 9, 99, 999]]])
>>> A[0,2,1]
33
I think that python uses this type of indexing to create arrays as shown in the following figure:
https://www.google.com.eg/search?q=python+indexing+arrays+numpy&biw=1555&bih=805&source=lnms&tbm=isch&sa=X&ved=0ahUKEwia7b2J1qzOAhUFPBQKHXtdCBkQ_AUIBygC#imgrc=7JQu1w_4TCaAnM%3A
And, there are many ways to store your data, you can choose order='F' to count the columns first as matlab does, while the default is order='C' that count the rows first....