Skip to content

Commit

Permalink
Add noise filter
Browse files Browse the repository at this point in the history
  • Loading branch information
srwi committed Jan 28, 2024
1 parent 0024251 commit ff8e0ba
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 12 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
# FalsiSignPy

Simulate the look of hand signed and scanned documents. FalsiSignPy is inspired
by [FalsiSign](https://gitlab.com/edouardklein/falsisign) by Edouard Klein.

## Installation

Use the package manager [pip](https://pip.pypa.io/en/stable/) to install FalsiSignPy.

```bash
pip install FalsiSignPy
```

## Usage

Before using FalsiSignPy, a collection of real signature images is needed. This can be
done by scanning and cropping signatures and saving them into a folder.

Afterwards, FalsiSignPy can be started with `falsisignpy`.

## License

FalsiSignPy is licensed under the [MIT](https://github.com/srwi/FalsiSignPy/blob/master/LICENSE) license.
21 changes: 11 additions & 10 deletions falsisignpy/falsisignpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from enum import Enum
from typing import Any, Callable, Dict, Optional, Tuple

import filter
import PySimpleGUI as sg
import scanner
import utils
from pdf import PDF
from PIL import Image
Expand Down Expand Up @@ -36,10 +36,11 @@ def __init__(self) -> None:
self._mode: Mode = Mode.EDIT

self._filters = [
scanner.Grayscale("Grayscale", enabled=True),
scanner.Blur("Blur", enabled=False, initial_strength=1, strength_range=(0, 20)),
scanner.Rotate("Random rotate", enabled=True, initial_strength=1, strength_range=(0, 10)),
scanner.AutoContrast("Autocontrast cutoff", enabled=True, initial_strength=2, strength_range=(0, 45)),
filter.Grayscale("Grayscale", enabled=True),
filter.Noise("Noise", enabled=False, initial_strength=0.1, strength_range=(0, 1)),
filter.Blur("Blur", enabled=False, initial_strength=1, strength_range=(0, 5)),
filter.Rotate("Random rotate", enabled=True, initial_strength=1, strength_range=(0, 10)),
filter.AutoContrast("Autocontrast cutoff", enabled=True, initial_strength=2, strength_range=(0, 45)),
]

def _create_window(self) -> sg.Window:
Expand All @@ -54,7 +55,7 @@ def _create_window(self) -> sg.Window:
signature_options = [
[
sg.Text("Selected signature:"),
sg.Combo([], key="-DROPDOWN-", enable_events=True, readonly=True, expand_x=True),
sg.Combo([], key="-DROPDOWN-", enable_events=True, readonly=True, expand_x=True, disabled=True),
sg.FolderBrowse("Browse", key="-SIGNATURE-BROWSE-", target="-SIGNATURE-BROWSE-", enable_events=True),
],
[
Expand Down Expand Up @@ -198,7 +199,7 @@ def _load_signatures(self, values: Dict[str, Any]) -> None:
return

signature_filenames = list(signatures.keys())
self._window["-DROPDOWN-"].update(values=signature_filenames, set_to_index=0)
self._window["-DROPDOWN-"].update(values=signature_filenames, set_to_index=0, disabled=False)
self._select_signature(signatures[signature_filenames[0]])
self._loaded_signatures = signatures

Expand Down Expand Up @@ -402,11 +403,11 @@ def _on_window_resized(self, _: Dict[str, Any]) -> None:
current_page = self._pdf.get_page_image(self._current_page, signed=self._mode == Mode.PREVIEW)
self._update_page(current_page)

def _set_filter_enabled(self, filter_: scanner.Filter, value: bool) -> None:
def _set_filter_enabled(self, filter_: filter.Filter, value: bool) -> None:
filter_.set_enabled(value)
self._update_current_page()

def _set_filter_strength(self, filter_: scanner.Filter, value: float) -> None:
def _set_filter_strength(self, filter_: filter.Filter, value: float) -> None:
filter_.set_strength(value)
self._update_current_page()

Expand Down Expand Up @@ -466,7 +467,7 @@ def start(self) -> None:
if __name__ == "__main__":
# Enable DPI awareness on Windows 8 and above
if os.name == "nt" and int(platform.release()) >= 8:
ctypes.windll.shcore.SetProcessDpiAwareness(True)
ctypes.windll.shcore.SetProcessDpiAwareness(True) # type: ignore

app = FalsiSignPy()
app.start()
12 changes: 12 additions & 0 deletions falsisignpy/scanner.py → falsisignpy/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import random
from typing import Optional, Tuple

import numpy as np
from PIL import Image, ImageFilter, ImageOps
from PIL.Image import Resampling

Expand Down Expand Up @@ -81,3 +82,14 @@ def _apply(self, image: Image.Image) -> Image.Image:

random_strength = random.uniform(-float(self.strength), float(self.strength))
return image.rotate(angle=random_strength, fillcolor="white", resample=Resampling.BILINEAR)


class Noise(Filter):
def _apply(self, image: Image.Image) -> Image.Image:
if self.strength is None:
return image

image_array = np.array(image)
salt_mask = np.random.random(image_array.shape[:2]) < (self.strength / 1000)
image_array[salt_mask] = np.random.randint(0, 255)
return Image.fromarray(image_array)
4 changes: 2 additions & 2 deletions falsisignpy/pdf.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import pathlib as pl
from typing import Dict, List

import filter
import fitz
import scanner
from PIL import Image
from signature import Signature

Expand Down Expand Up @@ -51,7 +51,7 @@ def clear_page_signatures(self, page_number: int) -> None:

self._signatures[page_number] = {}

def save(self, path: pl.Path, filters: List[scanner.Filter]) -> None:
def save(self, path: pl.Path, filters: List[filter.Filter]) -> None:
if len(self._pages) == 0:
raise RuntimeError("Can not save empty document.")

Expand Down

0 comments on commit ff8e0ba

Please sign in to comment.