Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,10 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
@@ -0,0 +1,42 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Utility package for LyTestTools.
Provides image capturing functionality.
"""
import pyscreenshot
def screencap(x1=None, y1=None, x2=None, y2=None, filename='screenshot.png'):
"""
Capture an arbitrary portion (in absolute coordinates) of the screen to a file. Note currently accepts coords
for the leftmost screen only. Any span beyond/below will be blank.
If any of the coordinate parameters is None, the entire screen will be captured.
Note this is a brittle way to verify a portion of an application.
:param x1: Top left X
:param y1: Top left Y
:param x2: Bottom right X
:param y2: Bottom right Y
:param filename: Filename to save to.
:return: Image that was saved in PIL "Image" class.
"""
if x1 is not None and x2 is not None and y1 is not None and y2 is not None:
im = pyscreenshot.grab(bbox=(x1, y1, x2, y2), childprocess=False)
else:
im = pyscreenshot.grab(childprocess=False)
im.save(filename)
return im
@@ -0,0 +1,161 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Python implementation of Quaternion Structural Similarity by Amir Kolaman and Orly Yadid-Pecht as seen in
IEEE Transaction on Image Processing Vol 21 No 4 April 2012.
"""
import imageio
import numpy
import os
from scipy import ndimage
def _quaternion_matrix_conj(q):
q_out = numpy.zeros(q.shape)
q_out[:, :, 0] = q[:, :, 0]
q_out[:, :, 1] = -q[:, :, 1]
q_out[:, :, 2] = -q[:, :, 2]
q_out[:, :, 3] = -q[:, :, 3]
return q_out
def _quaternion_matrix_dot(q1, q2):
return numpy.sqrt(numpy.sum(numpy.multiply(q1, q2), 2))
def _quaternion_matrix_norm(q):
return _quaternion_matrix_dot(q, q)
def _quaternion_matrix_mult(q1, q2):
"""q = (q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3],
q1[0] * q2[1] + q1[1] * q2[0] - q1[2] * q2[3] + q1[3] * q2[2],
q1[0] * q2[2] + q1[1] * q2[3] + q1[2] * q2[0] - q1[3] * q2[1],
q1[0] * q2[3] - q1[1] * q2[2] + q1[2] * q2[1] + q1[3] * q2[0])"""
# add error checking for q1 and q2 being same size
q = numpy.zeros(q1.shape)
q[:, :, 0] = numpy.multiply(q1[:, :, 0], q2[:, :, 0]) - numpy.multiply(q1[:, :, 1], q2[:, :, 1]) \
- numpy.multiply(q1[:, :, 2], q2[:, :, 2]) - numpy.multiply(q1[:, :, 3], q2[:, :, 3])
q[:, :, 1] = numpy.multiply(q1[:, :, 0], q2[:, :, 1]) + numpy.multiply(q1[:, :, 1], q2[:, :, 0]) \
- numpy.multiply(q1[:, :, 2], q2[:, :, 3]) + numpy.multiply(q1[:, :, 3], q2[:, :, 2])
q[:, :, 2] = numpy.multiply(q1[:, :, 0], q2[:, :, 2]) + numpy.multiply(q1[:, :, 1], q2[:, :, 3]) \
+ numpy.multiply(q1[:, :, 2], q2[:, :, 0]) - numpy.multiply(q1[:, :, 3], q2[:, :, 1])
q[:, :, 3] = numpy.multiply(q1[:, :, 0], q2[:, :, 3]) - numpy.multiply(q1[:, :, 1], q2[:, :, 2]) \
+ numpy.multiply(q1[:, :, 2], q2[:, :, 1]) + numpy.multiply(q1[:, :, 3], q2[:, :, 0])
return q
def _quaternion_matrix_div(q1, q2):
q2_norm = _quaternion_matrix_norm(q2)
q = _quaternion_matrix_mult(q1, _quaternion_matrix_conj(q2))
return numpy.divide(q, numpy.dstack([q2_norm] * 4))
def qssim(screenshot, goldenimage, channel_max=255, diff_path='.'):
"""
Returns the mean quaternion similarity index between two images.
For images that are the same the expected result is 1.000.
By default we are assuming a channel max of 255(8bit channels)
There are a series of tuning parameters that are taken from the 2004 paper by Wang et al
Image Quality Assesment: From Error Visibility to Structural Similarity.
:param screenshot: Screenshot filename to test
:param goldenimage: Golden image to test against.
:param channel_max: Maximum channel value.
:param diff_path: Target path where diff image should be stored.
:return: Mean quaternion similarity from 0.00->1.00 (identical).
"""
# load image and copy it into a 4 channel array to treat rgb like quaternions
img1 = imageio.imread(screenshot)
img2 = imageio.imread(goldenimage)
# Avoid later precision issues
img1 = img1.astype(numpy.float64) / channel_max
img2 = img2.astype(numpy.float64) / channel_max
im1_size = img1.shape
im2_size = img2.shape
# check im1 and im2 are same size
hue1 = numpy.zeros((im1_size[0], im1_size[1], im1_size[2] + 1))
hue2 = numpy.zeros((im1_size[0], im1_size[1], im1_size[2] + 1))
mu1 = numpy.zeros((im1_size[0], im1_size[1], im1_size[2] + 1))
mu2 = numpy.zeros((im1_size[0], im1_size[1], im1_size[2] + 1))
offset1 = numpy.zeros((im1_size[0], im1_size[1], im1_size[2] + 1))
offset2 = numpy.zeros((im1_size[0], im1_size[1], im1_size[2] + 1))
hue1[:, :, 1:4] = img1
hue2[:, :, 1:4] = img2
mu1[:, :, 1:4] = img1
mu2[:, :, 1:4] = img2
# Algorithm tuning parameters. Can me modified as needed.
sigma = 1.5
# These parameters are just to prevent divide by zero issues.
L = 1
K1 = 0.01
K2 = 0.03
C1 = (K1 * L) ** 2
C2 = (K2 * L) ** 2
offset1[:, :, 0] = C1
offset2[:, :, 0] = C2
# blur each color channel of both images
mu1 = ndimage.filters.gaussian_filter1d(mu1, sigma, 0)
mu1 = ndimage.filters.gaussian_filter1d(mu1, sigma, 1)
mu2 = ndimage.filters.gaussian_filter1d(mu2, sigma, 0)
mu2 = ndimage.filters.gaussian_filter1d(mu2, sigma, 1)
mu1_sq = _quaternion_matrix_mult(mu1, _quaternion_matrix_conj(mu1))
mu2_sq = _quaternion_matrix_mult(mu2, _quaternion_matrix_conj(mu2))
mu12 = _quaternion_matrix_mult(mu1, _quaternion_matrix_conj(mu2))
hue1_sq = _quaternion_matrix_mult(hue1 - mu1, _quaternion_matrix_conj(hue1 - mu1))
hue2_sq = _quaternion_matrix_mult(hue2 - mu2, _quaternion_matrix_conj(hue2 - mu2))
hue12 = _quaternion_matrix_mult(hue1 - mu1, _quaternion_matrix_conj(hue2 - mu2))
sigma1 = ndimage.filters.gaussian_filter1d(hue1_sq, sigma, 0)
sigma1 = ndimage.filters.gaussian_filter1d(sigma1, sigma, 1)
sigma2 = ndimage.filters.gaussian_filter1d(hue2_sq, sigma, 0)
sigma2 = ndimage.filters.gaussian_filter1d(sigma2, sigma, 1)
sigma12 = ndimage.filters.gaussian_filter1d(hue12, sigma, 0)
sigma12 = ndimage.filters.gaussian_filter1d(sigma12, sigma, 1)
numerator1 = 2 * mu12 + offset1
numerator2 = 2 * sigma12 + offset2
denominator1 = mu1_sq + mu2_sq + offset1
denominator2 = sigma1 + sigma2 + offset2
part1 = _quaternion_matrix_norm(numerator1) / denominator1[:, :, 0]
part2 = _quaternion_matrix_norm(numerator2) / denominator2[:, :, 0]
qssim_map = numpy.multiply(part1, part2)
extension = os.path.splitext(screenshot)[1]
screenshot_name = os.path.basename(screenshot)
diff_name = '.'.join(screenshot_name.split('.')[:-1]) + "_diff" + extension
diff_full_path = os.path.join(diff_path, diff_name)
imageio.imwrite(diff_full_path, (qssim_map * channel_max).astype(numpy.uint8))
return ndimage.mean(numpy.abs(qssim_map))
if __name__ == "__main__":
""" If this is run by accident, inform user that this is a module, not a separate script. """
print('qssim.py is not a standalone script.')