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.convert_to_negative as cn
|
||||
import digital_image_processing.sepia as sp
|
||||
import digital_image_processing.dithering.burkes as bs
|
||||
from cv2 import imread, cvtColor, COLOR_BGR2GRAY
|
||||
from numpy import array, uint8
|
||||
from PIL import Image
|
||||
|
@ -17,6 +18,7 @@ from PIL import Image
|
|||
img = imread(r"digital_image_processing/image_data/lena_small.jpg")
|
||||
gray = cvtColor(img, COLOR_BGR2GRAY)
|
||||
|
||||
|
||||
# Test: convert_to_negative()
|
||||
def test_convert_to_negative():
|
||||
negative_img = cn.convert_to_negative(img)
|
||||
|
@ -74,3 +76,9 @@ def test_sobel_filter():
|
|||
def test_sepia():
|
||||
sepia = sp.make_sepia(img, 20)
|
||||
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