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

WIP: cv2 #9

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,13 @@
setup_requires=[
'setuptools',
],
install_requires=[
'Pillow'
],
extras_require={
'PIL': [
'Pillow'
],
'cv2': [
'cv2'
],
'docs': [
'Sphinx',
'sphinx-autobuild',
Expand Down
62 changes: 52 additions & 10 deletions zxinglight/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
# -*- coding: utf-8 -*-

from enum import IntEnum, unique
from PIL import Image
try:
from PIL import Image
except ImportError:
pass
try:
import cv2
import numpy as np
except ImportError:
pass

from ._zxinglight import zxing_read_codes

Expand Down Expand Up @@ -67,9 +75,10 @@ class BarcodeType(IntEnum):
UPC_EAN_EXTENSION = 17


def read_codes(image, barcode_type=BarcodeType.NONE, try_harder=False, hybrid=False, multi=True):
def read_codes_full(image, barcode_type=BarcodeType.NONE,
try_harder=False, hybrid=False, multi=True):
"""
Reads codes from a PIL Image.
Reads codes from a PIL Image and includes metadata about what was found.

Args:
image (PIL.Image.Image): Image to read barcodes from.
Expand All @@ -80,21 +89,54 @@ def read_codes(image, barcode_type=BarcodeType.NONE, try_harder=False, hybrid=Fa
multi (bool): Search for multiple barcodes in a single image.

Returns:
A list of barcode values.
A list [(code, position, type), ...] containing each barcode found.

.. _ZXing's documentation:
https://zxing.github.io/zxing/apidocs/com/google/zxing/Binarizer.html
"""

if not Image.isImageType(image):
raise ValueError('Provided image is not a PIL image')
if "Image" in globals() and Image.isImageType(image):
grayscale_image = image.convert('L')

raw_image = grayscale_image.tobytes()
width, height = grayscale_image.size
elif "cv2" in globals() and isinstance(image, np.ndarray):
if image.dtype != np.uint8:
raise TypeError("Provided cv2 image is not in integer bitdepth.")
if len(image.shape) == 2:
greyscale_image = image
elif len(image.shape) == 3 and image.shape[-1] == 3:
greyscale_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
else:
raise TypeError("Provided cv2 image is not in BGR nor greyscale formats.")
raw_image = greyscale_image.tobytes()
height, width = image.shape[:2]
else:
raise TypeError('Provided image is not a PIL nor cv2 image')

if not isinstance(barcode_type, BarcodeType):
raise ValueError('barcode_type is not an enum member of BarcodeType')

grayscale_image = image.convert('L')
return zxing_read_codes(raw_image, width, height, barcode_type, try_harder, hybrid, multi)

raw_image = grayscale_image.tobytes()
width, height = grayscale_image.size

return zxing_read_codes(raw_image, width, height, barcode_type, try_harder, hybrid, multi)
def read_codes(*args, **kwargs):
"""
Reads codes from a PIL Image.

Args:
image (PIL.Image.Image): Image to read barcodes from.
barcode_type (zxinglight.BarcodeType): Barcode type to look for.
try_harder (bool): Spend more time trying to find a barcode.
hybrid (bool): Use Hybrid Binarizer instead of Global Binarizer. For more information,
see `ZXing's documentation`_.
multi (bool): Search for multiple barcodes in a single image.

Returns:
A list of barcode contents found.

.. _ZXing's documentation:
https://zxing.github.io/zxing/apidocs/com/google/zxing/Binarizer.html
"""
codes = read_codes_full(*args, **kwargs)
return [text for text, points, format in codes]
33 changes: 18 additions & 15 deletions zxinglight/_zxinglight.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ static void log_error(const string &msg) {
PyObject_CallMethod(logger, "error", "O", PyUnicode_FromString(msg.c_str()));
}

static vector<string> *_zxing_read_codes(
static vector<Ref<Result> > *_zxing_read_codes(
char *image, int image_size, int width, int height,int barcode_type, bool try_harder,
bool hybrid, bool multi
) {
Expand Down Expand Up @@ -67,24 +67,19 @@ static vector<string> *_zxing_read_codes(

Ref<BinaryBitmap> bitmap(new BinaryBitmap(binarizer));

vector<Ref<Result> > results;
vector<Ref<Result> > *results;
if (multi) {
MultiFormatReader delegate;
GenericMultipleBarcodeReader reader(delegate);
results = reader.decodeMultiple(bitmap, hints);
results = new vector<Ref<Result> >(reader.decodeMultiple(bitmap, hints));
} else {
// There is only one result, but wrap it in a vector anyway to give a consistent interface
// Ref<T> is an autodestructor; the `new` *does not leak*.
Ref<Reader> reader(new MultiFormatReader);
results = vector<Ref<Result> >(1, reader->decode(bitmap, hints));
results = new vector<Ref<Result> >(1, reader->decode(bitmap, hints));
}

vector<string> *codes = new vector<string>();

for (const Ref<Result> &result : results) {
codes->push_back(result->getText()->getText());
}

return codes;
return results;
} catch (const ReaderException &e) {
log_error((string) "zxing::ReaderException: " + e.what());
} catch (const zxing::IllegalArgumentException &e) {
Expand Down Expand Up @@ -113,17 +108,25 @@ static PyObject* zxing_read_codes(PyObject *self, PyObject *args) {

PyBytes_AsStringAndSize(python_image, &image, &image_size);

vector<string> *results = _zxing_read_codes(
vector<Ref<Result> > *results = _zxing_read_codes(
image, image_size, width, height, barcode_type, try_harder, hybrid, multi
);

PyObject *codes = PyList_New(0);

if (results != NULL) {
for (const string &code : *results) {
PyList_Append(codes, PyUnicode_FromString(code.c_str()));
}
for (const Ref<Result> &result : *results) {
PyObject* text = PyUnicode_FromString(result->getText()->getText().c_str());

PyObject* points = PyList_New(0);
for(auto point : result->getResultPoints()->values()) {
PyList_Append(points, Py_BuildValue("ff", point->getX(), point->getY()));
}

PyObject* format = PyLong_FromUnsignedLong(result->getBarcodeFormat());

PyList_Append(codes, PyTuple_Pack(3, text, points, format));
}
delete results;
}

Expand Down