Сравнить изображения построчно с помощью VIP - PullRequest
0 голосов
/ 22 октября 2019

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

  • В изображении 1 отсутствует изображение раздела 2
  • В изображении 1 отсутствует изображение раздела 2 имеет
  • Оба изображения имеют заданный раздел, но его содержание отличается

Я пытаюсь создать инструмент, который подчеркивает различия для рецензента-человека, по сути, это версия изображения линейно-ориентированного diff. С этой целью я пытаюсь сканировать изображения построчно и сравнивать их, чтобы решить, идентичны ли линии. Моей конечной целью является фактический вывод, похожий на diff, где он может обнаружить, что разделы отсутствуют / добавлены / отличаются, и синхронизировать изображения как можно скорее для оставшихся частей идентичного содержимого, но для первого среза япереходя к более простому подходу, когда два изображения накладываются друг на друга (альфа-смешивание), а линии, которые отличались друг от друга, выделяются определенным цветом (т. е. альфа-смешивается с третьей линией сплошного цвета). Сначала я попробовал использовать Python Imaging Library, но это было на несколько порядков медленнее, поэтому я решил попробовать vips, что должно быть намного быстрее. Тем не менее, я абсолютно не знаю, как выразить то, что я после использования vips операций. Псевдокод для более простой версии был бы по существу:

out = []
# image1 and image2 are expected, but not guaranteed to have the same height
# they are likely to have different heights if different
# most lines are entirely white pixels
for line1, line2 in zip(image1, image2):
    if line1 == line2:
        out.append(line1)
    else:
        # ALL_RED is a line composed of solid red pixels
        out.append(line1.blend(line2, 0.5).blend(ALL_RED, 0.5))

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

Редактировать : добавление образцов изображений по запросу image1 image2

Редактировать 2 : полноразмерные изображения с отсутствующими / добавленными / измененными разделами:

Ответы [ 2 ]

1 голос
/ 22 октября 2019

Как насчет использования diff? Это довольно быстро. Все, что вам нужно сделать, это превратить ваши PNG-файлы в текстовую строку развертки за раз, а затем проанализировать вывод diff.

Например:

#!/usr/bin/env python3

import sys
import os
import re
import pyvips

# calculate a checksum for each scanline and write to name_out    
def scanline_checksum(name_in, name_out):
    a = pyvips.Image.new_from_file(name_in, access="sequential")
    # unfold colour channels to make a wider 1-band image
    a = a.bandunfold()
    # xyz makes an index image, where the value of each pixel is its coordinate
    b = pyvips.Image.xyz(a.width, a.height)
    # make a pow gradient image ... each pixel is some power of the x coordinate
    b = b[0] ** 0.5
    # now multiply and sum to make a checksum for each scanline
    # "project" returns sum of columns, sum of rows
    sum_of_columns, sum_of_rows = (a * b).project()
    sum_of_rows.write_to_file(name_out)

to_csv(sys.argv[1], "1.csv")
to_csv(sys.argv[2], "2.csv")

os.system("diff 1.csv 2.csv > diff.csv")

for line in open("diff.csv", "r"):
    match = re.match("(\\d+),(\\d+)c(\\d+),(\\d+)", line)
    if not match:
        continue
    print(line)

Для ваших двух тестовых изображений я вижу:

$ time ./diff.py 1.png 2.png 
264,272c264,272
351,359c351,359
real    0m0.346s
user    0m0.445s
sys 0m0.033s

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

1 голос
/ 22 октября 2019

Если OpenCV и NumPy являются опциями, то было бы достаточно простое решение, по крайней мере, для поиска и раскраски различных строк.

В моем подходе я просто вычисляю пиксельные различия, используя np.abs и найдите ненулевые индексы строк с помощью np.nonzero. С помощью этих найденных индексов я настроил дополнительное черное изображение и нарисовал красные линии для каждой строки. Окончательное смешивание - это просто линейное смешивание:

0.5 * image1 + 0.5 * image2

для всех одинаковых строк или

0.333 * image1 + 0.333 * image2 + 0.333 * red

для всех разных строк.

Вот окончательный код:

import cv2
import numpy as np

# Load images
first = cv2.imread('9gOlq.png', cv2.IMREAD_COLOR)
second = cv2.imread('1Hdx4.png', cv2.IMREAD_COLOR)

# Calcluate absolute differences between images
diff = np.abs(np.float32(first) - np.float32(second))

# Find all non-zero rows
nz_rows = np.unique(np.nonzero(diff)[0])

# Set up image with red lines
red = np.zeros(first.shape, np.uint8)
red[nz_rows, :, :] = [0, 0, 255]

# Set up output image
output = np.uint8(0.5 * first + 0.5 * second)
output[nz_rows, :, :] = 0.333 * first[nz_rows, :, :] + 0.333 * second[nz_rows, :, :] + 0.333 * red[nz_rows, :, :]

# Show results
cv2.imshow("diff", np.array(diff, dtype=np.uint8))
cv2.imshow("output", output)
cv2.waitKey()
cv2.destroyAllWindows()

Разностное изображение diff выглядит следующим образом:

Difference image

Окончательный output выглядит так:

Output

Было бы интересно увидеть два входных изображения с пропущенными участками, как вы описали в своем вопросе. Кроме того, необходимо протестировать этот подход с использованием изображений оригинального размера, поскольку вы упомянули, что время имеет решающее значение.

В любом случае - надеюсь, это поможет!

...