├── .gitignore
├── LICENCE
├── README.md
├── assets
└── main-preview.jpg
├── requirements.txt
├── setup.py
├── src
├── depixelate.py
├── gaussian.py
├── lut.py
├── preview.py
└── shape.py
└── tests
├── __init__.py
├── assets
└── cat.jpg
└── test_shape.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | images/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | pip-wheel-metadata/
25 | share/python-wheels/
26 | *.egg-info/
27 | .installed.cfg
28 | *.egg
29 | MANIFEST
30 |
31 | # PyInstaller
32 | # Usually these files are written by a python script from a template
33 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
34 | *.manifest
35 | *.spec
36 |
37 | # Installer logs
38 | pip-log.txt
39 | pip-delete-this-directory.txt
40 |
41 | # Unit test / coverage reports
42 | htmlcov/
43 | .tox/
44 | .nox/
45 | .coverage
46 | .coverage.*
47 | .cache
48 | nosetests.xml
49 | coverage.xml
50 | *.cover
51 | *.py,cover
52 | .hypothesis/
53 | .pytest_cache/
54 |
55 | # Translations
56 | *.mo
57 | *.pot
58 |
59 | # Django stuff:
60 | *.log
61 | local_settings.py
62 | db.sqlite3
63 | db.sqlite3-journal
64 |
65 | # Flask stuff:
66 | instance/
67 | .webassets-cache
68 |
69 | # Scrapy stuff:
70 | .scrapy
71 |
72 | # Sphinx documentation
73 | docs/_build/
74 |
75 | # PyBuilder
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | .python-version
87 |
88 | # pipenv
89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
92 | # install all needed dependencies.
93 | #Pipfile.lock
94 |
95 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
96 | __pypackages__/
97 |
98 | # Celery stuff
99 | celerybeat-schedule
100 | celerybeat.pid
101 |
102 | # SageMath parsed files
103 | *.sage.py
104 |
105 | # Environments
106 | .env
107 | .venv
108 | env/
109 | venv/
110 | ENV/
111 | env.bak/
112 | venv.bak/
113 |
114 | # Spyder project settings
115 | .spyderproject
116 | .spyproject
117 |
118 | # Rope project settings
119 | .ropeproject
120 |
121 | # mkdocs documentation
122 | /site
123 |
124 | # mypy
125 | .mypy_cache/
126 | .dmypy.json
127 | dmypy.json
128 |
129 | # Pyre type checker
130 | .pyre/
131 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Vilmacio M. Silva
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Depixelate
2 | Upscaling and enhance low resolution images.
3 |
4 |
5 |
6 | ## Usage
7 | First of all, you might want to check out the [Online Demo](https://vilmacio.github.io/depixelate-app/). Once done, let's code.
8 |
9 | ```console
10 | $ pip install depixelate
11 | ```
12 |
13 | ```python
14 | import cv2
15 | import depixelate
16 |
17 | original_image = cv2.imread('image.jpg')
18 |
19 | result = depixelate.apply(original_image, 7, 600)
20 | ```
21 |
22 | ### Params
23 | The **apply** method accepts 3 parameters:
24 |
25 | - **Image (Required)**: Original image to be changed.
26 | - **Weight (Optional)**: Indicates the power of the gaussian-blur algorithm. The higher the value, the more shape distortion. It must be between 1 and 10. Default value is 6.
27 | - **Width Scale (Optional)**: Indicates the width of the output image. Default value is 800.
28 |
29 | ## Limitations
30 | Depixelate library helps with logos and some graphics, but it doesn't help when maximum sharpness and detail is needed.
31 | In the majority of cases, you'll want to improve the quality of low resolution images (about 300 pixels wide or less). Otherwise you might be a little disappointed.
32 |
33 | ## Plans
34 |
35 | - [ ] [Depixelizing Pixel Art](https://johanneskopf.de/publications/pixelart/paper/pixel.pdf) Implementation
36 |
--------------------------------------------------------------------------------
/assets/main-preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vilmacio/depixelate/909ce21baf997db664c83ec85fe204d835802a02/assets/main-preview.jpg
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | opencv-python==4.6.0.66
2 | numpy==1.23.1
3 | Pillow==9.2.0
4 | pytest==7.1.2
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | with open("README.md", "r") as fh:
4 | long_description = fh.read()
5 |
6 | setuptools.setup(
7 | name="depixelate", # This is the name of the package
8 | version="1.0.6", # The initial release version
9 | author="Vilmacio M. Silva", # Full name of the author
10 | description="Depixelate images",
11 | long_description=long_description, # Long description read from the the readme file
12 | long_description_content_type="text/markdown",
13 | packages=setuptools.find_packages('src'), # List of all python modules to be installed
14 | classifiers=[
15 | "Programming Language :: Python :: 3",
16 | "License :: OSI Approved :: MIT License",
17 | "Operating System :: OS Independent",
18 | ], # Information to filter the project on PyPi website
19 | python_requires='>=3.6', # Minimum version requirement of the package
20 | py_modules=["depixelate", "gaussian", "lut", "shape"], # Name of the python package
21 | package_dir={'':'src'}, # Directory of the source code of the package
22 | install_requires=['opencv-python', 'numpy', 'pytest', 'Pillow'] # Install other dependencies if any
23 | )
--------------------------------------------------------------------------------
/src/depixelate.py:
--------------------------------------------------------------------------------
1 | import gaussian
2 | import shape
3 | import lut
4 | import numpy
5 | from PIL import Image
6 | import cv2
7 |
8 | def apply(image, weight = 6, output_scale = 800):
9 |
10 | pil_image = Image.fromarray(image).convert('RGBA')
11 |
12 | width, height = pil_image.size
13 |
14 | image = numpy.array(pil_image)
15 |
16 | image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
17 |
18 | hr_image = shape.resize(image, (width, height))
19 |
20 | image_smothing = gaussian.apply(hr_image, weight)
21 |
22 | lut_result = lut.apply(image_smothing)
23 |
24 | result = shape.resize(lut_result, (width, height), output_scale)
25 |
26 | return result
27 |
--------------------------------------------------------------------------------
/src/gaussian.py:
--------------------------------------------------------------------------------
1 | import cv2
2 |
3 | def apply(image, weight = 5):
4 | result = image
5 | ksizes = [1, 1]
6 | max_applies = (weight // 2) + 1
7 |
8 | if (weight < 1 or weight > 10):
9 | raise Exception("Weight must be between 1 and 10. The received value was {}".format(weight))
10 |
11 | ksizes[0] = ksizes[1] = weight * 2
12 |
13 | if (ksizes[0] % 2 == 0):
14 | ksizes[0] = ksizes[1] = ksizes[1] - 1
15 |
16 | for _ in range(max_applies):
17 | result = cv2.GaussianBlur(result, ksizes, cv2.BORDER_DEFAULT)
18 |
19 | return result
20 |
--------------------------------------------------------------------------------
/src/lut.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy
3 |
4 | def apply(image):
5 | lut_in = [0, 255, 80]
6 | lut_out = [0, 0, 255]
7 |
8 | model = numpy.arange(0, 256)
9 |
10 | lut_8u = numpy.interp(model, lut_in, lut_out).astype(numpy.uint8)
11 | result = cv2.LUT(image, lut_8u)
12 |
13 | return result
14 |
--------------------------------------------------------------------------------
/src/preview.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy
3 | import shape
4 | import depixelate
5 |
6 | src = cv2.imread('assets/cat.jpg', cv2.IMREAD_UNCHANGED)
7 | mr_image = shape.resize(src, (110, 110), 600)
8 |
9 | result = depixelate.apply(src, 8, 600)
10 |
11 | cv2.imshow('Depixelate Preview', numpy.hstack((mr_image, result)))
12 | cv2.waitKey(0)
13 | cv2.destroyAllWindows()
14 |
--------------------------------------------------------------------------------
/src/shape.py:
--------------------------------------------------------------------------------
1 | import cv2
2 |
3 | def resize(image, original_size, width_scale = 1000):
4 | print(original_size)
5 | width, height = original_size
6 |
7 | ratio = width / height
8 |
9 | additional_width = width_scale - width
10 |
11 | additional_height = additional_width // ratio
12 |
13 | print(type(image))
14 |
15 | hr_image = cv2.resize(image, (int(width + additional_width), int(height + additional_height)))
16 |
17 | return hr_image
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vilmacio/depixelate/909ce21baf997db664c83ec85fe204d835802a02/tests/__init__.py
--------------------------------------------------------------------------------
/tests/assets/cat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vilmacio/depixelate/909ce21baf997db664c83ec85fe204d835802a02/tests/assets/cat.jpg
--------------------------------------------------------------------------------
/tests/test_shape.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import cv2
3 | import src.shape
4 |
5 | class TestSuite(unittest.TestCase):
6 |
7 | def test_dimensions(self):
8 | sut = src.shape.resize
9 | width_scale = 300
10 |
11 | img = cv2.imread('assets/cat.jpg')
12 |
13 | original_height, original_width, *_ = img.shape
14 |
15 | img_result = sut(img, width_scale)
16 |
17 | height, width, *_ = img_result.shape
18 |
19 | assert width == width_scale
20 | assert width != original_width
21 | assert height != original_height
22 |
23 | def test_invalid_params(self):
24 | sut = src.shape.resize
25 |
26 | self.assertRaises(AttributeError, sut, None, 300)
27 |
--------------------------------------------------------------------------------