Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29cd12358e | ||
|
|
5818bdf812 | ||
|
|
7cb0d7de12 | ||
|
|
7f60e01b9a | ||
|
|
f70b527f7c | ||
|
|
4f45656976 |
@@ -35,6 +35,7 @@ A dynamically sized QR code without any resampling scaled by the text length, th
|
||||
* `Quartile` - 25% error correction.
|
||||
* `High` - 30% error correction.
|
||||
* `border` - The border size (In multiples of `module` widths)
|
||||
* `module_drawer` - The shape the QR code modules should be. The default is `square`, but see [Alternate Module Drawers](#alternate-module-drawers) below for new examples.
|
||||
|
||||
#### Outputs
|
||||
|
||||
@@ -74,6 +75,7 @@ A QR code fixed to specific output dimensions through image resampling.
|
||||
* `Hamming` - Hamming interpolation
|
||||
* `Lanczos` - Lanczos interpolation
|
||||
* `Nearest` - Nearest Neighbor interpolation
|
||||
* `module_drawer` - The shape the QR code modules should be. The default is `square`, but see [Alternate Module Drawers](#alternate-module-drawers) below for new examples.
|
||||
|
||||
#### Outputs
|
||||
|
||||
@@ -129,6 +131,16 @@ And then aggressively increasing the strength of the ControlNet on only the erro
|
||||
|
||||
Any workflow in the example that ends with "validated" (and a few image examples) assume the installation of the [scanning pack](https://gitlab.com/sofuego-comfy-nodes/ComfyQR-scanning-nodes) as well.
|
||||
|
||||
## Alternate Module Drawers
|
||||
|
||||
The QR generation nodes now support alternate module styles. Experiment using different ones for greater flexibility during generation.
|
||||
|
||||
| Square | Gapped Square | Circle | Rounded | Vertical bars | Horizontal bars |
|
||||
|:---: | :---: | :---: | :---: | :---: | :---: |
|
||||
|  |  |  |  |  |  |
|
||||
|
||||
**Note** _Even unaltered non square styles will inherently register small deviations from RMSE values and possible minute deviations for correlations when using the [Mask QR Errors](#mask-qr-errors) node. When information is extracted, the `source_qr` values are rounded so that they can only be 0 or 1 (to allow alternate drawers to be used as inputs) and matched against the unrounded aggregation of the `modified_qr` values._
|
||||
|
||||
## Future updates
|
||||
|
||||
If the solution is clean enough and if it can definitively improve scannability, there may be additional plans for the seperation of alignment patterns (based on `module_size`, `border`, and `QR_VERSION`) for more advanced workflows.
|
||||
|
||||
|
Before Width: | Height: | Size: 909 KiB After Width: | Height: | Size: 907 KiB |
|
Before Width: | Height: | Size: 961 KiB After Width: | Height: | Size: 961 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_node_id": 51,
|
||||
"last_link_id": 114,
|
||||
"last_node_id": 52,
|
||||
"last_link_id": 117,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 19,
|
||||
@@ -204,7 +204,7 @@
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 82
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
@@ -212,31 +212,24 @@
|
||||
"outputs": [
|
||||
{
|
||||
"name": "COMBO",
|
||||
"type": "Http,Https,None",
|
||||
"type": "COMBO",
|
||||
"links": [
|
||||
46
|
||||
],
|
||||
"slot_index": 0,
|
||||
"widget": {
|
||||
"name": "protocol",
|
||||
"config": [
|
||||
[
|
||||
"Http",
|
||||
"Https",
|
||||
"None"
|
||||
],
|
||||
{
|
||||
"default": "Https"
|
||||
}
|
||||
]
|
||||
"name": "protocol"
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "Protocol",
|
||||
"properties": {},
|
||||
"properties": {
|
||||
"Run widget replace on values": false
|
||||
},
|
||||
"widgets_values": [
|
||||
"Https",
|
||||
"fixed"
|
||||
"fixed",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -262,18 +255,14 @@
|
||||
],
|
||||
"slot_index": 0,
|
||||
"widget": {
|
||||
"name": "text",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"multiline": true
|
||||
}
|
||||
]
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "URL",
|
||||
"properties": {},
|
||||
"properties": {
|
||||
"Run widget replace on values": false
|
||||
},
|
||||
"widgets_values": [
|
||||
"gitlab.com/sofuego-comfy-nodes/comfy-qr-validation-nodes"
|
||||
]
|
||||
@@ -290,7 +279,7 @@
|
||||
"1": 46
|
||||
},
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"order": 14,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -326,10 +315,10 @@
|
||||
647.9219000000045,
|
||||
458.3673000000005
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
474.00000762939453
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 474
|
||||
},
|
||||
"flags": {},
|
||||
"order": 25,
|
||||
"mode": 0,
|
||||
@@ -427,7 +416,7 @@
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -435,16 +424,7 @@
|
||||
"type": "INT",
|
||||
"link": 103,
|
||||
"widget": {
|
||||
"name": "width",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "width"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -452,16 +432,7 @@
|
||||
"type": "INT",
|
||||
"link": 104,
|
||||
"widget": {
|
||||
"name": "height",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "height"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -503,20 +474,10 @@
|
||||
"inputs": [
|
||||
{
|
||||
"name": "protocol",
|
||||
"type": "Http,Https,None",
|
||||
"type": "COMBO",
|
||||
"link": 46,
|
||||
"widget": {
|
||||
"name": "protocol",
|
||||
"config": [
|
||||
[
|
||||
"Http",
|
||||
"Https",
|
||||
"None"
|
||||
],
|
||||
{
|
||||
"default": "Https"
|
||||
}
|
||||
]
|
||||
"name": "protocol"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -524,13 +485,7 @@
|
||||
"type": "STRING",
|
||||
"link": 47,
|
||||
"widget": {
|
||||
"name": "text",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"multiline": true
|
||||
}
|
||||
]
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -591,7 +546,8 @@
|
||||
"#000000",
|
||||
"#FFFFFF",
|
||||
"High",
|
||||
1
|
||||
1,
|
||||
"Square"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -647,7 +603,7 @@
|
||||
"1": 166
|
||||
},
|
||||
"flags": {},
|
||||
"order": 20,
|
||||
"order": 19,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -718,7 +674,7 @@
|
||||
{
|
||||
"name": "destination",
|
||||
"type": "MASK",
|
||||
"link": 91
|
||||
"link": 115
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
@@ -783,113 +739,6 @@
|
||||
"Node name for S&R": "MaskToImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "comfy-qr-quantify-match",
|
||||
"pos": [
|
||||
97.9219000000007,
|
||||
-111.63269999999997
|
||||
],
|
||||
"size": {
|
||||
"0": 317.4000244140625,
|
||||
"1": 238
|
||||
},
|
||||
"flags": {},
|
||||
"order": 19,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "source_qr",
|
||||
"type": "IMAGE",
|
||||
"link": 4
|
||||
},
|
||||
{
|
||||
"name": "modified_qr",
|
||||
"type": "IMAGE",
|
||||
"link": 5
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "PERCENT_ERROR",
|
||||
"type": "FLOAT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "CORRELATION",
|
||||
"type": "FLOAT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "RMSE",
|
||||
"type": "FLOAT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "QR_ERROR_MASK",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
91
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "comfy-qr-quantify-match"
|
||||
},
|
||||
"widgets_values": [
|
||||
16,
|
||||
"mean",
|
||||
"mean",
|
||||
"module_pattern",
|
||||
0.5,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
-288,
|
||||
115
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314.0000305175781
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
5,
|
||||
87
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"unscannable_00001_ (1).png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 47,
|
||||
"type": "PrimitiveNode",
|
||||
@@ -902,7 +751,7 @@
|
||||
"1": 82
|
||||
},
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
@@ -914,21 +763,14 @@
|
||||
],
|
||||
"slot_index": 0,
|
||||
"widget": {
|
||||
"name": "height",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "height"
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "Height",
|
||||
"properties": {},
|
||||
"properties": {
|
||||
"Run widget replace on values": false
|
||||
},
|
||||
"widgets_values": [
|
||||
768,
|
||||
"fixed"
|
||||
@@ -946,7 +788,7 @@
|
||||
"1": 82
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
@@ -958,21 +800,14 @@
|
||||
],
|
||||
"slot_index": 0,
|
||||
"widget": {
|
||||
"name": "width",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "width"
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "Width",
|
||||
"properties": {},
|
||||
"properties": {
|
||||
"Run widget replace on values": false
|
||||
},
|
||||
"widgets_values": [
|
||||
768,
|
||||
"fixed"
|
||||
@@ -990,7 +825,7 @@
|
||||
"1": 82
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
@@ -1002,21 +837,14 @@
|
||||
],
|
||||
"slot_index": 0,
|
||||
"widget": {
|
||||
"name": "x",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "x"
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "X Offset",
|
||||
"properties": {},
|
||||
"properties": {
|
||||
"Run widget replace on values": false
|
||||
},
|
||||
"widgets_values": [
|
||||
16,
|
||||
"fixed"
|
||||
@@ -1034,7 +862,7 @@
|
||||
"1": 82
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
@@ -1046,21 +874,14 @@
|
||||
],
|
||||
"slot_index": 0,
|
||||
"widget": {
|
||||
"name": "y",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "y"
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "Y Offset",
|
||||
"properties": {},
|
||||
"properties": {
|
||||
"Run widget replace on values": false
|
||||
},
|
||||
"widgets_values": [
|
||||
16,
|
||||
"fixed"
|
||||
@@ -1078,7 +899,7 @@
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 14,
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -1086,16 +907,7 @@
|
||||
"type": "INT",
|
||||
"link": 105,
|
||||
"widget": {
|
||||
"name": "width",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "width"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1103,16 +915,7 @@
|
||||
"type": "INT",
|
||||
"link": 106,
|
||||
"widget": {
|
||||
"name": "height",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "height"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1167,16 +970,7 @@
|
||||
"type": "INT",
|
||||
"link": 109,
|
||||
"widget": {
|
||||
"name": "x",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "x"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1184,16 +978,7 @@
|
||||
"type": "INT",
|
||||
"link": 110,
|
||||
"widget": {
|
||||
"name": "y",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "y"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1283,93 +1068,6 @@
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "ImageCompositeMasked",
|
||||
"pos": [
|
||||
-332.0780999999991,
|
||||
-91.63270000000001
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 146
|
||||
},
|
||||
"flags": {},
|
||||
"order": 17,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "destination",
|
||||
"type": "IMAGE",
|
||||
"link": 3
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"type": "IMAGE",
|
||||
"link": 2
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "x",
|
||||
"type": "INT",
|
||||
"link": 107,
|
||||
"widget": {
|
||||
"name": "x",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"type": "INT",
|
||||
"link": 108,
|
||||
"widget": {
|
||||
"name": "y",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
4,
|
||||
12,
|
||||
111
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ImageCompositeMasked"
|
||||
},
|
||||
"widgets_values": [
|
||||
16,
|
||||
16,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"type": "VAEDecode",
|
||||
@@ -1492,7 +1190,7 @@
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
@@ -1537,6 +1235,183 @@
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "ImageCompositeMasked",
|
||||
"pos": [
|
||||
-332.0780999999991,
|
||||
-91.63270000000001
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 146
|
||||
},
|
||||
"flags": {},
|
||||
"order": 17,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "destination",
|
||||
"type": "IMAGE",
|
||||
"link": 3
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"type": "IMAGE",
|
||||
"link": 2
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "x",
|
||||
"type": "INT",
|
||||
"link": 107,
|
||||
"widget": {
|
||||
"name": "x"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"type": "INT",
|
||||
"link": 108,
|
||||
"widget": {
|
||||
"name": "y"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
12,
|
||||
111,
|
||||
116
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ImageCompositeMasked"
|
||||
},
|
||||
"widgets_values": [
|
||||
16,
|
||||
16,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
-288,
|
||||
115
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314.0000305175781
|
||||
},
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
87,
|
||||
117
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"unscannable_00001_ (1).png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 52,
|
||||
"type": "comfy-qr-mask_errors",
|
||||
"pos": [
|
||||
120,
|
||||
-146
|
||||
],
|
||||
"size": {
|
||||
"0": 317.4000244140625,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 20,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "source_qr",
|
||||
"type": "IMAGE",
|
||||
"link": 116
|
||||
},
|
||||
{
|
||||
"name": "modified_qr",
|
||||
"type": "IMAGE",
|
||||
"link": 117
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "QR_ERROR_MASK",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
115
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "PERCENT_ERROR",
|
||||
"type": "FLOAT",
|
||||
"links": null,
|
||||
"shape": 6
|
||||
},
|
||||
{
|
||||
"name": "CORRELATION",
|
||||
"type": "FLOAT",
|
||||
"links": null,
|
||||
"shape": 6
|
||||
},
|
||||
{
|
||||
"name": "RMSE",
|
||||
"type": "FLOAT",
|
||||
"links": null,
|
||||
"shape": 6
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "comfy-qr-mask_errors"
|
||||
},
|
||||
"widgets_values": [
|
||||
16,
|
||||
"mean",
|
||||
"mean",
|
||||
"module_pattern",
|
||||
0.5,
|
||||
false,
|
||||
2.2
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
@@ -1564,22 +1439,6 @@
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
4,
|
||||
6,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
5,
|
||||
2,
|
||||
0,
|
||||
3,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
7,
|
||||
8,
|
||||
@@ -1714,7 +1573,7 @@
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
"Http,Https,None"
|
||||
"COMBO"
|
||||
],
|
||||
[
|
||||
47,
|
||||
@@ -1748,14 +1607,6 @@
|
||||
1,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
91,
|
||||
3,
|
||||
3,
|
||||
39,
|
||||
0,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
92,
|
||||
39,
|
||||
@@ -1883,6 +1734,30 @@
|
||||
23,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
115,
|
||||
52,
|
||||
0,
|
||||
39,
|
||||
0,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
116,
|
||||
6,
|
||||
0,
|
||||
52,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
117,
|
||||
2,
|
||||
0,
|
||||
52,
|
||||
1,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
|
||||
@@ -342,7 +342,8 @@
|
||||
"#DDDDDD",
|
||||
"High",
|
||||
1,
|
||||
"Nearest"
|
||||
"Nearest",
|
||||
"Square"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -440,4 +441,4 @@
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +477,8 @@
|
||||
"#000000",
|
||||
"#FFFFFF",
|
||||
"Medium",
|
||||
1
|
||||
1,
|
||||
"Square"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -524,7 +524,8 @@
|
||||
"#000000",
|
||||
"#FFFFFF",
|
||||
"High",
|
||||
1
|
||||
1,
|
||||
"Square"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -394,7 +394,8 @@
|
||||
"#000000",
|
||||
"#FFFFFF",
|
||||
"High",
|
||||
1
|
||||
1,
|
||||
"Square"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -357,7 +357,8 @@
|
||||
"#000000",
|
||||
"#FFFFFF",
|
||||
"High",
|
||||
1
|
||||
1,
|
||||
"Square"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -423,4 +424,4 @@
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,8 @@
|
||||
"FF0",
|
||||
"High",
|
||||
1,
|
||||
"Nearest"
|
||||
"Nearest",
|
||||
"Circle"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -88,4 +89,4 @@
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -1,5 +1,12 @@
|
||||
import numpy as np
|
||||
import qrcode
|
||||
from qrcode.image.styles.moduledrawers import (GappedSquareModuleDrawer,
|
||||
CircleModuleDrawer,
|
||||
RoundedModuleDrawer,
|
||||
VerticalBarsDrawer,
|
||||
HorizontalBarsDrawer)
|
||||
from qrcode.image.styledpil import StyledPilImage
|
||||
from qrcode.image.styles.colormasks import SolidFillColorMask
|
||||
from qrcode.compat.pil import Image
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
@@ -27,11 +34,19 @@ class QRBase:
|
||||
out_image = np.array(img, dtype=np.uint8).astype(np.float32) / 255
|
||||
return torch.from_numpy(out_image).unsqueeze(0)
|
||||
|
||||
def _make_qr(self, qr, fill_hexcolor, back_hexcolor):
|
||||
def _make_qr(self, qr, fill_hexcolor, back_hexcolor, module_drawer):
|
||||
self.fill = self._parse_hexcolor_string(fill_hexcolor, "fill_hexcolor")
|
||||
self.back = self._parse_hexcolor_string(back_hexcolor, "back_hexcolor")
|
||||
qr.make(fit=True)
|
||||
return qr.make_image(fill_color=self.fill, back_color=self.back)
|
||||
if module_drawer == "Square":
|
||||
# Keeps using Square QR generation the old way for faster speeds.
|
||||
return qr.make_image(fill_color=self.fill, back_color=self.back)
|
||||
color_mask = SolidFillColorMask(back_color=self.back,
|
||||
front_color=self.fill)
|
||||
module_drawing_method = self._select_module_drawer(module_drawer)
|
||||
return qr.make_image(image_factory=StyledPilImage,
|
||||
color_mask=color_mask,
|
||||
module_drawer=module_drawing_method)
|
||||
|
||||
def _parse_hexcolor_string(self, s, parameter):
|
||||
if s.startswith("#"):
|
||||
@@ -45,11 +60,31 @@ class QRBase:
|
||||
try:
|
||||
return tuple(int(channel, 16) for channel in rgb)
|
||||
except ValueError:
|
||||
raise ValueError(f"{parameter} contains invalid hexadecimal characters")
|
||||
raise ValueError(f"{parameter} contains invalid hexadecimal "
|
||||
f"characters")
|
||||
|
||||
def _validate_qr_size(self, size, max_size):
|
||||
if size > max_size:
|
||||
raise RuntimeError(f"QR dimensions of {size} exceed max size of {max_size}.")
|
||||
raise RuntimeError(f"QR dimensions of {size} exceed max size of "
|
||||
f"{max_size}.")
|
||||
|
||||
def _select_module_drawer(self, module_drawer_string):
|
||||
"""Square is not included in the results, for a speed optimization
|
||||
applying color masks. Current version of python-qr code suffers a
|
||||
slowdown when using custom colors combined with custom module drawers.
|
||||
By bypassing square QRs, non standard colors will load faster."""
|
||||
if module_drawer_string == "Gapped square":
|
||||
return GappedSquareModuleDrawer()
|
||||
if module_drawer_string == "Circle":
|
||||
return CircleModuleDrawer()
|
||||
if module_drawer_string == "Rounded":
|
||||
return RoundedModuleDrawer()
|
||||
if module_drawer_string == "Vertical bars":
|
||||
return VerticalBarsDrawer()
|
||||
if module_drawer_string == "Horizontal bars":
|
||||
return HorizontalBarsDrawer()
|
||||
raise ValueError(f"Module drawing method of {module_drawer_string} "
|
||||
f"not supported")
|
||||
|
||||
def update_text(self, protocol, text):
|
||||
"""This function takes input from a text box and a chosen internet
|
||||
@@ -79,12 +114,34 @@ class QRByImageSize(QRBase):
|
||||
"required": {
|
||||
"protocol": (["Http", "Https", "None"], {"default": "Https"}),
|
||||
"text": ("STRING", {"multiline": True}),
|
||||
"image_size": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 64}),
|
||||
"fill_hexcolor": ("STRING", {"multiline": False, "default": "#000000"}),
|
||||
"back_hexcolor": ("STRING", {"multiline": False, "default": "#FFFFFF"}),
|
||||
"error_correction": (["Low", "Medium", "Quartile", "High"], {"default": "High"}),
|
||||
"border": ("INT", {"default": 1, "min": 0, "max": 100, "step": 1}),
|
||||
"resampling": (["Bicubic", "Bilinear", "Box", "Hamming", "Lanczos", "Nearest"], {"default": "Nearest"}),
|
||||
"image_size": ("INT", {"default": 512,
|
||||
"min": 64,
|
||||
"max": 4096,
|
||||
"step": 64}),
|
||||
"fill_hexcolor": ("STRING", {"multiline": False,
|
||||
"default": "#000000"}),
|
||||
"back_hexcolor": ("STRING", {"multiline": False,
|
||||
"default": "#FFFFFF"}),
|
||||
"error_correction": (["Low", "Medium", "Quartile", "High"],
|
||||
{"default": "High"}),
|
||||
"border": ("INT", {"default": 1,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"step": 1}),
|
||||
"resampling": (["Bicubic",
|
||||
"Bilinear",
|
||||
"Box",
|
||||
"Hamming",
|
||||
"Lanczos",
|
||||
"Nearest"
|
||||
], {"default": "Nearest"}),
|
||||
"module_drawer": (["Square",
|
||||
"Gapped square",
|
||||
"Circle",
|
||||
"Rounded",
|
||||
"Vertical bars",
|
||||
"Horizontal bars"
|
||||
], {"default": "Square"})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -104,7 +161,8 @@ class QRByImageSize(QRBase):
|
||||
return Image.BOX
|
||||
if resampling_string == "Hamming":
|
||||
return Image.HAMMING
|
||||
raise ValueError(f"Resampling method of {resampling_string} not supported")
|
||||
raise ValueError(f"Resampling method of {resampling_string} not "
|
||||
f"supported")
|
||||
|
||||
def generate_qr(
|
||||
self,
|
||||
@@ -115,18 +173,18 @@ class QRByImageSize(QRBase):
|
||||
back_hexcolor,
|
||||
error_correction,
|
||||
border,
|
||||
resampling
|
||||
resampling,
|
||||
module_drawer
|
||||
):
|
||||
resampling_method = self._select_resampling_method(resampling)
|
||||
error_level = self._get_error_correction_constant(error_correction)
|
||||
self.update_text(protocol, text)
|
||||
qr = qrcode.QRCode(
|
||||
error_correction=error_level,
|
||||
box_size=1,
|
||||
box_size=16,
|
||||
border=border)
|
||||
qr.add_data(self.text)
|
||||
img = self._make_qr(qr, fill_hexcolor, back_hexcolor)
|
||||
self._validate_qr_size(img.pixel_size, image_size)
|
||||
img = self._make_qr(qr, fill_hexcolor, back_hexcolor, module_drawer)
|
||||
img = img.resize((image_size, image_size), resample=resampling_method)
|
||||
return (self._img_to_tensor(img), qr.version)
|
||||
|
||||
@@ -139,12 +197,31 @@ class QRByModuleSize(QRBase):
|
||||
"required": {
|
||||
"protocol": (["Http", "Https", "None"], {"default": "Https"}),
|
||||
"text": ("STRING", {"multiline": True}),
|
||||
"module_size": ("INT", {"default": 16, "min": 1, "max": 64, "step": 1}),
|
||||
"max_image_size": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 64}),
|
||||
"fill_hexcolor": ("STRING", {"multiline": False, "default": "#000000"}),
|
||||
"back_hexcolor": ("STRING", {"multiline": False, "default": "#FFFFFF"}),
|
||||
"error_correction": (["Low", "Medium", "Quartile", "High"], {"default": "High"}),
|
||||
"border": ("INT", {"default": 1, "min": 0, "max": 100, "step": 1}),
|
||||
"module_size": ("INT", {"default": 16,
|
||||
"min": 1,
|
||||
"max": 64,
|
||||
"step": 1}),
|
||||
"max_image_size": ("INT", {"default": 512,
|
||||
"min": 64,
|
||||
"max": 4096,
|
||||
"step": 64}),
|
||||
"fill_hexcolor": ("STRING", {"multiline": False,
|
||||
"default": "#000000"}),
|
||||
"back_hexcolor": ("STRING", {"multiline": False,
|
||||
"default": "#FFFFFF"}),
|
||||
"error_correction": (["Low", "Medium", "Quartile", "High"],
|
||||
{"default": "High"}),
|
||||
"border": ("INT", {"default": 1,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"step": 1}),
|
||||
"module_drawer": (["Square",
|
||||
"Gapped square",
|
||||
"Circle",
|
||||
"Rounded",
|
||||
"Vertical bars",
|
||||
"Horizontal bars"
|
||||
], {"default": "Square"})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -160,7 +237,8 @@ class QRByModuleSize(QRBase):
|
||||
fill_hexcolor,
|
||||
back_hexcolor,
|
||||
error_correction,
|
||||
border
|
||||
border,
|
||||
module_drawer
|
||||
):
|
||||
self.update_text(protocol, text)
|
||||
error_level = self._get_error_correction_constant(error_correction)
|
||||
@@ -169,7 +247,7 @@ class QRByModuleSize(QRBase):
|
||||
box_size=module_size,
|
||||
border=border)
|
||||
qr.add_data(self.text)
|
||||
img = self._make_qr(qr, fill_hexcolor, back_hexcolor)
|
||||
img = self._make_qr(qr, fill_hexcolor, back_hexcolor, module_drawer)
|
||||
self._validate_qr_size(img.pixel_size, max_image_size)
|
||||
return (self._img_to_tensor(img), qr.version, img.pixel_size)
|
||||
|
||||
@@ -182,17 +260,41 @@ class QRByModuleSizeSplitFunctionPatterns(QRBase):
|
||||
"required": {
|
||||
"protocol": (["Http", "Https", "None"], {"default": "Https"}),
|
||||
"text": ("STRING", {"multiline": True}),
|
||||
"module_size": ("INT", {"default": 16, "min": 1, "max": 64, "step": 1}),
|
||||
"max_image_size": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 64}),
|
||||
"fill_hexcolor": ("STRING", {"multiline": False, "default": "#000000"}),
|
||||
"back_hexcolor": ("STRING", {"multiline": False, "default": "#FFFFFF"}),
|
||||
"error_correction": (["Low", "Medium", "Quartile", "High"], {"default": "High"}),
|
||||
"border": ("INT", {"default": 1, "min": 0, "max": 100, "step": 1}),
|
||||
"module_size": ("INT", {"default": 16,
|
||||
"min": 1,
|
||||
"max": 64,
|
||||
"step": 1}),
|
||||
"max_image_size": ("INT", {"default": 512,
|
||||
"min": 64,
|
||||
"max": 4096,
|
||||
"step": 64}),
|
||||
"fill_hexcolor": ("STRING", {"multiline": False,
|
||||
"default": "#000000"}),
|
||||
"back_hexcolor": ("STRING", {"multiline": False,
|
||||
"default": "#FFFFFF"}),
|
||||
"error_correction": (["Low", "Medium", "Quartile", "High"],
|
||||
{"default": "High"}),
|
||||
"border": ("INT", {"default": 1,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"step": 1}),
|
||||
"module_drawer": (["Square",
|
||||
"Gapped square",
|
||||
"Circle",
|
||||
"Rounded",
|
||||
"Vertical bars",
|
||||
"Horizontal bars"
|
||||
], {"default": "Square"})
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE", "MASK", "INT", "INT")
|
||||
RETURN_NAMES = ("QR_CODE", "MODULE_LAYER", "FINDER_LAYER", "FINDER_MASK", "QR_VERSION", "IMAGE_SIZE")
|
||||
RETURN_NAMES = ("QR_CODE",
|
||||
"MODULE_LAYER",
|
||||
"FINDER_LAYER",
|
||||
"FINDER_MASK",
|
||||
"QR_VERSION",
|
||||
"IMAGE_SIZE")
|
||||
|
||||
def _generate_finder_pattern_ranges(self, module_size, border_size):
|
||||
outer = module_size * border_size
|
||||
@@ -205,9 +307,16 @@ class QRByModuleSizeSplitFunctionPatterns(QRBase):
|
||||
(-inner, far_outer, outer, inner)
|
||||
]
|
||||
|
||||
def _generate_finder_pattern_mask(self, pixel_size, module_size, border_size):
|
||||
def _generate_finder_pattern_mask(self,
|
||||
pixel_size,
|
||||
module_size,
|
||||
border_size):
|
||||
mask = np.zeros((pixel_size, pixel_size), dtype=bool)
|
||||
for x_min, x_max, y_min, y_max in self._generate_finder_pattern_ranges(module_size, border_size):
|
||||
for (x_min,
|
||||
x_max,
|
||||
y_min,
|
||||
y_max) in self._generate_finder_pattern_ranges(module_size,
|
||||
border_size):
|
||||
mask[y_min:y_max, x_min:x_max] = True
|
||||
return mask
|
||||
|
||||
@@ -219,7 +328,7 @@ class QRByModuleSizeSplitFunctionPatterns(QRBase):
|
||||
|
||||
def _mask_to_tensor(self, mask):
|
||||
out_image = mask.astype(np.float32)
|
||||
return torch.from_numpy(out_image)
|
||||
return torch.from_numpy(out_image).unsqueeze(0)
|
||||
|
||||
def generate_qr(
|
||||
self,
|
||||
@@ -230,7 +339,8 @@ class QRByModuleSizeSplitFunctionPatterns(QRBase):
|
||||
fill_hexcolor,
|
||||
back_hexcolor,
|
||||
error_correction,
|
||||
border
|
||||
border,
|
||||
module_drawer
|
||||
):
|
||||
self.update_text(protocol, text)
|
||||
error_level = self._get_error_correction_constant(error_correction)
|
||||
@@ -239,10 +349,12 @@ class QRByModuleSizeSplitFunctionPatterns(QRBase):
|
||||
box_size=module_size,
|
||||
border=border)
|
||||
qr.add_data(self.text)
|
||||
img = self._make_qr(qr, fill_hexcolor, back_hexcolor)
|
||||
img = self._make_qr(qr, fill_hexcolor, back_hexcolor, module_drawer)
|
||||
pixel_size = img.pixel_size
|
||||
self._validate_qr_size(pixel_size, max_image_size)
|
||||
mask = self._generate_finder_pattern_mask(pixel_size, module_size, border)
|
||||
mask = self._generate_finder_pattern_mask(pixel_size,
|
||||
module_size,
|
||||
border)
|
||||
module_image = self._apply_fill_to_mask(img, mask)
|
||||
function_image = self._apply_fill_to_mask(img, ~mask)
|
||||
return (
|
||||
@@ -265,6 +377,7 @@ class QRErrorMasker:
|
||||
CATEGORY = "ComfyQR"
|
||||
RETURN_TYPES = ("MASK", "FLOAT", "FLOAT", "FLOAT")
|
||||
RETURN_NAMES = ("QR_ERROR_MASK", "PERCENT_ERROR", "CORRELATION", "RMSE")
|
||||
OUTPUT_IS_LIST = (False, True, True, True)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
@@ -272,13 +385,24 @@ class QRErrorMasker:
|
||||
"required": {
|
||||
"source_qr": ("IMAGE",),
|
||||
"modified_qr": ("IMAGE",),
|
||||
"module_size": ("INT", {"default": 16, "min": 1, "max": 64, "step": 1}),
|
||||
"grayscale_method": (["mean", "luminance"], {"default": "luminance"}),
|
||||
"module_size": ("INT", {"default": 16,
|
||||
"min": 1,
|
||||
"max": 64,
|
||||
"step": 1}),
|
||||
"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}),
|
||||
"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}),
|
||||
"gamma": ("FLOAT", {"default": 2.2,
|
||||
"min": .1,
|
||||
"max": 2.8,
|
||||
"step": .1}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -288,10 +412,13 @@ class QRErrorMasker:
|
||||
indices = torch.nonzero(module_pixels, as_tuple=True)
|
||||
# The viewer patterns will guarentee a module pixel in the upper left
|
||||
# The bottom right does not have that guarentee so max is used.
|
||||
return indices[0][0], indices[0].max() + 1, indices[1][0], indices[1].max() + 1
|
||||
return (indices[0][0],
|
||||
indices[0].max() + 1,
|
||||
indices[1][0], indices[1].max() + 1)
|
||||
|
||||
def _extract_pattern_from_bounds(self, tensor):
|
||||
return tensor[self.qr_bounds[0]:self.qr_bounds[1], self.qr_bounds[2]:self.qr_bounds[3]]
|
||||
return tensor[self.qr_bounds[0]: self.qr_bounds[1],
|
||||
self.qr_bounds[2]: self.qr_bounds[3]]
|
||||
|
||||
def _trim_to_qr_area(self, source_qr, modified_qr, inverted_pattern):
|
||||
self.qr_bounds = self._get_qr_bounds(source_qr, inverted_pattern)
|
||||
@@ -302,20 +429,35 @@ class QRErrorMasker:
|
||||
|
||||
def _reshape_tensor_to_modules(self, tensor):
|
||||
if len(tensor.shape) != 2:
|
||||
raise RuntimeError("Module reshaping requires a 2 dimensional array.")
|
||||
raise RuntimeError("Module reshaping requires a 2 dimensional "
|
||||
"array.")
|
||||
length = tensor.shape[0] // self.module_size
|
||||
reshaped_tensor = tensor.view(length, self.module_size, length, self.module_size)
|
||||
reshaped_tensor = tensor.view(length,
|
||||
self.module_size,
|
||||
length,
|
||||
self.module_size)
|
||||
rehaped_tensor = reshaped_tensor.permute(0, 2, 1, 3).contiguous()
|
||||
return rehaped_tensor.view(length, length, self.module_size ** 2)
|
||||
|
||||
def _check_bounds_and_module_size(self):
|
||||
height = self.qr_bounds[1] - self.qr_bounds[0]
|
||||
width = self.qr_bounds[3] - self.qr_bounds[2]
|
||||
color_warning = "Make sure that qr_fill and back colors have exact #FFFFFFF and #000000 values (and that module color values do not occur outside the QR) and invert is set correctly."
|
||||
color_warning = "Make sure that qr_fill and back colors have exact "
|
||||
"#FFFFFFF and #000000 values (and that module color values do not "
|
||||
"occur outside the QR) and invert is set correctly."
|
||||
if width != height:
|
||||
raise RuntimeError(f"Source QR dimensions are {width} x {height}. They must be a perfect square. {color_warning}")
|
||||
raise RuntimeError(f"Source QR dimensions are {width} x {height}. "
|
||||
f"They must be a perfect square. "
|
||||
f"{color_warning}")
|
||||
if width % self.module_size:
|
||||
raise RuntimeError(f"QR width of {width} does not fit module_size of {self.module_size}. It must be perfectly divisible. {color_warning}")
|
||||
raise RuntimeError(f"QR width of {width} does not fit module_size "
|
||||
f"of {self.module_size}. It must be perfectly "
|
||||
f"divisible. {color_warning}")
|
||||
|
||||
def _check_equal_shape(self, source_qr, modified_qr):
|
||||
if source_qr.shape != modified_qr.shape:
|
||||
raise ValueError("Source and modified QR must have the same batch "
|
||||
"size and dimensions.")
|
||||
|
||||
def _squeeze_by_mean(self, tensor):
|
||||
return torch.mean(tensor, dim=-1)
|
||||
@@ -324,14 +466,18 @@ class QRErrorMasker:
|
||||
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 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 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):
|
||||
@@ -346,7 +492,8 @@ class QRErrorMasker:
|
||||
tensor = self._reshape_tensor_to_modules(tensor)
|
||||
if method == "mean":
|
||||
return self._squeeze_by_mean(tensor)
|
||||
raise RuntimeError("Module aggregation currently only supports the mean.")
|
||||
raise RuntimeError("Module aggregation currently only supports the "
|
||||
"mean.")
|
||||
|
||||
def _reduce_to_modules(
|
||||
self,
|
||||
@@ -358,12 +505,8 @@ class QRErrorMasker:
|
||||
inverted_pattern,
|
||||
gamma
|
||||
):
|
||||
if source_qr.shape != modified_qr.shape:
|
||||
raise ValueError("Source and modified QR must have the same dimensions.")
|
||||
self.module_size = module_size
|
||||
self.canvas_shape = (source_qr.shape[1], source_qr.shape[2])
|
||||
# Ignore batch dimension
|
||||
source_qr, modified_qr = source_qr[0], modified_qr[0]
|
||||
self.canvas_shape = (source_qr.shape[0], source_qr.shape[1])
|
||||
# Processed first for simplified indexing of QR bounds.
|
||||
source_qr = self._squeeze_by_mean(source_qr)
|
||||
source_qr, modified_qr = self._trim_to_qr_area(source_qr,
|
||||
@@ -375,8 +518,9 @@ class QRErrorMasker:
|
||||
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")
|
||||
raise ValueError("Currently only mean is supported for rgb to "
|
||||
"grayscale conversion.")
|
||||
source_qr = torch.round(self._squeeze_to_modules(source_qr, "mean"))
|
||||
modified_qr = self._squeeze_to_modules(modified_qr, aggregate_method)
|
||||
return source_qr, modified_qr
|
||||
|
||||
@@ -411,7 +555,9 @@ class QRErrorMasker:
|
||||
def _replace_qr_to_canvas(self, tensor):
|
||||
length = tensor.shape[0] * self.module_size
|
||||
bounds = self.qr_bounds
|
||||
tensor = F.interpolate(tensor.unsqueeze(0).unsqueeze(0), size=(length, length), mode='nearest')
|
||||
tensor = F.interpolate(tensor.unsqueeze(0).unsqueeze(0),
|
||||
size=(length, length),
|
||||
mode='nearest')
|
||||
canvas = torch.zeros(self.canvas_shape, dtype=torch.float32)
|
||||
canvas[bounds[0]:bounds[1], bounds[2]:bounds[3]] = tensor.squeeze()
|
||||
return canvas
|
||||
@@ -423,12 +569,14 @@ class QRErrorMasker:
|
||||
mask,
|
||||
error_difficulty
|
||||
):
|
||||
modified_qr = self._bin_tensor_to_threshold(modified_qr, error_difficulty)
|
||||
modified_qr = self._bin_tensor_to_threshold(modified_qr,
|
||||
error_difficulty)
|
||||
error = source_qr != modified_qr
|
||||
percent_error = error[mask].sum().item() / error[mask].numel()
|
||||
if mask is not None:
|
||||
error[~mask] = False
|
||||
return self._replace_qr_to_canvas((error).to(torch.float32)), percent_error
|
||||
return (self._replace_qr_to_canvas((error).to(torch.float32)),
|
||||
percent_error)
|
||||
|
||||
def _qr_correlation(self, source_qr, modified_qr, mask):
|
||||
source_qr = source_qr[mask].numpy().reshape((-1))
|
||||
@@ -451,36 +599,47 @@ class QRErrorMasker:
|
||||
inverted_pattern,
|
||||
gamma,
|
||||
):
|
||||
source_qr, modified_qr = self._reduce_to_modules(source_qr,
|
||||
modified_qr,
|
||||
module_size,
|
||||
grayscale_method,
|
||||
aggregate_method,
|
||||
inverted_pattern,
|
||||
gamma
|
||||
)
|
||||
mask = self._create_qr_mask(source_qr.shape[0], evaluate)
|
||||
error_mask, percent_error = self._compare_modules(source_qr,
|
||||
modified_qr,
|
||||
mask,
|
||||
error_difficulty
|
||||
)
|
||||
correlation = self._qr_correlation(source_qr, modified_qr, mask)
|
||||
rmse = self._qr_rmse(source_qr, modified_qr, mask)
|
||||
return (error_mask, percent_error, correlation, rmse)
|
||||
self._check_equal_shape(source_qr, modified_qr)
|
||||
error_masks, error_percents, correlations, rmses = [], [], [], []
|
||||
for i in range(source_qr.shape[0]):
|
||||
qr_s, qr_m = source_qr[i], modified_qr[i]
|
||||
qr_s, qr_m = self._reduce_to_modules(qr_s,
|
||||
qr_m,
|
||||
module_size,
|
||||
grayscale_method,
|
||||
aggregate_method,
|
||||
inverted_pattern,
|
||||
gamma
|
||||
)
|
||||
mask = self._create_qr_mask(qr_s.shape[0], evaluate)
|
||||
error_mask, percent_error = self._compare_modules(qr_s,
|
||||
qr_m,
|
||||
mask,
|
||||
error_difficulty
|
||||
)
|
||||
correlation = self._qr_correlation(qr_s, qr_m, mask)
|
||||
rmse = self._qr_rmse(qr_s, qr_m, mask)
|
||||
error_masks.append(error_mask)
|
||||
error_percents.append(percent_error)
|
||||
correlations.append(correlation)
|
||||
rmses.append(rmse)
|
||||
error_masks = torch.stack(error_masks, dim=0)
|
||||
return (error_masks, error_percents, correlations, rmses)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"comfy-qr-by-module-size": QRByModuleSize,
|
||||
"comfy-qr-by-image-size": QRByImageSize,
|
||||
"comfy-qr-by-module-split": QRByModuleSizeSplitFunctionPatterns,
|
||||
"comfy-qr-by-module-split":
|
||||
QRByModuleSizeSplitFunctionPatterns,
|
||||
"comfy-qr-mask_errors": QRErrorMasker,
|
||||
}
|
||||
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"comfy-qr-by-module-size": "QR Code",
|
||||
"comfy-qr-by-image-size": "QR Code (Conformed to Image Size)",
|
||||
"comfy-qr-by-image-size": "QR Code (Conformed "
|
||||
"to Image Size)",
|
||||
"comfy-qr-by-module-split": "QR Code (Split)",
|
||||
"comfy-qr-mask_errors": "Mask QR Errors",
|
||||
}
|
||||
|
||||