Python укладка изображений - PullRequest
1 голос
/ 12 апреля 2020

У меня есть 20 изображений, и я хочу сложить их, как для каждого пикселя, укладка изображений - это процесс, в котором мы берем среднее значение по серии изображений. В частности, мы создадим новое изображение, где каждый пиксель в позиции (x, y) на изображении является средним значением всех пикселей в одной и той же позиции (x, y) в каждом из N изображений пакета:

x_stacked, y_stacked = сумма от i до N из (x_i, y_i) / N

Но мне также нужно удалить области белого пикселя. Любой полностью белый пиксель (пиксель 255 во всех цветовых компонентах) является зашумленным пикселем и, следовательно, не должен учитываться при расчете среднего значения пикселя. То есть, перебирая каждую из 20 версий фотографии и принимая среднее значение каждого пикселя, мы пропустим пиксель, если он имеет интенсивность (255, 255, 255). Мы также должны соблюдать осторожность при делении на N, чтобы уменьшить значение N для каждого белого пикселя для этого местоположения (x, y) (например, если 5 из 20 версий имели полностью белый пиксель для местоположения (x, y) , затем при расчете среднего пикселя для этого местоположения мы должны разделить на 15 вместо 20).

это то, что у меня есть:

def denoise(filename):
n = 20
images = []
for i in range(n):
    image = io.imread(filename + str(i) + '.png')
    h, w, _ = image.shape
    image = image.reshape((h * w, 3))
    images.append(image)
finalImage = np.zeros((h * w, 3))
for i in range(len(images[0])):
    pixel = []
    for j in range(n):
        if (images[j][i].all() != 255):
            pixel.append(images[j][i])
    pixel = np.asarray(pixel)
    pixel = pixel.T
    finalImage[i][0] = np.mean(pixel[0])
    finalImage[i][1] = np.mean(pixel[1])
    finalImage[i][2] = np.mean(pixel[2])
finalImage = finalImage.reshape((h, w, 3))
avgImg = Image.fromarray(finalImage.astype('uint8'))
avgImg.show()

это работает, за исключением удаления белый шум

1 Ответ

1 голос
/ 12 апреля 2020

Вы можете вычислить сумму всех изображений, вычислить общее количество «суммированных» элементов на изображение и разделить сумму на счет в конце:

Определить две матрицы:

  • 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 входных изображений:

enter image description here enter image description here enter image description here enter image description here

avg_img (конечный результат):
enter image description here


Обновление:

Чтение изображений с использованием 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()
...