/* * Copyright 2019 Tim Rae * Copyright 2021 Antoine Humbert * Copyright 2021 Axel Waggershauser */ // SPDX-License-Identifier: Apache-2.0 #include "ZXingCpp.h" #include "ZXAlgorithms.h" #include #include #include #include #include #include #include #include #include #include #include using namespace ZXing; namespace nb = nanobind; using namespace nb::literals; // to bring in the `_a` literal static void deprecation_warning(std::string_view msg) { auto warnings = nb::module_::import_("warnings"); auto builtins = nb::module_::import_("builtins"); warnings.attr("warn")(msg, builtins.attr("DeprecationWarning")); } std::string ToString(nb::dlpack::dtype dt) { std::string res; if (dt.code == (uint8_t)nb::dlpack::dtype_code::Bool) { res = "bool"; } else { switch (dt.code) { case (uint8_t)nb::dlpack::dtype_code::Int: res = "int"; break; case (uint8_t)nb::dlpack::dtype_code::UInt: res = "uint"; break; case (uint8_t)nb::dlpack::dtype_code::Float: res = "float"; break; case (uint8_t)nb::dlpack::dtype_code::Complex: res = "complex"; break; } res += std::to_string(dt.bits); } return res; } // MARK: - Buffer protocol struct ImageViewPyBufferInfo { Py_ssize_t shape[3]; Py_ssize_t strides[3]; }; static int ImageView_getbuffer(PyObject* obj, Py_buffer* view, int /*flags*/) { const ImageView& iv = nb::isinstance(nb::handle(obj)) ? nb::cast(nb::handle(obj)) : nb::cast(nb::handle(obj)); auto* info = new ImageViewPyBufferInfo{{iv.height(), iv.width(), PixStride(iv.format())}, {iv.rowStride(), iv.pixStride(), 1}}; view->obj = obj; view->buf = const_cast(iv.data()); view->len = iv.rowStride() * iv.height(); view->itemsize = sizeof(uint8_t); view->readonly = true; view->ndim = iv.format() == ImageFormat::Lum ? 2 : 3; view->format = const_cast("B"); view->shape = info->shape; view->strides = info->strides; view->suboffsets = nullptr; view->internal = info; Py_INCREF(obj); return 0; } static void ImageView_releasebuffer(PyObject*, Py_buffer* view) { delete static_cast(view->internal); } static PyType_Slot ImageView_slots[] = { {Py_bf_getbuffer, (void*)ImageView_getbuffer}, {Py_bf_releasebuffer, (void*)ImageView_releasebuffer}, {0, nullptr} }; // MARK: - Reader auto read_barcodes_impl(nb::object _image, const BarcodeFormats& formats, bool try_rotate, bool try_downscale, bool try_invert, TextMode text_mode, Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol, bool return_errors, uint8_t max_number_of_symbols = 0xff) { const auto opts = ReaderOptions() .formats(formats) .tryRotate(try_rotate) .tryDownscale(try_downscale) .tryInvert(try_invert) .textMode(text_mode) .binarizer(binarizer) .isPure(is_pure) .maxNumberOfSymbols(max_number_of_symbols) .eanAddOnSymbol(ean_add_on_symbol) .returnErrors(return_errors); if (nb::isinstance(_image)) { // Disables the GIL during zxing processing (restored automatically upon completion) nb::gil_scoped_release release; return ReadBarcodes(nb::cast(_image), opts); } const auto _type = nb::str(_image.type()); nb::ndarray arr; ImageFormat imgfmt = ImageFormat::None; try { if (nb::hasattr(_image, "__array_interface__")) { if (Contains(_type.c_str(), "PIL.")) { // TODO: make sure we don't leak memory (anymore), see https://github.com/zxing-cpp/zxing-cpp/discussions/762 // TODO: make sure we don't make unnecessary copies of the data, see https://uploadcare.com/blog/fast-import-of-pillow-images-to-numpy-opencv-arrays/ _image.attr("load")(); const auto mode = nb::cast(_image.attr("mode")); if (mode == "L") imgfmt = ImageFormat::Lum; else if (mode == "RGB") imgfmt = ImageFormat::RGB; else if (mode == "RGBA") imgfmt = ImageFormat::RGBA; else { // Unsupported mode in ImageFormat. Let's do conversion to L mode with PIL. _image = _image.attr("convert")("L"); imgfmt = ImageFormat::Lum; } } auto ai = nb::cast(_image.attr("__array_interface__")); auto shape = nb::cast>(ai["shape"]); auto typestr = nb::cast(ai["typestr"]); if (typestr != "|u1") nb::raise_type_error("Incompatible __array_interface__ data type (%s): expected a uint8_t array (|u1).", typestr.c_str()); if (ai.contains("data")) { auto adata = ai["data"]; if (nb::try_cast(adata, arr)) { // PIL and our own __array_interface__ passes data as a buffer/bytes object // PIL's bytes object has wrong dim/shape/strides info if (arr.ndim() != shape.size()) arr = nb::ndarray{arr.data(), shape.size(), shape.data(), nb::find(arr), nullptr, nb::dtype()}; } else if (nb::isinstance(adata)) { auto data_tuple = nb::cast(adata); // numpy data is passed as a tuple auto strides = std::vector{}; if (ai.contains("strides") && !ai["strides"].is_none()) strides = nb::cast>(ai["strides"]); auto data_ptr = reinterpret_cast(nb::cast(nb::cast(adata)[0])); arr = nb::ndarray{data_ptr, shape.size(), shape.data(), nb::find(_image), strides.empty() ? nullptr : strides.data(), nb::dtype()}; } else if (!nb::try_cast(_image, arr)) { nb::raise_type_error("No way to get data from __array_interface__"); } } else { arr = nb::cast>(_image); } } else if (Contains(_type.c_str(), "QtGui.QImage")) { const std::string format = nb::cast(nb::str(_image.attr("format")())); if (format.ends_with("Format_ARGB32") || format.ends_with("Format_RGB32")) { if constexpr (std::endian::native == std::endian::little) imgfmt = ImageFormat::BGRA; else imgfmt = ImageFormat::ARGB; } else if (format.ends_with("Format_RGBA8888")) imgfmt = ImageFormat::RGBA; else if (format.ends_with("Format_RGB888")) imgfmt = ImageFormat::RGB; else if (format.ends_with("Format_BGR888")) imgfmt = ImageFormat::BGR; else if (format.ends_with("Format_Grayscale8")) imgfmt = ImageFormat::Lum; else { _image = _image.attr("convertToFormat")(24); // 24 is Format_Greyscale8 imgfmt = ImageFormat::Lum; } arr = nb::cast>(_image.attr("constBits")()); arr = nb::ndarray{arr.data(), {nb::cast(_image.attr("height")()), nb::cast(_image.attr("width")()), static_cast(PixStride(imgfmt))}, nb::find(arr), {nb::cast(_image.attr("bytesPerLine")()), PixStride(imgfmt), 1}, nb::dtype()}; } else { arr = nb::cast>(_image); } } catch (nb::python_error& e) { nb::raise_from(e, PyExc_TypeError, "Invalid input: %s does not support the buffer protocol.", _type.c_str()); } catch (...) { nb::raise_type_error("Invalid input: %s does not support the buffer protocol.", _type.c_str()); } if (arr.dtype() != nb::dtype()) nb::raise_type_error("Incompatible buffer format '%s': expected a uint8_t array.", ToString(arr.dtype()).c_str()); if (arr.ndim() != 2 && arr.ndim() != 3) nb::raise_type_error("Incompatible buffer dimension %s (needs to be 2 or 3).", std::to_string(arr.ndim()).c_str()); const auto height = narrow_cast(arr.shape(0)); const auto width = narrow_cast(arr.shape(1)); const auto channels = arr.ndim() == 2 ? 1 : narrow_cast(arr.shape(2)); const auto rowStride = narrow_cast(arr.stride(0)); const auto pixStride = narrow_cast(arr.stride(1)); if (imgfmt == ImageFormat::None) { // Assume grayscale or BGR image depending on channels number if (channels == 1) imgfmt = ImageFormat::Lum; else if (channels == 3) imgfmt = ImageFormat::BGR; else throw nb::value_error(("Unsupported number of channels for buffer: " + std::to_string(channels)).c_str()); } const auto bytes = static_cast(arr.data()); // Disables the GIL during zxing processing (restored automatically upon completion) nb::gil_scoped_release release; return ReadBarcodes({bytes, width, height, imgfmt, rowStride, pixStride}, opts); } std::optional read_barcode(nb::object _image, const BarcodeFormats& formats, bool try_rotate, bool try_downscale, bool try_invert, TextMode text_mode, Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol, bool return_errors) { auto res = read_barcodes_impl(_image, formats, try_rotate, try_downscale, try_invert, text_mode, binarizer, is_pure, ean_add_on_symbol, return_errors, 1); return res.empty() ? std::nullopt : std::optional(res.front()); } Barcodes read_barcodes(nb::object _image, const BarcodeFormats& formats, bool try_rotate, bool try_downscale, bool try_invert, TextMode text_mode, Binarizer binarizer, bool is_pure, EanAddOnSymbol ean_add_on_symbol, bool return_errors) { return read_barcodes_impl(_image, formats, try_rotate, try_downscale, try_invert, text_mode, binarizer, is_pure, ean_add_on_symbol, return_errors); } // MARK: - Writer Barcode create_barcode(nb::object content, BarcodeFormat format, const nb::kwargs& kwargs) { auto cOpts = CreatorOptions(format, nb::cast(nb::str(kwargs))); // see https://github.com/pybind/pybind11/issues/5938 if (nb::isinstance(content)) return CreateBarcodeFromText(nb::cast(content), cOpts); else if (nb::isinstance(content)) { // TODO: fix CreateBarcodeFromBytes overload auto content_bytes = nb::cast(content); return CreateBarcodeFromBytes(content_bytes.data(), content_bytes.size(), cOpts); } else nb::raise_type_error("Invalid input: only 'str' and 'bytes' supported."); } Image write_barcode_to_image(Barcode barcode, int scale, bool add_hrt, bool add_quiet_zones) { return WriteBarcodeToImage(barcode, WriterOptions().scale(scale).addHRT(add_hrt).addQuietZones(add_quiet_zones)); } std::string write_barcode_to_svg(Barcode barcode, int scale, bool add_hrt, bool add_quiet_zones) { return WriteBarcodeToSVG(barcode, WriterOptions().scale(scale).addHRT(add_hrt).addQuietZones(add_quiet_zones)); } Image write_barcode(BarcodeFormat format, nb::object content, int width, int height, int quiet_zone, int ec_level) { deprecation_warning("write_barcode() is deprecated, use create_barcode() and write_barcode_to_image() instead."); #ifdef ZXING_USE_ZINT ec_level = format & BarcodeFormat::QRCode ? ec_level / 2 : ec_level; // Zint uses 0-4 for QR code EC level #endif nb::kwargs args; args["ec_level"] = ec_level; auto barcode = create_barcode(content, format, args); return write_barcode_to_image(barcode, -std::max(width, height), false, quiet_zone != 0); } // MARK: - Python NB_MODULE(zxingcpp, m) { m.doc() = "python bindings for zxing-cpp"; // forward declaration of BarcodeFormats to fix BarcodeFormat function header typings // see https://github.com/zxing-cpp/zxing-cpp/pull/271 nb::class_ pyBarcodeFormats(m, "BarcodeFormats"); // MARK: - Enums nb::enum_(m, "BarcodeFormat", nb::is_arithmetic{}, "Enumeration of zxing supported barcode formats") #define X(NAME, SYM, VAR, FLAGS, ZINT, ENABLED, HRI) .value(#NAME, BarcodeFormat::NAME) ZX_BCF_LIST(X) #undef X // use upper case 'NONE' because 'None' is a reserved identifier in python .value("NONE", BarcodeFormat::None) .value("DataBarExpanded", BarcodeFormat::DataBarExp) // backward compatibility alias .value("DataBarLimited", BarcodeFormat::DataBarLtd) // backward compatibility alias .value("LinearCodes", BarcodeFormat::AllLinear) // backward compatibility alias .value("MatrixCodes", BarcodeFormat::AllMatrix) // backward compatibility alias .export_values() // see https://github.com/pybind/pybind11/issues/2221 .def("__or__", [](BarcodeFormat f1, BarcodeFormat f2) { deprecation_warning("operator | is deprecated, pass array or tuple instead."); return BarcodeFormats(f1 | f2); }) .def("__str__", [](BarcodeFormat f) { return ToString(f); }) .def_prop_ro("symbology", [](BarcodeFormat f) { return Symbology(f); }); pyBarcodeFormats .def("__repr__", [](const BarcodeFormats& f) { return ToString(f); }) .def("__eq__", [](const BarcodeFormats& f1, const BarcodeFormats& f2) { return f1 == f2; }) .def("__or__", [](const BarcodeFormats& fs, BarcodeFormat f) { deprecation_warning("operator | is deprecated, pass array or tuple instead."); auto res = std::vector(fs.begin(), fs.end()); res.push_back(f); return BarcodeFormats(std::move(res)); }) .def("__len__", [](const BarcodeFormats& fs) { return static_cast(fs.size()); }) .def( "__iter__", [](const BarcodeFormats& fs) { return nb::make_iterator(nb::type(), "Iterator", fs); }, nb::keep_alive<0, 1>()) .def("__getitem__", [](const BarcodeFormats& fs, nb::ssize_t idx) { if (idx < 0) idx += static_cast(fs.size()); if (idx < 0 || idx >= static_cast(fs.size())) throw nb::index_error("BarcodeFormats index out of range"); return *(fs.begin() + idx); }) .def(nb::init_implicit()) .def("__init__", [](BarcodeFormats* self, std::vector vec) { new (self) BarcodeFormats(std::move(vec)); }); nb::implicitly_convertible(); nb::implicitly_convertible(); nb::enum_(m, "Binarizer", "Enumeration of binarizers used before decoding images") .value("BoolCast", Binarizer::BoolCast) .value("FixedThreshold", Binarizer::FixedThreshold) .value("GlobalHistogram", Binarizer::GlobalHistogram) .value("LocalAverage", Binarizer::LocalAverage) .export_values(); nb::enum_(m, "EanAddOnSymbol", "Enumeration of options for EAN-2/5 add-on symbols check") .value("Ignore", EanAddOnSymbol::Ignore, "Ignore any Add-On symbol during read/scan") .value("Read", EanAddOnSymbol::Read, "Read EAN-2/EAN-5 Add-On symbol if found") .value("Require", EanAddOnSymbol::Require, "Require EAN-2/EAN-5 Add-On symbol to be present") .export_values(); nb::enum_(m, "ContentType", "Enumeration of content types") .value("Text", ContentType::Text) .value("Binary", ContentType::Binary) .value("Mixed", ContentType::Mixed) .value("GS1", ContentType::GS1) .value("ISO15434", ContentType::ISO15434) .value("UnknownECI", ContentType::UnknownECI) .export_values(); nb::enum_(m, "TextMode", "") .value("Plain", TextMode::Plain, "bytes() transcoded to unicode based on ECI info or guessed charset (the default mode prior to 2.0)") .value("ECI", TextMode::ECI, "standard content following the ECI protocol with every character set ECI segment transcoded to unicode") .value("HRI", TextMode::HRI, "Human Readable Interpretation (dependent on the ContentType)") .value("Escaped", TextMode::Escaped, "Use the EscapeNonGraphical() function (e.g. ASCII 29 will be transcoded to ''") .value("Hex", TextMode::Hex, "bytes() transcoded to ASCII string of HEX values") .value("HexECI", TextMode::HexECI, "bytesECI() transcoded to ASCII string of HEX values") .export_values(); nb::enum_(m, "ImageFormat", "Enumeration of image formats supported by read_barcodes") .value("Lum", ImageFormat::Lum) .value("LumA", ImageFormat::LumA) .value("RGB", ImageFormat::RGB) .value("BGR", ImageFormat::BGR) .value("RGBA", ImageFormat::RGBA) .value("ARGB", ImageFormat::ARGB) .value("BGRA", ImageFormat::BGRA) .value("ABGR", ImageFormat::ABGR) .export_values(); // MARK: - Classes nb::class_(m, "Point", "Represents the coordinates of a point in an image", nb::is_final{}) .def_ro("x", &PointI::x, ":return: horizontal coordinate of the point\n" ":rtype: int") .def_ro("y", &PointI::y, ":return: vertical coordinate of the point\n" ":rtype: int"); nb::class_(m, "Position", "The position of a decoded symbol", nb::is_final{}) .def_prop_ro("top_left", &Position::topLeft, ":return: coordinate of the symbol's top-left corner\n" ":rtype: zxingcpp.Point") .def_prop_ro("top_right", &Position::topRight, ":return: coordinate of the symbol's top-right corner\n" ":rtype: zxingcpp.Point") .def_prop_ro("bottom_left", &Position::bottomLeft, ":return: coordinate of the symbol's bottom-left corner\n" ":rtype: zxingcpp.Point") .def_prop_ro("bottom_right", &Position::bottomRight, ":return: coordinate of the symbol's bottom-right corner\n" ":rtype: zxingcpp.Point") .def("__str__", [](Position pos) { return ToString(pos); }); nb::enum_(m, "ErrorType", "") .value("None", Error::Type::None, "No error") .value("Format", Error::Type::Format, "Data format error") .value("Checksum", Error::Type::Checksum, "Checksum error") .value("Unsupported", Error::Type::Unsupported, "Unsupported content error") .export_values(); nb::class_(m, "Error", "Barcode reading error", nb::is_final{}) .def_prop_ro("type", &Error::type, ":return: Error type\n" ":rtype: zxingcpp.ErrorType") .def_prop_ro("message", &Error::msg, ":return: Error message\n" ":rtype: str") .def("__str__", [](Error e) { return ToString(e); }); nb::class_(m, "Barcode", "The Barcode class", nb::dynamic_attr{}, nb::is_final{}) .def_prop_ro("valid", &Barcode::isValid, ":return: whether or not barcode is valid (i.e. a symbol was found and decoded)\n" ":rtype: bool") .def_prop_ro("text", [](const Barcode& res) { return res.text(); }, ":return: text of the decoded symbol (see also TextMode parameter)\n" ":rtype: str") .def_prop_ro("bytes", [](const Barcode& res) { return nb::bytes(res.bytes().data(), res.bytes().size()); }, ":return: uninterpreted bytes of the decoded symbol\n" ":rtype: bytes") .def_prop_ro("format", &Barcode::format, ":return: decoded symbol format\n" ":rtype: zxingcpp.BarcodeFormat") .def_prop_ro("symbology", &Barcode::symbology, ":return: decoded symbol symbology\n" ":rtype: zxingcpp.BarcodeFormat") .def_prop_ro("symbology_identifier", &Barcode::symbologyIdentifier, ":return: decoded symbology idendifier\n" ":rtype: str") .def_prop_ro("ec_level", &Barcode::ecLevel, ":return: error correction level of the symbol (empty string if not applicable)\n" ":rtype: str") .def_prop_ro("content_type", &Barcode::contentType, ":return: content type of symbol\n" ":rtype: zxingcpp.ContentType") .def_prop_ro("position", &Barcode::position, ":return: position of the decoded symbol\n" ":rtype: zxingcpp.Position") .def_prop_ro("orientation", &Barcode::orientation, ":return: orientation (in degree) of the decoded symbol\n" ":rtype: int") .def_prop_ro( "error", [](const Barcode& res) { return res.error() ? std::optional(res.error()) : std::nullopt; }, ":return: Error code or None\n" ":rtype: zxingcpp.Error") .def_prop_ro( "extra", [](nb::object self) -> nb::object { if (nb::hasattr(self, "_cached_extra")) return self.attr("_cached_extra"); const auto extra = nb::cast(self).extra(); if (extra.empty()) { self.attr("_cached_extra") = nb::none(); } else { try { auto json = nb::module_::import_("json"); auto parsed = json.attr("loads")(extra); self.attr("_cached_extra") = parsed; } catch (nb::python_error& e) { throw nb::value_error((std::string("Invalid JSON in Barcode::extra(): ") + e.what()).c_str()); } } return self.attr("_cached_extra"); }, ":return: Symbology specific extra information as a Python dictionary (might be empty)\n" ":rtype: dict") .def("to_image", &write_barcode_to_image, nb::arg("scale") = 1, nb::arg("add_hrt") = false, nb::arg("add_quiet_zones") = true) .def("to_svg", &write_barcode_to_svg, nb::arg("scale") = 1, nb::arg("add_hrt") = false, nb::arg("add_quiet_zones") = true) ; m.attr("Result") = m.attr("Barcode"); // alias to deprecated name for the Barcode class // MARK: - Functions m.def("barcode_format_from_str", &BarcodeFormatFromString, nb::arg("str"), "Convert string to BarcodeFormat\n\n" ":type str: str\n" ":param str: string representing barcode format\n" ":return: corresponding barcode format\n" ":rtype: zxingcpp.BarcodeFormat"); m.def("barcode_formats_from_str", &BarcodeFormatsFromString, nb::arg("str"), "Convert string to BarcodeFormats\n\n" ":type str: str\n" ":param str: string representing a list of barcodes formats\n" ":return: corresponding barcode formats\n" ":rtype: zxingcpp.BarcodeFormats"); m.def("barcode_formats_list", &BarcodeFormats::list, nb::arg("filter") = BarcodeFormats{}, "Returns a list of available/supported barcode formats, optionally filtered by the provided format(s).\n\n" ":type filter: zxingcpp.BarcodeFormats\n" ":param filter: the BarcodeFormat(s) to filter by\n" ":return: list of available/supported barcode formats (optionally filtered)\n" ":rtype: list[zxingcpp.BarcodeFormat]"); m.def("read_barcode", &read_barcode, nb::arg("image"), nb::arg("formats") = BarcodeFormats{}, nb::arg("try_rotate") = true, nb::arg("try_downscale") = true, nb::arg("try_invert") = true, nb::arg("text_mode") = TextMode::HRI, nb::arg("binarizer") = Binarizer::LocalAverage, nb::arg("is_pure") = false, nb::arg("ean_add_on_symbol") = EanAddOnSymbol::Ignore, nb::arg("return_errors") = false, "Read (decode) a barcode from a numpy BGR or grayscale image array or from a PIL image.\n\n" ":type image: buffer|numpy.ndarray|PIL.Image.Image\n" ":param image: The image object to decode. The image can be either:\n" " - a buffer with the correct shape, use .cast on memory view to convert\n" " - a numpy array containing image either in grayscale (1 byte per pixel) or BGR mode (3 bytes per pixel)\n" " - a PIL Image\n" " - a QtGui.QImage\n" " - a zxingcpp.ImageView object, which is effectively a memory view but with custom strides and ImageFormat\n" ":type formats: zxing.BarcodeFormat|zxing.BarcodeFormats\n" ":param formats: the format(s) to decode. If ``None``, decode all formats.\n" ":type try_rotate: bool\n" ":param try_rotate: if ``True`` (the default), decoder searches for barcodes in any direction; \n" " if ``False``, it will not search for 90° / 270° rotated barcodes.\n" ":type try_downscale: bool\n" ":param try_downscale: if ``True`` (the default), decoder also scans downscaled versions of the input; \n" " if ``False``, it will only search in the resolution provided.\n" ":type try_invert: bool\n" ":param try_invert: if ``True`` (the default), decoder also tries inverted (light on dark) barcodes.\n" ":type text_mode: zxing.TextMode\n" ":param text_mode: specifies the TextMode that governs how the raw bytes content is transcoded to text.\n" " Defaults to :py:attr:`zxing.TextMode.HRI`." ":type binarizer: zxing.Binarizer\n" ":param binarizer: the binarizer used to convert image before decoding barcodes.\n" " Defaults to :py:attr:`zxing.Binarizer.LocalAverage`." ":type is_pure: bool\n" ":param is_pure: Set to True if the input contains nothing but a perfectly aligned barcode (generated image).\n" " Speeds up detection in that case. Default is False." ":type ean_add_on_symbol: zxing.EanAddOnSymbol\n" ":param ean_add_on_symbol: Specify whether to Ignore, Read or Require EAN-2/5 add-on symbols while scanning \n" " EAN/UPC codes. Default is ``Ignore``.\n" ":type return_errors: bool\n" ":param return_errors: Set to True to return the barcodes with errors as well (e.g. checksum errors); see ``Barcode.error``.\n" " Default is False." ":rtype: zxingcpp.Barcode\n" ":return: a Barcode if found, None otherwise" ); m.def("read_barcodes", &read_barcodes, nb::arg("image"), nb::arg("formats") = BarcodeFormats{}, nb::arg("try_rotate") = true, nb::arg("try_downscale") = true, nb::arg("try_invert") = true, nb::arg("text_mode") = TextMode::HRI, nb::arg("binarizer") = Binarizer::LocalAverage, nb::arg("is_pure") = false, nb::arg("ean_add_on_symbol") = EanAddOnSymbol::Ignore, nb::arg("return_errors") = false, "Read (decode) multiple barcodes from a numpy BGR or grayscale image array or from a PIL image.\n\n" ":type image: buffer|numpy.ndarray|PIL.Image.Image\n" ":param image: The image object to decode. The image can be either:\n" " - a buffer with the correct shape, use .cast on memoryview to convert\n" " - a numpy array containing image either in grayscale (1 byte per pixel) or BGR mode (3 bytes per pixel)\n" " - a PIL Image\n" " - a QtGui.QImage\n" " - a zxingcpp.ImageView object, which is effectively a memory view but with custom strides and ImageFormat\n" ":type formats: zxing.BarcodeFormat|zxing.BarcodeFormats\n" ":param formats: the format(s) to decode. If ``None``, decode all formats.\n" ":type try_rotate: bool\n" ":param try_rotate: if ``True`` (the default), decoder searches for barcodes in any direction; \n" " if ``False``, it will not search for 90° / 270° rotated barcodes.\n" ":type try_downscale: bool\n" ":param try_downscale: if ``True`` (the default), decoder also scans downscaled versions of the input; \n" " if ``False``, it will only search in the resolution provided.\n" ":type try_invert: bool\n" ":param try_invert: if ``True`` (the default), decoder also tries inverted (light on dark) barcodes.\n" ":type text_mode: zxing.TextMode\n" ":param text_mode: specifies the TextMode that governs how the raw bytes content is transcoded to text.\n" " Defaults to :py:attr:`zxing.TextMode.HRI`." ":type binarizer: zxing.Binarizer\n" ":param binarizer: the binarizer used to convert image before decoding barcodes.\n" " Defaults to :py:attr:`zxing.Binarizer.LocalAverage`." ":type is_pure: bool\n" ":param is_pure: Set to True if the input contains nothing but a perfectly aligned barcode (generated image).\n" " Speeds up detection in that case. Default is False." ":type ean_add_on_symbol: zxing.EanAddOnSymbol\n" ":param ean_add_on_symbol: Specify whether to Ignore, Read or Require EAN-2/5 add-on symbols while scanning \n" " EAN/UPC codes. Default is ``Ignore``.\n" ":type return_errors: bool\n" ":param return_errors: Set to True to return the barcodes with errors as well (e.g. checksum errors); see ``Barcode.error``.\n" " Default is False.\n" ":rtype: list[zxingcpp.Barcode]\n" ":return: a list of Barcodes, the list is empty if none is found" ); nb::class_(m, "Image", nb::type_slots(ImageView_slots), nb::is_final{}) .def_prop_ro("__array_interface__", [](const Image& image) { auto d = nb::dict(); d["version"] = 3; d["data"] = ℑ d["shape"] = nb::make_tuple(image.height(), image.width()); d["typestr"] = "|u1"; return d; }) .def_prop_ro("shape", [](const Image& m) { return nb::make_tuple(m.height(), m.width()); }) ; m.def("create_barcode", &create_barcode, nb::arg("content"), nb::arg("format"), nb::arg("**kwargs") ); m.def("write_barcode_to_image", &write_barcode_to_image, nb::arg("barcode"), nb::arg("scale") = 1, nb::arg("add_hrt") = false, nb::arg("add_quiet_zones") = true ); m.def("write_barcode_to_svg", &write_barcode_to_svg, nb::arg("barcode"), nb::arg("scale") = 1, nb::arg("add_hrt") = false, nb::arg("add_quiet_zones") = true ); nb::class_(m, "ImageView", nb::type_slots(ImageView_slots), nb::is_final{}) .def("__init__", [](ImageView* self, nb::ndarray buffer, int width, int height, ImageFormat format, int rowStride, int pixStride) { if (buffer.dtype() != nb::dtype()) nb::raise_type_error("Incompatible buffer format '%s': expected a uint8_t array.", ToString(buffer.dtype()).c_str()); new (self) ImageView(static_cast(buffer.data()), buffer.size(), width, height, format, rowStride, pixStride); }, nb::arg("buffer"), nb::arg("width"), nb::arg("height"), nb::arg("format"), nb::arg("row_stride") = 0, nb::arg("pix_stride") = 0) .def_prop_ro("format", [](const ImageView& iv) { return iv.format(); }) ; m.attr("Bitmap") = m.attr("Image"); // alias to deprecated name for the Image class m.def("write_barcode", &write_barcode, nb::arg("format"), nb::arg("text"), nb::arg("width") = 0, nb::arg("height") = 0, nb::arg("quiet_zone") = -1, nb::arg("ec_level") = -1, "Write (encode) a text into a barcode and return 8-bit grayscale bitmap buffer\n\n" ":type format: zxing.BarcodeFormat\n" ":param format: format of the barcode to create\n" ":type text: str|bytes\n" ":param text: the text/content of the barcode. A str is encoded as utf8 text and bytes as binary data\n" ":type width: int\n" ":param width: width (in pixels) of the barcode to create. If undefined (or set to 0), barcode will be\n" " created with the minimum possible width\n" ":type height: int\n" ":param height: height (in pixels) of the barcode to create. If undefined (or set to 0), barcode will be\n" " created with the minimum possible height\n" ":type quiet_zone: int\n" ":param quiet_zone: minimum size (in pixels) of the quiet zone around barcode. If undefined (or set to -1), \n" " the minimum quiet zone of respective barcode is used." ":type ec_level: int\n" ":param ec_level: error correction level of the barcode (Used for Aztec, PDF417, and QRCode only).\n" ":rtype: zxingcpp.Bitmap\n" ); }