Spaces:
Running
Running
Commit
·
36ccd32
0
Parent(s):
Transfer project
Browse files- .bumpversion.cfg +16 -0
- .gitattributes +3 -0
- .github/workflows/main.yml +25 -0
- .gitignore +21 -0
- .pre-commit-config.yaml +74 -0
- Dockerfile +145 -0
- README.md +104 -0
- app.py +85 -0
- docker/entrypoint-test.sh +15 -0
- docker/entrypoint.sh +7 -0
- docker/pre_commit_init.sh +10 -0
- packages.txt +1 -0
- pyproject.toml +76 -0
- requirements.txt +72 -0
- src/deep_barcode_reader/__init__.py +14 -0
- src/deep_barcode_reader/barcode.py +160 -0
- src/deep_barcode_reader/base.py +70 -0
- src/deep_barcode_reader/logging.py +30 -0
- src/deep_barcode_reader/main.py +72 -0
- src/deep_barcode_reader/models/sr.caffemodel +3 -0
- src/deep_barcode_reader/models/sr.prototxt +3 -0
- tests/__init__.py +0 -0
- tests/test_data/sample.jpg +3 -0
- tests/test_deep_barcode_reader.py +8 -0
.bumpversion.cfg
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[bumpversion]
|
| 2 |
+
current_version = 0.3.0
|
| 3 |
+
commit = False
|
| 4 |
+
tag = False
|
| 5 |
+
|
| 6 |
+
[bumpversion:file:pyproject.toml]
|
| 7 |
+
search = version = "{current_version}"
|
| 8 |
+
replace = version = "{new_version}"
|
| 9 |
+
|
| 10 |
+
[bumpversion:file:src/deep_barcode_reader/__init__.py]
|
| 11 |
+
search = __version__ = "{current_version}"
|
| 12 |
+
replace = __version__ = "{new_version}"
|
| 13 |
+
|
| 14 |
+
[bumpversion:file:tests/test_deep_barcode_reader.py]
|
| 15 |
+
search = __version__ == "{current_version}"
|
| 16 |
+
replace = __version__ == "{new_version}"
|
.gitattributes
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
src/deep_barcode_reader/models/sr.caffemodel filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
src/deep_barcode_reader/models/sr.prototxt filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
tests/test_data/sample.jpg filter=lfs diff=lfs merge=lfs -text
|
.github/workflows/main.yml
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Sync to Hugging Face hub
|
| 2 |
+
on:
|
| 3 |
+
push:
|
| 4 |
+
branches:
|
| 5 |
+
- '**'
|
| 6 |
+
|
| 7 |
+
# to run this workflow manually from the Actions tab
|
| 8 |
+
workflow_dispatch:
|
| 9 |
+
|
| 10 |
+
jobs:
|
| 11 |
+
sync-to-hub:
|
| 12 |
+
runs-on: ubuntu-latest
|
| 13 |
+
steps:
|
| 14 |
+
- uses: actions/checkout@v3
|
| 15 |
+
with:
|
| 16 |
+
fetch-depth: 0
|
| 17 |
+
lfs: true
|
| 18 |
+
- name: Add remote
|
| 19 |
+
env:
|
| 20 |
+
HF: ${{ secrets.HG }}
|
| 21 |
+
run: git remote add space https://industoai:$HF@huggingface.co/spaces/industoai/Deep-Barcode-Reader
|
| 22 |
+
- name: Push to hub
|
| 23 |
+
env:
|
| 24 |
+
HF: ${{ secrets.HG }}
|
| 25 |
+
run: git push --force https://industoai:$HF@huggingface.co/spaces/industoai/Deep-Barcode-Reader ${{ github.ref }}:main
|
.gitignore
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Pycharm files
|
| 2 |
+
.idea/
|
| 3 |
+
|
| 4 |
+
# poetry
|
| 5 |
+
poetry.lock
|
| 6 |
+
|
| 7 |
+
# Unit test files
|
| 8 |
+
.coverage
|
| 9 |
+
.coverage.*
|
| 10 |
+
.cache
|
| 11 |
+
.pytest_cache/
|
| 12 |
+
src/*/__pycache__/*
|
| 13 |
+
|
| 14 |
+
# mypy files
|
| 15 |
+
.mypy_cache/
|
| 16 |
+
|
| 17 |
+
# Probable environments files
|
| 18 |
+
.env
|
| 19 |
+
.venv
|
| 20 |
+
env/
|
| 21 |
+
venv/
|
.pre-commit-config.yaml
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# See https://pre-commit.com for more information
|
| 2 |
+
# See https://pre-commit.com/hooks.html for more hooks
|
| 3 |
+
default_language_version:
|
| 4 |
+
python: python3
|
| 5 |
+
repos:
|
| 6 |
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
| 7 |
+
rev: v4.5.0 # the release, git tag, or commit you want to use
|
| 8 |
+
hooks:
|
| 9 |
+
- id: check-toml
|
| 10 |
+
- id: check-yaml
|
| 11 |
+
- id: check-json
|
| 12 |
+
- id: end-of-file-fixer
|
| 13 |
+
- id: trailing-whitespace
|
| 14 |
+
- id: no-commit-to-branch
|
| 15 |
+
- id: check-executables-have-shebangs
|
| 16 |
+
- id: check-added-large-files
|
| 17 |
+
- id: check-case-conflict
|
| 18 |
+
- id: check-merge-conflict
|
| 19 |
+
- id: pretty-format-json
|
| 20 |
+
args:
|
| 21 |
+
- --autofix
|
| 22 |
+
- id: check-symlinks
|
| 23 |
+
- id: check-ast
|
| 24 |
+
- id: detect-private-key
|
| 25 |
+
- repo: https://github.com/psf/black
|
| 26 |
+
rev: 24.1.1
|
| 27 |
+
hooks:
|
| 28 |
+
- id: black
|
| 29 |
+
language: python
|
| 30 |
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
| 31 |
+
rev: v1.8.0
|
| 32 |
+
hooks:
|
| 33 |
+
- id: mypy
|
| 34 |
+
language: system
|
| 35 |
+
args: [--strict, --ignore-missing-imports]
|
| 36 |
+
- repo: https://github.com/pycqa/pylint
|
| 37 |
+
rev: v3.0.3
|
| 38 |
+
hooks:
|
| 39 |
+
- id: pylint
|
| 40 |
+
language: system
|
| 41 |
+
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
| 42 |
+
rev: v1.5.4
|
| 43 |
+
hooks:
|
| 44 |
+
- id: forbid-crlf
|
| 45 |
+
- id: remove-crlf
|
| 46 |
+
- id: forbid-tabs
|
| 47 |
+
- id: remove-tabs
|
| 48 |
+
- repo: https://github.com/PyCQA/bandit
|
| 49 |
+
rev: 1.7.7
|
| 50 |
+
hooks:
|
| 51 |
+
- id: bandit
|
| 52 |
+
args: ["--skip=B101"]
|
| 53 |
+
- repo: https://github.com/Lucas-C/pre-commit-hooks-markup
|
| 54 |
+
rev: v1.0.1
|
| 55 |
+
hooks:
|
| 56 |
+
- id: rst-linter
|
| 57 |
+
- repo: https://github.com/Yelp/detect-secrets
|
| 58 |
+
rev: v1.4.0
|
| 59 |
+
hooks:
|
| 60 |
+
- id: detect-secrets
|
| 61 |
+
language: python
|
| 62 |
+
exclude: "poetry.lock"
|
| 63 |
+
# args: ['--baseline', '.secrets.baseline']
|
| 64 |
+
- repo: https://github.com/shellcheck-py/shellcheck-py
|
| 65 |
+
rev: v0.9.0.6
|
| 66 |
+
hooks:
|
| 67 |
+
- id: shellcheck
|
| 68 |
+
args: ["--external-sources"]
|
| 69 |
+
- repo: https://github.com/python-poetry/poetry
|
| 70 |
+
rev: '1.7.1'
|
| 71 |
+
hooks:
|
| 72 |
+
- id: poetry-check
|
| 73 |
+
- id: poetry-lock
|
| 74 |
+
args: ["--no-update"]
|
Dockerfile
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## syntax=docker/dockerfile:1.1.7-experimental
|
| 2 |
+
|
| 3 |
+
################
|
| 4 |
+
# Base builder #
|
| 5 |
+
################
|
| 6 |
+
FROM python:3.10-bookworm as base_build
|
| 7 |
+
|
| 8 |
+
ENV \
|
| 9 |
+
# locale environment variables
|
| 10 |
+
LC_ALL=C.UTF-8 \
|
| 11 |
+
# python environemnt variables
|
| 12 |
+
PYTHONFAULTHANDLER=1 \
|
| 13 |
+
PYTHONUNBUFFERED=1 \
|
| 14 |
+
PYTHONHASHSEED=random \
|
| 15 |
+
# pip environmental variables
|
| 16 |
+
PIP_NO_CACHE_DIR=off \
|
| 17 |
+
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
| 18 |
+
PIP_DEFAULT_TIMEOUT=100 \
|
| 19 |
+
# poetry version
|
| 20 |
+
POETRY_VERSION=1.5.0
|
| 21 |
+
|
| 22 |
+
# Install requirements
|
| 23 |
+
RUN apt-get update && apt-get install -y \
|
| 24 |
+
curl \
|
| 25 |
+
git \
|
| 26 |
+
bash \
|
| 27 |
+
build-essential \
|
| 28 |
+
libffi-dev \
|
| 29 |
+
libssl-dev \
|
| 30 |
+
tini \
|
| 31 |
+
openssh-client \
|
| 32 |
+
cargo \
|
| 33 |
+
musl-dev \
|
| 34 |
+
libzbar0 \
|
| 35 |
+
&& apt-get autoremove -y \
|
| 36 |
+
&& rm -rf /var/lib/apt/lists/* \
|
| 37 |
+
# github ssh key setting
|
| 38 |
+
&& mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com | sort > ~/.ssh/known_hosts \
|
| 39 |
+
# Installing poetry and set the PATH
|
| 40 |
+
&& curl -sSL https://install.python-poetry.org | python3 - \
|
| 41 |
+
&& echo 'export PATH="/root/.local/bin:$PATH"' >>/root/.profile \
|
| 42 |
+
&& export PATH="/root/.local/bin:$PATH" \
|
| 43 |
+
&& true
|
| 44 |
+
SHELL ["/bin/bash", "-lc"]
|
| 45 |
+
|
| 46 |
+
# Copy poetry lock and pyproject config files to the container
|
| 47 |
+
WORKDIR /pysetup
|
| 48 |
+
COPY ./poetry.lock ./pyproject.toml /pysetup/
|
| 49 |
+
# Install pip/wheel/virtualenv and build the wheels based on the poetry lock
|
| 50 |
+
RUN --mount=type=ssh pip3 install wheel virtualenv poetry-plugin-export \
|
| 51 |
+
&& poetry export -f requirements.txt --without-hashes -o /tmp/requirements.txt \
|
| 52 |
+
&& pip3 wheel --wheel-dir=/tmp/wheelhouse --trusted-host 172.17.0.1 --find-links=http://172.17.0.1:3141/debian/ -r /tmp/requirements.txt \
|
| 53 |
+
&& virtualenv /.venv && source /.venv/bin/activate && echo 'source /.venv/bin/activate' >>/root/.profile \
|
| 54 |
+
&& pip3 install --no-deps --trusted-host 172.17.0.1 --find-links=http://172.17.0.1:3141/debian/ --find-links=/tmp/wheelhouse/ /tmp/wheelhouse/*.whl \
|
| 55 |
+
&& true
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
###########################
|
| 59 |
+
# Production base builder #
|
| 60 |
+
###########################
|
| 61 |
+
FROM base_build as production_build
|
| 62 |
+
# Copy entrypoint script to the container and src files to the app directory
|
| 63 |
+
COPY ./docker/entrypoint.sh /docker-entrypoint.sh
|
| 64 |
+
COPY . /app/
|
| 65 |
+
WORKDIR /app
|
| 66 |
+
# Build the wheel packages with poetry and add them to the wheelhouse
|
| 67 |
+
RUN --mount=type=ssh source /.venv/bin/activate \
|
| 68 |
+
&& poetry build -f wheel --no-interaction --no-ansi \
|
| 69 |
+
&& cp dist/*.whl /tmp/wheelhouse \
|
| 70 |
+
&& chmod a+x /docker-entrypoint.sh \
|
| 71 |
+
&& true
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
########################
|
| 76 |
+
# Production Container #
|
| 77 |
+
########################
|
| 78 |
+
FROM python:3.10-bookworm as production
|
| 79 |
+
COPY --from=production_build /tmp/wheelhouse /tmp/wheelhouse
|
| 80 |
+
COPY --from=production_build /docker-entrypoint.sh /docker-entrypoint.sh
|
| 81 |
+
WORKDIR /app
|
| 82 |
+
# Install system level deps for running the package and install the wheels we built in the previous step.
|
| 83 |
+
RUN --mount=type=ssh apt-get update && apt-get install -y \
|
| 84 |
+
bash \
|
| 85 |
+
libffi8 \
|
| 86 |
+
libgl1 \
|
| 87 |
+
tini \
|
| 88 |
+
libzbar0 \
|
| 89 |
+
&& apt-get autoremove -y \
|
| 90 |
+
&& rm -rf /var/lib/apt/lists/* \
|
| 91 |
+
&& chmod a+x /docker-entrypoint.sh \
|
| 92 |
+
&& WHEELFILE=`echo /tmp/wheelhouse/deep_barcode_reader-*.whl` \
|
| 93 |
+
&& pip3 install --trusted-host 172.17.0.1 --find-links=http://172.17.0.1:3141/debian/ --find-links=/tmp/wheelhouse/ "$WHEELFILE"[all] \
|
| 94 |
+
&& rm -rf /tmp/wheelhouse/ \
|
| 95 |
+
&& true
|
| 96 |
+
ENTRYPOINT ["/usr/bin/tini", "--", "/docker-entrypoint.sh"]
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
############################
|
| 101 |
+
# Development base builder #
|
| 102 |
+
############################
|
| 103 |
+
FROM base_build as development_build
|
| 104 |
+
# Copy src to app directory
|
| 105 |
+
COPY . /app
|
| 106 |
+
WORKDIR /app
|
| 107 |
+
# Install dependencies from poetry lock
|
| 108 |
+
RUN --mount=type=ssh source /.venv/bin/activate \
|
| 109 |
+
&& apt-get update && apt-get install -y libgl1 libzbar0 \
|
| 110 |
+
&& export PIP_FIND_LINKS=http://172.17.0.1:3141/debian/ \
|
| 111 |
+
&& export PIP_TRUSTED_HOST=172.17.0.1 \
|
| 112 |
+
&& pip3 install nvidia-cublas-cu12 nvidia-cusparse-cu12 triton nvidia-nccl-cu12 nvidia-cudnn-cu12 nvidia-cufft-cu12 nvidia-cusolver-cu12 \
|
| 113 |
+
&& poetry install --no-interaction --no-ansi \
|
| 114 |
+
&& true
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
###################
|
| 119 |
+
# Tests Container #
|
| 120 |
+
###################
|
| 121 |
+
FROM development_build as test
|
| 122 |
+
RUN --mount=type=ssh source /.venv/bin/activate \
|
| 123 |
+
&& chmod a+x docker/*.sh \
|
| 124 |
+
&& docker/pre_commit_init.sh \
|
| 125 |
+
&& true
|
| 126 |
+
ENTRYPOINT ["/usr/bin/tini", "--", "docker/entrypoint-test.sh"]
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
#########################
|
| 130 |
+
# Development Container #
|
| 131 |
+
#########################
|
| 132 |
+
FROM development_build as development
|
| 133 |
+
RUN apt-get update && apt-get install -y zsh libzbar0 \
|
| 134 |
+
&& sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" \
|
| 135 |
+
&& echo "if [ \"\$NO_WHEELHOUSE\" = \"1\" ]" >>/root/.profile \
|
| 136 |
+
&& echo "then" >>/root/.profile \
|
| 137 |
+
&& echo " echo \"Wheelhouse disabled\"" >>/root/.profile \
|
| 138 |
+
&& echo "else">>/root/.profile \
|
| 139 |
+
&& echo " export PIP_TRUSTED_HOST=172.17.0.1" >>/root/.profile \
|
| 140 |
+
&& echo " export PIP_FIND_LINKS=http://172.17.0.1:3141/debian/" >>/root/.profile \
|
| 141 |
+
&& echo "fi" >>/root/.profile \
|
| 142 |
+
&& echo "source /root/.profile" >>/root/.zshrc \
|
| 143 |
+
&& pip3 install git-up \
|
| 144 |
+
&& true
|
| 145 |
+
ENTRYPOINT ["/bin/zsh", "-l"]
|
README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Deep Barcode Reader
|
| 3 |
+
emoji: 🔳
|
| 4 |
+
colorFrom: gray
|
| 5 |
+
colorTo: red
|
| 6 |
+
sdk: streamlit
|
| 7 |
+
sdk_version: 1.42.2
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
---
|
| 11 |
+
# Deep-Barcode-Reader
|
| 12 |
+
This repository is used for reading different types of barcodes and QR codes from images.
|
| 13 |
+
The code is written in Python and uses OpenCV, Pyzbar, and other libraries for reading barcodes and QR codes.
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
## How to Use
|
| 17 |
+
The library offers several functions for reading barcodes and QR codes from images.
|
| 18 |
+
General options can be selected before applying the reader method. These options are:
|
| 19 |
+
- `--data_path` is the path to the image file.
|
| 20 |
+
- `--result_path` is the path to the output file.
|
| 21 |
+
- `--verbose` is the verbosity level of the code.
|
| 22 |
+
- `--method` is the method for reading the barcode or QR code.
|
| 23 |
+
- `--model_size` is the size of the deep learning model.
|
| 24 |
+
|
| 25 |
+
The available methods are as:
|
| 26 |
+
- [Opencv Barcode Reader](#opencv-barcode-reader)
|
| 27 |
+
- [Zbar Barcode Reader](#zbar-barcode-reader)
|
| 28 |
+
- [QR Reader](#qr-reader)
|
| 29 |
+
|
| 30 |
+
### Opencv Barcode Reader
|
| 31 |
+
This method can only detect barcodes but NOT QR codes. It can detect and decode EAN-8, EAN-13, UPC-A and UPC-E
|
| 32 |
+
barcode types. You can use the method by running the following command:
|
| 33 |
+
```shell
|
| 34 |
+
deep_barcode_reader -vv -d tests/test_data/sample.jpg -m opencv
|
| 35 |
+
```
|
| 36 |
+
This method can only detect specific types of barcodes. However, it is faster than the other methods.
|
| 37 |
+
|
| 38 |
+
### Zbar Barcode Reader
|
| 39 |
+
This method can detect and decode both barcodes and QR codes.
|
| 40 |
+
It is very powerful to detect and decode different types of barcodes and QR codes.
|
| 41 |
+
You can use the method by running the following command:
|
| 42 |
+
```shell
|
| 43 |
+
deep_barcode_reader -vv -d tests/test_data/sample.jpg -m zbar
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
### QR Reader
|
| 47 |
+
This method can only detect and decode QR codes.
|
| 48 |
+
Depending on the model size as `n` as nano, `s` as small, `m` as medium, or `l` as large,
|
| 49 |
+
the detection and decoding process time and accuracy can be changed.
|
| 50 |
+
```shell
|
| 51 |
+
deep_barcode_reader -vv -d tests/test_data/sample.jpg -m zbar --model_size l
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
## How to Develop
|
| 56 |
+
Do the following only once after creating your project:
|
| 57 |
+
- Init the git repo with `git init`.
|
| 58 |
+
- Add files with `git add .`.
|
| 59 |
+
- Then `git commit -m 'initialize the project'`.
|
| 60 |
+
- Add remote url with `git remote add origin REPO_URL`.
|
| 61 |
+
- Then `git branch -M master`.
|
| 62 |
+
- `git push origin main`.
|
| 63 |
+
Then create a branch with `git checkout -b BRANCH_NAME` for further developments.
|
| 64 |
+
- Install poetry if you do not have it in your system from [here](https://python-poetry.org/docs/#installing-with-pipx).
|
| 65 |
+
- Create a virtual env preferably with virtualenv wrapper and `mkvirtualenv -p $(which python3.10) ENVNAME`.
|
| 66 |
+
- Then `git add poetry.lock`.
|
| 67 |
+
- Then `pre-commit install`.
|
| 68 |
+
- For applying changes use `pre-commit run --all-files`.
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
## Docker Container
|
| 72 |
+
To run the docker with ssh, do the following first and then based on your need select ,test, development, or production containers:
|
| 73 |
+
```shell
|
| 74 |
+
export DOCKER_BUILDKIT=1
|
| 75 |
+
export DOCKER_SSHAGENT="-v $SSH_AUTH_SOCK:$SSH_AUTH_SOCK -e SSH_AUTH_SOCK"
|
| 76 |
+
```
|
| 77 |
+
### Test Container
|
| 78 |
+
This container is used for testing purposes while it runs the test
|
| 79 |
+
```shell
|
| 80 |
+
docker build --progress plain --ssh default --target test -t barcode_docker:test .
|
| 81 |
+
docker run -it --rm -v "$(pwd):/app" $(echo $DOCKER_SSHAGENT) barcode_docker:test
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
### Development Container
|
| 85 |
+
This container can be used for development purposes:
|
| 86 |
+
```shell
|
| 87 |
+
docker build --progress plain --ssh default --target development -t barcode_docker:development .
|
| 88 |
+
docker run -it --rm -v "$(pwd):/app" -v /tmp:/tmp $(echo $DOCKER_SSHAGENT) barcode_docker:development
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
### Production Container
|
| 92 |
+
This container can be used for production purposes:
|
| 93 |
+
```shell
|
| 94 |
+
docker build --progress plain --ssh default --target production -t barcode_docker:production .
|
| 95 |
+
docker run -it --rm -v "$(pwd):/app" -v /tmp:/tmp $(echo $DOCKER_SSHAGENT) barcode_docker:production deep_barcode_reader -vv -d tests/test_data/sample.jpg -m zbar --model_size l
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
## Hugging Face Deployment
|
| 99 |
+
The repository is also deployed in [hugging face](https://huggingface.co/spaces/afshin-dini/Deep-Barcode-Reader) in which one can upload images, select the appropriate method and its parameters and detect and decode the barcodes or QR codes.
|
| 100 |
+
It is good to mention that you can also run the application locally by running the following command:
|
| 101 |
+
```shell
|
| 102 |
+
streamlit run app.py
|
| 103 |
+
```
|
| 104 |
+
and then open the browser and go to the address `http://localhost:8501`.
|
app.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""This is a demo for running the barcode QR code reader usng streamlit library"""
|
| 2 |
+
|
| 3 |
+
from dataclasses import dataclass, field
|
| 4 |
+
from typing import Any, Optional
|
| 5 |
+
import asyncio
|
| 6 |
+
|
| 7 |
+
import streamlit as st
|
| 8 |
+
from PIL import Image
|
| 9 |
+
import numpy as np
|
| 10 |
+
import pandas as pd
|
| 11 |
+
|
| 12 |
+
from src.deep_barcode_reader.barcode import Wrapper
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@dataclass
|
| 16 |
+
class DemoBarcodeReader:
|
| 17 |
+
"""This is a demo class for barcode/qr code reader using different methods"""
|
| 18 |
+
|
| 19 |
+
image: Optional[Any] = field(init=False, default=None)
|
| 20 |
+
model_option: str = field(init=False, default="opencv")
|
| 21 |
+
model_size: str = field(init=False, default="n")
|
| 22 |
+
|
| 23 |
+
def upload_image(self) -> None:
|
| 24 |
+
"""Upload an image from the streamlit page"""
|
| 25 |
+
uploaded_file = st.file_uploader(
|
| 26 |
+
"Choose an image...", type=["jpg", "png", "jpeg"]
|
| 27 |
+
)
|
| 28 |
+
if uploaded_file is not None:
|
| 29 |
+
self.image = Image.open(uploaded_file)
|
| 30 |
+
else:
|
| 31 |
+
self.image = Image.open("tests/test_data/sample.jpg")
|
| 32 |
+
|
| 33 |
+
st.image(
|
| 34 |
+
self.image, caption="Original/Uploaded Image", use_container_width=True
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
def select_model(self) -> None:
|
| 38 |
+
"""Select a model for barcode/qr code reader"""
|
| 39 |
+
self.model_option = st.selectbox(
|
| 40 |
+
"Choose a reader/decoder model", ["zbar", "opencv", "qrreader"]
|
| 41 |
+
)
|
| 42 |
+
if self.model_option == "qrreader":
|
| 43 |
+
ml_size = st.selectbox(
|
| 44 |
+
"Choose a model size for QRReader method",
|
| 45 |
+
["nano", "small", "medium", "large"],
|
| 46 |
+
)
|
| 47 |
+
self.model_size = (
|
| 48 |
+
"n"
|
| 49 |
+
if ml_size == "nano"
|
| 50 |
+
else "s" if ml_size == "small" else "m" if ml_size == "medium" else "l"
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
def process_image(self) -> None:
|
| 54 |
+
"""Process the image for barcode/qr code reader"""
|
| 55 |
+
if st.button("Read/Decode Barcode/QR Code"):
|
| 56 |
+
reader = Wrapper(model_size=self.model_size, method=self.model_option)
|
| 57 |
+
detections, result_img = asyncio.run(
|
| 58 |
+
reader.method_selection(image=np.array(self.image), result_path="")
|
| 59 |
+
)
|
| 60 |
+
st.markdown("<h3>Detected Results</h3>", unsafe_allow_html=True)
|
| 61 |
+
st.image(result_img, caption="Decoded Result", use_container_width=True)
|
| 62 |
+
results = pd.DataFrame(
|
| 63 |
+
{
|
| 64 |
+
"Barcode/QR Types": detections.decoded_types,
|
| 65 |
+
"Data": detections.decoded_data,
|
| 66 |
+
"Boundary Box": [str(bbx) for bbx in detections.bbox_data],
|
| 67 |
+
}
|
| 68 |
+
)
|
| 69 |
+
st.markdown('<div class="center-container">', unsafe_allow_html=True)
|
| 70 |
+
st.markdown(
|
| 71 |
+
"<h3>Detailed Information of Detections</h3>", unsafe_allow_html=True
|
| 72 |
+
)
|
| 73 |
+
st.table(results)
|
| 74 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
| 75 |
+
|
| 76 |
+
def design_page(self) -> None:
|
| 77 |
+
"""Design the streamlit page for barcode/qr code reader"""
|
| 78 |
+
st.title("Image Barcode/QR Code Reader and Detector")
|
| 79 |
+
self.upload_image()
|
| 80 |
+
self.select_model()
|
| 81 |
+
self.process_image()
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
demo = DemoBarcodeReader()
|
| 85 |
+
demo.design_page()
|
docker/entrypoint-test.sh
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash -l
|
| 2 |
+
set -e
|
| 3 |
+
if [ "$#" -eq 0 ]; then
|
| 4 |
+
# Kill cache, pytest complains about it if running local and docker tests in mapped volume
|
| 5 |
+
find tests -type d -name '__pycache__' -print0 | xargs -0 rm -rf {}
|
| 6 |
+
# Make sure the service itself is installed
|
| 7 |
+
poetry install
|
| 8 |
+
# Make sure pre-commit checks were not missed and run tests
|
| 9 |
+
git config --global --add safe.directory /app
|
| 10 |
+
poetry run pre-commit install
|
| 11 |
+
pre-commit run --all-files
|
| 12 |
+
pytest -v --junitxml=pytest.xml tests/
|
| 13 |
+
else
|
| 14 |
+
exec "$@"
|
| 15 |
+
fi
|
docker/entrypoint.sh
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash -l
|
| 2 |
+
set -e
|
| 3 |
+
if [ "$#" -eq 0 ]; then
|
| 4 |
+
exec deep_barcode_reader --help
|
| 5 |
+
else
|
| 6 |
+
exec "$@"
|
| 7 |
+
fi
|
docker/pre_commit_init.sh
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash -l
|
| 2 |
+
if [ ! -d .git ]
|
| 3 |
+
then
|
| 4 |
+
git init
|
| 5 |
+
git checkout -b precommit_init
|
| 6 |
+
git add .
|
| 7 |
+
fi
|
| 8 |
+
set -e
|
| 9 |
+
poetry run pre-commit install
|
| 10 |
+
SKIP="poetry-lock" poetry run pre-commit run --all-files
|
packages.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
libzbar0
|
pyproject.toml
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[tool.poetry]
|
| 2 |
+
name = "deep_barcode_reader"
|
| 3 |
+
version = "0.3.0"
|
| 4 |
+
description = "It can read different types of barcodes"
|
| 5 |
+
authors = ["Afshin Dini <Afshin Dini>"]
|
| 6 |
+
readme = "README.md"
|
| 7 |
+
packages = [{include = "deep_barcode_reader", from = "src"}]
|
| 8 |
+
|
| 9 |
+
[tool.poetry.scripts]
|
| 10 |
+
deep_barcode_reader = "deep_barcode_reader.main:deep_barcode_reader_cli"
|
| 11 |
+
|
| 12 |
+
[tool.pylint.format]
|
| 13 |
+
max-line-length=150 # This defines the maximum number of characters on a single line in pylint
|
| 14 |
+
|
| 15 |
+
[tool.pylint.design]
|
| 16 |
+
max-attributes=10
|
| 17 |
+
max-positional-arguments=6
|
| 18 |
+
max-args=6
|
| 19 |
+
|
| 20 |
+
[tool.pylint.messages_control]
|
| 21 |
+
disable=["fixme"]
|
| 22 |
+
|
| 23 |
+
[tool.pylint.similarities]
|
| 24 |
+
min-similarity-lines = 8 # Minimum lines number of a similarity.
|
| 25 |
+
ignore-imports = true # Ignore imports when computing similarities.
|
| 26 |
+
|
| 27 |
+
[tool.pytest.ini_options]
|
| 28 |
+
junit_family="xunit2"
|
| 29 |
+
addopts="--cov=deep_barcode_reader --cov-fail-under=65 --cov-branch"
|
| 30 |
+
asyncio_mode="strict"
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
[tool.coverage.run]
|
| 34 |
+
omit = ["tests/*"]
|
| 35 |
+
branch = true
|
| 36 |
+
|
| 37 |
+
[[tool.poetry.source]]
|
| 38 |
+
name = "pytorch"
|
| 39 |
+
url = "https://download.pytorch.org/whl/cu118"
|
| 40 |
+
priority = "explicit"
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
[tool.poetry.dependencies]
|
| 44 |
+
python = "^3.10"
|
| 45 |
+
click = "^8.1.7"
|
| 46 |
+
ultralytics = "8.2.18"
|
| 47 |
+
qreader = "3.12"
|
| 48 |
+
pyzbar = "^0.1.9"
|
| 49 |
+
opencv-python = "^4.9.0.80"
|
| 50 |
+
torch = [
|
| 51 |
+
{ version = "^2.1.2", source = "pytorch", platform = "!=darwin"},
|
| 52 |
+
{ version = "^2.1.2", source = "pypi", platform = "darwin"},
|
| 53 |
+
]
|
| 54 |
+
torchvision = [
|
| 55 |
+
{ version = "^0.16.0", source = "pytorch", platform = "!=darwin"},
|
| 56 |
+
{ version = "^0.16.0", source = "pypi", platform = "darwin"},
|
| 57 |
+
]
|
| 58 |
+
numpy = "1.26.4"
|
| 59 |
+
streamlit = "^1.42.2"
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
[tool.poetry.group.dev.dependencies]
|
| 63 |
+
pytest = "^8.3.3"
|
| 64 |
+
coverage = "^7.6"
|
| 65 |
+
pytest-cov = "^6.0"
|
| 66 |
+
pylint = "^3.3.1"
|
| 67 |
+
black = "^24.10.0"
|
| 68 |
+
mypy = "^1.13.0"
|
| 69 |
+
bump2version = "^1.0.1"
|
| 70 |
+
bandit = "^1.7.10"
|
| 71 |
+
pre-commit = "^4.0.1"
|
| 72 |
+
detect-secrets = "^1.5"
|
| 73 |
+
|
| 74 |
+
[build-system]
|
| 75 |
+
requires = ["poetry-core>=1.5.0"]
|
| 76 |
+
build-backend = "poetry.core.masonry.api"
|
requirements.txt
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--extra-index-url https://download.pytorch.org/whl/cu118
|
| 2 |
+
|
| 3 |
+
altair==5.5.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 4 |
+
attrs==25.1.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 5 |
+
blinker==1.9.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 6 |
+
cachetools==5.5.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 7 |
+
certifi==2025.1.31 ; python_version >= "3.10" and python_version < "4.0"
|
| 8 |
+
charset-normalizer==3.4.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 9 |
+
click==8.1.8 ; python_version >= "3.10" and python_version < "4.0"
|
| 10 |
+
colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" and platform_system == "Windows"
|
| 11 |
+
contourpy==1.3.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 12 |
+
cycler==0.12.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 13 |
+
filelock==3.17.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 14 |
+
fonttools==4.56.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 15 |
+
fsspec==2025.2.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 16 |
+
gitdb==4.0.12 ; python_version >= "3.10" and python_version < "4.0"
|
| 17 |
+
gitpython==3.1.44 ; python_version >= "3.10" and python_version < "4.0"
|
| 18 |
+
idna==3.10 ; python_version >= "3.10" and python_version < "4.0"
|
| 19 |
+
jinja2==3.1.5 ; python_version >= "3.10" and python_version < "4.0"
|
| 20 |
+
jsonschema-specifications==2024.10.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 21 |
+
jsonschema==4.23.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 22 |
+
kiwisolver==1.4.8 ; python_version >= "3.10" and python_version < "4.0"
|
| 23 |
+
markdown-it-py==3.0.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 24 |
+
markupsafe==3.0.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 25 |
+
matplotlib==3.10.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 26 |
+
mdurl==0.1.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 27 |
+
mpmath==1.3.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 28 |
+
narwhals==1.27.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 29 |
+
networkx==3.4.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 30 |
+
numpy==1.26.4 ; python_version >= "3.10" and python_version < "4.0"
|
| 31 |
+
opencv-python==4.11.0.86 ; python_version >= "3.10" and python_version < "4.0"
|
| 32 |
+
packaging==24.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 33 |
+
pandas==2.2.3 ; python_version >= "3.10" and python_version < "4.0"
|
| 34 |
+
pillow==11.1.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 35 |
+
protobuf==5.29.3 ; python_version >= "3.10" and python_version < "4.0"
|
| 36 |
+
psutil==7.0.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 37 |
+
py-cpuinfo==9.0.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 38 |
+
pyarrow==19.0.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 39 |
+
pydeck==0.9.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 40 |
+
pygments==2.19.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 41 |
+
pyparsing==3.2.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 42 |
+
python-dateutil==2.9.0.post0 ; python_version >= "3.10" and python_version < "4.0"
|
| 43 |
+
pytz==2025.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 44 |
+
pyyaml==6.0.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 45 |
+
pyzbar==0.1.9 ; python_version >= "3.10" and python_version < "4.0"
|
| 46 |
+
qrdet==2.5 ; python_version >= "3.10" and python_version < "4.0"
|
| 47 |
+
qreader==3.12 ; python_version >= "3.10" and python_version < "4.0"
|
| 48 |
+
quadrilateral-fitter==1.12 ; python_version >= "3.10" and python_version < "4.0"
|
| 49 |
+
referencing==0.36.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 50 |
+
requests==2.32.3 ; python_version >= "3.10" and python_version < "4.0"
|
| 51 |
+
rich==13.9.4 ; python_version >= "3.10" and python_version < "4.0"
|
| 52 |
+
rpds-py==0.23.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 53 |
+
scipy==1.15.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 54 |
+
seaborn==0.13.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 55 |
+
shapely==2.0.7 ; python_version >= "3.10" and python_version < "4.0"
|
| 56 |
+
six==1.17.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 57 |
+
smmap==5.0.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 58 |
+
streamlit==1.42.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 59 |
+
sympy==1.13.3 ; python_version >= "3.10" and python_version < "4.0"
|
| 60 |
+
tenacity==9.0.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 61 |
+
thop==0.1.1.post2209072238 ; python_version >= "3.10" and python_version < "4.0"
|
| 62 |
+
toml==0.10.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 63 |
+
torch==2.1.2+cu118 ; python_version >= "3.10" and python_version < "4.0"
|
| 64 |
+
torchvision==0.16.2+cu118 ; python_version >= "3.10" and python_version < "4.0"
|
| 65 |
+
tornado==6.4.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 66 |
+
tqdm==4.67.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 67 |
+
triton==2.1.0 ; platform_system == "Linux" and platform_machine == "x86_64" and python_version >= "3.10" and python_version < "4.0"
|
| 68 |
+
typing-extensions==4.12.2 ; python_version >= "3.10" and python_version < "4.0"
|
| 69 |
+
tzdata==2025.1 ; python_version >= "3.10" and python_version < "4.0"
|
| 70 |
+
ultralytics==8.2.18 ; python_version >= "3.10" and python_version < "4.0"
|
| 71 |
+
urllib3==2.3.0 ; python_version >= "3.10" and python_version < "4.0"
|
| 72 |
+
watchdog==6.0.0 ; python_version >= "3.10" and python_version < "4.0" and platform_system != "Darwin"
|
src/deep_barcode_reader/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" It can read different types of barcodes """
|
| 2 |
+
|
| 3 |
+
__version__ = "0.3.0"
|
| 4 |
+
|
| 5 |
+
from .barcode import Wrapper, QRreader, BarcodeOpencv, BarcodeQRZbar
|
| 6 |
+
from .base import ReaderResults
|
| 7 |
+
|
| 8 |
+
__all__ = [
|
| 9 |
+
"Wrapper",
|
| 10 |
+
"QRreader",
|
| 11 |
+
"BarcodeOpencv",
|
| 12 |
+
"BarcodeQRZbar",
|
| 13 |
+
"ReaderResults",
|
| 14 |
+
]
|
src/deep_barcode_reader/barcode.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Barcode detector and reader models"""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=E1101
|
| 4 |
+
import logging
|
| 5 |
+
from typing import Any, Tuple
|
| 6 |
+
from dataclasses import dataclass, field
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
|
| 9 |
+
import numpy as np
|
| 10 |
+
import cv2
|
| 11 |
+
|
| 12 |
+
from pyzbar.pyzbar import decode
|
| 13 |
+
from qreader import QReader
|
| 14 |
+
|
| 15 |
+
from .base import BarcodeBase, ReaderResults
|
| 16 |
+
|
| 17 |
+
logger = logging.getLogger(__name__)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
@dataclass
|
| 21 |
+
class BarcodeOpencv(BarcodeBase):
|
| 22 |
+
"""This a simple 1D barcode reader using OpenCV. It can be used with
|
| 23 |
+
EAN-8, EAN-13, UPC-A and UPC-E types of barcodes"""
|
| 24 |
+
|
| 25 |
+
proto_file: Path = field(
|
| 26 |
+
init=False, default=Path("./src/deep_barcode_reader/models/sr.prototxt")
|
| 27 |
+
)
|
| 28 |
+
model_file: Path = field(
|
| 29 |
+
init=False, default=Path("./src/deep_barcode_reader/models/sr.caffemodel")
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
def __post_init__(self) -> None:
|
| 33 |
+
"""Post initialization of the barcode reader class"""
|
| 34 |
+
if not self.model_file.exists():
|
| 35 |
+
logger.error(
|
| 36 |
+
"The model file does not exist for the BarcodeOpencv model. Please check the path."
|
| 37 |
+
)
|
| 38 |
+
if not self.proto_file.exists():
|
| 39 |
+
logger.error(
|
| 40 |
+
"The proto file does not exist for the BarcodeOpencv model. Please check the path."
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
async def detect_decode(self, image: Any) -> ReaderResults:
|
| 44 |
+
"""Detect and decode barcode method from an image"""
|
| 45 |
+
result = ReaderResults(image=image)
|
| 46 |
+
barcode_reader = cv2.barcode.BarcodeDetector(
|
| 47 |
+
str(self.proto_file), str(self.model_file)
|
| 48 |
+
)
|
| 49 |
+
retval, decoded_info, barcode_types, boundary_boxs = (
|
| 50 |
+
barcode_reader.detectAndDecodeWithType(image)
|
| 51 |
+
)
|
| 52 |
+
if retval:
|
| 53 |
+
for idx, barc_txt in enumerate(decoded_info):
|
| 54 |
+
if barc_txt:
|
| 55 |
+
result.decoded_data.append(barc_txt)
|
| 56 |
+
result.bbox_data.append(
|
| 57 |
+
np.array(boundary_boxs[idx], dtype=np.int32)
|
| 58 |
+
)
|
| 59 |
+
result.decoded_types.append(barcode_types[idx])
|
| 60 |
+
if result.decoded_data:
|
| 61 |
+
logger.info(
|
| 62 |
+
"The barcode/qr code is detected and decoded successfully with BarcodeOpencv method."
|
| 63 |
+
)
|
| 64 |
+
else:
|
| 65 |
+
logger.warning(
|
| 66 |
+
"The barcode/qr code is not detected or decoded with BarcodeOpencv method."
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
return result
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
@dataclass
|
| 73 |
+
class BarcodeQRZbar(BarcodeBase):
|
| 74 |
+
"""This is a QR code and barcode reader using Zbar library"""
|
| 75 |
+
|
| 76 |
+
async def detect_decode(self, image: Any) -> ReaderResults:
|
| 77 |
+
"""Detect and decode barcode/qr method from an image"""
|
| 78 |
+
result = ReaderResults(image=image)
|
| 79 |
+
decoder = decode(image)
|
| 80 |
+
decoded_data = []
|
| 81 |
+
decoded_types = []
|
| 82 |
+
bbox_data = []
|
| 83 |
+
if decoder:
|
| 84 |
+
for decoded in decoder:
|
| 85 |
+
if decoded.data.decode("utf-8"):
|
| 86 |
+
decoded_data.append(decoded.data.decode("utf-8"))
|
| 87 |
+
decoded_types.append(decoded.type)
|
| 88 |
+
bbox_data.append(np.array(decoded.polygon, np.int64))
|
| 89 |
+
result.decoded_data = decoded_data
|
| 90 |
+
result.decoded_types = decoded_types
|
| 91 |
+
result.bbox_data = bbox_data
|
| 92 |
+
if result.decoded_data:
|
| 93 |
+
logger.info(
|
| 94 |
+
"The barcode/qr code is detected and decoded successfully with BarcodeQRZbar method."
|
| 95 |
+
)
|
| 96 |
+
else:
|
| 97 |
+
logger.warning(
|
| 98 |
+
"The barcode/qr code is not detected or decoded with BarcodeQRZbar method."
|
| 99 |
+
)
|
| 100 |
+
return result
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
@dataclass
|
| 104 |
+
class QRreader(BarcodeBase):
|
| 105 |
+
"""This is a QR code reader using Qreader library"""
|
| 106 |
+
|
| 107 |
+
model_size: str = field(default="l")
|
| 108 |
+
|
| 109 |
+
async def detect_decode(self, image: Any) -> ReaderResults:
|
| 110 |
+
"""Detect and decode barcode/qr method from an image"""
|
| 111 |
+
result = ReaderResults(image=image)
|
| 112 |
+
qr_reader = QReader(model_size=self.model_size)
|
| 113 |
+
detections = qr_reader.detect(image=image)
|
| 114 |
+
decoded_data = []
|
| 115 |
+
decoded_types = []
|
| 116 |
+
bbox_data = []
|
| 117 |
+
if detections:
|
| 118 |
+
for detection in detections:
|
| 119 |
+
decoded_text = qr_reader.decode(image, detection)
|
| 120 |
+
if decoded_text:
|
| 121 |
+
decoded_data.append(decoded_text)
|
| 122 |
+
decoded_types.append("QR")
|
| 123 |
+
bbox_data.append(np.array(detection["polygon_xy"], np.int64))
|
| 124 |
+
if decoded_data:
|
| 125 |
+
result.decoded_data = decoded_data
|
| 126 |
+
result.decoded_types = decoded_types
|
| 127 |
+
result.bbox_data = bbox_data
|
| 128 |
+
logger.info(
|
| 129 |
+
"The barcode/qr code is detected and decoded successfully with QRreader method."
|
| 130 |
+
)
|
| 131 |
+
else:
|
| 132 |
+
logger.warning(
|
| 133 |
+
"The barcode/qr code is not detected or decoded with QRreader method."
|
| 134 |
+
)
|
| 135 |
+
else:
|
| 136 |
+
logger.warning("The barcode/qr code is not detected with QRreader method.")
|
| 137 |
+
|
| 138 |
+
return result
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
@dataclass
|
| 142 |
+
class Wrapper:
|
| 143 |
+
"""Wrapper class for barcode readers"""
|
| 144 |
+
|
| 145 |
+
method: str = field(default="opencv")
|
| 146 |
+
model_size: str = field(default="l")
|
| 147 |
+
|
| 148 |
+
async def method_selection(self, image: Any, result_path: str) -> Tuple[Any, Any]:
|
| 149 |
+
"""Wrap the method selection for barcode reader"""
|
| 150 |
+
if self.method == "opencv":
|
| 151 |
+
barcode: Any = BarcodeOpencv()
|
| 152 |
+
elif self.method == "zbar":
|
| 153 |
+
barcode = BarcodeQRZbar()
|
| 154 |
+
elif self.method == "qrreader":
|
| 155 |
+
barcode = QRreader(model_size=self.model_size)
|
| 156 |
+
else:
|
| 157 |
+
barcode = BarcodeOpencv()
|
| 158 |
+
detections = await barcode.detect_decode(image)
|
| 159 |
+
result_image = await detections.visualize_results_async(result_path)
|
| 160 |
+
return detections, result_image
|
src/deep_barcode_reader/base.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""This is the base class for implementing barcode reader and decoder models"""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=E1101
|
| 4 |
+
import logging
|
| 5 |
+
from typing import Any, List, Optional
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from abc import ABC, abstractmethod
|
| 8 |
+
from dataclasses import dataclass, field
|
| 9 |
+
|
| 10 |
+
import cv2
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@dataclass
|
| 17 |
+
class BarcodeBase(ABC):
|
| 18 |
+
"""This is the base class for implementing different methods of barcode/qr code reading and decoding"""
|
| 19 |
+
|
| 20 |
+
@abstractmethod
|
| 21 |
+
async def detect_decode(self, image: Any) -> Any:
|
| 22 |
+
"""Detect and decode barcode/qr method from an image"""
|
| 23 |
+
raise NotImplementedError()
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@dataclass
|
| 27 |
+
class ReaderResults:
|
| 28 |
+
"""Dataclass for saving/visualizing barcode/qr code reader results"""
|
| 29 |
+
|
| 30 |
+
image: Any
|
| 31 |
+
decoded_data: List[str] = field(default_factory=list)
|
| 32 |
+
decoded_types: List[str] = field(default_factory=list)
|
| 33 |
+
bbox_data: List[Any] = field(default_factory=list)
|
| 34 |
+
|
| 35 |
+
def save_image(self, img: Any, file_name: str = "") -> None:
|
| 36 |
+
"""Save the image to the output path for debugging"""
|
| 37 |
+
if file_name != "":
|
| 38 |
+
Path(file_name).parent.mkdir(parents=True, exist_ok=True)
|
| 39 |
+
cv2.imwrite(file_name, img)
|
| 40 |
+
logger.info("Image is saved to %s", file_name)
|
| 41 |
+
|
| 42 |
+
async def visualize_results_async(self, file_name: str) -> Optional[Any]:
|
| 43 |
+
"""Visualize the results of the barcode/qr code reader"""
|
| 44 |
+
if self.image is None:
|
| 45 |
+
logger.error(
|
| 46 |
+
"The image is not provided as the image is not loaded properly or does not exist."
|
| 47 |
+
)
|
| 48 |
+
return None
|
| 49 |
+
img_bounding_box = self.image.copy()
|
| 50 |
+
if self.decoded_data is not None and self.bbox_data is not None:
|
| 51 |
+
for data, bbox in zip(self.decoded_data, self.bbox_data):
|
| 52 |
+
cv2.polylines(
|
| 53 |
+
img_bounding_box,
|
| 54 |
+
[bbox],
|
| 55 |
+
isClosed=True,
|
| 56 |
+
color=(0, 0, 255),
|
| 57 |
+
thickness=1,
|
| 58 |
+
)
|
| 59 |
+
cv2.putText(
|
| 60 |
+
img_bounding_box,
|
| 61 |
+
str(data),
|
| 62 |
+
(int(bbox[0][0]) - 10, int(bbox[0][1]) - 10),
|
| 63 |
+
cv2.FONT_HERSHEY_SIMPLEX,
|
| 64 |
+
0.5,
|
| 65 |
+
(0, 0, 255),
|
| 66 |
+
1,
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
self.save_image(img_bounding_box, file_name)
|
| 70 |
+
return img_bounding_box
|
src/deep_barcode_reader/logging.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Logger initialization"""
|
| 2 |
+
|
| 3 |
+
import logging
|
| 4 |
+
import logging.config
|
| 5 |
+
from typing import Any
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def config_logger(loglevel: int) -> Any:
|
| 9 |
+
"""Initialize a custom logger"""
|
| 10 |
+
default_logging_config = {
|
| 11 |
+
"version": 1,
|
| 12 |
+
"disable_existing_loggers": False,
|
| 13 |
+
"formatters": {
|
| 14 |
+
"standard": {
|
| 15 |
+
"format": "%(asctime)s - [%(levelname)s] [%(name)s.%(funcName)s:%(lineno)d (%(process)d)] | %(message)s",
|
| 16 |
+
"datefmt": "%Y-%m-%d %H:%M:%S",
|
| 17 |
+
},
|
| 18 |
+
},
|
| 19 |
+
"handlers": {
|
| 20 |
+
"console": {
|
| 21 |
+
"class": "logging.StreamHandler",
|
| 22 |
+
"formatter": "standard",
|
| 23 |
+
},
|
| 24 |
+
},
|
| 25 |
+
"root": {
|
| 26 |
+
"handlers": ["console"],
|
| 27 |
+
"level": loglevel,
|
| 28 |
+
},
|
| 29 |
+
}
|
| 30 |
+
logging.config.dictConfig(default_logging_config)
|
src/deep_barcode_reader/main.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Run the main code for Deep-Barcode-Reader"""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=E1101
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import logging
|
| 6 |
+
import asyncio
|
| 7 |
+
import click
|
| 8 |
+
import cv2
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
from deep_barcode_reader import __version__
|
| 12 |
+
from deep_barcode_reader.logging import config_logger
|
| 13 |
+
from deep_barcode_reader.barcode import Wrapper
|
| 14 |
+
|
| 15 |
+
logger = logging.getLogger(__name__)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@click.command()
|
| 19 |
+
@click.version_option(version=__version__)
|
| 20 |
+
@click.option(
|
| 21 |
+
"-v",
|
| 22 |
+
"--verbose",
|
| 23 |
+
count=True,
|
| 24 |
+
help="Shorthand for info/debug/warning/error loglevel (-v/-vv/-vvv/-vvvv)",
|
| 25 |
+
)
|
| 26 |
+
@click.option(
|
| 27 |
+
"-s",
|
| 28 |
+
"--result_path",
|
| 29 |
+
type=str,
|
| 30 |
+
default="output/test.png",
|
| 31 |
+
help="Path to save the result file.",
|
| 32 |
+
)
|
| 33 |
+
@click.option(
|
| 34 |
+
"-d",
|
| 35 |
+
"--data_path",
|
| 36 |
+
required=True,
|
| 37 |
+
type=click.Path(exists=True),
|
| 38 |
+
help="Path to data file.",
|
| 39 |
+
)
|
| 40 |
+
@click.option(
|
| 41 |
+
"-m",
|
| 42 |
+
"--method",
|
| 43 |
+
type=click.Choice(["opencv", "zbar", "qrreader"], case_sensitive=False),
|
| 44 |
+
default="opencv",
|
| 45 |
+
help="Path to data file.",
|
| 46 |
+
)
|
| 47 |
+
@click.option(
|
| 48 |
+
"--model_size",
|
| 49 |
+
type=click.Choice(["n", "s", "m", "l"], case_sensitive=False),
|
| 50 |
+
default="m",
|
| 51 |
+
help="Model size for the barcode reader.",
|
| 52 |
+
)
|
| 53 |
+
def deep_barcode_reader_cli(
|
| 54 |
+
verbose: int, result_path: str, data_path: Path, method: str, model_size: str
|
| 55 |
+
) -> None:
|
| 56 |
+
"""It can read different types of barcodes"""
|
| 57 |
+
if verbose == 1:
|
| 58 |
+
log_level = 10
|
| 59 |
+
elif verbose == 2:
|
| 60 |
+
log_level = 20
|
| 61 |
+
elif verbose == 3:
|
| 62 |
+
log_level = 30
|
| 63 |
+
else:
|
| 64 |
+
log_level = 40
|
| 65 |
+
config_logger(log_level)
|
| 66 |
+
|
| 67 |
+
reader = Wrapper(model_size=model_size, method=method)
|
| 68 |
+
_ = asyncio.get_event_loop().run_until_complete(
|
| 69 |
+
reader.method_selection(
|
| 70 |
+
image=cv2.imread(str(data_path)), result_path=result_path
|
| 71 |
+
)
|
| 72 |
+
)
|
src/deep_barcode_reader/models/sr.caffemodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e5d36889d8e6ef2f1c1f515f807cec03979320ac81792cd8fb927c31fd658ae3
|
| 3 |
+
size 23929
|
src/deep_barcode_reader/models/sr.prototxt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8ae41acba97e8b4a8e741ee350481e49b8e01d787193f470a4c95ee1c02d5b61
|
| 3 |
+
size 5984
|
tests/__init__.py
ADDED
|
File without changes
|
tests/test_data/sample.jpg
ADDED
|
Git LFS Details
|
tests/test_deep_barcode_reader.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Package level tests"""
|
| 2 |
+
|
| 3 |
+
from deep_barcode_reader import __version__
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def test_version() -> None:
|
| 7 |
+
"""Unit test for checking the version of the code"""
|
| 8 |
+
assert __version__ == "0.3.0"
|