added luminance based grayscale conversion
This commit is contained in:
@@ -99,6 +99,7 @@ A node that that will analyze the differences between a modified QR and create a
|
||||
* `module_size` - The size in pixels of each QR module (the individual blocks that make up a qr code) in the provided images.
|
||||
* `grayscale_method` - How to convert the RGB color channels to a single grayscale value.
|
||||
* `mean` - A simple average of the RGB values.
|
||||
* `luminance` - Converts colors to grayscale using a [more advanced formula](https://en.wikipedia.org/wiki/Grayscale#Colorimetric_(perceptual_luminance-preserving)_conversion_to_grayscale) to better match percieved brightness.
|
||||
* `aggregate_method` - How to aggregate the many pixels that make up a QR module into a single value.
|
||||
* `mean` - Within each module a simple average is taken of all constituent pixels.
|
||||
* `evaluate` - Which part of the QR to examine.
|
||||
@@ -107,6 +108,7 @@ A node that that will analyze the differences between a modified QR and create a
|
||||
* `finder_pattern` - Ignores the modules while examining only the 3 finder patterns at the corners of the QR.
|
||||
* `error_difficulty` - It is possible that while using the same reader a QR code could be easily scannable on one monitor, but not on another. The way the colors are displayed can have a major effect on unsability. By increasing this threshold, you are simulating inability to distinguish midrange pixels for both the `PERCENT_ERROR` and `QR_ERROR_MASK` outputs. It ranges from 0 to 1 where 0 only automatically fails perfect grays while 1 accepts only exactly matched modules that are perfectly black or white.
|
||||
* `inverted_pattern` - If set to `True` this assumes that the QR code has inverted colors (a black background with white modules.)
|
||||
* `gamma` - If using the `grayscale` method based on `luminance`, this will influence the formula by appending the gamma expansion and compression formulas before and after the color conversion. A gamma of 2.2 will use the [formula specific to srgb](qr_nodes.py#0.04045), while other gamma values will simply use the gamma amount as an exponent.
|
||||
|
||||
#### Outputs
|
||||
|
||||
|
||||
+31
-3
@@ -273,11 +273,12 @@ class QRErrorMasker:
|
||||
"source_qr": ("IMAGE",),
|
||||
"modified_qr": ("IMAGE",),
|
||||
"module_size": ("INT", {"default": 16, "min": 1, "max": 64, "step": 1}),
|
||||
"grayscale_method": (["mean",], {"default": "mean"}),
|
||||
"grayscale_method": (["mean", "luminance"], {"default": "luminance"}),
|
||||
"aggregate_method": (["mean",], {"default": "mean"}),
|
||||
"evaluate": (["full_qr", "module_pattern", "finder_pattern"], {"default": "module_pattern"}),
|
||||
"error_difficulty": ("FLOAT", {"default": 0, "min": 0, "max": 1, "step": .01}),
|
||||
"inverted_pattern": ("BOOLEAN", {"default": False}),
|
||||
"gamma": ("FLOAT", {"default": 2.2, "min": .1, "max": 2.8, "step": .1}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -319,6 +320,28 @@ class QRErrorMasker:
|
||||
def _squeeze_by_mean(self, tensor):
|
||||
return torch.mean(tensor, dim=-1)
|
||||
|
||||
def _gamma_expansion(self, tensor, gamma):
|
||||
if gamma == 1:
|
||||
return tensor
|
||||
if gamma == 2.2:
|
||||
return torch.where(tensor <= 0.04045, tensor / 12.92, ((tensor + 0.055) / 1.055) ** 2.4)
|
||||
return tensor ** gamma
|
||||
|
||||
def _gamma_compression(self, tensor, gamma):
|
||||
if gamma == 1:
|
||||
return tensor
|
||||
if gamma == 2.2:
|
||||
return torch.where(tensor <= .0031308, tensor * 12.92, 1.055 * tensor ** (1/2.4) - 0.055)
|
||||
return tensor ** (1/gamma)
|
||||
|
||||
def _grayscale_by_luminance(self, tensor, gamma):
|
||||
weights = torch.tensor([0.2125, 0.7154, 0.0721], dtype=torch.float32)
|
||||
tensor = self._gamma_expansion(tensor, gamma)
|
||||
tensor = tensor @ weights
|
||||
if gamma != 1:
|
||||
tensor = tensor ** gamma
|
||||
return self._gamma_compression(tensor, gamma)
|
||||
|
||||
def _squeeze_to_modules(self, tensor, method):
|
||||
tensor = self._reshape_tensor_to_modules(tensor)
|
||||
if method == "mean":
|
||||
@@ -332,7 +355,8 @@ class QRErrorMasker:
|
||||
module_size,
|
||||
grayscale_method,
|
||||
aggregate_method,
|
||||
inverted_pattern
|
||||
inverted_pattern,
|
||||
gamma
|
||||
):
|
||||
if source_qr.shape != modified_qr.shape:
|
||||
raise ValueError("Source and modified QR must have the same dimensions.")
|
||||
@@ -348,6 +372,8 @@ class QRErrorMasker:
|
||||
)
|
||||
if grayscale_method == "mean":
|
||||
modified_qr = self._squeeze_by_mean(modified_qr)
|
||||
elif grayscale_method == "luminance":
|
||||
modified_qr = self._grayscale_by_luminance(modified_qr, gamma)
|
||||
else:
|
||||
raise ValueError("Currently only mean is supported for rgb to grayscale conversion.")
|
||||
source_qr = self._squeeze_to_modules(source_qr, "mean")
|
||||
@@ -423,13 +449,15 @@ class QRErrorMasker:
|
||||
evaluate,
|
||||
error_difficulty,
|
||||
inverted_pattern,
|
||||
gamma,
|
||||
):
|
||||
source_qr, modified_qr = self._reduce_to_modules(source_qr,
|
||||
modified_qr,
|
||||
module_size,
|
||||
grayscale_method,
|
||||
aggregate_method,
|
||||
inverted_pattern
|
||||
inverted_pattern,
|
||||
gamma
|
||||
)
|
||||
mask = self._create_qr_mask(source_qr.shape[0], evaluate)
|
||||
error_mask, percent_error = self._compare_modules(source_qr,
|
||||
|
||||
Reference in New Issue
Block a user