Skip to content

Commit 404a343

Browse files
committed
Make decodeImageFilesToJson returning a dict of origin/json-string; Use temp preProcessor options given on method call overruling defaults
1 parent 7006923 commit 404a343

13 files changed

Lines changed: 154 additions & 133 deletions

File tree

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"name": "pythoncpp launch python debugger",
1616
"type": "debugpy",
1717
"request": "launch",
18-
"program": "source/python/run.py",
18+
"program": "source/python/filter_keys.py",
1919
"console": "integratedTerminal",
2020
"env": {
2121
"PYTHONPATH": "${workspaceFolder}/build/Debug/bin"

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,8 @@ to execute the following minimal Python script:
5656
```
5757
from ticket_decoder import DecoderFacade
5858
59-
decoder_facade = DecoderFacade(fail_on_interpreter_error = False)
60-
for result in decoder_facade.decode_files('path/2/your/ticket.pdf'):
61-
print(result[1])
59+
decoder_facade = DecoderFacade()
60+
print(decoder_facade.decode_files('path/2/your/ticket.pdf'))
6261
```
6362

6463
## ticket-analyzer

source/lib/api/include/DecoderFacade.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,16 @@ namespace api
102102
DecoderFacadeBuilder::Options const &options;
103103

104104
template <typename T>
105-
void decodeImage(input::api::InputElement image, std::function<void(T &&, std::string)> transformer);
105+
void decodeImage(
106+
input::api::InputElement image,
107+
std::optional<dip::PreProcessorOptions> preProcessorOptions,
108+
std::function<void(T &&, std::string)> transformer);
106109

107110
template <typename T>
108-
void decodeImageFiles(std::filesystem::path path, std::function<void(T &&, std::string)> transformer);
111+
void decodeImageFiles(
112+
std::filesystem::path path,
113+
std::optional<dip::PreProcessorOptions> preProcessorOptions,
114+
std::function<void(T &&, std::string)> transformer);
109115

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

source/lib/api/source/DecoderFacade.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -316,9 +316,12 @@ namespace api
316316
}
317317

318318
template <typename T>
319-
void DecoderFacade::decodeImage(input::api::InputElement inputElement, std::function<void(T &&, std::string)> transformer)
319+
void DecoderFacade::decodeImage(
320+
input::api::InputElement inputElement,
321+
std::optional<dip::PreProcessorOptions> preProcessorOptions,
322+
std::function<void(T &&, std::string)> transformer)
320323
{
321-
auto source = internal->preProcessor.get(std::move(inputElement));
324+
auto source = internal->preProcessor.get(std::move(inputElement), preProcessorOptions);
322325
options.visitPreProcessorResult(source);
323326
if (!source.isValid())
324327
{
@@ -345,10 +348,13 @@ namespace api
345348
}
346349

347350
template <typename T>
348-
void DecoderFacade::decodeImageFiles(std::filesystem::path path, std::function<void(T &&, std::string)> transformer)
351+
void DecoderFacade::decodeImageFiles(
352+
std::filesystem::path path,
353+
std::optional<dip::PreProcessorOptions> preProcessorOptions,
354+
std::function<void(T &&, std::string)> transformer)
349355
{
350356
auto loadHandler = [&, this](auto &&inputElement)
351-
{ decodeImage(std::move(inputElement), transformer); };
357+
{ decodeImage(std::move(inputElement), preProcessorOptions, transformer); };
352358

353359
if (options.getAsynchronousLoad())
354360
{
@@ -468,7 +474,7 @@ namespace api
468474
std::optional<dip::PreProcessorOptions> preProcessorOptions)
469475
{
470476
auto result = std::vector<std::pair<std::string, std::string>>{};
471-
decodeImageFiles<decoder::api::Result>(path, [&](auto &&decoderResult, auto origin)
477+
decodeImageFiles<decoder::api::Result>(path, preProcessorOptions, [&](auto &&decoderResult, auto origin)
472478
{ result.emplace_back(std::make_pair(std::move(origin), interpretRawBytes(std::move(decoderResult.payload), origin))); });
473479
return result;
474480
}
@@ -478,7 +484,7 @@ namespace api
478484
std::optional<dip::PreProcessorOptions> preProcessorOptions)
479485
{
480486
auto result = std::vector<std::pair<std::string, std::vector<std::uint8_t>>>{};
481-
decodeImageFiles<decoder::api::Result>(path, [&](auto &&decoderResult, auto origin)
487+
decodeImageFiles<decoder::api::Result>(path, preProcessorOptions, [&](auto &&decoderResult, auto origin)
482488
{ result.emplace_back(std::make_pair(std::move(origin), std::move(decoderResult.payload))); });
483489
return result;
484490
}
@@ -488,15 +494,15 @@ namespace api
488494
std::optional<dip::PreProcessorOptions> preProcessorOptions)
489495
{
490496
auto result = std::vector<std::pair<std::string, std::string>>{};
491-
decodeImageFiles<decoder::api::Result>(path, [&](auto &&decoderResult, auto origin)
497+
decodeImageFiles<decoder::api::Result>(path, preProcessorOptions, [&](auto &&decoderResult, auto origin)
492498
{ result.emplace_back(std::make_pair(origin, toMinimalJson(origin, decoderResult.payload, options.getJsonIndent()))); });
493499
return result;
494500
}
495501

496502
std::vector<std::string> DecoderFacade::decodeImageToJson(input::api::InputElement inputElement)
497503
{
498504
auto result = std::vector<std::string>{};
499-
decodeImage<decoder::api::Result>(std::move(inputElement), [&](auto &&decoderResult, auto origin)
505+
decodeImage<decoder::api::Result>(std::move(inputElement), std::nullopt, [&](auto &&decoderResult, auto origin)
500506
{ result.emplace_back(interpretRawBytes(std::move(decoderResult.payload), origin)); });
501507
return result;
502508
}

source/lib/dip/include/PreProcessor.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <opencv2/core.hpp>
1515

1616
#include <map>
17+
#include <optional>
1718

1819
namespace dip
1920
{
@@ -31,8 +32,6 @@ namespace dip
3132

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

34-
void updatePartMap();
35-
3635
public:
3736
void enable(bool enabled);
3837

@@ -52,7 +51,7 @@ namespace dip
5251

5352
std::string reset();
5453

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

5756
ParameterTypeList supplyParameters() const;
5857

source/lib/dip/source/PreProcessor.cpp

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,20 @@ namespace dip
6363
return result;
6464
}
6565

66+
std::tuple<unsigned int, unsigned int> updatePartMap(std::map<unsigned int, unsigned int> const &partMap)
67+
{
68+
return *std::max_element(partMap.begin(), partMap.end(),
69+
[](auto const &a, auto const &b)
70+
{ return (std::min(1u, a.second) * a.first) < (std::min(1u, b.second) * b.first); });
71+
}
72+
6673
PreProcessor::PreProcessor(infrastructure::Context &context, PreProcessorOptions o)
6774
: logger(CREATE_LOGGER(context.getLoggerFactory())),
6875
options(std::move(o)),
6976
isEnabled(true),
7077
partMap(splitPairToMap(splitStringToPair(options.splittingMode))),
71-
parts()
78+
parts(updatePartMap(partMap))
7279
{
73-
updatePartMap();
74-
}
75-
76-
void PreProcessor::updatePartMap()
77-
{
78-
parts = *std::max_element(partMap.begin(), partMap.end(),
79-
[](auto const &a, auto const &b)
80-
{ return (std::min(1u, a.second) * a.first) < (std::min(1u, b.second) * b.first); });
8180
}
8281

8382
void PreProcessor::enable(bool e)
@@ -99,14 +98,14 @@ namespace dip
9998
std::string PreProcessor::toggleSplit2()
10099
{
101100
::utility::rotate(partMap.at(2), 2);
102-
updatePartMap();
101+
parts = updatePartMap(partMap);
103102
return std::to_string(std::get<0>(parts)) + "/" + std::to_string(std::get<1>(parts));
104103
}
105104

106105
std::string PreProcessor::toggleSplit4()
107106
{
108107
::utility::rotate(partMap.at(4), 4);
109-
updatePartMap();
108+
parts = updatePartMap(partMap);
110109
return std::to_string(std::get<0>(parts)) + "/" + std::to_string(std::get<1>(parts));
111110
}
112111

@@ -129,28 +128,35 @@ namespace dip
129128
{
130129
auto const defaultOptions = PreProcessorOptions{};
131130
partMap = splitPairToMap(splitStringToPair(defaultOptions.splittingMode));
131+
parts = updatePartMap(partMap);
132132
options.rotationDegree = defaultOptions.rotationDegree;
133133
options.scalePercent = defaultOptions.scalePercent;
134134
options.flippingMode = defaultOptions.flippingMode;
135-
updatePartMap();
136135
return "";
137136
}
138137

139-
input::api::InputElement PreProcessor::get(input::api::InputElement &&element) const
138+
input::api::InputElement PreProcessor::get(input::api::InputElement &&element, std::optional<dip::PreProcessorOptions> tempOptions) const
140139
{
141140
if (!isEnabled || !element.isValid())
142141
{
143142
return std::move(element);
144143
}
145144

145+
auto const effectiveOptions = tempOptions.value_or(this->options);
146+
auto effectiveParts = std::tuple<unsigned int, unsigned int>(parts);
147+
if (tempOptions)
148+
{
149+
effectiveParts = updatePartMap(splitPairToMap(splitStringToPair(tempOptions->splittingMode)));
150+
}
151+
146152
auto image = element.getImage();
147-
if (std::get<1>(parts) != 0)
153+
if (std::get<1>(effectiveParts) != 0)
148154
{
149-
image = dip::split(image, std::get<0>(parts), std::get<1>(parts));
155+
image = dip::split(image, std::get<0>(effectiveParts), std::get<1>(effectiveParts));
150156
}
151-
if (options.flippingMode != 0)
157+
if (effectiveOptions.flippingMode != 0)
152158
{
153-
switch (options.flippingMode)
159+
switch (effectiveOptions.flippingMode)
154160
{
155161
case 1:
156162
image = dip::flipX(image);
@@ -163,13 +169,13 @@ namespace dip
163169
break;
164170
}
165171
}
166-
if (options.rotationDegree != 0)
172+
if (effectiveOptions.rotationDegree != 0)
167173
{
168-
image = dip::rotate(image, (float)options.rotationDegree);
174+
image = dip::rotate(image, (float)effectiveOptions.rotationDegree);
169175
}
170-
if (options.scalePercent != 100)
176+
if (effectiveOptions.scalePercent != 100)
171177
{
172-
image = dip::scale(image, options.scalePercent * 0.01f);
178+
image = dip::scale(image, effectiveOptions.scalePercent * 0.01f);
173179
}
174180

175181
return std::move(element.replaceImage(std::move(image)));

source/python/README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Optional arguments for instance creation of DecoderFacade.
1212
Relative path to the LDIF file containing VDV certificates to decrypt VDV barcode content.
1313
* `fail_on_decoder_error` = false
1414
Fail when the aztec/barcode decoder gets an error if true or deliver an empty result when false.
15-
* `fail_on_interpreter_error` = true
15+
* `fail_on_interpreter_error` = false
1616
Fail when the interpretation of the data gets an error if true or deliver an empty result when false.
1717

1818
Provided python API is in an early state and the class DecoderFacade supports 3 methods right now only.
@@ -27,34 +27,39 @@ Provided python API is in an early state and the class DecoderFacade supports 3
2727
option, try using 'ISO 8859-1' and cast the result string to raw bytes.
2828

2929
* `decode_files('...')` detects and decodes aztec-codes from file or directories (PDF, image) and decodes barcode
30-
data to json. This is using zxing-cpp internally. It returns an array of tuples (input-path and json-result)
30+
data to json. This is using zxing-cpp and opencv internally. It returns a dict (origin/input-path and json-string)
3131
of size x, while x is the amount of aztec-codes found on input. An image can contain always multiple barcodes,
32-
the the return value is alway a mapping of origin to content.
32+
the return value is alway a mapping of origin to json content.
33+
* rotation_degree - Rotate image before processing for the given amount of degrees (default 0)
34+
* scale_percent - Scale image before processing in percent (default 100)
35+
* splitting_mode - Split image, 1st number specifies the no of parts to split, 2nd is the part used for processing, clockwise from top/left (default 11)
36+
* flipping_mode - Flip image around X if 1, around Y if 2, and around X and Y if 3 (default 0)
3337

3438
## Example decoding directly from PDF or image
3539
```
3640
from ticket_decoder import DecoderFacade
3741
38-
decoder_facade = DecoderFacade(fail_on_interpreter_error = False)
39-
for result in decoder_facade.decode_files('path/2/your/ticket.pdf'):
40-
print(result[1])
42+
decoder_facade = DecoderFacade()
43+
print(decoder_facade.decode_files('path/2/your/ticket.pdf'))
4144
```
4245

43-
## Decoding Aztec-Code in advance using zxingcpp on Python side
46+
## Example decoding Aztec-Code in advance using zxingcpp and opencv on Python side
4447
```
4548
from ticket_decoder import DecoderFacade
4649
from zxingcpp import read_barcodes
4750
from cv2 import imread
4851
4952
image = imread('path/2/your/ticket.jpg')
53+
if image is None:
54+
print("Image not found")
55+
exit(1)
5056
5157
barcodes = read_barcodes(image)
52-
if not barcodes:
58+
if barcodes is None:
5359
print("No barcodes found")
5460
exit(1)
5561
5662
decoder_facade = DecoderFacade(\
57-
fail_on_interpreter_error = False,\
5863
uic_public_key_xml_file = "cert/UIC_PublicKeys.xml",\
5964
vdv_certificate_ldif_file = "cert/VDV_Certificates.ldif")
6065

source/python/filter_keys.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# SPDX-FileCopyrightText: (C) 2022 user4223 and (other) contributors to ticket-decoder <https://github.com/user4223/ticket-decoder>
2+
# SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
from ticket_decoder import DecoderFacade
5+
from json import loads
6+
from jsonpath_ng.ext import parse
7+
8+
decoder_facade = DecoderFacade()
9+
10+
11+
UIC918_9_EXPRESSION = parse("$..[firstName, lastName, uniqueTicketKey, issuerPNR, editionTime]")
12+
result = decoder_facade.decode_files("images/Muster-UIC918-9", splitting_mode='21')
13+
print(f'### UIC918-9 items found: {len(result)}')
14+
for origin, value in result.items():
15+
print(f'{origin}: {", ".join([x.value for x in UIC918_9_EXPRESSION.find(loads(value))])}')
16+
17+
18+
UIC918_3_EXPRESSION = parse("$..fields[S023, S015, S016].value")
19+
result = decoder_facade.decode_files("images/Muster-UIC918-3", rotation_degree=2, splitting_mode='42')
20+
print(f'### UIC918-3 items found: {len(result)}')
21+
for origin, value in result.items():
22+
print(f'{origin}: {", ".join([x.value for x in UIC918_3_EXPRESSION.find(loads(value))])}')
23+
24+
25+
result = decoder_facade.decode_files("images")
26+
verified = 0
27+
for origin, value in result.items():
28+
verified += 1 if loads(value).get('validated') == 'true' else 0
29+
print(f'### Overall items found: {len(result)} (verified: {verified})')

source/python/interpret_only.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@
55
from zxingcpp import read_barcodes
66
from cv2 import imread
77

8-
image = imread('source/test/decoder/etc/Muster 918-9 Länderticket Schleswig-Holstein_0_decoded.jpg')
8+
file = 'source/test/decoder/etc/Muster 918-9 Länderticket Schleswig-Holstein_0_decoded.jpg'
9+
10+
image = imread(file)
11+
if image is None:
12+
print("Image not found")
13+
exit(1)
14+
915
barcodes = read_barcodes(image)
10-
if not barcodes:
16+
if barcodes is None:
1117
print("No barcodes found")
1218
exit(1)
1319

14-
decoder_facade = DecoderFacade(\
15-
fail_on_interpreter_error = False,\
16-
uic_public_key_xml_file = "cert/UIC_PublicKeys.xml",\
17-
vdv_certificate_ldif_file = "cert/VDV_Certificates.ldif")
20+
decoder_facade = DecoderFacade()
1821

19-
print(decoder_facade.decode_bytes(barcodes[0].bytes))
22+
print(decoder_facade.decode_bytes(barcodes[0].bytes, file))

source/python/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
jsonpath2
1+
jsonpath-ng
22
zxing-cpp
33
opencv-python

0 commit comments

Comments
 (0)