mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-23 21:11:08 +00:00
Added Burkes dithering algorithm. (#1916)
* Added Burkes dithering algorithm * Added unit tests for burkes algorithm * Fix burkes algorithm * Added some additional information * Fixed CI tests * Update digital_image_processing/dithering/burkes.py Co-Authored-By: Christian Clauss <cclauss@me.com> * Update digital_image_processing/dithering/burkes.py Co-Authored-By: Christian Clauss <cclauss@me.com> * Update digital_image_processing/dithering/burkes.py Co-Authored-By: Christian Clauss <cclauss@me.com> * Propogate the += and add a doctest * Fix doctest * @staticmethod --> @ classmethod to ease testing * def test_burkes(file_path): * Fix for mypy checks * Fix variable order in get_greyscale * Fix get_greyscale method * Fix get_greyscale method * 3.753 Co-authored-by: Christian Clauss <cclauss@me.com>
This commit is contained in:
parent
fbc038d532
commit
3d0680eddf
0
digital_image_processing/dithering/__init__.py
Normal file
0
digital_image_processing/dithering/__init__.py
Normal file
87
digital_image_processing/dithering/burkes.py
Normal file
87
digital_image_processing/dithering/burkes.py
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
"""
|
||||||
|
Implementation Burke's algorithm (dithering)
|
||||||
|
"""
|
||||||
|
from cv2 import destroyAllWindows, imread, imshow, waitKey
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class Burkes:
|
||||||
|
"""
|
||||||
|
Burke's algorithm is using for converting grayscale image to black and white version
|
||||||
|
Source: Source: https://en.wikipedia.org/wiki/Dither
|
||||||
|
|
||||||
|
Note:
|
||||||
|
* Best results are given with threshold= ~1/2 * max greyscale value.
|
||||||
|
* This implementation get RGB image and converts it to greyscale in runtime.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, input_img, threshold: int):
|
||||||
|
self.min_threshold = 0
|
||||||
|
# max greyscale value for #FFFFFF
|
||||||
|
self.max_threshold = int(self.get_greyscale(255, 255, 255))
|
||||||
|
|
||||||
|
if not self.min_threshold < threshold < self.max_threshold:
|
||||||
|
raise ValueError(f"Factor value should be from 0 to {self.max_threshold}")
|
||||||
|
|
||||||
|
self.input_img = input_img
|
||||||
|
self.threshold = threshold
|
||||||
|
self.width, self.height = self.input_img.shape[1], self.input_img.shape[0]
|
||||||
|
|
||||||
|
# error table size (+4 columns and +1 row) greater than input image because of
|
||||||
|
# lack of if statements
|
||||||
|
self.error_table = [
|
||||||
|
[0 for _ in range(self.height + 4)] for __ in range(self.width + 1)
|
||||||
|
]
|
||||||
|
self.output_img = np.ones((self.width, self.height, 3), np.uint8) * 255
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_greyscale(cls, blue: int, green: int, red: int) -> float:
|
||||||
|
"""
|
||||||
|
>>> Burkes.get_greyscale(3, 4, 5)
|
||||||
|
3.753
|
||||||
|
"""
|
||||||
|
return 0.114 * blue + 0.587 * green + 0.2126 * red
|
||||||
|
|
||||||
|
def process(self) -> None:
|
||||||
|
for y in range(self.height):
|
||||||
|
for x in range(self.width):
|
||||||
|
greyscale = int(self.get_greyscale(*self.input_img[y][x]))
|
||||||
|
if self.threshold > greyscale + self.error_table[y][x]:
|
||||||
|
self.output_img[y][x] = (0, 0, 0)
|
||||||
|
current_error = greyscale + self.error_table[x][y]
|
||||||
|
else:
|
||||||
|
self.output_img[y][x] = (255, 255, 255)
|
||||||
|
current_error = greyscale + self.error_table[x][y] - 255
|
||||||
|
"""
|
||||||
|
Burkes error propagation (`*` is current pixel):
|
||||||
|
|
||||||
|
* 8/32 4/32
|
||||||
|
2/32 4/32 8/32 4/32 2/32
|
||||||
|
"""
|
||||||
|
self.error_table[y][x + 1] += int(8 / 32 * current_error)
|
||||||
|
self.error_table[y][x + 2] += int(4 / 32 * current_error)
|
||||||
|
self.error_table[y + 1][x] += int(8 / 32 * current_error)
|
||||||
|
self.error_table[y + 1][x + 1] += int(4 / 32 * current_error)
|
||||||
|
self.error_table[y + 1][x + 2] += int(2 / 32 * current_error)
|
||||||
|
self.error_table[y + 1][x - 1] += int(4 / 32 * current_error)
|
||||||
|
self.error_table[y + 1][x - 2] += int(2 / 32 * current_error)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# create Burke's instances with original images in greyscale
|
||||||
|
burkes_instances = [
|
||||||
|
Burkes(imread("image_data/lena.jpg", 1), threshold)
|
||||||
|
for threshold in (1, 126, 130, 140)
|
||||||
|
]
|
||||||
|
|
||||||
|
for burkes in burkes_instances:
|
||||||
|
burkes.process()
|
||||||
|
|
||||||
|
for burkes in burkes_instances:
|
||||||
|
imshow(
|
||||||
|
f"Original image with dithering threshold: {burkes.threshold}",
|
||||||
|
burkes.output_img,
|
||||||
|
)
|
||||||
|
|
||||||
|
waitKey(0)
|
||||||
|
destroyAllWindows()
|
|
@ -10,6 +10,7 @@ import digital_image_processing.filters.convolve as conv
|
||||||
import digital_image_processing.change_contrast as cc
|
import digital_image_processing.change_contrast as cc
|
||||||
import digital_image_processing.convert_to_negative as cn
|
import digital_image_processing.convert_to_negative as cn
|
||||||
import digital_image_processing.sepia as sp
|
import digital_image_processing.sepia as sp
|
||||||
|
import digital_image_processing.dithering.burkes as bs
|
||||||
from cv2 import imread, cvtColor, COLOR_BGR2GRAY
|
from cv2 import imread, cvtColor, COLOR_BGR2GRAY
|
||||||
from numpy import array, uint8
|
from numpy import array, uint8
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
@ -17,6 +18,7 @@ from PIL import Image
|
||||||
img = imread(r"digital_image_processing/image_data/lena_small.jpg")
|
img = imread(r"digital_image_processing/image_data/lena_small.jpg")
|
||||||
gray = cvtColor(img, COLOR_BGR2GRAY)
|
gray = cvtColor(img, COLOR_BGR2GRAY)
|
||||||
|
|
||||||
|
|
||||||
# Test: convert_to_negative()
|
# Test: convert_to_negative()
|
||||||
def test_convert_to_negative():
|
def test_convert_to_negative():
|
||||||
negative_img = cn.convert_to_negative(img)
|
negative_img = cn.convert_to_negative(img)
|
||||||
|
@ -74,3 +76,9 @@ def test_sobel_filter():
|
||||||
def test_sepia():
|
def test_sepia():
|
||||||
sepia = sp.make_sepia(img, 20)
|
sepia = sp.make_sepia(img, 20)
|
||||||
assert sepia.all()
|
assert sepia.all()
|
||||||
|
|
||||||
|
|
||||||
|
def test_burkes(file_path: str="digital_image_processing/image_data/lena_small.jpg"):
|
||||||
|
burkes = bs.Burkes(imread(file_path, 1), 120)
|
||||||
|
burkes.process()
|
||||||
|
assert burkes.output_img.any()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user