Skip to content

Commit

Permalink
Implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
JanPalasek committed Oct 18, 2021
1 parent c617028 commit f2db4b4
Show file tree
Hide file tree
Showing 26 changed files with 473 additions and 42 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,8 @@ fabric.properties
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
.idea/caches/build_file_checksums.ser

# logs
logs/**/*
!logs/.keep
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"python.pythonPath": "venv/bin/python3",
"python.pythonPath": "venv/bin/python",

"python.linting.pylintEnabled": true,
"python.languageServer": "Pylance",

"python.testing.pytestArgs": [
Expand Down
63 changes: 45 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@
# Python Data Science Template
## How to install
# Fast Reflection Removal
Removes reflections quickly and easily.

![demo](docs/demo.png)

## How to install
**Prerequisities**
- Python: 3.8

### As a package
In your own project, just perform the following command:
```bash
# python refers to the virtual environment to install package to
python -m pip install git+https://github.com/JanPalasek/fast-reflection-removal
```

Now, you can use the reflection removal in your project in the following manner:
```python
from frr.core import FastReflectionRemoval

...
# numpy array with values between 0 and 1 of shape (H, W, C)
img = ...
# instantiate the algoroithm class
alg = FastReflectionRemoval(h = 0.11)
# run the algorithm and get result of shape (H, W, C)
dereflected_img = alg.remove_reflection(img)


...
```

### As a repository
1. Clone the project and go to its root directory.
2. Create and activate the virtual environment:
```bash
Expand All @@ -19,31 +46,31 @@
```
4. Install the packages from requirements and the project:
```bash
# install from 'requirements.txt'
python -m piptools sync
# install the current modules
python -m pip install -e .
make sync # on windows just perform the following commands: python -m piptools sync requirements.txt; python -m pip install -e .
```

## How to work with pip-tools
Pip-tools has 2 operations: *compile* and *sync*.
## How to run the project
To run the project, we need to run script bin/run.py. The important parameters of this script are:
- ***h***: larger h leads to more reflections removed, but the result image will be blurrier,
- ***input_path***: path to the input image,
- ***output_path***: path to the output image.

1. **compile**: Generates *requirements.in* into *requirements.txt*. Do not modify requirements.txt manually, it must be generated.
Example:
```bash
python -m piptools compile
# activate the environment
source venv/bin/activate # on Windows ./venv/Scripts/activate.ps1 in Powershell
python bin/run.py --h=0.11 --input_path="docs/toy_example.jpg" --output_path="docs/toy_example_out.jpg"
```

2. **sync**: Based on *requirements.txt*, updates the installed packages of the virtual environment. If you keep your local packages of the project (installed by pip install -e .) separately, after this step you need to install them again with the same command. If you add them to *requirements.in*, then it is generated as an absolute path into *requirements.txt* and the project is not well distributable.
```bash
python -m piptools sync
# (optionally) python -m pip install -e .
```
The program, in this example, loads input image from the path docs/toy_example.jpg, processes it with parameter h=0.11 and outputs it into docs/toy_example_out.jpg.


## Project structure
Folders:
- *bin*: executable python files. They should be included into the root namespace by setup.py scripts.
- *config*: configuration files, usually in yaml or ini.
- *bin*: executable python files.
- *docs*: documentation.
- *notebooks*: jupyter notebooks for analysis etc.
- *src*: contains list of folders for sources, e.g. python.
- *tests*: follows the structure of src/python.

## Credits
This repository implements paper [Fast Single Image Reflection Suppression via Convex Optimization](https://arxiv.org/pdf/1903.03889.pdf) authored by Yang Yang, Wenye Ma, Yin Zheng, Jian-Feng Cai and Weiyu Xu.
28 changes: 28 additions & 0 deletions bin/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from frr.core import FastReflectionRemoval
from frr.utils import FileWriter
import matplotlib.pyplot as plt
import argparse

def main(args):
frr = FastReflectionRemoval(h=args.h, debug_writer=FileWriter(path="logs") if args.debug else None)

# read image and normalize it into [0, 1]
img = plt.imread(args.input_path) / 255

# remove reflection
result_img = frr.remove_reflection(img)

# store image
plt.imsave(args.output_path, result_img)


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--input_path", default=None, type=str)
parser.add_argument("--output_path", default=None, type=str)
parser.add_argument("--debug", action="store_true", default=None)

parser.add_argument("--h", default=0.03, type=float)
args = parser.parse_args()

main(args)
Binary file added docs/demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/demo.psd
Binary file not shown.
Binary file added docs/error_screen.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/error_screen_out.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/toy_example.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/toy_example_out.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/train2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/train2_out.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
14 changes: 14 additions & 0 deletions makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
PYTHON="venv/bin/python"

install:
@ ${PYTHON} -m pip install -r requirements.txt

compile:
@ ${PYTHON} -m piptools compile requirements.in

sync:
@ ${PYTHON} -m piptools sync requirements.txt
@ ${PYTHON} -m pip install -e .

clean_logs:
@ rm -r logs && mkdir logs && touch logs/.keep
Empty file removed notebooks/.keep
Empty file.
8 changes: 6 additions & 2 deletions requirements.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
pylint==2.7.4
#pylint==2.7.4
autopep8==1.5.6
pytest==6.2.3
pytest==6.2.3
numpy==1.21.2
scipy==1.7.1
matplotlib==3.4.3
argparse==1.4.0
46 changes: 29 additions & 17 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,41 +1,53 @@
#
# This file is autogenerated by pip-compile
# This file is autogenerated by pip-compile with python 3.8
# To update, run:
#
# pip-compile
# pip-compile requirements.in
#
astroid==2.5.6
# via pylint
argparse==1.4.0
# via -r requirements.in
attrs==21.2.0
# via pytest
autopep8==1.5.6
# via -r requirements.in
cycler==0.10.0
# via matplotlib
iniconfig==1.1.1
# via pytest
isort==5.8.0
# via pylint
lazy-object-proxy==1.6.0
# via astroid
mccabe==0.6.1
# via pylint
kiwisolver==1.3.2
# via matplotlib
matplotlib==3.4.3
# via -r requirements.in
numpy==1.21.2
# via
# -r requirements.in
# matplotlib
# scipy
packaging==20.9
# via pytest
pillow==8.4.0
# via matplotlib
pluggy==0.13.1
# via pytest
py==1.10.0
# via pytest
pycodestyle==2.7.0
pycodestyle==2.8.0
# via autopep8
pylint==2.7.4
# via -r requirements.in
pyparsing==2.4.7
# via packaging
# via
# matplotlib
# packaging
pytest==6.2.3
# via -r requirements.in
python-dateutil==2.8.2
# via matplotlib
scipy==1.7.1
# via -r requirements.in
six==1.16.0
# via
# cycler
# python-dateutil
toml==0.10.2
# via
# autopep8
# pylint
# pytest
wrapt==1.12.1
# via astroid
10 changes: 8 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@
long_description = fh.read()

setuptools.setup(
name='template_project',
name='fast-reflection-removal',
version='0.1a',
description="",
long_description=long_description,
long_description_content_type="text/markdown",
package_dir={
"": "src/python"
},
install_requires=[
"argparse~=1.4.0",
"numpy~=1.21.2",
"scipy~=1.7.1",
"matplotlib~=3.4",
],
packages=setuptools.find_packages("src/python"),
scripts=[],
scripts=["bin/run.py"],
python_requires="~=3.8"
)
Empty file removed src/python/.keep
Empty file.
File renamed without changes.
71 changes: 71 additions & 0 deletions src/python/frr/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import numpy as np
from typing import Tuple
from scipy.fftpack import dct, idct

def gradient(A: np.ndarray, append_zeros=False) -> Tuple[np.ndarray]:
"""
Computes gradients of input numpy array. Returns tuple, where the first
part is gradient in direction of axis 0 (rows), then axis 1 (columns),...
Args:
f (np.ndarray): Input numpy array.
Returns:
Returns tuple of numpy arrays denoting gradients in different directions.
"""

rows, cols = A.shape

grad_x = np.zeros_like(A)
grad_x[:, 0: cols - 1] = np.diff(A, axis=1)

grad_y = np.zeros_like(A)
grad_y[0:rows - 1, :] = np.diff(A, axis=0)

B = np.concatenate((grad_x[..., np.newaxis], grad_y[..., np.newaxis]), axis=-1)

return B

def divergence(A):
m, n, _ = A.shape
B = np.zeros(shape=(m, n))

T = A[:, :, 0]
T1 = np.zeros(shape=(m, n))
T1[:, 1:n] = T[:, 0:n-1]

B = B + T - T1

T = A[:, :, 1]
T1 = np.zeros(shape=(m, n))
T1[1:m, :] = T[0:m-1, :]

B = B + T - T1
return B


def laplacian(f, h: float = None):
rows, cols = f.shape
dims = 2

grads = gradient(f)

if h is not None:
# remove edges (gradients) smaller than 0
# norm = np.sqrt(np.sum(grads * grads, axis=-1))
norm = np.linalg.norm(grads, axis=-1)

mask = (norm < h)[..., np.newaxis].repeat(dims, axis=-1)
grads[mask] = 0

# and compute its divergence by summing the second-order gradients
laplacian = divergence(grads)

return laplacian


def dct2(block):
return dct(dct(block.T, norm='ortho').T, norm='ortho')

def idct2(block):
return idct(idct(block.T, norm='ortho').T, norm='ortho')
Loading

0 comments on commit f2db4b4

Please sign in to comment.