Skip to content

Commit e2bebd9

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 e2bebd9

13 files changed

Lines changed: 152 additions & 131 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: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,34 +27,35 @@ 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 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,
3232
the the return value is alway a mapping of origin to content.
3333

3434
## Example decoding directly from PDF or image
3535
```
3636
from ticket_decoder import DecoderFacade
3737
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])
38+
decoder_facade = DecoderFacade()
39+
print(decoder_facade.decode_files('path/2/your/ticket.pdf'))
4140
```
4241

43-
## Decoding Aztec-Code in advance using zxingcpp on Python side
42+
## Example decoding Aztec-Code in advance using zxingcpp and opencv on Python side
4443
```
4544
from ticket_decoder import DecoderFacade
4645
from zxingcpp import read_barcodes
4746
from cv2 import imread
4847
4948
image = imread('path/2/your/ticket.jpg')
49+
if image is None:
50+
print("Image not found")
51+
exit(1)
5052
5153
barcodes = read_barcodes(image)
52-
if not barcodes:
54+
if barcodes is None:
5355
print("No barcodes found")
5456
exit(1)
5557
5658
decoder_facade = DecoderFacade(\
57-
fail_on_interpreter_error = False,\
5859
uic_public_key_xml_file = "cert/UIC_PublicKeys.xml",\
5960
vdv_certificate_ldif_file = "cert/VDV_Certificates.ldif")
6061

source/python/filter_keys.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
UIC918_9_EXPRESSION = parse("$..[firstName, lastName, uniqueTicketKey, issuerPNR, editionTime]")
11+
result = decoder_facade.decode_files("images/Muster-UIC918-9")
12+
print(f'### UIC918-9 items found: {len(result)}')
13+
for origin, value in result.items():
14+
print(f'{origin}: {", ".join([x.value for x in UIC918_9_EXPRESSION.find(loads(value))])}')
15+
16+
UIC918_3_EXPRESSION = parse("$..fields[S023, S015, S016].value")
17+
result = decoder_facade.decode_files("images/Muster-UIC918-3", rotation_degree=2, splitting_mode="42")
18+
print(f'### UIC918-3 items found: {len(result)}')
19+
for origin, value in result.items():
20+
print(f'{origin}: {", ".join([x.value for x in UIC918_3_EXPRESSION.find(loads(value))])}')
21+
22+
# print("\n### Raw input")
23+
# # Does not make sense in real world since it decodes the data twice, but it shows whats possible when uic918-data is available from plain aztec-decoder
24+
# results = [loads(item[1]) for item in decoder_facade.decode_files("images/")]
25+
# for result in results:
26+
# if 'raw' in result:
27+
# print(get_details(decoder_facade.decode_base64(result['raw'])))
28+
#
29+
# validated = 0
30+
# for result in results:
31+
# if 'validated' in result and result['validated'] == "true":
32+
# validated += 1
33+
# print("\n### Validated: " + str(validated) + "/" + str(len(results)))

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)