使用 vips 逐行比较图像
Compare images line by line using vips
背景:我有一些图像需要比较差异。图片较大(1400x9000 px量级),机器生成,高度受限(某条线性的截图UI),预计几乎相同,差异为以下三个之一可能性:
- 图片 1 有部分图片 2 丢失
- 图片 1 缺少剖面图 2 有
- 两张图片都有给定的部分,但其内容不同
我正在尝试构建一个工具来突出显示人类审阅者的差异,本质上是面向行差异的图像版本。为此,我试图逐行扫描图像并比较它们以确定线条是否相同。我的最终目标是一个实际的 diff-like 输出,它可以检测到部分是 missing/added/different,并尽快同步图像以获取相同内容的其余部分,但对于第一次剪辑,我采用更简单的方法,其中两个图像重叠(alpha 混合),并且不同的线条用特定颜色突出显示(即 alpha 混合与第三条纯色线)。起初我尝试使用 Python 图像库,但速度太慢了几个数量级,所以我决定尝试使用 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
或任何其他绑定的代码感兴趣,因为这些操作是共享的并且可以轻松地跨方言翻译。
编辑:按要求添加示例图片
编辑 2:包含 missing/added/changed 个部分的全尺寸图像:
- reference
- comparison
如果 OpenCV 和 NumPy 是您的选择,那么至少对于查找和着色不同的行会有一个非常简单的解决方案。
在我的方法中,我只是使用 np.abs
, and find non-zero row indices with 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
看起来像这样:
最后的 output
看起来像这样:
如您在问题中所述,看到两张带有省略部分的输入图像会很有趣。此外,由于您提到时间至关重要,因此有必要使用原始大小的图像测试此方法。
无论如何 - 希望对您有所帮助!
只使用 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
在这台老式笔记本电脑上。您需要做的就是使用这些 "change" 命令来标记您的图像。
背景:我有一些图像需要比较差异。图片较大(1400x9000 px量级),机器生成,高度受限(某条线性的截图UI),预计几乎相同,差异为以下三个之一可能性:
- 图片 1 有部分图片 2 丢失
- 图片 1 缺少剖面图 2 有
- 两张图片都有给定的部分,但其内容不同
我正在尝试构建一个工具来突出显示人类审阅者的差异,本质上是面向行差异的图像版本。为此,我试图逐行扫描图像并比较它们以确定线条是否相同。我的最终目标是一个实际的 diff-like 输出,它可以检测到部分是 missing/added/different,并尽快同步图像以获取相同内容的其余部分,但对于第一次剪辑,我采用更简单的方法,其中两个图像重叠(alpha 混合),并且不同的线条用特定颜色突出显示(即 alpha 混合与第三条纯色线)。起初我尝试使用 Python 图像库,但速度太慢了几个数量级,所以我决定尝试使用 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
或任何其他绑定的代码感兴趣,因为这些操作是共享的并且可以轻松地跨方言翻译。
编辑:按要求添加示例图片
编辑 2:包含 missing/added/changed 个部分的全尺寸图像:
- reference
- comparison
如果 OpenCV 和 NumPy 是您的选择,那么至少对于查找和着色不同的行会有一个非常简单的解决方案。
在我的方法中,我只是使用 np.abs
, and find non-zero row indices with 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
看起来像这样:
最后的 output
看起来像这样:
如您在问题中所述,看到两张带有省略部分的输入图像会很有趣。此外,由于您提到时间至关重要,因此有必要使用原始大小的图像测试此方法。
无论如何 - 希望对您有所帮助!
只使用 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
在这台老式笔记本电脑上。您需要做的就是使用这些 "change" 命令来标记您的图像。