Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inverted QR codes #94

Open
doTeam opened this issue Jul 29, 2020 · 24 comments
Open

Inverted QR codes #94

doTeam opened this issue Jul 29, 2020 · 24 comments

Comments

@doTeam
Copy link

doTeam commented Jul 29, 2020

Would it be possible to add functionality to scan inverted QR codes as well? Inverted meaning dark background and white foreground; currently it is not supported.

I thought about adding such functionality thru inverting canvas, but I'd first like your input if there are any better solutions that could be implemented library-wide, seems like quite a missing functionality.

@mebjas
Copy link
Owner

mebjas commented Aug 2, 2020

This is interesting - are there use-cases like that where inverted QR codes are used?

If there is a major use case and you can provide me with few samples - it should be possible to support this. One quick workaround to try is to invert the canvas as you shared.

@OlegKireev
Copy link

I've faced with the same problem. How can I change in the library to make it read the white QR codes?

@thk-root
Copy link

thk-root commented Apr 1, 2021

I've faced with the same problem. How can I change in the library to make it read the white QR codes?

I have made it work simply by inverting the colours of the canvas each time it scans and it doesn't detect normal code. I can't really make a pull request, because I hotfixed it on a refactored version, but here's the snippet (line ~1310):

https://pastebin.com/7DBcWpr3

The relevant part is the inverting of canvas (_context.filter = 'invert(100%)')

I'm sure this is not the most reliable fix, but it works wonders for us. Hope it will for you too

@OlegKireev
Copy link

I've faced with the same problem. How can I change in the library to make it read the white QR codes?

I have made it work simply by inverting the colours of the canvas each time it scans and it doesn't detect normal code. I can't really make a pull request, because I hotfixed it on a refactored version, but here's the snippet (line ~1310):

https://pastebin.com/7DBcWpr3

The relevant part is the inverting of canvas (_context.filter = 'invert(100%)')

I'm sure this is not the most reliable fix, but it works wonders for us. Hope it will for you too

Man, you saved my life! It really works at qr camera-scanner, this is awsome! But now I can't make the script works with attaching qr-images. Haven't you fixed the attaching images?

@thk-root
Copy link

thk-root commented Apr 9, 2021

I've faced with the same problem. How can I change in the library to make it read the white QR codes?

I have made it work simply by inverting the colours of the canvas each time it scans and it doesn't detect normal code. I can't really make a pull request, because I hotfixed it on a refactored version, but here's the snippet (line ~1310):
https://pastebin.com/7DBcWpr3
The relevant part is the inverting of canvas (_context.filter = 'invert(100%)')
I'm sure this is not the most reliable fix, but it works wonders for us. Hope it will for you too

Man, you saved my life! It really works at qr camera-scanner, this is awsome! But now I can't make the script works with attaching qr-images. Haven't you fixed the attaching images?

Unforunately no, because we only use the camera scanner. I'm sure the concept is the same (filtering the canvas), it's just a shame this isn't supported in the core code

@eduardo-digi4ind
Copy link

This is interesting - are there use-cases like that where inverted QR codes are used?

If there is a major use case and you can provide me with few samples - it should be possible to support this. One quick workaround to try is to invert the canvas as you shared.

You asked for a major use case, so there we go...

My company develops software for industry (asset management, plant maintenance, etc). Each equipment receives an unique ID, printed in a label as a QR-Code. In aggressive environments (heat, chemical solutions, etc.) we have to print the QR-Codes in metal labels (anodized aluminum, stainless steel, etc). In only one customer, we have literally thousands of electric motors about to receive their metal labels with black background and white foreground).

I'll be happy to provide some samples.

IMG_6898

@Silinator
Copy link

Silinator commented Mar 17, 2022

This will invert the qr before scan eternally.
Just add this part before you initialize your scanner.

   import {Html5Qrcode} from "html5-qrcode";

    //....start of stuff to add
    Html5Qrcode.prototype.foreverScan = function(internalConfig, qrCodeSuccessCallback, qrCodeErrorCallback) {
      var _this = this;
      if (!this.shouldScan) {
        return;
      }
      if (!this.localMediaStream) {
        return;
      }
      var videoElement = this.videoElement;
      var widthRatio = videoElement.videoWidth / videoElement.clientWidth;
      var heightRatio = videoElement.videoHeight / videoElement.clientHeight;
      if (!this.qrRegion) {
        throw "qrRegion undefined when localMediaStream is ready.";
      }
      var sWidthOffset = this.qrRegion.width * widthRatio;
      var sHeightOffset = this.qrRegion.height * heightRatio;
      var sxOffset = this.qrRegion.x * widthRatio;
      var syOffset = this.qrRegion.y * heightRatio;
      this.context.drawImage(videoElement, sxOffset, syOffset, sWidthOffset, sHeightOffset, 0, 0, this.qrRegion.width, this.qrRegion.height);
      this.context.filter = 'invert(100%)'; // <-- Thx to [thk-root](https://github.com/thk-root)
      var triggerNextScan = function() {
        _this.foreverScanTimeout = setTimeout(function() {
          _this.foreverScan(internalConfig, qrCodeSuccessCallback, qrCodeErrorCallback);
        }, _this.getTimeoutFps(internalConfig.fps));
      };
      this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback).then(function(isSuccessfull) {
        if (!isSuccessfull && internalConfig.disableFlip !== true) {
          _this.context.translate(_this.context.canvas.width, 0);
          _this.context.scale(-1, 1);
          _this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback).finally(function() {
            triggerNextScan();
          });
        } else {
          triggerNextScan();
        }
      }).catch(function(error) {
        _this.logger.logError("Error happend while scanning context", error);
        triggerNextScan();
      });
    };
    
    //....end stuff to add
    html5QrCode = new Html5Qrcode('[YOUR_DIV_ID]');

The part who inverts the image is this: this.context.filter = invert(100%); part.
You can add some if around it so it is not inverted all the time. That's up to you.

@nivthetool
Copy link

Hi all,
I've added the Html5Qrcode.prototype.foreverScan handler with no luck.
still cannot read inverted qrcode.
has anyone have any alternatives/workaround for inverted qrcode detection
TIA

@nivthetool
Copy link

I've faced with the same problem. How can I change in the library to make it read the white QR codes?

I have made it work simply by inverting the colours of the canvas each time it scans and it doesn't detect normal code. I can't really make a pull request, because I hotfixed it on a refactored version, but here's the snippet (line ~1310):
https://pastebin.com/7DBcWpr3
The relevant part is the inverting of canvas (_context.filter = 'invert(100%)')
I'm sure this is not the most reliable fix, but it works wonders for us. Hope it will for you too

Man, you saved my life! It really works at qr camera-scanner, this is awsome! But now I can't make the script works with attaching qr-images. Haven't you fixed the attaching images?

can you please guide me too i could not make it work - which files and what code should i change
TIA mate really appreciate

@nivthetool
Copy link

I've faced with the same problem. How can I change in the library to make it read the white QR codes?

I have made it work simply by inverting the colours of the canvas each time it scans and it doesn't detect normal code. I can't really make a pull request, because I hotfixed it on a refactored version, but here's the snippet (line ~1310):

https://pastebin.com/7DBcWpr3

The relevant part is the inverting of canvas (_context.filter = 'invert(100%)')

I'm sure this is not the most reliable fix, but it works wonders for us. Hope it will for you too

can you please guide me too,
i could not make it work - which files and what code should i change
TIA mate really appreciate

@nivthetool
Copy link

to whom it might help, i have found this library that works with inverted qrcode
https://github.com/cozmo/jsQR
thank you all for your help

@poonth
Copy link

poonth commented Apr 18, 2022

to whom it might help, i have found this library that works with inverted qrcode https://github.com/cozmo/jsQR thank you all for your help

but it freeze after 30 seconds

@poonth
Copy link

poonth commented Apr 18, 2022

Inverted QR codes still not supported after 2.0
@mebjas Any updates?

@johnnyb
Copy link

johnnyb commented May 12, 2022

I monkey-patched the library to support both simultaneously. This reads alternate frames as positive/negative:

https://gist.github.com/johnnyb/8e4c888aa0ca5d4503b5e52391f4207a

Based off of a pull request on ZXing: zxing-js/library@7644e27

@mariusvdw
Copy link

This will invert the qr before scan eternally. Just add this part before you initialize your scanner.

   import {Html5Qrcode} from "html5-qrcode";

    //....start of stuff to add
    Html5Qrcode.prototype.foreverScan = function(internalConfig, qrCodeSuccessCallback, qrCodeErrorCallback) {
      var _this = this;
      if (!this.shouldScan) {
        return;
      }
      if (!this.localMediaStream) {
        return;
      }
      var videoElement = this.videoElement;
      var widthRatio = videoElement.videoWidth / videoElement.clientWidth;
      var heightRatio = videoElement.videoHeight / videoElement.clientHeight;
      if (!this.qrRegion) {
        throw "qrRegion undefined when localMediaStream is ready.";
      }
      var sWidthOffset = this.qrRegion.width * widthRatio;
      var sHeightOffset = this.qrRegion.height * heightRatio;
      var sxOffset = this.qrRegion.x * widthRatio;
      var syOffset = this.qrRegion.y * heightRatio;
      this.context.drawImage(videoElement, sxOffset, syOffset, sWidthOffset, sHeightOffset, 0, 0, this.qrRegion.width, this.qrRegion.height);
      this.context.filter = 'invert(100%)'; // <-- Thx to [thk-root](https://github.com/thk-root)
      var triggerNextScan = function() {
        _this.foreverScanTimeout = setTimeout(function() {
          _this.foreverScan(internalConfig, qrCodeSuccessCallback, qrCodeErrorCallback);
        }, _this.getTimeoutFps(internalConfig.fps));
      };
      this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback).then(function(isSuccessfull) {
        if (!isSuccessfull && internalConfig.disableFlip !== true) {
          _this.context.translate(_this.context.canvas.width, 0);
          _this.context.scale(-1, 1);
          _this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback).finally(function() {
            triggerNextScan();
          });
        } else {
          triggerNextScan();
        }
      }).catch(function(error) {
        _this.logger.logError("Error happend while scanning context", error);
        triggerNextScan();
      });
    };
    
    //....end stuff to add
    html5QrCode = new Html5Qrcode('[YOUR_DIV_ID]');

The part who inverts the image is this: this.context.filter = invert(100%); part. You can add some if around it so it is not inverted all the time. That's up to you.

Thanks to @Silinator , i have manage to adapt the code to be able to scan both (inverted and non-inverted) QR Codes.

Just added the following logic to the this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback) promise.

                if (!isSuccessfull) {
                  _this.invertQR = !_this.invertQR;
                   if (_this.invertQR) {
                       _this.context.filter = 'invert(100%)';
                   }
                   else {
                       _this.context.filter = 'none';
                   }

                   _this.context.drawImage(videoElement, sxOffset, syOffset, sWidthOffset, sHeightOffset, 0, 0, _this.qrRegion.width, _this.qrRegion.height);

                   _this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback).finally(function () {
                       triggerNextScan();
                   });
               } else {
                   triggerNextScan();
               }

@ROBERT-MCDOWELL
Copy link

@mariusvdw
why not to fork the project and create a PR?

@DeniCarvalho
Copy link

Updates on this feature for the Html5Qrcode class? For me it only works in the Html5QrcodeScanner class.

@marcostalder85
Copy link

This will invert the qr before scan eternally. Just add this part before you initialize your scanner.

   import {Html5Qrcode} from "html5-qrcode";

    //....start of stuff to add
    Html5Qrcode.prototype.foreverScan = function(internalConfig, qrCodeSuccessCallback, qrCodeErrorCallback) {
      var _this = this;
      if (!this.shouldScan) {
        return;
      }
      if (!this.localMediaStream) {
        return;
      }
      var videoElement = this.videoElement;
      var widthRatio = videoElement.videoWidth / videoElement.clientWidth;
      var heightRatio = videoElement.videoHeight / videoElement.clientHeight;
      if (!this.qrRegion) {
        throw "qrRegion undefined when localMediaStream is ready.";
      }
      var sWidthOffset = this.qrRegion.width * widthRatio;
      var sHeightOffset = this.qrRegion.height * heightRatio;
      var sxOffset = this.qrRegion.x * widthRatio;
      var syOffset = this.qrRegion.y * heightRatio;
      this.context.drawImage(videoElement, sxOffset, syOffset, sWidthOffset, sHeightOffset, 0, 0, this.qrRegion.width, this.qrRegion.height);
      this.context.filter = 'invert(100%)'; // <-- Thx to [thk-root](https://github.com/thk-root)
      var triggerNextScan = function() {
        _this.foreverScanTimeout = setTimeout(function() {
          _this.foreverScan(internalConfig, qrCodeSuccessCallback, qrCodeErrorCallback);
        }, _this.getTimeoutFps(internalConfig.fps));
      };
      this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback).then(function(isSuccessfull) {
        if (!isSuccessfull && internalConfig.disableFlip !== true) {
          _this.context.translate(_this.context.canvas.width, 0);
          _this.context.scale(-1, 1);
          _this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback).finally(function() {
            triggerNextScan();
          });
        } else {
          triggerNextScan();
        }
      }).catch(function(error) {
        _this.logger.logError("Error happend while scanning context", error);
        triggerNextScan();
      });
    };
    
    //....end stuff to add
    html5QrCode = new Html5Qrcode('[YOUR_DIV_ID]');

The part who inverts the image is this: this.context.filter = invert(100%); part. You can add some if around it so it is not inverted all the time. That's up to you.

We put some additional code inside yours, and this works like a charm for us:

Html5Qrcode.prototype.foreverScan = function (internalConfig, qrCodeSuccessCallback, qrCodeErrorCallback) {
    var _this = this;
    if (!this.shouldScan) {
        return;
    }
    if (!this.localMediaStream) {
        return;
    }
    var videoElement = this.videoElement;
    var widthRatio = videoElement.videoWidth / videoElement.clientWidth;
    var heightRatio = videoElement.videoHeight / videoElement.clientHeight;
    if (!this.qrRegion) {
        throw "qrRegion undefined when localMediaStream is ready.";
    }
    var sWidthOffset = this.qrRegion.width * widthRatio;
    var sHeightOffset = this.qrRegion.height * heightRatio;
    var sxOffset = this.qrRegion.x * widthRatio;
    var syOffset = this.qrRegion.y * heightRatio;

    this.context.drawImage(videoElement, sxOffset, syOffset, sWidthOffset, sHeightOffset, 0, 0, this.qrRegion.width, this.qrRegion.height);

    if (this.context.filter) {
        this.context.filter = 'invert(1)';
        this.context.drawImage(videoElement, sxOffset, syOffset, sWidthOffset, sHeightOffset, 0, 0, this.qrRegion.width, this.qrRegion.height);
    } else {
        this.context.drawImage(videoElement, sxOffset, syOffset, sWidthOffset, sHeightOffset, 0, 0, this.qrRegion.width, this.qrRegion.height);
        this.context.globalCompositeOperation = 'difference';

        if (this.context.globalCompositeOperation === 'difference') {
            this.context.fillStyle = 'white';
            this.context.fillRect(0, 0, this.qrRegion.width, this.qrRegion.height);

            var canvas2 = document.createElement('canvas');
            var ctx2 = canvas2.getContext('2d');
            canvas2.width = this.qrRegion.width;
            canvas2.height = this.qrRegion.height;
            ctx2.drawImage(videoElement, sxOffset, syOffset, sWidthOffset, sHeightOffset, 0, 0, this.qrRegion.width, this.qrRegion.height);
            ctx2.globalCompositeOperation = 'source-in';
            ctx2.fillStyle = 'black';
            ctx2.fillRect(0, 0, this.qrRegion.width, this.qrRegion.height);
            ctx2.globalCompositeOperation = 'destination-in';
            this.context.drawImage(canvas2, sxOffset, syOffset, sWidthOffset, sHeightOffset, 0, 0, this.qrRegion.width, this.qrRegion.height);
        } else {
            try {
                var imgData = this.context.getImageData(0, 0, this.qrRegion.width, this.qrRegion.height);
                var data = imgData.data;
                for (var i = 0; i < data.length; i += (i % 4 === 2 ? 2 : 1)) {
                    data[i] = 255 - data[i];
                }
                this.context.putImageData(imgData, 0, 0);
            } catch (e) { }
        }
    }

    var triggerNextScan = function () {
        _this.foreverScanTimeout = setTimeout(function () {
            _this.foreverScan(internalConfig, qrCodeSuccessCallback, qrCodeErrorCallback);
        }, _this.getTimeoutFps(internalConfig.fps));
    };

    this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback).then(function (isSuccessfull) {
        if (!isSuccessfull) {
            _this.context.drawImage(videoElement, sxOffset, syOffset, sWidthOffset, sHeightOffset, 0, 0, _this.qrRegion.width, _this.qrRegion.height);
            _this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback).finally(function () {
                triggerNextScan();
            });
        } else {
            triggerNextScan();
        }
    }).catch(function (error) {
        _this.logger.logError("Error happend while scanning context", error);
        triggerNextScan();
    });
};

@marcostalder85
Copy link

We've found out, that our code works well on iOS and Webcams, but Android now just scan inverted QR codes...

@Parakoos
Copy link

This is just another example of a place where you will find codes (this time a Data Matrix) in the wild with inverted colors. Parker is a huge supplier of mechanical components so definitely a big player, and one I am trying to scan codes for.
unnamed

@ssoima
Copy link

ssoima commented Jun 8, 2023

When will this be solved? All our codes are inverted also printed on anodised aluminium.

@chrismask
Copy link

Fix this issue, its unacceptable that this library cant scan inverted codes at least, let alone some other qr code visual modifications, that the majority of qr codes are. Approve this ASAP: fante90@b9ee6ef

@Jervx
Copy link

Jervx commented Feb 22, 2024

Fix this issue, its unacceptable that this library cant scan inverted codes at least, let alone some other qr code visual modifications, that the majority of qr codes are. Approve this ASAP: fante90@b9ee6ef

LGTM!

@Mbengparis
Copy link

This will invert the qr before scan eternally. Just add this part before you initialize your scanner.

   import {Html5Qrcode} from "html5-qrcode";

    //....start of stuff to add
    Html5Qrcode.prototype.foreverScan = function(internalConfig, qrCodeSuccessCallback, qrCodeErrorCallback) {
      var _this = this;
      if (!this.shouldScan) {
        return;
      }
      if (!this.localMediaStream) {
        return;
      }
      var videoElement = this.videoElement;
      var widthRatio = videoElement.videoWidth / videoElement.clientWidth;
      var heightRatio = videoElement.videoHeight / videoElement.clientHeight;
      if (!this.qrRegion) {
        throw "qrRegion undefined when localMediaStream is ready.";
      }
      var sWidthOffset = this.qrRegion.width * widthRatio;
      var sHeightOffset = this.qrRegion.height * heightRatio;
      var sxOffset = this.qrRegion.x * widthRatio;
      var syOffset = this.qrRegion.y * heightRatio;
      this.context.drawImage(videoElement, sxOffset, syOffset, sWidthOffset, sHeightOffset, 0, 0, this.qrRegion.width, this.qrRegion.height);
      this.context.filter = 'invert(100%)'; // <-- Thx to [thk-root](https://github.com/thk-root)
      var triggerNextScan = function() {
        _this.foreverScanTimeout = setTimeout(function() {
          _this.foreverScan(internalConfig, qrCodeSuccessCallback, qrCodeErrorCallback);
        }, _this.getTimeoutFps(internalConfig.fps));
      };
      this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback).then(function(isSuccessfull) {
        if (!isSuccessfull && internalConfig.disableFlip !== true) {
          _this.context.translate(_this.context.canvas.width, 0);
          _this.context.scale(-1, 1);
          _this.scanContext(qrCodeSuccessCallback, qrCodeErrorCallback).finally(function() {
            triggerNextScan();
          });
        } else {
          triggerNextScan();
        }
      }).catch(function(error) {
        _this.logger.logError("Error happend while scanning context", error);
        triggerNextScan();
      });
    };
    
    //....end stuff to add
    html5QrCode = new Html5Qrcode('[YOUR_DIV_ID]');

The part who inverts the image is this: this.context.filter = invert(100%); part. You can add some if around it so it is not inverted all the time. That's up to you.

i added the above code and its not working. Nothing happens when i scan both the inverted and non-inverted codes. How can i solve this problem?.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests