Files
UnrealEngineUWP/Engine/Source/Developer/ScreenShotComparisonTools/Public/ImageComparer.h
Ryan Vance 7c51ff94af Merging //UE4/Dev-Main to Dev-VR (//UE4/Dev-VR)
CL 1 of 8
#rb integration

[CL 4748712 by Ryan Vance in Dev-VR branch]
2019-01-17 18:54:05 -05:00

312 lines
6.3 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "ImageComparer.generated.h"
class Error;
class FComparableImage;
/**
*
*/
USTRUCT()
struct FImageTolerance
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY()
uint8 Red;
UPROPERTY()
uint8 Green;
UPROPERTY()
uint8 Blue;
UPROPERTY()
uint8 Alpha;
UPROPERTY()
uint8 MinBrightness;
UPROPERTY()
uint8 MaxBrightness;
UPROPERTY()
bool IgnoreAntiAliasing;
UPROPERTY()
bool IgnoreColors;
UPROPERTY()
float MaximumLocalError;
UPROPERTY()
float MaximumGlobalError;
FImageTolerance()
: Red(0)
, Green(0)
, Blue(0)
, Alpha(0)
, MinBrightness(0)
, MaxBrightness(255)
, IgnoreAntiAliasing(false)
, IgnoreColors(false)
, MaximumLocalError(0.0f)
, MaximumGlobalError(0.0f)
{
}
FImageTolerance(uint8 R, uint8 G, uint8 B, uint8 A, uint8 InMinBrightness, uint8 InMaxBrightness, bool InIgnoreAntiAliasing, bool InIgnoreColors, float InMaximumLocalError, float InMaximumGlobalError)
: Red(R)
, Green(G)
, Blue(B)
, Alpha(A)
, MinBrightness(InMinBrightness)
, MaxBrightness(InMaxBrightness)
, IgnoreAntiAliasing(InIgnoreAntiAliasing)
, IgnoreColors(InIgnoreColors)
, MaximumLocalError(InMaximumLocalError)
, MaximumGlobalError(InMaximumGlobalError)
{
}
public:
const static FImageTolerance DefaultIgnoreNothing;
const static FImageTolerance DefaultIgnoreLess;
const static FImageTolerance DefaultIgnoreAntiAliasing;
const static FImageTolerance DefaultIgnoreColors;
};
class FComparableImage;
class FPixelOperations
{
public:
static FORCEINLINE double GetLuminance(const FColor& Color)
{
// https://en.wikipedia.org/wiki/Relative_luminance
return (0.2126 * Color.R + 0.7152 * Color.G + 0.0722 * Color.B) * (Color.A / 255.0);
}
static bool IsBrightnessSimilar(const FColor& ColorA, const FColor& ColorB, const FImageTolerance& Tolerance)
{
const bool AlphaSimilar = FMath::IsNearlyEqual((float)ColorA.A, ColorB.A, Tolerance.Alpha);
const float BrightnessA = FPixelOperations::GetLuminance(ColorA);
const float BrightnessB = FPixelOperations::GetLuminance(ColorB);
const bool BrightnessSimilar = FMath::IsNearlyEqual(BrightnessA, BrightnessB, Tolerance.MinBrightness);
return BrightnessSimilar && AlphaSimilar;
}
static FORCEINLINE bool IsRGBSame(const FColor& ColorA, const FColor& ColorB)
{
return ColorA.R == ColorB.R &&
ColorA.G == ColorB.G &&
ColorA.B == ColorB.B;
}
static FORCEINLINE bool IsRGBSimilar(const FColor& ColorA, const FColor& ColorB, const FImageTolerance& Tolerance)
{
const bool RedSimilar = FMath::IsNearlyEqual((float)ColorA.R, ColorB.R, Tolerance.Red);
const bool GreenSimilar = FMath::IsNearlyEqual((float)ColorA.G, ColorB.G, Tolerance.Green);
const bool BlueSimilar = FMath::IsNearlyEqual((float)ColorA.B, ColorB.B, Tolerance.Blue);
const bool AlphaSimilar = FMath::IsNearlyEqual((float)ColorA.A, ColorB.A, Tolerance.Alpha);
return RedSimilar && GreenSimilar && BlueSimilar && AlphaSimilar;
}
static FORCEINLINE bool IsContrasting(const FColor& ColorA, const FColor& ColorB, const FImageTolerance& Tolerance)
{
const float BrightnessA = FPixelOperations::GetLuminance(ColorA);
const float BrightnessB = FPixelOperations::GetLuminance(ColorB);
return FMath::Abs(BrightnessA - BrightnessB) > Tolerance.MaxBrightness;
}
static float GetHue(const FColor& Color);
static bool IsAntialiased(const FColor& SourcePixel, FComparableImage* Image, int32 X, int32 Y, const FImageTolerance& Tolerance);
};
/**
*
*/
class FComparableImage
{
public:
int32 Width;
int32 Height;
TArray<uint8> Bytes;
FComparableImage()
: RedTotal(0)
, GreenTotal(0)
, BlueTotal(0)
, AlphaTotal(0)
, LuminanceTotal(0)
, RedAverage(0)
, GreenAverage(0)
, BlueAverage(0)
, AlphaAverage(0)
, LuminanceAverage(0)
{
}
FORCEINLINE bool CanGetPixel(int32 X, int32 Y)
{
return X >= 0 && Y >= 0 && X < Width && Y < Height;
}
FORCEINLINE FColor GetPixel(int32 X, int32 Y)
{
int32 Offset = ( Y * Width + X ) * 4;
check(Offset < ( Width * Height * 4 ));
return FColor(
Bytes[Offset],
Bytes[Offset + 1],
Bytes[Offset + 2],
Bytes[Offset + 3]);
}
void Process();
public:
// Processed Data
double RedTotal;
double GreenTotal;
double BlueTotal;
double AlphaTotal;
double LuminanceTotal;
double RedAverage;
double GreenAverage;
double BlueAverage;
double AlphaAverage;
double LuminanceAverage;
};
/**
*
*/
USTRUCT()
struct FImageComparisonResult
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY()
FString ApprovedFile;
UPROPERTY()
FString IncomingFile;
UPROPERTY()
FString ComparisonFile;
UPROPERTY()
FString ReportApprovedFile;
UPROPERTY()
FString ReportIncomingFile;
UPROPERTY()
FString ReportComparisonFile;
UPROPERTY()
double MaxLocalDifference;
UPROPERTY()
double GlobalDifference;
UPROPERTY()
FText ErrorMessage;
UPROPERTY()
FImageTolerance Tolerance;
FImageComparisonResult()
: MaxLocalDifference(0.0f)
, GlobalDifference(0.0f)
, ErrorMessage()
{
}
FImageComparisonResult(const FText& Error)
: MaxLocalDifference(0.0f)
, GlobalDifference(0.0f)
, ErrorMessage(Error)
{
}
bool IsNew() const
{
return ApprovedFile.IsEmpty();
}
bool AreSimilar() const
{
if ( IsNew() )
{
return false;
}
if ( MaxLocalDifference > Tolerance.MaximumLocalError || GlobalDifference > Tolerance.MaximumGlobalError )
{
return false;
}
return true;
}
};
struct SCREENSHOTCOMPARISONTOOLS_API FComparisonReport
{
public:
FComparisonReport(const FString& InReportRootDirectory, const FString& InReportFile);
FString ReportRootDirectory;
FString ReportFile;
FString ReportFolder;
FImageComparisonResult Comparison;
};
/**
*
*/
class SCREENSHOTCOMPARISONTOOLS_API FImageComparer
{
public:
FString ImageRootA;
FString ImageRootB;
FString DeltaDirectory;
FImageComparer(const FString& Directory = "");
FImageComparisonResult Compare(const FString& ImagePathA, const FString& ImagePathB, FImageTolerance Tolerance);
enum class EStructuralSimilarityComponent : uint8
{
Luminance,
Color
};
/**
* https://en.wikipedia.org/wiki/Structural_similarity
*/
double CompareStructuralSimilarity(const FString& ImagePathA, const FString& ImagePathB, EStructuralSimilarityComponent InCompareComponent);
private:
TSharedPtr<FComparableImage> Open(const FString& ImagePath, FText& OutError);
};