Вы можете вычислить сумму всех изображений, вычислить общее количество «суммированных» элементов на изображение и разделить сумму на счет в конце:
Определить две матрицы:
img_sum
- Сумма изображений (за исключением компонентов, где все равны 255) img_cnt
- Подсчет компонентов, которые не являются всеми 255 (в вашем посте они названы N
).
Окончательный результат равен img_sum / img_cnt
.
Чтобы избежать деления на ноль, рекомендуется заменить нули в знаменателе перед делением.
Рекомендуется также установить результат 255, где значение равно 255 во всех входных изображениях.
Вместо перебора всех пикселей лучше использовать NumPy операции с массивами, такие как сумма массива, деление массива и логическое индексирование.
Вот код (пожалуйста, прочитайте комментарии):
import cv2
import numpy as np
# Generate list of synthetic images (for testing):
images = []
###############################################################################
width, height, n_frames = 640, 480, 20 # 20 images, resolution 640x480
for i in range(n_frames):
img = np.full((height, width, 3), 60, np.uint8)
cv2.putText(img, str(i+1), (width//2-100*len(str(i+1)), height//2+100), cv2.FONT_HERSHEY_DUPLEX, 10, (255, 255, 255), 20) # White number
cv2.rectangle(img, (20, 20), (width-19, height-19), (200, 0, 0), thickness=10) # Blue rectangle
cv2.rectangle(img, (100, 100), (width-99, height-99), (0, 200, 0), thickness=8) # Green rectangle
cv2.rectangle(img, (10, 10), (30, 30), (255, 255, 255), thickness=6) # White rectangle (for testing pixels where with 255 in all input images)
images.append(img)
#Show image for testing
cv2.imshow('img', img)
cv2.waitKey(50)
cv2.destroyAllWindows()
###############################################################################
img_sum = np.zeros(img.shape) # Sum of images (excluding intensities equal 255)
img_cnt = np.zeros(img.shape) # Count intensities that are not 255
# Iterate list of images, and update img_sum and img_cnt
for img in images:
no_sat = np.any(img < 255, 2) # Matrix with True where all color components are not saturated (255), and False where all components are 255
no_sat = np.dstack((no_sat, no_sat, no_sat)) # Duplicate no_sat three times (from single plane to 3 plances)
img_cnt = img_cnt + no_sat.astype(float) # Count non-saturated pixels (True is counted as 1.0 and False counted as 0)
img[np.logical_not(no_sat)] = 0 # Replace 255 values to zero (use logical indexing)
img_sum = img_sum + img.astype(float) # Sum images (with 0 where value was 255), cast to float for avoiding overflow.
# Before dividing img_sum by img_cnt, we must verify there are no elements with img_cnt = 0 (avoid division by zero)
nonzeros_img_cnt = np.maximum(img_cnt, 1) # Use maximum with 1 for replacing all zeros with 1
# Divide sum by count, for computing the average.
avg_img = img_sum / nonzeros_img_cnt
# What about the pixels that equals 255 in all the input images?
# Best solution is setting them to 255 in avg_img
# The elements where all equals 255 are equal zero in img_cnt
# Set avg_img elements to 255 where img_cnt = 0 (use logical indexing):
avg_img[img_cnt == 0] = 255
# Round an cast to uint8
avg_img = np.round(avg_img).astype(np.uint8)
# Show result
cv2.imshow('avg_img', avg_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Пример для синтетического c входных изображений:
avg_img
(конечный результат):
Обновление:
Чтение изображений с использованием scikit.io
из scikit-image Модуль:
Вы можете прочитать изображения, использующие io.imread
, как в исходном коде.
Удалите строку image = image.reshape((h * w, 3))
, потому что мой пример кода работает с трехмерными массивами.
Вот пример кода, который сохраняет синтезированные c изображения в файлы изображений PNG с использованием OpenCV и считывает изображения, используя io.imread
из skimage
:
import cv2
import numpy as np
from skimage import io
# Generate list of synthetic images (for testing):
###############################################################################
width, height, n_frames = 640, 480, 20 # 20 images, resolution 640x480
filename = 'im'
for i in range(n_frames):
img = np.full((height, width, 3), 60, np.uint8)
cv2.putText(img, str(i+1), (width//2-100*len(str(i+1)), height//2+100), cv2.FONT_HERSHEY_DUPLEX, 10, (255, 255, 255), 20) # White number
cv2.rectangle(img, (20, 20), (width-19, height-19), (255, 0, 0), thickness=10) # Blue rectangle
cv2.rectangle(img, (100, 100), (width-99, height-99), (0, 255, 0), thickness=8) # Green rectangle
cv2.rectangle(img, (10, 10), (30, 30), (255, 255, 255), thickness=6) # White rectangle (for testing pixels where with 255 in all input images)
cv2.imwrite(filename + str(i) + '.png', img) # Write synthetic image to PNG image file
###############################################################################
# Read images into a list
images = []
for i in range(n_frames):
image = io.imread(filename + str(i) + '.png')
images.append(image)
img_sum = np.zeros(image.shape) # Sum of images (excluding intensities equal 255)
img_cnt = np.zeros(image.shape) # Count intensities that are not 255
# Iterate list of images, and update img_sum and img_cnt
for img in images:
no_sat = np.any(img < 255, 2) # Matrix with True where all color components are not saturated (255), and False where all components are 255
no_sat = np.dstack((no_sat, no_sat, no_sat)) # Duplicate no_sat three times (from single plane to 3 plances)
img_cnt = img_cnt + no_sat.astype(float) # Count non-saturated pixels (True is counted as 1.0 and False counted as 0)
img[np.logical_not(no_sat)] = 0 # Replace 255 values to zero (use logical indexing)
img_sum = img_sum + img.astype(float) # Sum images (with 0 where value was 255), cast to float for avoiding overflow.
# Before dividing img_sum by img_cnt, we must verify there are no elements with img_cnt = 0 (avoid division by zero)
nonzeros_img_cnt = np.maximum(img_cnt, 1) # Use maximum with 1 for replacing all zeros with 1
# Divide sum by count, for computing the average.
avg_img = img_sum / nonzeros_img_cnt
# What about the pixels that equals 255 in all the input images?
# Best solution is setting them to 255 in avg_img
# The elements where all equals 255 are equal zero in img_cnt
# Set avg_img elements to 255 where img_cnt = 0 (use logical indexing):
avg_img[img_cnt == 0] = 255
# Round an cast to uint8
avg_img = np.round(avg_img).astype(np.uint8)
# Show result
io.imshow(avg_img)
io.show()