- Notifications
You must be signed in to change notification settings - Fork 46.7k
/
Copy pathburkes.py
98 lines (82 loc) · 3.55 KB
/
burkes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
"""
Implementation Burke's algorithm (dithering)
"""
importnumpyasnp
fromcv2importdestroyAllWindows, imread, imshow, waitKey
classBurkes:
"""
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))
ifnotself.min_threshold<threshold<self.max_threshold:
msg=f"Factor value should be from 0 to {self.max_threshold}"
raiseValueError(msg)
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= [
[0for_inrange(self.height+4)] for__inrange(self.width+1)
]
self.output_img=np.ones((self.width, self.height, 3), np.uint8) *255
@classmethod
defget_greyscale(cls, blue: int, green: int, red: int) ->float:
"""
>>> Burkes.get_greyscale(3, 4, 5)
4.185
>>> Burkes.get_greyscale(0, 0, 0)
0.0
>>> Burkes.get_greyscale(255, 255, 255)
255.0
"""
"""
Formula from https://en.wikipedia.org/wiki/HSL_and_HSV
cf Lightness section, and Fig 13c.
We use the first of four possible.
"""
return0.114*blue+0.587*green+0.299*red
defprocess(self) ->None:
foryinrange(self.height):
forxinrange(self.width):
greyscale=int(self.get_greyscale(*self.input_img[y][x]))
ifself.threshold>greyscale+self.error_table[y][x]:
self.output_img[y][x] = (0, 0, 0)
current_error=greyscale+self.error_table[y][x]
else:
self.output_img[y][x] = (255, 255, 255)
current_error=greyscale+self.error_table[y][x] -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)
forthresholdin (1, 126, 130, 140)
]
forburkesinburkes_instances:
burkes.process()
forburkesinburkes_instances:
imshow(
f"Original image with dithering threshold: {burkes.threshold}",
burkes.output_img,
)
waitKey(0)
destroyAllWindows()