Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/zxing-cpp/core/src/ReadBarcode.cpp @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /* | |
| 2 * Copyright 2019 Axel Waggershauser | |
| 3 */ | |
| 4 // SPDX-License-Identifier: Apache-2.0 | |
| 5 | |
| 6 #include "ReadBarcode.h" | |
| 7 | |
| 8 #if !defined(ZXING_READERS) && !defined(ZXING_WRITERS) | |
| 9 #include "Version.h" | |
| 10 #endif | |
| 11 | |
| 12 #ifdef ZXING_READERS | |
| 13 #include "GlobalHistogramBinarizer.h" | |
| 14 #include "HybridBinarizer.h" | |
| 15 #include "MultiFormatReader.h" | |
| 16 #include "Pattern.h" | |
| 17 #include "ThresholdBinarizer.h" | |
| 18 #endif | |
| 19 | |
| 20 #include <climits> | |
| 21 #include <memory> | |
| 22 #include <stdexcept> | |
| 23 | |
| 24 namespace ZXing { | |
| 25 | |
| 26 #ifdef ZXING_READERS | |
| 27 | |
| 28 class LumImage : public Image | |
| 29 { | |
| 30 public: | |
| 31 using Image::Image; | |
| 32 | |
| 33 uint8_t* data() { return const_cast<uint8_t*>(Image::data()); } | |
| 34 }; | |
| 35 | |
| 36 template<typename P> | |
| 37 static LumImage ExtractLum(const ImageView& iv, P projection) | |
| 38 { | |
| 39 LumImage res(iv.width(), iv.height()); | |
| 40 | |
| 41 auto* dst = res.data(); | |
| 42 for(int y = 0; y < iv.height(); ++y) | |
| 43 for(int x = 0, w = iv.width(); x < w; ++x) | |
| 44 *dst++ = projection(iv.data(x, y)); | |
| 45 | |
| 46 return res; | |
| 47 } | |
| 48 | |
| 49 class LumImagePyramid | |
| 50 { | |
| 51 std::vector<LumImage> buffers; | |
| 52 | |
| 53 template<int N> | |
| 54 void addLayer() | |
| 55 { | |
| 56 auto siv = layers.back(); | |
| 57 buffers.emplace_back(siv.width() / N, siv.height() / N); | |
| 58 layers.push_back(buffers.back()); | |
| 59 auto& div = buffers.back(); | |
| 60 auto* d = div.data(); | |
| 61 | |
| 62 for (int dy = 0; dy < div.height(); ++dy) | |
| 63 for (int dx = 0; dx < div.width(); ++dx) { | |
| 64 int sum = (N * N) / 2; | |
| 65 for (int ty = 0; ty < N; ++ty) | |
| 66 for (int tx = 0; tx < N; ++tx) | |
| 67 sum += *siv.data(dx * N + tx, dy * N + ty); | |
| 68 *d++ = sum / (N * N); | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 void addLayer(int factor) | |
| 73 { | |
| 74 // help the compiler's auto-vectorizer by hard-coding the scale factor | |
| 75 switch (factor) { | |
| 76 case 2: addLayer<2>(); break; | |
| 77 case 3: addLayer<3>(); break; | |
| 78 case 4: addLayer<4>(); break; | |
| 79 default: throw std::invalid_argument("Invalid ReaderOptions::downscaleFactor"); break; | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 public: | |
| 84 std::vector<ImageView> layers; | |
| 85 | |
| 86 LumImagePyramid(const ImageView& iv, int threshold, int factor) | |
| 87 { | |
| 88 if (factor < 2) | |
| 89 throw std::invalid_argument("Invalid ReaderOptions::downscaleFactor"); | |
| 90 | |
| 91 layers.push_back(iv); | |
| 92 // TODO: if only matrix codes were considered, then using std::min would be sufficient (see #425) | |
| 93 while (threshold > 0 && std::max(layers.back().width(), layers.back().height()) > threshold && | |
| 94 std::min(layers.back().width(), layers.back().height()) >= factor) | |
| 95 addLayer(factor); | |
| 96 #if 0 | |
| 97 // Reversing the layers means we'd start with the smallest. that can make sense if we are only looking for a | |
| 98 // single symbol. If we start with the higher resolution, we get better (high res) position information. | |
| 99 // TODO: see if masking out higher res layers based on found symbols in lower res helps overall performance. | |
| 100 std::reverse(layers.begin(), layers.end()); | |
| 101 #endif | |
| 102 } | |
| 103 }; | |
| 104 | |
| 105 ImageView SetupLumImageView(ImageView iv, LumImage& lum, const ReaderOptions& opts) | |
| 106 { | |
| 107 if (iv.format() == ImageFormat::None) | |
| 108 throw std::invalid_argument("Invalid image format"); | |
| 109 | |
| 110 if (opts.binarizer() == Binarizer::GlobalHistogram || opts.binarizer() == Binarizer::LocalAverage) { | |
| 111 // manually spell out the 3 most common pixel formats to get at least gcc to vectorize the code | |
| 112 if (iv.format() == ImageFormat::RGB && iv.pixStride() == 3) { | |
| 113 lum = ExtractLum(iv, [](const uint8_t* src) { return RGBToLum(src[0], src[1], src[2]); }); | |
| 114 } else if (iv.format() == ImageFormat::RGBA && iv.pixStride() == 4) { | |
| 115 lum = ExtractLum(iv, [](const uint8_t* src) { return RGBToLum(src[0], src[1], src[2]); }); | |
| 116 } else if (iv.format() == ImageFormat::BGR && iv.pixStride() == 3) { | |
| 117 lum = ExtractLum(iv, [](const uint8_t* src) { return RGBToLum(src[2], src[1], src[0]); }); | |
| 118 } else if (iv.format() != ImageFormat::Lum) { | |
| 119 lum = ExtractLum(iv, [r = RedIndex(iv.format()), g = GreenIndex(iv.format()), b = BlueIndex(iv.format())]( | |
| 120 const uint8_t* src) { return RGBToLum(src[r], src[g], src[b]); }); | |
| 121 } else if (iv.pixStride() != 1) { | |
| 122 // GlobalHistogram and LocalAverage need dense line memory layout | |
| 123 lum = ExtractLum(iv, [](const uint8_t* src) { return *src; }); | |
| 124 } | |
| 125 if (lum.data()) | |
| 126 return lum; | |
| 127 } | |
| 128 return iv; | |
| 129 } | |
| 130 | |
| 131 std::unique_ptr<BinaryBitmap> CreateBitmap(ZXing::Binarizer binarizer, const ImageView& iv) | |
| 132 { | |
| 133 switch (binarizer) { | |
| 134 case Binarizer::BoolCast: return std::make_unique<ThresholdBinarizer>(iv, 0); | |
| 135 case Binarizer::FixedThreshold: return std::make_unique<ThresholdBinarizer>(iv, 127); | |
| 136 case Binarizer::GlobalHistogram: return std::make_unique<GlobalHistogramBinarizer>(iv); | |
| 137 case Binarizer::LocalAverage: return std::make_unique<HybridBinarizer>(iv); | |
| 138 } | |
| 139 return {}; // silence gcc warning | |
| 140 } | |
| 141 | |
| 142 Barcode ReadBarcode(const ImageView& _iv, const ReaderOptions& opts) | |
| 143 { | |
| 144 return FirstOrDefault(ReadBarcodes(_iv, ReaderOptions(opts).setMaxNumberOfSymbols(1))); | |
| 145 } | |
| 146 | |
| 147 Barcodes ReadBarcodes(const ImageView& _iv, const ReaderOptions& opts) | |
| 148 { | |
| 149 if (sizeof(PatternType) < 4 && (_iv.width() > 0xffff || _iv.height() > 0xffff)) | |
| 150 throw std::invalid_argument("Maximum image width/height is 65535"); | |
| 151 | |
| 152 if (!_iv.data() || _iv.width() * _iv.height() == 0) | |
| 153 throw std::invalid_argument("ImageView is null/empty"); | |
| 154 | |
| 155 LumImage lum; | |
| 156 ImageView iv = SetupLumImageView(_iv, lum, opts); | |
| 157 MultiFormatReader reader(opts); | |
| 158 | |
| 159 if (opts.isPure()) | |
| 160 return {reader.read(*CreateBitmap(opts.binarizer(), iv)).setReaderOptions(opts)}; | |
| 161 | |
| 162 std::unique_ptr<MultiFormatReader> closedReader; | |
| 163 #ifdef ZXING_EXPERIMENTAL_API | |
| 164 auto formatsBenefittingFromClosing = BarcodeFormat::Aztec | BarcodeFormat::DataMatrix | BarcodeFormat::QRCode | BarcodeFormat::MicroQRCode; | |
| 165 ReaderOptions closedOptions = opts; | |
| 166 if (opts.tryDenoise() && opts.hasFormat(formatsBenefittingFromClosing) && _iv.height() >= 3) { | |
| 167 closedOptions.setFormats((opts.formats().empty() ? BarcodeFormat::Any : opts.formats()) & formatsBenefittingFromClosing); | |
| 168 closedReader = std::make_unique<MultiFormatReader>(closedOptions); | |
| 169 } | |
| 170 #endif | |
| 171 LumImagePyramid pyramid(iv, opts.downscaleThreshold() * opts.tryDownscale(), opts.downscaleFactor()); | |
| 172 | |
| 173 Barcodes res; | |
| 174 int maxSymbols = opts.maxNumberOfSymbols() ? opts.maxNumberOfSymbols() : INT_MAX; | |
| 175 for (auto&& iv : pyramid.layers) { | |
| 176 auto bitmap = CreateBitmap(opts.binarizer(), iv); | |
| 177 for (int close = 0; close <= (closedReader ? 1 : 0); ++close) { | |
| 178 if (close) | |
| 179 bitmap->close(); | |
| 180 | |
| 181 // TODO: check if closing after invert would be beneficial | |
| 182 for (int invert = 0; invert <= static_cast<int>(opts.tryInvert() && !close); ++invert) { | |
| 183 if (invert) | |
| 184 bitmap->invert(); | |
| 185 auto rs = (close ? *closedReader : reader).readMultiple(*bitmap, maxSymbols); | |
| 186 for (auto& r : rs) { | |
| 187 if (iv.width() != _iv.width()) | |
| 188 r.setPosition(Scale(r.position(), _iv.width() / iv.width())); | |
| 189 if (!Contains(res, r)) { | |
| 190 r.setReaderOptions(opts); | |
| 191 r.setIsInverted(bitmap->inverted()); | |
| 192 res.push_back(std::move(r)); | |
| 193 --maxSymbols; | |
| 194 } | |
| 195 } | |
| 196 if (maxSymbols <= 0) | |
| 197 return res; | |
| 198 } | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 return res; | |
| 203 } | |
| 204 | |
| 205 #else // ZXING_READERS | |
| 206 | |
| 207 Barcode ReadBarcode(const ImageView&, const ReaderOptions&) | |
| 208 { | |
| 209 throw std::runtime_error("This build of zxing-cpp does not support reading barcodes."); | |
| 210 } | |
| 211 | |
| 212 Barcodes ReadBarcodes(const ImageView&, const ReaderOptions&) | |
| 213 { | |
| 214 throw std::runtime_error("This build of zxing-cpp does not support reading barcodes."); | |
| 215 } | |
| 216 | |
| 217 #endif // ZXING_READERS | |
| 218 | |
| 219 } // ZXing |
