Skip to content

Commit

Permalink
Feature plugginability (#6)
Browse files Browse the repository at this point in the history
* WIP - migrating from hardcoded model loading/usage to plugins

* Abstracted logic for `OCRBoxModel`s

* Reimplemented proper usage of ENV variables in the premade plugins.

* Added entrypoints to JSON of validated models.

* WIP - remaking tests to reflect changes

* Fixed tests for 100% coverage

* Updated README with info on writing plugins

* Bump version number

* Added changelog file

* pylint

* Fix needed to carry entrypoints with pyinstaller
  • Loading branch information
Crivella authored Sep 17, 2023
1 parent bcbd292 commit b28a143
Show file tree
Hide file tree
Showing 46 changed files with 2,478 additions and 1,871 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Change Log

List of changes between versions

## 0.2.0

Restructured the code to make it pluginable.
No change should be noticeable from a user experience point of view, but now it should be much easier to contribute to the code (new functionalities can be introduced by writing a plugin without having to modify this codebase).

- The models entries in the database now requires an `entrypoint` field to identify which model should be used to load it.
- The functionality related to `easyocr`, `tesseract` and `hugginface` models have been moved to the `ocr_translate/plugins` folder, and are now plugins (kept in the main codebase to leave an example on how a plugin can work).

## 0.1.4
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ Unzip the file and from inside the folder, run the `run_server-XXX.exe` file (XX
The server will run with sensible defaults. Most notably the models files and database will be downloaded/created under `%userprofile%/.ocr_translate`.
Also the gpu version will attempt to run on GPU by default, and fall-back to CPU if the former is not available.

For customization, you can set the [environment variable](#environment-variables) yourself, either via powershell or by searching for *environment variable* in the settings menu.
For customization, you can set the [environment variable](#environment-variables) yourself:

- Linux: use `export ENV_VAR_NAME=XXX` before launching the code from terminal.
- Windows: either via powershell or by searching for *environment variable* in the settings menu.

### From Github installation

Expand Down Expand Up @@ -147,6 +150,32 @@ before installing the python package.

- Hugging Face [Seq2Seq](https://huggingface.co/learn/nlp-course/chapter1/7) models

## Writing plugins for the server

Since version 0.2.0 the server has been made pluginable.
You can write a plugin for a model/web-service that has not yet been implemented, by subclassing the following models

- `ocr_translate.models.OCRBoxModel`: Must define the following methods (see the base models and the plugins under `ocr_translate.plugins` for example of function signature and expected input outputs):
- `load`: function to load the model into memory. Can be defined to do nothing if not needed (e.g. another library that load the model on import or a plugin for a web-service)
- `unload`: function to unload the model from memory.
- `_box_detection`: Function that takes a PIL image as input and returns a list of bounding boxes.
- `ocr_translate.models.OCRModel`: Must define the following methods (see the base models and the plugins under `ocr_translate.plugins` for example of function signature and expected input outputs):
- `load`: function to load the model into memory. Can be defined to do nothing if not needed (e.g. another library that load the model on import or a plugin for a web-service)
- `unload`: function to unload the model from memory.
- `_ocr`: Function that takes an image as input and returns the OCRed text (the image is the content of the bounding generated by a BOX model run)
- `ocr_translate.models.TSLModel`: Must define the following methods (see the base models and the plugins under `ocr_translate.plugins` for example of function signature and expected input outputs):
- `load`: function to load the model into memory. Can be defined to do nothing if not needed (e.g. another library that load the model on import or a plugin for a web-service)
- `unload`: function to unload the model from memory.
- `_translate`: Function that takes a list of tokens or a list(list(tokens)) as inputs and returns the translated text as output either as a `str` or `list[str]` (this is needed to work efficiently with AI models that can perform multiple translations simultaneously)

NOTE:
When subclassing the following has to be set inside the class (see [django models doc](https://docs.djangoproject.com/en/4.2/topics/db/models/#proxy-models))

class Meta:
proxy = True

Until there is a registry service, do contact me if you write a plugin for the server so i can add a link to it in this README

## Endpoints

This is not a REST API. As of now the communication between the server and a front-end is stateful and depend on the languages and models currently loaded on the server.
Expand Down
2 changes: 2 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ pyinstaller \
--icon icon.ico \
--add-data "ocr_translate/ocr_tsl/languages.json:ocr_translate/ocr_tsl" \
--add-data "ocr_translate/ocr_tsl/models.json:ocr_translate/ocr_tsl" \
--collect-all djang-ocr_translate \
--collect-all torch \
--collect-all torchvision \
--collect-all transformers \
--collect-all unidic_lite \
--collect-all sacremoses \
--collect-all sentencepiece \
--recursive-copy-metadata djang-ocr_translate \
--recursive-copy-metadata torch \
--recursive-copy-metadata torchvision \
--recursive-copy-metadata transformers \
Expand Down
2 changes: 1 addition & 1 deletion ocr_translate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
###################################################################################
"""OCR and translation of images."""

__version__ = '0.1.4'
__version__ = '0.2.0'
18 changes: 18 additions & 0 deletions ocr_translate/migrations/0006_tslmodel_entrypoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.4 on 2023-08-03 13:30

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('ocr_translate', '0002_ocrboxmodel_default_options_ocrmodel_default_options_and_more_squashed_0005_alter_ocrboxmodel_default_options_and_more'),
]

operations = [
migrations.AddField(
model_name='tslmodel',
name='entrypoint',
field=models.CharField(max_length=128, null=True),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 4.2.4 on 2023-08-03 14:48

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('ocr_translate', '0006_tslmodel_entrypoint'),
]

operations = [
migrations.AddField(
model_name='ocrmodel',
name='entrypoint',
field=models.CharField(max_length=128, null=True),
),
migrations.AlterField(
model_name='ocrmodel',
name='default_options',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='used_by_%(class)s', to='ocr_translate.optiondict'),
),
migrations.AlterField(
model_name='tslmodel',
name='default_options',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='used_by_%(class)s', to='ocr_translate.optiondict'),
),
]
24 changes: 24 additions & 0 deletions ocr_translate/migrations/0008_ocrboxmodel_entrypoint_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2.4 on 2023-08-03 16:27

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('ocr_translate', '0007_ocrmodel_entrypoint_alter_ocrmodel_default_options_and_more'),
]

operations = [
migrations.AddField(
model_name='ocrboxmodel',
name='entrypoint',
field=models.CharField(max_length=128, null=True),
),
migrations.AlterField(
model_name='ocrboxmodel',
name='default_options',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='used_by_%(class)s', to='ocr_translate.optiondict'),
),
]
Loading

0 comments on commit b28a143

Please sign in to comment.