So I've got the x and y values of a curve that I want to plot held as float values in numpy arrays. Now, I want to round them to the nearest int, and plot them as pixel values in an empty PIL image.
Leaving out how I actually fill my x and y vectors, here is what we're working with:
# create blank image
new_img = Image.new('L', (500,500))
pix = new_img.load()
# round to int and convert to int
xx = np.rint(x).astype(int)
yy = np.rint(y).astype(int)
ordered_pairs = set(zip(xx, yy))
for i in ordered_pairs:
pix[i[0], i[1]] = 255
This gives me an error message:
File "makeCurves.py", line 105, in makeCurve
pix[i[0], i[1]] = 255
TypeError: an integer is required
However, this makes no sense to me since the .astype(int) should have cast these puppies to an integer. If I use pix[int(i[0]], int(i[1])] it works, but that's gross.
Why isn't my .astype(int) being recognized as int by PIL?
I think the problem is that your numpy arrays have type numpy.int64 or something similar, which PIL does not understand as an int that it can use to index into the image.
Try this, which converts all the numpy.int64s to Python ints:
# round to int and convert to int
xx = map(int, np.rint(x).astype(int))
yy = map(int, np.rint(y).astype(int))
In case you're wondering how I figured this out, I used the type function on a value from a numpy array:
>>> a = np.array([[1.3, 403.2], [1.0, 0.3]])
>>> b = np.rint(a).astype(int)
>>> b.dtype
dtype('int64')
>>> type(b[0, 0])
numpy.int64
>>> type(int(b[0, 0]))
int
Not sure what you're up to in the first part of your code, but why don't you replace pix = new_img.load() using this instead:
# create blank image
new_img = Image.new('L', (500,500))
pix = array(new_img) # create an array with 500 rows and 500 columns
And then you can follow your original code:
# round to int and convert to int
xx = np.rint(x).astype(int)
yy = np.rint(y).astype(int)
ordered_pairs = set(zip(xx, yy))
for i in ordered_pairs:
pix[i[0], i[1]] = 255
Out[23]:
array([[ 0, 0, 0, ..., 0, 0, 0],
[ 0, 255, 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, 0]], dtype=uint8)
Related
I have a simple problem that I am trying to solve using numpy in an efficient manner. The jist of it is that I have a simple 2D array containing ones and zeros representing an image mask.
What I want to do is convert these ones and zeros into their RGB equivalent where one is a white pixel [255, 255, 255] and zero is a black pixel [0, 0, 0].
How would I go about doing this using NumPy?
mask = [[0, 0, 1],
[1, 0, 0]]
# something
result = [
[[0, 0, 0], [0, 0, 0], [255, 255, 255]],
[[255, 255, 255], [0, 0, 0], [0, 0, 0]]
]
The intent is to take the result and feed it into PIL to save into a PNG.
I've tried using numpy.where but can't seem to coax it into broadcasting another array out.
A possible solution:
np.stack([255 * mask, 255 * mask, 255 * mask], axis=2)
Output:
array([[[ 0, 0, 0],
[ 0, 0, 0],
[255, 255, 255]],
[[255, 255, 255],
[ 0, 0, 0],
[ 0, 0, 0]]])
As your image contains only two colours, I would suggest you consider saving it as a palette image, a.k.a. an indexed image.
Rather than needlessly inflating your image by a factor of 3 to enable it to store 16.7 million colours, you can just store one byte per pixel which will still enable you to have 256 colours which seems plenty when you only have 2 "colours", namely black and white.
That looks like this:
import numpy as np
from PIL import Image
# Make Numpy array "na" from your list
na = np.array(mask, dtype=np.uint8)
# Make PIL Image from Numpy array - this image will be 'L' mode
im = Image.fromarray(na)
# Now push a palette into the image that says:
# index 0 => black, i.e. [0,0,0]
# index 1 => white, i.e. [255,255,255]
# all other 254 indices are black
# Afterwards the image will be 'P' mode
im.putpalette([0,0,0, 255,255,255] + [0,0,0]*254)
# Save
im.save('result.png')
Since you need to repeat each item three times, np.repeat in conjunction with reshape could be used:
mask = np.array([[0, 0, 1], [1, 0, 0]])
255 * np.repeat(mask, 3, axis=1).reshape(*mask.shape, -1)
>>> array([[[ 0, 0, 0],
[ 0, 0, 0],
[255, 255, 255]],
[[255, 255, 255],
[ 0, 0, 0],
[ 0, 0, 0]]])
Numpy provides packbits function to convert from values to individual bits. With bitorder='little' I can read them in C as uint8_t values without issues. However, I would like to read them as uint32_t values. This means that I have to reverse the order of each 4 bytes.
I tried to use
import numpy as np
array = np.array([1,0,1,1,0,1,0,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,1,1,0,0,1,0,1,0,1,
1,0,0,1,1,0,1,0,1,1,0,0,1,1,1,0,0,1])
array = np.packbits(array, bitorder='little')
array.dtype = np.uint32
array.byteswap(inplace=True)
print(array)
but have the following error:
Traceback (most recent call last):
File "sample.py", line 5, in <module>
array.dtype = np.uint32
ValueError: When changing to a larger dtype, its size must be a divisor of the total size in bytes of the last axis of the array.
I have 50 bits in the input. The first chunk of 32 bits written in the little-endian format (earliest input bit is the least significant bit) are 0b10101001101011001101001010101101 = 2846675629, the second is 0b100111001101011001 = 160601. So the expected output is
[2846675629 160601]
My first answer fixes the exception.
This answer, relies on this and this
Pad the array from the right to the nearest power of 2
Reshape to have some arrays, each array of size 32
Pack bits PER ARRAY and only then view as unit32.
import numpy as np
import math
# https://stackoverflow.com/questions/49791312/numpy-packbits-pack-to-uint16-array
# https://stackoverflow.com/questions/36534035/pad-0s-of-numpy-array-to-nearest-power-of-two/36534077
def next_power_of_2(number):
# Returns next power of two following 'number'
return 2**math.ceil(math.log(number, 2))
a = np.array([
1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1,
1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1
])
# a = np.array([
# 0 for _ in range(31)
# ] + [1])
padding_size = next_power_of_2(len(a)) - len(a)
b = np.concatenate([a, np.zeros(padding_size)])
c = b.reshape((-1, 32)).astype(np.uint8)
d = np.packbits(c, bitorder='little').view(np.uint32)
print(d)
output:
[2846675629 160601]
You can't use array.dtype = np.uint32 as you did, because numpy arrays have to be consecutive in memory.
Instead, you can create a new array of the new type.
import numpy as np
array = np.array([1,0,1,1,0,1,0,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,1,1,0,0,1,0,1,0,1,1,0,0,1,1,0,1,0,1,1,0,0,1,1,1,0,0,1])
array = np.packbits(array, bitorder='little')
array = np.array(array, dtype=np.uint32)
array.byteswap(inplace=True)
print(array)
>>>import numpy as np
>>>np.__version__
'1.18.5'
>>>a = np.linspace(202012121024, 202012131024, dtype=np.float64).reshape(5,5,2)
>>>a[0, 0, 0]
202012121024.0
>>>int(a[0, 0, 0])
202012121024
>>>b = a.astype('float32')
>>>b[0, 0, 0]
202012110000.0
>>>int(b[0, 0, 0])
202012114944
Why do the values change first by simply changing the dtype of a and assigning it to b and then even for b, if I change its one value to int, the actual value which is converted to integer is changed.
It's because of you set b to a.astype('float32') try instead a.astype('float64') and assign it to b then the value of a and b will be the same.
import numpy as np
a = np.linspace(202012121024, 202012131024, dtype=np.float64).reshape(5,5,2)
print(a[0, 0, 0])
print(int(a[0, 0, 0]))
b = a.astype('float64')
print(b[0, 0, 0]
Output
202012121024.0
202012121024
202012121024.0
I need to solve a problem in which I have spent hours, with the data from my excel sheet I have created a 6x36 '' zeros '' matrix of zeros and a 6x6 '' matrix_tran '' coordinate transformation matrix [image 1].
My problem is that I can't find a way to replace the zeros of the '' zeros '' matrix with the values that the matrix '' matrix_tran '' dictates, and whose location must be in the columns (4,5,6, 7,8,9) that are given by the connection vector (4,5,6,7,8,9) of element 15 of the Excel sheet, that is, the last row of the for loop iteration [image 2].
In summary: Below I show how it fits and how it should look [image 3 and 4 respectively].
I would very much appreciate your help, and excuse my English, but it is not my native language, a big greeting.
import pandas as pd
import numpy as np
ex = pd.ExcelFile('matrix_tr.xlsx')
hoja = ex.parse('Hoja1')
cols = 36
for n in range(0,len(hoja)):
A = hoja['ELEMENT #'][n]
B = hoja['1(i)'][n]
C = hoja['2(i)'][n]
D = hoja['3(i)'][n]
E = hoja['1(j)'][n]
F = hoja['2(j)'][n]
G = hoja['3(j)'][n]
H = hoja['X(i)'][n]
I = hoja['Y(i)'][n]
J = hoja['X(j)'][n]
K = hoja['Y(j)'][n]
L = np.sqrt((J-H)**2+(K-I)**2)
lx = (J-H)/L
ly = (K-I)/L
zeros = np.zeros((6, cols))
counters = hoja.loc[:, ["1(i)", "2(i)", "3(i)", "1(j)", "2(j)", "3(j)"]]
for _, i1, i2, i3, j1, j2, j3 in counters.itertuples():
matrix_tran = np.array([[lx, ly, 0, 0, 0, 0],
[-ly, lx, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, lx, ly, 0],
[0, 0, 0, -ly, lx, 0],
[0, 0, 0, 0, 0, 1]])
zeros[:, [i1 - 1, i2 - 1, i3 - 1, j1 - 1, j2 - 1 , j3 - 1]] = matrix_tran
Try with a transposed zeros matrix
import pandas as pd
import numpy as np
ex = pd.ExcelFile('c:/tmp/SO/matrix_tr.xlsx')
hoja = ex.parse('Hoja1')
counters = hoja.loc[:, ["1(i)", "2(i)", "3(i)", "1(j)", "2(j)", "3(j)"]]
# zeros matrix transposed
cols = 36
zeros_trans = np.zeros((cols,6))
# last row only
for n in range(14,len(hoja)):
Xi = hoja['X(i)'][n]
Yi = hoja['Y(i)'][n]
Xj = hoja['X(j)'][n]
Yj = hoja['Y(j)'][n]
X = Xj-Xi
Y = Yj-Yi
L = np.sqrt(X**2+Y**2)
lx = X/L
ly = Y/L
matrix_tran = np.array([[lx, ly, 0, 0, 0, 0],
[-ly, lx, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, lx, ly, 0],
[0, 0, 0, -ly, lx, 0],
[0, 0, 0, 0, 0, 1]])
i = 0
for r in counters.iloc[n]:
zeros_trans[r-1] = matrix_tran[i]
i += 1
print(np.transpose(zeros_trans))
I have 2d binary numpy arrays of varying size, which contain certain patterns.
Just like this:
import numpy
a = numpy.zeros((6,6), dtype=numpy.int)
a[1,2] = a[1,3] = 1
a[4,4] = a[5,4] = a[4,3] = 1
Here the "image" contains two patches one with 2 and one with 3 connected cells.
print a
array([[0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 1, 0]])
I want to know how often a non-zero cell borders another non-zero cell ( neighbours defined as rook's case, so the cells to the left, right, below and above each cell) including their pseudo-replication (so vice-versa).
A previous approach for inner boundaries returns wrong values (5) as it was intended to calculate outer boundaries.
numpy.abs(numpy.diff(a, axis=1)).sum()
So for the above test array, the correct total result would be 6 (The upper patch has two internal borders, the lower four ).
Grateful for any tips!
EDIT:
Mistake: The lower obviously has 4 internal edges (neighbouring cells with the same value)
Explained the desired neighbourhood a bit more
I think the result is 8 if it's 8-connected neighborhood. Here is the code:
import numpy
a = numpy.zeros((6,6), dtype=numpy.int)
a[1,2] = a[1,3] = 1
a[4,4] = a[5,4] = a[4,3] = 1
from scipy.ndimage import convolve
kernel = np.ones((3, 3))
kernel[1, 1] = 0
b = convolve(a, kernel, mode="constant")
b[a != 0].sum()
but you said rook's case.
edit
Here is the code for 4-connected neighborhood:
import numpy as np
a = np.zeros((6,6), dtype=np.int)
a[1,2] = a[1,3] = 1
a[4,4] = a[5,4] = a[4,3] = 1
from scipy import ndimage
kernel = ndimage.generate_binary_structure(2, 1)
kernel[1, 1] = 0
b = convolve(a, kernel, mode="constant")
b[a != 0].sum()