├── .gitignore ├── Dither.py ├── LICENSE ├── README.md ├── images └── img1.jpg └── main.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | venv/ 3 | 4 | *.pyc 5 | -------------------------------------------------------------------------------- /Dither.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | class ditherModule(object): 4 | def dither(self, img, method='floyd-steinberg', resize = False): 5 | if(resize): 6 | img = cv2.resize(img, (int(0.5*(np.shape(img)[1])), int(0.5*(np.shape(img)[0])))) 7 | if(method == 'simple2D'): 8 | img = cv2.copyMakeBorder(img, 1, 1, 1, 1, cv2.BORDER_REPLICATE) 9 | rows, cols = np.shape(img) 10 | out = cv2.normalize(img.astype('float'), None, 0.0, 1.0, cv2.NORM_MINMAX) 11 | for i in range(1, rows-1): 12 | for j in range(1, cols-1): 13 | # threshold step 14 | if(out[i][j] > 0.5): 15 | err = out[i][j] - 1 16 | out[i][j] = 1 17 | else: 18 | err = out[i][j] 19 | out[i][j] = 0 20 | 21 | # error diffusion step 22 | out[i][j + 1] = out[i][j + 1] + (0.5 * err) 23 | out[i + 1][j] = out[i + 1][j] + (0.5 * err) 24 | 25 | return(out[1:rows-1, 1:cols-1]) 26 | 27 | elif(method == 'floyd-steinberg'): 28 | img = cv2.copyMakeBorder(img, 1, 1, 1, 1, cv2.BORDER_REPLICATE) 29 | rows, cols = np.shape(img) 30 | out = cv2.normalize(img.astype('float'), None, 0.0, 1.0, cv2.NORM_MINMAX) 31 | for i in range(1, rows - 1): 32 | for j in range(1, cols - 1): 33 | # threshold step 34 | if (out[i][j] > 0.5): 35 | err = out[i][j] - 1 36 | out[i][j] = 1 37 | else: 38 | err = out[i][j] 39 | out[i][j] = 0 40 | 41 | # error diffusion step 42 | out[i][j + 1] = out[i][j + 1] + ((7/16) * err) 43 | out[i + 1][j - 1] = out[i + 1][j - 1] + ((3/16) * err) 44 | out[i + 1][j] = out[i + 1][j] + ((5/16) * err) 45 | out[i + 1][j + 1] = out[i + 1][j + 1] + ((1/16) * err) 46 | 47 | return (out[1:rows - 1, 1:cols - 1]) 48 | 49 | elif (method == 'jarvis-judice-ninke'): 50 | img = cv2.copyMakeBorder(img, 2, 2, 2, 2, cv2.BORDER_REPLICATE) 51 | rows, cols = np.shape(img) 52 | out = cv2.normalize(img.astype('float'), None, 0.0, 1.0, cv2.NORM_MINMAX) 53 | for i in range(2, rows - 2): 54 | for j in range(2, cols - 2): 55 | # threshold step 56 | if (out[i][j] > 0.5): 57 | err = out[i][j] - 1 58 | out[i][j] = 1 59 | else: 60 | err = out[i][j] 61 | out[i][j] = 0 62 | 63 | # error diffusion step 64 | out[i][j + 1] = out[i][j + 1] + ((7 / 48) * err) 65 | out[i][j + 2] = out[i][j + 2] + ((5 / 48) * err) 66 | 67 | out[i + 1][j - 2] = out[i + 1][j - 2] + ((3 / 48) * err) 68 | out[i + 1][j - 1] = out[i + 1][j - 1] + ((5 / 48) * err) 69 | out[i + 1][j] = out[i + 1][j] + ((7 / 48) * err) 70 | out[i + 1][j + 1] = out[i + 1][j + 1] + ((5 / 48) * err) 71 | out[i + 1][j + 2] = out[i + 1][j + 2] + ((3 / 48) * err) 72 | 73 | out[i + 2][j - 2] = out[i + 2][j - 2] + ((1 / 48) * err) 74 | out[i + 2][j - 1] = out[i + 2][j - 1] + ((3 / 48) * err) 75 | out[i + 2][j] = out[i + 2][j] + ((5 / 48) * err) 76 | out[i + 2][j + 1] = out[i + 2][j + 1] + ((3 / 48) * err) 77 | out[i + 2][j + 2] = out[i + 2][j + 2] + ((1 / 48) * err) 78 | 79 | return (out[2:rows - 2, 2:cols - 2]) 80 | 81 | else: 82 | raise TypeError('specified method does not exist. available methods = "simple2D", "floyd-steinberg(default)", "jarvis-judice-ninke"') 83 | 84 | 85 | def dither(img, method='floyd-steinberg', resize = False): 86 | dither_object = ditherModule() 87 | out = dither_object.dither(img, method, resize) 88 | return(out) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2021, Utkarsh Deshmukh 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # image-dithering-python 2 | multiple python implementations for image dithering 3 | 4 | image dithering - wikipedia definition: 5 | Dither is an intentionally applied form of noise used to randomize quantization error, preventing large-scale patterns such as color banding in images. Dither is routinely used in processing of both digital audio and video data. 6 | 7 | # implementation: 8 | ## method 1: 9 | ```pip install PyDither``` 10 | 11 | ``` 12 | import cv2 13 | import Dither 14 | 15 | img = cv2.imread('images/img1.jpg', 0) # read image 16 | 17 | out = Dither.dither(img, 'simple2D', resize=True) # perform image dithering 18 | cv2.imshow('dithered image', out) 19 | cv2.waitKey(0) 20 | ``` 21 | 22 | ## method 2: 23 | Run the file main.py 24 | 25 | # Results: 26 | ![readme1](https://user-images.githubusercontent.com/13918778/103830175-f213c900-502f-11eb-9939-050d0fb973ba.png) 27 | 28 | # Theory: 29 | this repository implements 3 different types of error diffusion based image dithering algorithms. 30 | 31 | ![image](https://user-images.githubusercontent.com/13918778/103832335-e970c200-5032-11eb-9d5a-97f9b6be7d16.png) 32 | 33 | # License: 34 | This project is licensed under the BSD 2 License - see the LICENSE.md file for details 35 | 36 | -------------------------------------------------------------------------------- /images/img1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Utkarsh-Deshmukh/image-dithering-python/7dfd5d7af2cdc97e95e265ae88f88a2757da330e/images/img1.jpg -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import Dither 4 | 5 | 6 | if __name__ == '__main__': 7 | img = cv2.imread('images/img1.jpg', 0) # read image 8 | 9 | out = Dither.dither(img, 'simple2D', resize=True) # perform image dithering 10 | cv2.imshow('dithered image', out) 11 | cv2.waitKey(0) --------------------------------------------------------------------------------