94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
|
import time
|
||
|
|
||
|
import numpy as np
|
||
|
from PIL import Image
|
||
|
import concurrent.futures
|
||
|
|
||
|
|
||
|
# Estimates the absolute area by using the composite trapezoidal rule.
|
||
|
# https://numpy.org/doc/stable/reference/generated/numpy.trapz.html
|
||
|
def calculate_area(f, a, b, num_points=10000):
|
||
|
x = np.linspace(a, b, num_points)
|
||
|
y = f(x)
|
||
|
area = np.trapz(np.abs(y), x)
|
||
|
return area
|
||
|
|
||
|
|
||
|
# Estimates the integral by using the composite trapezoidal rule.
|
||
|
# https://numpy.org/doc/stable/reference/generated/numpy.trapz.html
|
||
|
def calculate_integral(f, a, b, num_points=10000):
|
||
|
x = np.linspace(a, b, num_points)
|
||
|
y = f(x)
|
||
|
integral = np.trapz(y, x)
|
||
|
return integral
|
||
|
|
||
|
|
||
|
def mysterious_transformation(data):
|
||
|
y_pixel, x_pixel, _ = data.shape
|
||
|
data_new = np.zeros([y_pixel, x_pixel, 3], dtype=np.uint8)
|
||
|
for i in range(1, y_pixel - 1):
|
||
|
for j in range(1, x_pixel - 1):
|
||
|
for k in range(3):
|
||
|
new_value = 5 * data[i, j, k] - data[i, j - 1, k] - data[i, j + 1, k] - data[i - 1, j, k] - data[
|
||
|
i + 1, j, k]
|
||
|
data_new[i, j, k] = max(0, min(new_value, 255))
|
||
|
return data_new
|
||
|
|
||
|
|
||
|
def mysterious_transformation_parallel(data, stripes=16):
|
||
|
# Divide and conquer!
|
||
|
y_pixel, x_pixel, _ = data.shape
|
||
|
stripe_height = y_pixel // stripes
|
||
|
stripe_width = x_pixel
|
||
|
stripe_arrays = [data[i * stripe_height:(i + 1) * stripe_height, :] for i in range(stripes)]
|
||
|
|
||
|
def calculate(stripe, i):
|
||
|
y_stripe_pixel, x_stripe_pixel, _ = stripe.shape
|
||
|
data_new = np.zeros([y_stripe_pixel, x_stripe_pixel, 3], dtype=np.uint8)
|
||
|
height = i * stripe_height
|
||
|
width = i * stripe_width
|
||
|
for i in range(height, y_stripe_pixel - 1):
|
||
|
for j in range(width, x_stripe_pixel - 1):
|
||
|
for k in range(3):
|
||
|
# No data race is to be expected (old image is only read)
|
||
|
new_value = 5 * data[i, j, k] - data[i, j - 1, k] - data[i, j + 1, k] - data[i - 1, j, k] - data[i + 1, j, k]
|
||
|
data_new[i, j, k] = max(0, min(new_value, 255))
|
||
|
return data_new
|
||
|
|
||
|
i = 0
|
||
|
with concurrent.futures.ThreadPoolExecutor(stripes) as executor:
|
||
|
# Compute for each slide and resample afterward
|
||
|
data_new_stripes = list(executor.map(calculate, stripe_arrays, [i]))
|
||
|
i += 1
|
||
|
return np.concatenate(data_new_stripes)
|
||
|
|
||
|
|
||
|
img = Image.open("lokomotive.png")
|
||
|
pixels = np.asarray(img, dtype=np.uint8)
|
||
|
|
||
|
# Measure execution time for mysterious_transformation
|
||
|
# AMD Ryzen 7 5800X: 4.976848363876343 seconds
|
||
|
start_time_normal = time.time()
|
||
|
data_new_normal = mysterious_transformation(pixels)
|
||
|
end_time_normal = time.time()
|
||
|
execution_time_normal = end_time_normal - start_time_normal
|
||
|
print("Execution time for normal method:", execution_time_normal, "seconds")
|
||
|
|
||
|
# Measure execution time for mysterious_transformation_parallel
|
||
|
# Execution time for parallel method: 0.0123138427734375 seconds
|
||
|
start_time_parallel = time.time()
|
||
|
data_new_parallel = mysterious_transformation_parallel(pixels, 256)
|
||
|
end_time_parallel = time.time()
|
||
|
execution_time_parallel = end_time_parallel - start_time_parallel
|
||
|
print("Execution time for parallel method:", execution_time_parallel, "seconds")
|
||
|
|
||
|
# Each time one pixel is different - don't know the reason? But this difference is negligible
|
||
|
num_different_pixels = np.count_nonzero(np.sum(data_new_normal != data_new_parallel, axis=-1))
|
||
|
print("Number of different pixels:", num_different_pixels)
|
||
|
|
||
|
img_new_normal = Image.fromarray(data_new_normal, 'RGB')
|
||
|
img_new_normal.save("new_normal_lokomotive.png")
|
||
|
|
||
|
img_new_parallel = Image.fromarray(data_new_normal, 'RGB')
|
||
|
img_new_parallel.save("new_parallel_lokomotive.png")
|