Skip to content

Commit

Permalink
Add helper to add background color on image
Browse files Browse the repository at this point in the history
For some images with alpha channel, the background color and the
foreground color are the same after grey scale conversion, this will no
allow us to detect 1D or 2D code on the image.

The copy_image_on_background() allow to add a background color on a
image to avoid this issue.

diff --git a/README.rst b/README.rst
index 917726b..3866048 100644
--- a/README.rst
+++ b/README.rst
@@ -35,3 +35,24 @@ How To use ZbarLight

     codes = zbarlight.scan_codes('qrcode', image)
     print('QR codes: %s' % codes)
+
+Troubleshooting
+===============
+
+In some case ``zbarlight`` will not be able to detect the 1D or 2D code in an image, one of the known cause is that the
+image background color is the same as the foreground color after conversion to grey scale (it's happen on images with
+alpha channel). You can use the ``copy_image_on_background`` function to add a background color on your image.
+
+.. code-block:: python
+
+    from PIL import Image
+    import zbarlight
+
+    file_path = './tests/fixtures/two_qr_codes.png'
+    with open(file_path, 'rb') as image_file:
+        image = Image.open(image_file)
+        image.load()
+
+    new_image = zbarlight.copy_image_on_background(image, color=zbarlight.WHITE)  # <<<<<<<<<<<<<<<< Add this line <<<<
+    codes = zbarlight.scan_codes('qrcode', new_image)
+    print('QR codes: %s' % codes)
diff --git a/docs/reference.rst b/docs/reference.rst
index 829f6f6..0e361df 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -2,4 +2,4 @@ Reference
 =========

 .. automodule:: zbarlight
-    :members: scan_codes
+    :members: scan_codes, copy_image_on_background
diff --git a/src/zbarlight/__init__.py b/src/zbarlight/__init__.py
index a0171f5..0f05c83 100644
--- a/src/zbarlight/__init__.py
+++ b/src/zbarlight/__init__.py
@@ -7,7 +7,17 @@ from ._zbarlight import Symbologies, zbar_code_scanner

 __version__ = pkg_resources.get_distribution('zbarlight').version
-__ALL__ = ['Symbologies', 'UnknownSymbologieError', 'scan_codes']
+__ALL__ = [
+    'Symbologies',
+    'UnknownSymbologieError',
+    'scan_codes',
+    'copy_image_on_background',
+    'BLACK',
+    'WHITE',
+]
+
+WHITE = (255, 255, 255)
+BLACK = (0, 0, 0)

 class UnknownSymbologieError(Exception):
@@ -42,3 +52,18 @@ def scan_codes(code_type, image):
     if not symbologie:
         raise UnknownSymbologieError('Unknown Symbologie: %s' % code_type)
     return zbar_code_scanner(symbologie, raw, width, height)
+
+
+def copy_image_on_background(image, color=WHITE):
+    """Create a new image by copying the image on a *color* background
+
+    Args:
+        image (PIL.Image.Image): Image to copy
+        color (tuple): Background color usually WHITE or BLACK
+
+    Returns:
+        PIL.Image.Image
+    """
+    background = Image.new("RGB", image.size, color)
+    background.paste(image, mask=image.split()[3])
+    return background
diff --git a/tests/fixtures/sample_need_white_background.png b/tests/fixtures/sample_need_white_background.png
new file mode 100644
index 0000000..1c2b5ef
Binary files /dev/null and b/tests/fixtures/sample_need_white_background.png differ
diff --git a/tests/test_scan_codes.py b/tests/test_scan_codes.py
index 2b4ad81..f7ae31b 100644
--- a/tests/test_scan_codes.py
+++ b/tests/test_scan_codes.py
@@ -17,9 +17,9 @@ class ScanCodeTestCase(unittest.TestCase):
     def assertIsNone(self, obj, msg=None):  # Python 2.6 hack
         return self.assertTrue(obj is None, '%s is not None' % repr(obj))

-    def get_image(self, name):
+    def get_image(self, name, ext='png'):
         return pil_image(
-            os.path.join(os.path.dirname(__file__), 'fixtures', '{0}.png'.format(name))
+            os.path.join(os.path.dirname(__file__), 'fixtures', '{0}.{1}'.format(name, ext))
         )

     def test_no_qr_code(self):
@@ -59,3 +59,22 @@ class ScanCodeTestCase(unittest.TestCase):
             zbarlight.UnknownSymbologieError,
             zbarlight.scan_codes, 'not-a-zbar-symbologie', image,
         )
+
+    def test_need_white_background(self):
+        """
+        User submitted sample that can only be decoded after add a white background
+        """
+        # Not working
+        image = self.get_image('sample_need_white_background')
+
+        self.assertEqual(
+            sorted(zbarlight.scan_codes('qrcode', image) or []),
+            [],
+        )
+
+        # Working when adding white background
+        image_with_background = zbarlight.copy_image_on_background(image)
+        self.assertEqual(
+            sorted(zbarlight.scan_codes('qrcode', image_with_background) or []),
+            sorted([b'http://en.m.wikipedia.org']),
+        )
  • Loading branch information
Fabien Bochu committed Aug 22, 2017
1 parent 28ea46f commit 6597448
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 5 deletions.
3 changes: 2 additions & 1 deletion Changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ ChangeLog
2.0 (unreleased)
----------------

- Drop deprecated qr_code_scanner() method.
- Drop deprecated qr_code_scanner() method
- Add helper to add background color on image


1.2 (2017-03-09)
Expand Down
21 changes: 21 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,24 @@ How To use ZbarLight
codes = zbarlight.scan_codes('qrcode', image)
print('QR codes: %s' % codes)
Troubleshooting
===============

In some case ``zbarlight`` will not be able to detect the 1D or 2D code in an image, one of the known cause is that the
image background color is the same as the foreground color after conversion to grey scale (it's happen on images with
alpha channel). You can use the ``copy_image_on_background`` function to add a background color on your image.

.. code-block:: python
from PIL import Image
import zbarlight
file_path = './tests/fixtures/two_qr_codes.png'
with open(file_path, 'rb') as image_file:
image = Image.open(image_file)
image.load()
new_image = zbarlight.copy_image_on_background(image, color=zbarlight.WHITE) # <<<<<<<<<<<<<<<< Add this line <<<<
codes = zbarlight.scan_codes('qrcode', new_image)
print('QR codes: %s' % codes)
2 changes: 1 addition & 1 deletion docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ Reference
=========

.. automodule:: zbarlight
:members: scan_codes
:members: scan_codes, copy_image_on_background
27 changes: 26 additions & 1 deletion src/zbarlight/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@


__version__ = pkg_resources.get_distribution('zbarlight').version
__ALL__ = ['Symbologies', 'UnknownSymbologieError', 'scan_codes']
__ALL__ = [
'Symbologies',
'UnknownSymbologieError',
'scan_codes',
'copy_image_on_background',
'BLACK',
'WHITE',
]

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)


class UnknownSymbologieError(Exception):
Expand Down Expand Up @@ -42,3 +52,18 @@ def scan_codes(code_type, image):
if not symbologie:
raise UnknownSymbologieError('Unknown Symbologie: %s' % code_type)
return zbar_code_scanner(symbologie, raw, width, height)


def copy_image_on_background(image, color=WHITE):
"""Create a new image by copying the image on a *color* background
Args:
image (PIL.Image.Image): Image to copy
color (tuple): Background color usually WHITE or BLACK
Returns:
PIL.Image.Image
"""
background = Image.new("RGB", image.size, color)
background.paste(image, mask=image.split()[3])
return background
Binary file added tests/fixtures/sample_need_white_background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 21 additions & 2 deletions tests/test_scan_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ class ScanCodeTestCase(unittest.TestCase):
def assertIsNone(self, obj, msg=None): # Python 2.6 hack
return self.assertTrue(obj is None, '%s is not None' % repr(obj))

def get_image(self, name):
def get_image(self, name, ext='png'):
return pil_image(
os.path.join(os.path.dirname(__file__), 'fixtures', '{0}.png'.format(name))
os.path.join(os.path.dirname(__file__), 'fixtures', '{0}.{1}'.format(name, ext))
)

def test_no_qr_code(self):
Expand Down Expand Up @@ -59,3 +59,22 @@ def test_unknown_symbology(self):
zbarlight.UnknownSymbologieError,
zbarlight.scan_codes, 'not-a-zbar-symbologie', image,
)

def test_need_white_background(self):
"""
User submitted sample that can only be decoded after add a white background
"""
# Not working
image = self.get_image('sample_need_white_background')

self.assertEqual(
sorted(zbarlight.scan_codes('qrcode', image) or []),
[],
)

# Working when adding white background
image_with_background = zbarlight.copy_image_on_background(image)
self.assertEqual(
sorted(zbarlight.scan_codes('qrcode', image_with_background) or []),
sorted([b'http://en.m.wikipedia.org']),
)

0 comments on commit 6597448

Please sign in to comment.