Faster way to iterate through pixel using numpy with conditions? - python

def colorize(im, h, s, l_adjust):
result = Image.new('RGBA', im.size)
pixin = np.copy(im)
pixout = np.array(result)
>>>>>>>>>>>>>>>>> loop <<<<<<<<<<<<<<<<<
for y in range(pixout.shape[1]):
for x in range(pixout.shape[0]):
lum = currentRGB(pixin[x, y][0], pixin[x, y][1], pixin[x, y][2])
r, g, b = colorsys.hls_to_rgb(h, lum, s)
r, g, b = int(r * 255.99), int(g * 255.99), int(b * 255.99)
pixout[x, y] = (r, g, b, 255)
>>>>>>>>>>>>>>>>>>>>> Loop end <<<<<<<<<<<
return result
Trying to find the HSL per pixel value from a frame of input video but it's taking too much time about 1.5s but want to reduce the time to at least within 0.3s. Any faster way to do this without using these 2 loops? Looking for something like LUT(Look up table)/vectorize/something with NumPy shortcut to avoid those 2 loops. Thanks
OR
Part 2 ->>
If I break the custom currentRGB() into the for loops it looks like :
def colorize(im, h, s, l_adjust):
result = Image.new('RGBA', im.size)
pixin = np.copy(im)
pixout = np.array(result)
for y in range(pixout.shape[1]):
for x in range(pixout.shape[0]):
currentR, currentG, currentB = pixin[x, y][0]/255 , pixin[x, y][1]/255, pixin[x, y][2]/255
#luminance
lum = (currentR * 0.2126) + (currentG * 0.7152) + (currentB * 0.0722)
if l_adjust > 0:
lum = lum * (1 - l_adjust)
lum = lum + (1.0 - (1.0 - l_adjust))
else:
lum = lum * (l_adjust + 1)
l = lum
r, g, b = colorsys.hls_to_rgb(h, l, s)
r, g, b = int(r * 255.99), int(g * 255.99), int(b * 255.99)
pixout[x, y] = (r, g, b, 255)
return pixout

You can use Numba to drastically speed the computation up. Here is the implementation:
import numba as nb
#nb.njit('float32(float32,float32,float32)')
def hue_to_rgb(p, q, t):
if t < 0: t += 1
if t > 1: t -= 1
if t < 1./6: return p + (q - p) * 6 * t
if t < 1./2: return q
if t < 2./3: return p + (q - p) * (2./3 - t) * 6
return p
#nb.njit('UniTuple(uint8,3)(float32,float32,float32)')
def hls_to_rgb(h, l, s):
if s == 0:
# achromatic
r = g = b = l
else:
q = l * (1 + s) if l < 0.5 else l + s - l * s
p = 2 * l - q
r = hue_to_rgb(p, q, h + 1./3)
g = hue_to_rgb(p, q, h)
b = hue_to_rgb(p, q, h - 1./3)
return (int(r * 255.99), int(g * 255.99), int(b * 255.99))
#nb.njit('void(uint8[:,:,::1],uint8[:,:,::1],float32,float32,float32)', parallel=True)
def colorize_numba(pixin, pixout, h, s, l_adjust):
for x in nb.prange(pixout.shape[0]):
for y in range(pixout.shape[1]):
currentR, currentG, currentB = pixin[x, y, 0]/255 , pixin[x, y, 1]/255, pixin[x, y, 2]/255
#luminance
lum = (currentR * 0.2126) + (currentG * 0.7152) + (currentB * 0.0722)
if l_adjust > 0:
lum = lum * (1 - l_adjust)
lum = lum + (1.0 - (1.0 - l_adjust))
else:
lum = lum * (l_adjust + 1)
l = lum
r, g, b = hls_to_rgb(h, l, s)
pixout[x, y, 0] = r
pixout[x, y, 1] = g
pixout[x, y, 2] = b
pixout[x, y, 3] = 255
def colorize(im, h, s, l_adjust):
result = Image.new('RGBA', im.size)
pixin = np.copy(im)
pixout = np.array(result)
colorize_numba(pixin, pixout, h, s, l_adjust)
return pixout
This optimized parallel implementation is about 2000 times faster than the original code on my 6-core machine (on 800x600 images). The hls_to_rgb implementation is coming from this post. Note that the string in #nb.njit decorators are not mandatory but enable Numba to compile the function ahead of time instead of at the first call. For more information about the types, please read the Numba documentation.

Related

Getting current X, Y coordinate from a bezier curve moving object

I want to find the x, y coordinate of an object moving in a bezier curve after it moved for a certain amount of time.
I have included a simple graph of the object trajectory here.
Consider the following known attributes of the object:
startingXY = [900, 450]
destinationXY = [-300, -600]
innerXY = [100, -50]
move_time = 15 # Moving duration in seconds
I tried using the below formula to calculate the (x,y), but it does not appear to be correct.
def calculate(a, b, c, d, t):
return math.pow(1 - t, 3) * a + 3 * t * math.pow(1 - t, 2) * b + 3 * math.pow(t, 2) * (1 - t) * c + math.pow(t, 3) * d
t = (now - start_time) * 0.1
x = calculate(0, startx, innerx, destinationx, t)
y = calculate(0, starty, innery, destinationy, t)
I somewhat solved my problem with the following fomula:
def the_math(a, b, c, t, p, f):
return (math.pow((1 - t), 2) * a + 2 * math.pow((1 - t), t) * b + math.pow(t, 2) * c) + p * f
elapsed_time = round(now - start)
t = elapsed_time / self.config.move_time[fish.fish_type]
x = the_math(path[0][0], path[1][0], path[2][0], t, 667, 0.85)
y = the_math(path[0][1], path[1][1], path[2][1], t, 375, 0.8)
print(x,y)
What would be a better formula to calculate the (x,y) coordinates?

HSI image is equalized incorrectly in Python

I have implemented equalization for HSI color based images. I used numpy and math modules.
Firstly, I convert RGB image into HSI using this functions:
import math
import numpy as np
def rgb2hsi_px(px):
eps = 0.00000001
r, g, b = float(px[0]) / 255, float(px[1]) / 255, float(px[2]) / 255
# Hue component
numerator = 0.5 * ((r - g) + (r - b))
denominator = math.sqrt((r - g) ** 2 + (r - b) * (g - b))
theta = math.acos(numerator / (denominator + eps))
h = theta
if b > g:
h = 2 * math.pi - h
# Saturation component
num = min(r, g, b)
den = r + g + b
if den == 0:
den = eps
s = 1 - 3 * num / den
if s == 0:
h = 0
# Intensity component
i = (r + g + b) / 3
return h, s, i
def rgb2hsi(image):
hsi_image = np.zeros_like(image).astype('float')
height, width, _ = image.shape
for x in range(height):
for y in range(width):
px = rgb2hsi_px(image[x, y])
hsi_image[x, y] = px
return np.array(hsi_image)
Then I equalize an intensity value of converted image. The equalize function was implemented using this article:
import math
import numpy as np
def equalize(img):
eps = 0.000000000001
h, w, _ = img.shape
num_of_pxs = h * w
mean = 0.0
new_img = np.array(img)
while not abs(mean - 0.5) < eps:
for i in range(h):
for j in range(w):
mean += new_img[i, j, 2]
mean /= num_of_pxs
if mean != 0.5:
theta = math.log(0.5, math.e) / math.log(mean, math.e)
for x in range(h):
for y in range(w):
px = list(new_img[x, y])
px[2] = (px[2] ** theta)
new_img[x, y] = px
return new_img
After, I convert HSI image back to RGB using the next code:
import math
import numpy as np
def hsi2rgb_px(px):
h, s, i = float(px[0]), float(px[1]), float(px[2]) * 255
if 0 <= h < 2 * math.pi / 3:
b = i * (1 - s)
r = i * (1 + (s * math.cos(h)) / math.cos(math.pi / 3 - h))
g = 3 * i - (r + b)
elif 2 * math.pi / 3 <= h < 4 * math.pi / 3:
r = i * (1 - s)
g = i * (1 + (s * math.cos(h - 2 * math.pi / 3) / math.cos(math.pi / 3 - (h - 2 * math.pi / 3))))
b = 3 * i - (r + g)
elif 4 * math.pi / 3 <= h <= 2 * math.pi:
g = i * (1 - s)
b = i * (1 + (s * math.cos(h - 4 * math.pi / 3) / math.cos(math.pi / 3 - (h - 4 * math.pi / 3))))
r = 3 * i - (g + b)
else:
raise IndexError('h is out of range: {}'.format(h))
return round(r), round(g), round(b)
def hsi2rgb(image):
rgb_image = np.zeros_like(image).astype(np.uint8)
height, width, _ = image.shape
for x in range(height):
for y in range(width):
px = hsi2rgb_px(image[x, y])
rgb_image[x, y] = px
return np.array(rgb_image)
But an equalization gives an incorrect result. The size (in megabytes) of equalized image is larger than the original one. I'm not sure if it's normal but if yes, please, let me know. And another problem is that an output image has worse quality.
Here is an original image:
And the equalized image:
Can someone help me to fix my code, or reference me to similar article/question?
[UPDATE]
Driver program to test an algorithm:
import matplotlib.image as mp_img
input_img = mp_img.imread('input.bmp')
hsi_img = rgb2hsi(input_img)
equalized_img = equalize(hsi_img)
out_img = hsi2rgb(equalized_img)
mp_img.imsave('out.bmp', out_img)

Fitting a line with gradient descent

I am trying to fit a line to a couple of points using gradient descent. I am no expert on this and tried to write down the mathematical algorithm for it in python. It runs for a couple of iterations, but my predictions seem to explode at some point. Here is the code:
import numpy as np
import matplotlib.pyplot as plt
def mean_squared_error(n, A, b, m, c):
e = 0
for i in range(n):
e += (b[i] - (m*A[i] + c)) ** 2
return e/n
def der_wrt_m(n,A,b,m,c):
d = 0
for i in range(n):
d += (2 * (b[i] - (m*A[i] + c)) * (-A[i]))
return d/n
def der_wrt_c(n,A,b,m,c):
d = 0
for i in range(n):
d += (2 * (b[i] - (m*A[i] + c)))
return d/n
def update(n,A,b,m,c,descent_rate):
return descent_rate * der_wrt_m(n,A,b,m,c)), descent_rate * der_wrt_c(n,A,b,m,c))
A = np.array(((0,1),
(1,1),
(2,1),
(3,1)))
x = A.T[0]
b = np.array((1,2,0,3), ndmin=2 ).T
y = b.reshape(4)
def descent(x,y):
m = 0
c = 0
descent_rate = 0.00001
iterations = 100
n = len(x)
plt.scatter(x, y)
u = np.linspace(0,3,100)
prediction = 0
for itr in range(iterations):
print(m,c)
prediction = prediction + m * x + c
m,c = update(n,x,y,m,c,descent_rate)
plt.plot(u, u * m + c, '-')
descent(x,y)
And that's my output:
0 0
19.25 -10.5
-71335.1953125 24625.9453125
5593771382944640.0 -2166081169939480.2
-2.542705027685638e+48 9.692684648057364e+47
2.40856742196228e+146 -9.202614421953049e+145
-inf inf
nan nan
nan nan
nan nan
nan nan
nan nan
nan nan
etc...
Update: The values aren't exploding anymore, but it's still not converging in a nice manner:
# We could also solve it using gradient descent
import numpy as np
import matplotlib.pyplot as plt
def mean_squared_error(n, A, b, m, c):
e = 0
for i in range(n):
e += ((b[i] - (m * A[i] + c)) ** 2)
#print("mse:",e/n)
return e/n
def der_wrt_m(n,A,b,m,c):
d = 0
for i in range(n):
# d += (2 * (b[i] - (m*A[i] + c)) * (-A[i]))
d += (A[i] * (b[i] - (m*A[i] + c)))
#print("Dm",-2 * d/n)
return (-2 * d/n)
def der_wrt_c(n,A,b,m,c):
d = 0
for i in range(n):
d += (2 * (b[i] - (m*A[i] + c)))
#print("Dc",d/n)
return d/n
def update(n,A,b,m,c, descent_rate):
return (m - descent_rate * der_wrt_m(n,A,b,m,c)),(c - descent_rate * der_wrt_c(n,A,b,m,c))
A = np.array(((0,1),
(1,1),
(2,1),
(3,1)))
x = A.T[0]
b = np.array((1,2,0,3), ndmin=2 ).T
y = b.reshape(4)
def descent(x,y):
m = 0
c = 0
descent_rate = 0.0001
iterations = 10000
n = len(x)
plt.scatter(x, y)
u = np.linspace(0,3,100)
prediction = 0
for itr in range(iterations):
prediction = prediction + m * x + c
m,c = update(n,x,y,m,c,descent_rate)
loss = mean_squared_error(n, A, b, m, c)
print(loss)
print(m,c)
plt.plot(u, u * m + c, '-')
descent(x,y)
And now the graph looks like this after about 10000 iterations with a learning rate of 0.0001:
[4.10833186 5.21468937]
1.503547594304175 -1.9947003678083184
Whereas the least square fit shows something like this:
In your update function, you should subtract calculated gradients from current m and c
def update(n,A,b,m,c,descent_rate):
return m - (descent_rate * der_wrt_m(n,A,b,m,c)), c - (descent_rate * der_wrt_c(n,A,b,m,c))
Update: Here is the working version. I got rid of A matrix after obtaining x,y since it confuses me =). For example in your gradient calculations you have an expression d += (A[i] * (b[i] - (m*A[i] + c))) but it should be d += (x[i] * (b[i] - (m*x[i] + c))) since x[i] gives you a single element whereas A[i] gives you a list.
Also you forgot a minus sign while calculating derivative with respect to c. If your expression is (y - (m*x + c))^2) than derivative with respect to c should be 2 * (-1) * (y - (m*x + c)) since there is a minus in front of c.
# We could also solve it using gradient descent
import numpy as np
import matplotlib.pyplot as plt
def mean_squared_error(n, x, y, m, c):
e = 0
for i in range(n):
e += (m*x[i]+c - y[i])**2
e = e/n
return e/n
def der_wrt_m(n, x, y, m, c):
d = 0
for i in range(n):
d += x[i] * (y[i] - (m*x[i] + c))
d = -2 * d/n
return d
def der_wrt_c(n, x, y, m, c):
d = 0
for i in range(n):
d += (y[i] - (m*x[i] + c))
d = -2 * d/n
return d
def update(n,x,y,m,c, descent_rate):
return (m - descent_rate * der_wrt_m(n,x,y,m,c)),(c - descent_rate * der_wrt_c(n,x,y,m,c))
A = np.array(((0,1),
(1,1),
(2,1),
(3,1)))
x = A.T[0]
b = np.array((1,2,0,3), ndmin=2 ).T
y = b.reshape(4)
print(x)
print(y)
def descent(x,y):
m = 0.0
c = 0.0
descent_rate = 0.01
iterations = 10000
n = len(x)
plt.scatter(x, y)
u = np.linspace(0,3,100)
prediction = 0
for itr in range(iterations):
prediction = prediction + m * x + c
m,c = update(n,x,y,m,c,descent_rate)
loss = mean_squared_error(n, x, y, m, c)
print(loss)
print(loss)
print(m,c)
plt.plot(u, u * m + c, '-')
plt.show()
descent(x,y)

generating pi to more than 2315 places

I have managed to get my code working so it generates pi:
while True:
print("how many digits of pi would you like?")
def make_pi():
q, r, t, k, m, x = 1, 0, 1, 1, 3, 3
for j in range(1000000):
if 4 * q + r - t < m * t:
yield m
q, r, t, k, m, x = 10 * q, 10 * (r - m * t), t, k, (10 * (3 * q + r)) // t - 10 * m, x
else:
q, r, t, k, m, x = q * k, (2 * q + r) * x, t * x, k + 1, (q * (7 * k + 2) + r * x) // (t * x), x + 2
digits = make_pi()
pi_list = []
my_array = []
for i in make_pi():
my_array.append(str(i))
number = int(input())+2
my_array = my_array[:1] + ['.'] + my_array[1:]
big_string = "".join(my_array[: number ])
print("here is the string:\n %s" % big_string)
however no matter how much I increase the range the code only outputs a maximum of 2315 digits of pi after the decimal point
how can I fix this?
What about parametrizing that make_pi generator to accept the number of digits?
Something like this:
def make_pi(num_digits):
q, r, t, k, m, x = 1, 0, 1, 1, 3, 3
for j in range(num_digits):
if 4 * q + r - t < m * t:
yield m
q, r, t, k, m, x = 10 * q, 10 * \
(r - m * t), t, k, (10 * (3 * q + r)) // t - 10 * m, x
else:
q, r, t, k, m, x = q * \
k, (2 * q + r) * x, t * x, k + \
1, (q * (7 * k + 2) + r * x) // (t * x), x + 2
num_digits = 10000
pi = "".join([str(d) for d in make_pi(num_digits)])
print("{0}.{1}".format(pi[:1], pi[1:]))

Plotting a NACA 4-series airfoil

I'm trying to plot an airfoil from the formula as described on this wikipedia page.
This Jupyter notebook can be viewed on this github page.
%matplotlib inline
import math
import matplotlib.pyplot as pyplot
def frange( start, stop, step ):
yield start
while start <= stop:
start += step
yield start
#https://en.wikipedia.org/wiki/NACA_airfoil#Equation_for_a_cambered_4-digit_NACA_airfoil
def camber_line( x, m, p, c ):
if 0 <= x <= c * p:
yc = m * (x / math.pow(p,2)) * (2 * p - (x / c))
#elif p * c <= x <= c:
else:
yc = m * ((c - x) / math.pow(1-p,2)) * (1 + (x / c) - 2 * p )
return yc
def dyc_over_dx( x, m, p, c ):
if 0 <= x <= c * p:
dyc_dx = ((2 * m) / math.pow(p,2)) * (p - x / c)
#elif p * c <= x <= c:
else:
dyc_dx = ((2 * m ) / math.pow(1-p,2)) * (p - x / c )
return dyc_dx
def thickness( x, t, c ):
term1 = 0.2969 * (math.sqrt(x/c))
term2 = -0.1260 * (x/c)
term3 = -0.3516 * math.pow(x/c,2)
term4 = 0.2843 * math.pow(x/c,3)
term5 = -0.1015 * math.pow(x/c,4)
return 5 * t * c * (term1 + term2 + term3 + term4 + term5)
def naca4( m, p, t, c=1 ):
for x in frange( 0, 1.0, 0.01 ):
dyc_dx = dyc_over_dx( x, m, p, c )
th = math.atan( dyc_dx )
yt = thickness( x, t, c )
yc = camber_line( x, m, p, c )
xu = x - yt * math.sin(th)
xl = x + yt * math.sin(th)
yu = yc + yt * math.cos(th)
yl = yc - yt * math.cos(th)
yield (xu, yu), (xl, yl)
#naca2412
m = 0.02
p = 0.4
t = 12
naca4points = naca4( m, p, t )
for (xu,yu),(xl,yl) in naca4points:
pyplot.plot( xu, yu, 'r,')
pyplot.plot( xl, yl, 'r,')
pyplot.ylabel('y')
pyplot.xlabel('x')
pyplot.axis('equal')
figure = pyplot.gcf()
figure.set_size_inches(16,16,forward=True)
The result looks like .
I expected it to look more like .
Questions: Why is the line not completely smooth? There seems to be a discontinuity where the beginning and end meet. Why does it not look like the diagram on wikipedia? How do I remove the extra loop at the trailing edge? How do I fix the chord so that it runs from 0.0 to 1.0?
First, t should be 0.12 not 12. Second, to make a smoother plot, increase the sample points.
It is also a good idea to use vectorize method in numpy:
%matplotlib inline
import math
import matplotlib.pyplot as plt
import numpy as np
#https://en.wikipedia.org/wiki/NACA_airfoil#Equation_for_a_cambered_4-digit_NACA_airfoil
def camber_line( x, m, p, c ):
return np.where((x>=0)&(x<=(c*p)),
m * (x / np.power(p,2)) * (2.0 * p - (x / c)),
m * ((c - x) / np.power(1-p,2)) * (1.0 + (x / c) - 2.0 * p ))
def dyc_over_dx( x, m, p, c ):
return np.where((x>=0)&(x<=(c*p)),
((2.0 * m) / np.power(p,2)) * (p - x / c),
((2.0 * m ) / np.power(1-p,2)) * (p - x / c ))
def thickness( x, t, c ):
term1 = 0.2969 * (np.sqrt(x/c))
term2 = -0.1260 * (x/c)
term3 = -0.3516 * np.power(x/c,2)
term4 = 0.2843 * np.power(x/c,3)
term5 = -0.1015 * np.power(x/c,4)
return 5 * t * c * (term1 + term2 + term3 + term4 + term5)
def naca4(x, m, p, t, c=1):
dyc_dx = dyc_over_dx(x, m, p, c)
th = np.arctan(dyc_dx)
yt = thickness(x, t, c)
yc = camber_line(x, m, p, c)
return ((x - yt*np.sin(th), yc + yt*np.cos(th)),
(x + yt*np.sin(th), yc - yt*np.cos(th)))
#naca2412
m = 0.02
p = 0.4
t = 0.12
c = 1.0
x = np.linspace(0,1,200)
for item in naca4(x, m, p, t, c):
plt.plot(item[0], item[1], 'b')
plt.plot(x, camber_line(x, m, p, c), 'r')
plt.axis('equal')
plt.xlim((-0.05, 1.05))
# figure.set_size_inches(16,16,forward=True)
Thanks for the code.
I have modified the code for symmetrical airfoils:
def naca4s(x, t, c=1):
yt = thickness(x, t, c)
return ((x, yt),
(x, -yt))

Categories

Resources