Создание альфа-оверлейного изображения на основе разницы двух изображений - PullRequest
0 голосов
/ 19 июня 2019

У меня было странно трудное время с этой проблемой. У меня есть два изображения, назовем их базовыми

и свет

(реальные изображения имеют гораздо более высокое разрешение, но проблема должна быть одинаковой независимо). Моя цель - создать новое изображение, назовем его alpha, которое при наложении на base дает light. Я пытался делать все виды различий / вычитания, масштабирования, регулировки яркости и т. Д., Но, похоже, ничего из того, что я делаю, на самом деле не дает результата (часто это выглядит близко, но никогда не бывает точным).

Например, этот код примерно настолько же близок, насколько я понял, не используя программное обеспечение для редактирования изображений, чтобы угадать и проверить его ближе.

from PIL import Image, ImageEnhance
import numpy
import blend_modes
import sys
import scipy


background_img_raw = Image.open(sys.argv[1])
background_img_raw.putalpha(255)
background_img = numpy.array(background_img_raw)
background_img_float = background_img.astype(float)
foreground_img_raw = Image.open(sys.argv[2])
foreground_img_raw.putalpha(255)
foreground_img = numpy.array(foreground_img_raw)
foreground_img_float = foreground_img.astype(float)
opacity = 1.0
blended_img_float = blend_modes.difference(background_img_float, foreground_img_float, opacity)
r, g, b, a = np.transpose(blended_img_float)
alpha = np.clip((r+g+b)*2, 0, 255)
r = np.clip(r + 100, 0, 255)
g = np.clip(g + 100, 0, 255)
b = np.clip(b + 100, 0, 255)
blended_img_float = np.transpose([r, g, b, alpha])
blended_img = numpy.uint8(blended_img_float)
blended_img_raw = Image.fromarray(blended_img)
blended_img_raw.save(sys.argv[3])

Результат выглядит так

.

Есть предложения?

Редактировать: Перебираясь в Paint.net, вы можете подойти довольно близко, если сделаете следующее:

  1. Blend-> Разница слоев
  2. Использование Мрачный цветной жнец на черном
  3. Макс. Яркость и минимальная контрастность
  4. Смещение оттенка немного, максимальное насыщение и увеличение яркости

Это тонна преобразований, но результат гораздо ближе, чем то, что я придумал программно. То, что делает Grim Color Reaper, похоже, ближе к правильному, чем то, что я делаю. Вот результат:

К сожалению, это не решает проблему, так как мне нужно делать это неоднократно, но это дает мне надежду, что это как-то решаемо.

Редактировать 2: Пожалуйста, посмотрите в этом посте пример того, чего я пытаюсь достичь: https://community.home -assistant.io / t / floorplan-with-many-lights-in-one-area-say -hello к прозрачному-PNG-файлов / 90006/8

Редактировать 3: Мне нужно сейчас уйти от этой проблемы, но я думаю, что вместо того, чтобы так усердно работать, чтобы создать правильное изображение, я нашел обходной путь на стороне CSS, чтобы смешивание происходило ближе к что уместно Теперь я просто использую разницу между изображениями так:

import numpy as np
from PIL import Image
import sys

def create_mask(foreground_filename, background_filename, output_filename):
    foreground = cv2.imread(foreground_filename)
    background = cv2.imread(background_filename)
    diff = foreground - background
    diff = cv2.cvtColor(diff, cv2.COLOR_BGR2RGB)
    out_pil_image = Image.fromarray(diff)
    out_pil_image.save(output_filename)

if __name__ == "__main__":
    create_mask(sys.argv[1], sys.argv[2], sys.argv[3])

И результаты выглядят так: https://codepen.io/anon/pen/agBqXr

Что для меня сейчас достаточно. Я хотел бы знать правильный ответ на этот вопрос, если кто-нибудь выяснит это!

Ответы [ 3 ]

1 голос
/ 19 июня 2019

Я не очень хорошо знаю Opecv, но в ImageMagick я бы сделал следующее:

база:

enter image description here

свет

enter image description here

convert base.png light.png -compose minus -composite minus.png


enter image description here

convert base.png minus.png -compose plus -composite result.png


enter image description here

Сложение:

Вот альтернативный метод, который создает цветное и прозрачное изображение поверх оригинала. Но вам придется немного подправить цвета. Я измерил ярко-желтую точку на базовом изображении.

1) turn the minus image into gray by desaturating it. The stretch the dynamic range, then apply a gamma adjustment.

2) create a look-up table between black and that yellow color and apply it to the gray image with -clut

3) put the gray image into the alpha channel of the colorized image

4) composite the previous image over the base image


convert minus.png -modulate 100,0,100 -auto-level -gamma 1.5 minus_gray.png
convert minus_gray.png \( xc:black xc:"rgb(251,220,120)" +append \) -clut minus_color.png
convert minus_color.png minus_gray.png -alpha off -compose copy_opacity -composite alpha.png
convert base.png alpha.png -compose over -composite result3.png

Вот результаты шагов:

Серый:

enter image description here

Цветной серый:

enter image description here

Прозрачный

enter image description here

Результат:

enter image description here

ADDITION2:

convert light.png -alpha copy -channel a -evaluate multiply 2 +channel alpha.png
convert base.png alpha.png -compose over -composite result3.png


Alpha:

enter image description here

Результат:

enter image description here

0 голосов
/ 19 июня 2019

Это ответ Pure PIL на ваш вопрос.

Чтобы получить разницу между двумя изображениями: -

from PIL import Image, ImageChops, ImageEnhance

img1 = Image.open(r"base.png")
img2 = Image.open(r"light.png")

diff = ImageChops.difference(img2, img1)

ImageChops.difference() принимает два объекта изображения в качестве аргументов, ивозвращает абсолютную величину попиксельной разницы между ними.Короче говоря, он вычисляет разницу между двумя изображениями .

ВЫХОД diff.show(): -

enter image description here

Теперь, поскольку мы теперь получили точки различия обоих изображений, теперь мы можем создать составное изображение из diff изображения и исходного base image

Добавление этой строки кода к предыдущему коду: -

overlaid_img1 = Image.blend(img1, diff, 0.5)

Image.blend() принимает в качестве аргумента два объекта изображения и порог и создает новое изображение путем интерполяции между двумявходные изображения, используя постоянную альфа (пороговый аргумент)Вывод руководствуется формулой: -

image1 * (1.0 - альфа) + image2 * альфа

Где image1 & image2 - объекты изображения, а alpha - порог,Поскольку мы используем 0.5 в качестве порогового значения, формула меняется на: -

(image1 * 0.5) + (image2 * 0.5)

Это означает, чтомы берем половину значения цвета image1 (base) и половину image2 (diff).Поскольку разностное изображение (diff) содержало в основном черные пиксели, из-за этого значение яркости цветных пикселей нашего базового изображения уменьшается вдвое (или становится темнее, чем раньше).

ВЫХОД overlaid_img1.show(): -

enter image description here

Теперь, чтобы пиксели базового изображенияЧтобы получить исходные значения яркости, нам нужно удвоить яркость в выходном изображении, полученном с помощью Image.blend().

Добавление этой строки к исходному коду: -

bright_overlay_img1 = ImageEnhance.Brightness(overlaid_img1).enhance(2.0)

ImageEnhance.Brightness() являетсякласс, используемый для регулировки яркости изображения.ImageEnhance.Brightness.enhance() - это функция, используемая для этого.

, что enhance(2.0) сделал, это то, что он удвоил значение яркости каждого пикселя в overlaid_img1 объекте изображения.

ВЫХОД bright_overlay_img1.show(): -

enter image description here

PS: - Исходный код, создается путем добавления всехпредоставил код в ответе.Я сегментировал исходный код, чтобы я мог объяснить работу каждой функции и причины этого.Так что для полноценной работы просто добавьте каждый код, указанный в ответе.

РЕДАКТИРОВАТЬ: -

Только что увидел ваши комментарии к ответу @ fmw42, получаетсяВыяснилось, что ваше требование отличается от вашего вопроса.

Из того, что я понял, вам нужно, чтобы diff (изображение, сохраняющее разницу обоих изображений) в формате, который поддерживает альфа (прозрачность), а не полностью черное изображение.

Для этого используйте этот код: -

# This is the full code

from PIL import Image, ImageChops, ImageEnhance

img1 = Image.open(r"base.png")
img2 = Image.open(r"light.png")

diff = ImageChops.difference(img2, img1).convert("RGBA")

pixels = diff.load()

for x in range(diff.size[0]):
    for y in range(diff.size[1]):
        if pixels[x, y] == (0, 0, 0, 255):
            pixels[x, y] = (0, 0, 0, 0)

diff.save("alpha_difference.png")   # saving the difference image (having alpha channel)

overlaid_img1 = Image.blend(img1.convert("RGBA"), diff, 0.5)

bright_overlay_img1 = ImageEnhance.Brightness(overlaid_img1).enhance(2.0).convert("RGB")

Чем вышеупомянутый код отличается от того, который я упоминал ранее, так это то, что он преобразует разностное изображение diff в изображение альфа-канала, куда он превратитсяпрозрачный пиксель, если он будет иметь полностью черный цвет (без различий с исходным изображением).

разностное изображение (с альфа-каналом): -

enter image description here

Выходы обоих фрагментов кода будут одинаковыми.

PS: - Я бы порекомендовал вам изменитьназвание / описание вопроса и четко изложите свою проблему там.

0 голосов
/ 19 июня 2019

Метод получения различий между двумя изображениями состоит в использовании индекса структурного сходства (SSIM), который реализован в библиотеке scikit-image для обработки изображений.scikit-image может быть установлен с pip install scikit-image.

В частности, функция compare_ssim() возвращает score, измеряющий индекс сходства между двумя изображениями и diff изображение, которое содержит фактические различия изображений.

Результаты

enter image description here

from skimage.measure import compare_ssim
import cv2

before = cv2.imread('base.png')
after = cv2.imread('light.png')

# Convert images to grayscale
before_gray = cv2.cvtColor(before, cv2.COLOR_BGR2GRAY)
after_gray = cv2.cvtColor(after, cv2.COLOR_BGR2GRAY)

# Compute SSIM between two images
(score, diff) = compare_ssim(before_gray, after_gray, full=True)
print("Image similarity", score)

# The diff image contains the actual image differences between the two images
# and is represented as a floating point data type in the range [0,1] 
# so we must convert the array to 8-bit unsigned integers in the range
# [0,255] before we can use it with OpenCV
diff = (diff * 255).astype("uint8")

cv2.imshow('result', diff)
cv2.imwrite('result.png', diff)
cv2.waitKey(0)
...