Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"name": "pythoncpp launch python debugger",
"type": "debugpy",
"request": "launch",
"program": "source/python/run.py",
"program": "source/python/filter_keys.py",
"console": "integratedTerminal",
"env": {
"PYTHONPATH": "${workspaceFolder}/build/Debug/bin"
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ to execute the following minimal Python script:
```
from ticket_decoder import DecoderFacade

decoder_facade = DecoderFacade(fail_on_interpreter_error = False)
for result in decoder_facade.decode_files('path/2/your/ticket.pdf'):
print(result[1])
decoder_facade = DecoderFacade()
print(decoder_facade.decode_files('path/2/your/ticket.pdf'))
```

## ticket-analyzer
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

[project]
name = "ticket-decoder"
version = "v0.19.1"
version = "v0.19.3"
description = "Train ticket barcode to json decoding library, using zxing-cpp decoding in combination with record interpreter, to transform content into json structure"
license = "GPL-3.0-or-later"
authors = [ { name = "user4223" } ]
Expand Down
24 changes: 19 additions & 5 deletions source/lib/api/include/DecoderFacade.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <lib/infrastructure/include/ParameterCollector.h>
#include <lib/infrastructure/include/ContextFwd.h>

#include <lib/dip/include/PreProcessorOptions.h>

#include <lib/input/api/include/InputElement.h>
#include <lib/input/api/include/LoadResult.h>

Expand Down Expand Up @@ -100,10 +102,16 @@ namespace api
DecoderFacadeBuilder::Options const &options;

template <typename T>
void decodeImage(input::api::InputElement image, std::function<void(T &&, std::string)> transformer);
void decodeImage(
input::api::InputElement image,
std::optional<dip::PreProcessorOptions> preProcessorOptions,
std::function<void(T &&, std::string)> transformer);

template <typename T>
void decodeImageFiles(std::filesystem::path path, std::function<void(T &&, std::string)> transformer);
void decodeImageFiles(
std::filesystem::path path,
std::optional<dip::PreProcessorOptions> preProcessorOptions,
std::function<void(T &&, std::string)> transformer);

std::string interpretRawBytes(std::vector<std::uint8_t> bytes, std::string origin);

Expand Down Expand Up @@ -144,11 +152,17 @@ namespace api

/* Barcodes from image or PDF input file/directory to json, raw byte-array or raw base64-string
*/
std::vector<std::pair<std::string, std::string>> decodeImageFilesToJson(std::filesystem::path path);
std::vector<std::pair<std::string, std::string>> decodeImageFilesToJson(
std::filesystem::path path,
std::optional<dip::PreProcessorOptions> preProcessorOptions = std::nullopt);

std::vector<std::pair<std::string, std::vector<std::uint8_t>>> decodeImageFilesToRawBytes(std::filesystem::path path);
std::vector<std::pair<std::string, std::vector<std::uint8_t>>> decodeImageFilesToRawBytes(
std::filesystem::path path,
std::optional<dip::PreProcessorOptions> preProcessorOptions = std::nullopt);

std::vector<std::pair<std::string, std::string>> decodeImageFilesToRawBase64(std::filesystem::path path);
std::vector<std::pair<std::string, std::string>> decodeImageFilesToRawBase64(
std::filesystem::path path,
std::optional<dip::PreProcessorOptions> preProcessorOptions = std::nullopt);

/* Pre-loaded image data as input-element to json
*/
Expand Down
40 changes: 26 additions & 14 deletions source/lib/api/source/DecoderFacade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ namespace api
std::optional<unsigned int> loadOptionDpi;
std::optional<int> imageRotation;
std::optional<unsigned int> imageScale;
std::optional<std::string> imageSplit;
std::optional<std::string> imageSplitting;
std::optional<unsigned int> imageFlipping;
std::optional<detector::api::DetectorType> detectorType;
std::optional<bool> pureBarcode;
Expand All @@ -62,7 +62,7 @@ namespace api

unsigned int getImageScale() const { return imageScale.value_or(dip::PreProcessorOptions::DEFAULT.scalePercent); }

std::string getImageSplit() const { return imageSplit.value_or(dip::PreProcessorOptions::DEFAULT.split); }
std::string getImageSplit() const { return imageSplitting.value_or(dip::PreProcessorOptions::DEFAULT.splittingMode); }

unsigned int getImageFlipping() const { return imageFlipping.value_or(dip::PreProcessorOptions::DEFAULT.flippingMode); }

Expand Down Expand Up @@ -199,7 +199,7 @@ namespace api

DecoderFacadeBuilder &DecoderFacadeBuilder::withImageSplit(std::string split)
{
options->imageSplit = std::make_optional(split);
options->imageSplitting = std::make_optional(split);
return *this;
}

Expand Down Expand Up @@ -316,9 +316,12 @@ namespace api
}

template <typename T>
void DecoderFacade::decodeImage(input::api::InputElement inputElement, std::function<void(T &&, std::string)> transformer)
void DecoderFacade::decodeImage(
input::api::InputElement inputElement,
std::optional<dip::PreProcessorOptions> preProcessorOptions,
std::function<void(T &&, std::string)> transformer)
{
auto source = internal->preProcessor.get(std::move(inputElement));
auto source = internal->preProcessor.get(std::move(inputElement), preProcessorOptions);
options.visitPreProcessorResult(source);
if (!source.isValid())
{
Expand All @@ -345,10 +348,13 @@ namespace api
}

template <typename T>
void DecoderFacade::decodeImageFiles(std::filesystem::path path, std::function<void(T &&, std::string)> transformer)
void DecoderFacade::decodeImageFiles(
std::filesystem::path path,
std::optional<dip::PreProcessorOptions> preProcessorOptions,
std::function<void(T &&, std::string)> transformer)
{
auto loadHandler = [&, this](auto &&inputElement)
{ decodeImage(std::move(inputElement), transformer); };
{ decodeImage(std::move(inputElement), preProcessorOptions, transformer); };

if (options.getAsynchronousLoad())
{
Expand Down Expand Up @@ -463,34 +469,40 @@ namespace api
return decodeRawBytesToJson(utility::base64::decode(base64RawData), origin);
}

std::vector<std::pair<std::string, std::string>> DecoderFacade::decodeImageFilesToJson(std::filesystem::path path)
std::vector<std::pair<std::string, std::string>> DecoderFacade::decodeImageFilesToJson(
std::filesystem::path path,
std::optional<dip::PreProcessorOptions> preProcessorOptions)
{
auto result = std::vector<std::pair<std::string, std::string>>{};
decodeImageFiles<decoder::api::Result>(path, [&](auto &&decoderResult, auto origin)
decodeImageFiles<decoder::api::Result>(path, preProcessorOptions, [&](auto &&decoderResult, auto origin)
{ result.emplace_back(std::make_pair(std::move(origin), interpretRawBytes(std::move(decoderResult.payload), origin))); });
return result;
}

std::vector<std::pair<std::string, std::vector<std::uint8_t>>> DecoderFacade::decodeImageFilesToRawBytes(std::filesystem::path path)
std::vector<std::pair<std::string, std::vector<std::uint8_t>>> DecoderFacade::decodeImageFilesToRawBytes(
std::filesystem::path path,
std::optional<dip::PreProcessorOptions> preProcessorOptions)
{
auto result = std::vector<std::pair<std::string, std::vector<std::uint8_t>>>{};
decodeImageFiles<decoder::api::Result>(path, [&](auto &&decoderResult, auto origin)
decodeImageFiles<decoder::api::Result>(path, preProcessorOptions, [&](auto &&decoderResult, auto origin)
{ result.emplace_back(std::make_pair(std::move(origin), std::move(decoderResult.payload))); });
return result;
}

std::vector<std::pair<std::string, std::string>> DecoderFacade::decodeImageFilesToRawBase64(std::filesystem::path path)
std::vector<std::pair<std::string, std::string>> DecoderFacade::decodeImageFilesToRawBase64(
std::filesystem::path path,
std::optional<dip::PreProcessorOptions> preProcessorOptions)
{
auto result = std::vector<std::pair<std::string, std::string>>{};
decodeImageFiles<decoder::api::Result>(path, [&](auto &&decoderResult, auto origin)
decodeImageFiles<decoder::api::Result>(path, preProcessorOptions, [&](auto &&decoderResult, auto origin)
{ result.emplace_back(std::make_pair(origin, toMinimalJson(origin, decoderResult.payload, options.getJsonIndent()))); });
return result;
}

std::vector<std::string> DecoderFacade::decodeImageToJson(input::api::InputElement inputElement)
{
auto result = std::vector<std::string>{};
decodeImage<decoder::api::Result>(std::move(inputElement), [&](auto &&decoderResult, auto origin)
decodeImage<decoder::api::Result>(std::move(inputElement), std::nullopt, [&](auto &&decoderResult, auto origin)
{ result.emplace_back(interpretRawBytes(std::move(decoderResult.payload), origin)); });
return result;
}
Expand Down
17 changes: 4 additions & 13 deletions source/lib/dip/include/PreProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#pragma once

#include "PreProcessorOptions.h"

#include "lib/input/api/include/InputElement.h"

#include "lib/infrastructure/include/ParameterSupplier.h"
Expand All @@ -12,19 +14,10 @@
#include <opencv2/core.hpp>

#include <map>
#include <optional>

namespace dip
{
struct PreProcessorOptions
{
int rotationDegree = 0;
unsigned int scalePercent = 100u;
std::string split = "11";
unsigned int flippingMode = 0; // 0 nothing, 1 flip around X, 2 flip around Y, 3 flip around X and Y

static PreProcessorOptions const DEFAULT;
};

std::pair<unsigned int, unsigned int> splitStringToPair(std::string input);

std::map<unsigned int, unsigned int> splitPairToMap(std::pair<unsigned int, unsigned int> input);
Expand All @@ -39,8 +32,6 @@ namespace dip

PreProcessor(infrastructure::Context &context, PreProcessorOptions options);

void updatePartMap();

public:
void enable(bool enabled);

Expand All @@ -60,7 +51,7 @@ namespace dip

std::string reset();

input::api::InputElement get(input::api::InputElement &&element) const;
input::api::InputElement get(input::api::InputElement &&element, std::optional<dip::PreProcessorOptions> options = std::nullopt) const;

ParameterTypeList supplyParameters() const;

Expand Down
35 changes: 35 additions & 0 deletions source/lib/dip/include/PreProcessorOptions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: (C) 2026 user4223 and (other) contributors to ticket-decoder <https://github.com/user4223/ticket-decoder>
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <string>

namespace dip
{
class OptionsBuilder;

struct PreProcessorOptions
{
int rotationDegree = 0;
unsigned int scalePercent = 100u;
std::string splittingMode = "11"; // 11 first of one part, 2x x of two parts, 4x x of four parts
unsigned int flippingMode = 0; // 0 nothing, 1 flip around X, 2 flip around Y, 3 flip around X and Y

static PreProcessorOptions const DEFAULT;

OptionsBuilder create();
};

class OptionsBuilder
{
PreProcessorOptions options;

OptionsBuilder(PreProcessorOptions o);

public:
friend PreProcessorOptions;

PreProcessorOptions build();
};
}
54 changes: 29 additions & 25 deletions source/lib/dip/source/PreProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@

namespace dip
{
PreProcessorOptions const PreProcessorOptions::DEFAULT = PreProcessorOptions{};

std::pair<unsigned int, unsigned int> splitStringToPair(std::string input)
{
if (input.size() != 2)
Expand Down Expand Up @@ -65,21 +63,20 @@ namespace dip
return result;
}

std::tuple<unsigned int, unsigned int> updatePartMap(std::map<unsigned int, unsigned int> const &partMap)
{
return *std::max_element(partMap.begin(), partMap.end(),
[](auto const &a, auto const &b)
{ return (std::min(1u, a.second) * a.first) < (std::min(1u, b.second) * b.first); });
}

PreProcessor::PreProcessor(infrastructure::Context &context, PreProcessorOptions o)
: logger(CREATE_LOGGER(context.getLoggerFactory())),
options(std::move(o)),
isEnabled(true),
partMap(splitPairToMap(splitStringToPair(options.split))),
parts()
{
updatePartMap();
}

void PreProcessor::updatePartMap()
partMap(splitPairToMap(splitStringToPair(options.splittingMode))),
parts(updatePartMap(partMap))
{
parts = *std::max_element(partMap.begin(), partMap.end(),
[](auto const &a, auto const &b)
{ return (std::min(1u, a.second) * a.first) < (std::min(1u, b.second) * b.first); });
}

void PreProcessor::enable(bool e)
Expand All @@ -101,14 +98,14 @@ namespace dip
std::string PreProcessor::toggleSplit2()
{
::utility::rotate(partMap.at(2), 2);
updatePartMap();
parts = updatePartMap(partMap);
return std::to_string(std::get<0>(parts)) + "/" + std::to_string(std::get<1>(parts));
}

std::string PreProcessor::toggleSplit4()
{
::utility::rotate(partMap.at(4), 4);
updatePartMap();
parts = updatePartMap(partMap);
return std::to_string(std::get<0>(parts)) + "/" + std::to_string(std::get<1>(parts));
}

Expand All @@ -130,29 +127,36 @@ namespace dip
std::string PreProcessor::reset()
{
auto const defaultOptions = PreProcessorOptions{};
partMap = splitPairToMap(splitStringToPair(defaultOptions.split));
partMap = splitPairToMap(splitStringToPair(defaultOptions.splittingMode));
parts = updatePartMap(partMap);
options.rotationDegree = defaultOptions.rotationDegree;
options.scalePercent = defaultOptions.scalePercent;
options.flippingMode = defaultOptions.flippingMode;
updatePartMap();
return "";
}

input::api::InputElement PreProcessor::get(input::api::InputElement &&element) const
input::api::InputElement PreProcessor::get(input::api::InputElement &&element, std::optional<dip::PreProcessorOptions> tempOptions) const
{
if (!isEnabled || !element.isValid())
{
return std::move(element);
}

auto const effectiveOptions = tempOptions.value_or(this->options);
auto effectiveParts = std::tuple<unsigned int, unsigned int>(parts);
if (tempOptions)
{
effectiveParts = updatePartMap(splitPairToMap(splitStringToPair(tempOptions->splittingMode)));
}

auto image = element.getImage();
if (std::get<1>(parts) != 0)
if (std::get<1>(effectiveParts) != 0)
{
image = dip::split(image, std::get<0>(parts), std::get<1>(parts));
image = dip::split(image, std::get<0>(effectiveParts), std::get<1>(effectiveParts));
}
if (options.flippingMode != 0)
if (effectiveOptions.flippingMode != 0)
{
switch (options.flippingMode)
switch (effectiveOptions.flippingMode)
{
case 1:
image = dip::flipX(image);
Expand All @@ -165,13 +169,13 @@ namespace dip
break;
}
}
if (options.rotationDegree != 0)
if (effectiveOptions.rotationDegree != 0)
{
image = dip::rotate(image, (float)options.rotationDegree);
image = dip::rotate(image, (float)effectiveOptions.rotationDegree);
}
if (options.scalePercent != 100)
if (effectiveOptions.scalePercent != 100)
{
image = dip::scale(image, options.scalePercent * 0.01f);
image = dip::scale(image, effectiveOptions.scalePercent * 0.01f);
}

return std::move(element.replaceImage(std::move(image)));
Expand Down
Loading