├── __init__.py ├── image_enhancement ├── __init__.py ├── quantitation.py ├── utils.py ├── histogram.py └── image_enhancement.py ├── images ├── couple-FLH.jpg ├── couple-GHE.jpg ├── couple-AGCCPF.jpg ├── couple-AGCWD.jpg ├── couple-BBHE.jpg ├── couple-BHEPL.jpg ├── couple-BPHEME.jpg ├── couple-BUBOHE.jpg ├── couple-DCRGC.jpg ├── couple-DSIHE.jpg ├── couple-FHSABP.jpg ├── couple-QBHE.jpg ├── couple-RLBHE.jpg ├── couple-RMSHE.jpg ├── couple-RSIHE.jpg ├── couple-WTHE.jpg ├── couple-origin.jpg ├── couple-MMBEBHE.jpg ├── couple-RSWHE-D.jpg └── couple-RSWHE-M.jpg ├── renovate.json ├── .gitignore ├── setup.py ├── LICENSE └── README.md /__init__.py: -------------------------------------------------------------------------------- 1 | from . import image_enhancement -------------------------------------------------------------------------------- /image_enhancement/__init__.py: -------------------------------------------------------------------------------- 1 | from . import image_enhancement 2 | from . import quantitation -------------------------------------------------------------------------------- /images/couple-FLH.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-FLH.jpg -------------------------------------------------------------------------------- /images/couple-GHE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-GHE.jpg -------------------------------------------------------------------------------- /images/couple-AGCCPF.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-AGCCPF.jpg -------------------------------------------------------------------------------- /images/couple-AGCWD.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-AGCWD.jpg -------------------------------------------------------------------------------- /images/couple-BBHE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-BBHE.jpg -------------------------------------------------------------------------------- /images/couple-BHEPL.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-BHEPL.jpg -------------------------------------------------------------------------------- /images/couple-BPHEME.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-BPHEME.jpg -------------------------------------------------------------------------------- /images/couple-BUBOHE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-BUBOHE.jpg -------------------------------------------------------------------------------- /images/couple-DCRGC.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-DCRGC.jpg -------------------------------------------------------------------------------- /images/couple-DSIHE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-DSIHE.jpg -------------------------------------------------------------------------------- /images/couple-FHSABP.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-FHSABP.jpg -------------------------------------------------------------------------------- /images/couple-QBHE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-QBHE.jpg -------------------------------------------------------------------------------- /images/couple-RLBHE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-RLBHE.jpg -------------------------------------------------------------------------------- /images/couple-RMSHE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-RMSHE.jpg -------------------------------------------------------------------------------- /images/couple-RSIHE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-RSIHE.jpg -------------------------------------------------------------------------------- /images/couple-WTHE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-WTHE.jpg -------------------------------------------------------------------------------- /images/couple-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-origin.jpg -------------------------------------------------------------------------------- /images/couple-MMBEBHE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-MMBEBHE.jpg -------------------------------------------------------------------------------- /images/couple-RSWHE-D.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-RSWHE-D.jpg -------------------------------------------------------------------------------- /images/couple-RSWHE-M.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nguyen-Hoang-Nam/image-enhancement/HEAD/images/couple-RSWHE-M.jpg -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tests/ 2 | result/ 3 | __pycache__/ 4 | .pypirc 5 | test.py 6 | 7 | build 8 | # sphinx build directory 9 | _build 10 | # setup.py dist directory 11 | dist 12 | doc/build 13 | doc/cdoc/build 14 | # Egg metadata 15 | *.egg-info 16 | # The shelf plugin uses this dir 17 | ./.shelf 18 | MANIFEST 19 | .cache 20 | pip-wheel-metadata -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='image_enhancement', 5 | version='0.2.1', 6 | url='https://github.com/Nguyen-Hoang-Nam/image-enhancement', 7 | license='MIT', 8 | author='Nguyen Hoang Nam', 9 | author_email='nguyenhoangnam.dev@gmail.com', 10 | description='🌈 Library to enhance image', 11 | packages=find_packages(exclude=['tests', 'images']), 12 | long_description=open('README.md').read(), 13 | long_description_content_type="text/markdown", 14 | install_requires=["numpy", "opencv-python"], 15 | zip_safe=False 16 | ) 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nguyen Hoang Nam 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. -------------------------------------------------------------------------------- /image_enhancement/quantitation.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | 4 | class Quantitation: 5 | # Absolute Mean Brightness Error 6 | def AMBE(self, image_input, image_output): 7 | return abs(np.mean(image_input) - np.mean(image_output)) 8 | 9 | # Mean Square Error 10 | def MSE(self, image_input, image_output): 11 | err = np.sum((image_input.astype("float") - image_output.astype("float")) ** 2) 12 | err /= float(image_input.shape[0] * image_input.shape[1]) 13 | 14 | return err 15 | 16 | # Peak Signal to Noise Ratio 17 | def PSNR(self, image_input, image_output): 18 | return 10 * math.log10(255 * 255 / MSE(image_input, image_output)) 19 | 20 | # Entropy 21 | def Entropy(self, image_output): 22 | pdf, _ = np.histogram(image_output, 256, [0, 255]) 23 | pdf = pdf / float(image_output.shape[0] * image_output.shape[1]) 24 | 25 | ent = 0 26 | for probility in pdf: 27 | ent += probility * math.log2(probility) 28 | 29 | return -ent 30 | 31 | # Contrast improvement index 32 | def CII(self, image_input, image_output): 33 | return 0 34 | 35 | # Tenengrad measure 36 | def T(self, input_input, image_output): 37 | return 0 38 | -------------------------------------------------------------------------------- /image_enhancement/utils.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import cv2 as cv 4 | 5 | class Utils: 6 | def __init__(self, image, color_space = 'HSV'): 7 | self.image = image 8 | self.color_space = color_space 9 | 10 | def image_gray(self): 11 | if (self.color_space == 'HSV'): 12 | image_hsv = cv.cvtColor(self.image, cv.COLOR_BGR2HSV) 13 | self.image_color = image_hsv 14 | 15 | return image_hsv[:, :, 2] 16 | elif (self.color_space == 'Gray'): 17 | image_gray = cv.cvtColor(self.image, cv.COLOR_BGR2GRAY) 18 | self.image_color = image_gray 19 | 20 | return image_gray 21 | else: 22 | self.image_color = self.image 23 | return self.image 24 | 25 | def LUT_image(self, LUT): 26 | if (self.color_space == 'HSV'): 27 | for i in range(0, len(self.image_color)): 28 | for j in range(0, len(self.image_color[0])): 29 | self.image_color[i][j][2] = LUT[self.image_color[i][j][2]] 30 | 31 | return cv.cvtColor(self.image_color, cv.COLOR_HSV2BGR) 32 | elif (self.color_space == 'Gray'): 33 | return LUT[self.image_color] 34 | else: 35 | return self.image_color 36 | 37 | def is_gray_image(self): 38 | blue, gree, red = cv2.split(self.image) 39 | 40 | difference_red_green = np.count_nonzero(abs(red - green)) 41 | difference_green_blue = np.count_nonzero(abs(green - blue)) 42 | difference_blue_red = np.count_nonzero(abs(blue - red)) 43 | 44 | difference_sum = float(difference_red_green + difference_green_blue + difference_blue_red) 45 | 46 | ratio = diff_sum / self.image.size 47 | 48 | if ratio>0.005: 49 | return False 50 | else: 51 | return True 52 | 53 | def minimum_mean_brightness_error(self, image_1d): 54 | length = len(image_1d) 55 | 56 | unique_1d = np.unique(image_1d) 57 | max_1d = len(unique_1d) 58 | 59 | histogram, _ = np.histogram(image_1d, 256, [0, 255]) 60 | 61 | mean = 0 62 | for i in range(0, len(unique_1d)): 63 | mean += i * histogram[unique_1d[i]] 64 | 65 | smbe = max_1d * (length - histogram[unique_1d[0]]) - 2 * mean 66 | asmbe = abs(smbe) 67 | position = 0 68 | for i in range(1, len(unique_1d)): 69 | smbe += (length - max_1d * histogram[unique_1d[i]]) 70 | if asmbe > abs(smbe): 71 | asmbe = abs(smbe) 72 | position = i 73 | 74 | return unique_1d[position] 75 | 76 | 77 | 78 | 79 | def RGB_TO_HSI(image): 80 | with np.errstate(divide='ignore', invalid='ignore'): 81 | #Load image with 32 bit floats as variable type 82 | bgr = np.float32(img) / 255 83 | 84 | #Separate color channels 85 | blue = bgr[:,:,0] 86 | green = bgr[:,:,1] 87 | red = bgr[:,:,2] 88 | 89 | minimum = np.minimum(np.minimum(red, green), blue) 90 | maximum = np.maximum(np.maximum(red, green), blue) 91 | delta = maximum - minimum 92 | 93 | intensity = np.divide(blue + green + red, 3) 94 | 95 | if intensity == 0: 96 | saturation = 0 97 | else: 98 | saturation = 1 - 3 * np.divide(minimum, red + green + blue) 99 | 100 | sqrt_calc = np.sqrt(((red - green) * (red - green)) + ((red - blue) * (green - blue))) 101 | if (green >= blue).any(): 102 | hue = np.arccos((1 / 2 * ((red-green) + (red - blue)) / sqrt_calc)) 103 | else: 104 | hue = 2 * math.pi - np.arccos((1 / 2 * ((red-green) + (red - blue)) / sqrt_calc)) 105 | 106 | hue = hue * 180 / math.pi 107 | 108 | #Merge channels into picture and return image 109 | hsi = cv2.merge((intensity, saturation, hue)) 110 | return hsi 111 | 112 | def pdf(image, parameter = 'HSI'): 113 | if parameter == 'GRAY': 114 | image_gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) 115 | image_1d = image_gray.flatten() 116 | 117 | histogram, _ = np.histogram(image_1d, 256, [0, 256]) 118 | elif parameter == 'INTENSITY': 119 | image_hsi = RGB_TO_HSI(image) 120 | 121 | intensity = image_hsi[:, :, 0] 122 | intensity_1d = intensity.flatten() 123 | 124 | return histogram 125 | -------------------------------------------------------------------------------- /image_enhancement/histogram.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | 4 | class Histogram: 5 | 6 | def cdf_matching(self, input_cdf, output_cdf): 7 | ratio = input_cdf[255] / output_cdf[255] 8 | output_cdf = output_cdf * ratio 9 | 10 | LUT = np.array([]) 11 | position_new = 0 12 | for i in range(0, 256): 13 | while output_cdf[position_new] < input_cdf[i]: 14 | position_new += 1 15 | 16 | LUT = np.append(LUT, position_new) 17 | 18 | return LUT 19 | 20 | def histogram_specification(self, input_histogram, output_histogram): 21 | input_cdf = input_histogram.cumsum() 22 | output_cdf = output_histogram.cumsum() 23 | 24 | return self.cdf_matching(input_cdf, output_cdf) 25 | 26 | def sub_histogram_equalization(self, histogram, range_min = 0, range_max = 255): 27 | cdf = histogram.cumsum() 28 | cdf_mask = np.ma.masked_equal(cdf, 0) 29 | 30 | # Scale cdf to [range_min, range_max] 31 | scale_cdf_mask = ((cdf_mask - cdf_mask.min()) * (range_max - range_min) / (cdf_mask.max() - cdf_mask.min())) + range_min 32 | LUT = np.ma.filled(scale_cdf_mask, 0).astype('uint8') 33 | 34 | return LUT 35 | 36 | def histogram_equalization(self, image_1d, range_min = 0, range_max = 255): 37 | histogram, _ = np.histogram(image_1d, range_max - range_min + 1, [range_min, range_max]) 38 | 39 | return self.sub_histogram_equalization(histogram, range_min, range_max) 40 | 41 | def histogram_equalization_threshold(self, image_1d, threshold, start = 0, end = 255): 42 | lower_filter = image_1d <= threshold 43 | lower_1d = image_1d[lower_filter] 44 | 45 | upper_filter = image_1d > threshold 46 | upper_1d = image_1d[upper_filter] 47 | 48 | lower_input_lut = np.array([]) 49 | if start > 0: 50 | for i in range(0, start): 51 | lower_input_lut = np.append(lower_input_lut, i) 52 | 53 | upper_input_lut = np.array([]) 54 | if end < 255: 55 | for i in range(end + 1, 256): 56 | upper_input_lut = np.append(upper_input_lut, i) 57 | 58 | lower_LUT = self.histogram_equalization(lower_1d, start, threshold) 59 | upper_LUT = self.histogram_equalization(upper_1d, threshold + 1, end) 60 | 61 | lower_LUT = np.concatenate((lower_input_lut, lower_LUT)) 62 | upper_LUT = np.concatenate((upper_LUT, upper_input_lut)) 63 | 64 | LUT = np.concatenate((lower_LUT, upper_LUT)) 65 | 66 | return LUT 67 | 68 | def histogram_equalization_recursively(self, image_1d, separate_func, recursive, start = 0, end = 255): 69 | if recursive > 0: 70 | separate = separate_func(image_1d) 71 | separate = math.floor(separate) 72 | 73 | lower_filter = image_1d <= separate 74 | lower_1d = image_1d[lower_filter] 75 | 76 | lower_equalization = self.histogram_equalization_recursively(lower_1d, separate_func, recursive - 1, start, separate) 77 | 78 | upper_filter = image_1d > separate 79 | upper_1d = image_1d[upper_filter] 80 | 81 | upper_equalization = self.histogram_equalization_recursively(upper_1d, separate_func, recursive - 1, separate + 1, end) 82 | 83 | return np.concatenate((lower_equalization, upper_equalization)) 84 | else: 85 | return self.histogram_equalization(image_1d, start, end) 86 | 87 | def histogram_segmentation(self, image_1d, separate_func, recursive, start = 0, end = 255): 88 | if recursive > 0: 89 | separate = separate_func(image_1d) 90 | separate = math.floor(separate) 91 | 92 | lower_filter = image_1d <= separate 93 | lower_1d = image_1d[lower_filter] 94 | 95 | lower_equalization = self.histogram_segmentation(lower_1d, separate_func, recursive - 1, start, separate) 96 | 97 | upper_filter = image_1d > separate 98 | upper_1d = image_1d[upper_filter] 99 | 100 | upper_equalization = self.histogram_segmentation(upper_1d, separate_func, recursive - 1, separate + 1, end) 101 | 102 | return lower_equalization + upper_equalization 103 | else: 104 | sub_histogram, _ = np.histogram(image_1d, end - start + 1, [start, end]) 105 | return [sub_histogram] 106 | 107 | 108 | # def histogram_weighting_mean(cdf, start = 0, end = 255): 109 | # weighting_mean = 0 110 | # for i in range(start, end + 1): 111 | # weighting_mean += i * cdf[i] 112 | 113 | # weighting_mean = weighting_mean / np.sum(cdf) 114 | # weighting_mean = math.floor(weighting_mean) 115 | 116 | # if weighting_mean == 255: 117 | # return [weighting_mean] 118 | # else: 119 | # lower = histogram_weighting_mean(cdf, start, weighting_mean) 120 | # upper = histogram_weighting_mean(cdf, weighting_mean + 1, end) 121 | 122 | # return lower + upper -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Image Enhancement 2 | 3 | Base on multiple papers about image enhancement, I create this library as API to call them easily. Image enhancement makes color of images more equalization by automatic or parameters. 4 | 5 | 6 | 7 | (a) **Origin**, (b) **GHE**, (c) **BBHE**, (d) **QBHE**, (e) **DSIHE**, (f) **MMBEBHE**, (g) **RMSHE**, (h) **BUBOHE**, (i) **BPHEME**, (j) **RSIHE**, (k) **WTHE**, (l) **RSWHE-D**, (m) **RSWHE-M**, (n) **FHSABP**, (o) **BHEPL**, (p) **RLBHE**, (q) **DCRGC**, (r) **AGCWD**, (s) **AGCCPF**, (t) **FLH** 8 | 9 | ## Installation 10 | 11 | ```bash 12 | pip install image-enhancement 13 | ``` 14 | 15 | ## Usage 16 | 17 | ```python 18 | from image_enhancement import image_enhancement 19 | import cv2 as cv 20 | 21 | input = cv.imread('input.jpg') 22 | 23 | ie = image_enhancement.IE(input, 'HSV') 24 | output = ie.GHE() 25 | 26 | cv.imwrite('output.jpg', output) 27 | ``` 28 | 29 | ## IE (Image Enhancement) 30 | 31 | Entry point to call image enhancement functions. Currently, there are three main groups, histogram equalization, gamma correction and other. 32 | 33 | ```python 34 | from image_enhancement import image_enhancement 35 | 36 | ie = image_enhancement.IE(image, color_space = 'HSV') 37 | ``` 38 | 39 | ### Histogram Equalization 40 | 41 | #### GHE (Global Histogram Equalization) 42 | 43 | This function is similar to `equalizeHist(image)` in opencv. 44 | 45 | ```python 46 | ie.GHE() 47 | ``` 48 | 49 | #### BBHE (Brightness Preserving Histogram Equalization) 50 | 51 | Kim, Yeong-Taeg. 52 | 53 | Contrast enhancement using brightness preserving bi-histogram equalization. 54 | 55 | IEEE transactions on Consumer Electronics 43, no. 1 (1997): 1-8. 56 | 57 | ```python 58 | ie.BBHE() 59 | ``` 60 | 61 | #### QBHE (Quantized Bi-Histogram Equalization) 62 | 63 | Kim, Yeong-Taeg. 64 | 65 | Quantized bi-histogram equalization. 66 | 67 | In 1997 IEEE International Conference on Acoustics, Speech, and Signal Processing, vol. 4, pp. 2797-2800. IEEE, 1997. 68 | 69 | ```python 70 | ie.QBHE(number_gray) 71 | ``` 72 | 73 | #### DSIHE (Dualistic Sub-Image Histogram Equalization) 74 | 75 | Wang, Yu, Qian Chen, and Baeomin Zhang. 76 | 77 | Image enhancement based on equal area dualistic sub-image histogram equalization method. 78 | 79 | IEEE Transactions on Consumer Electronics 45, no. 1 (1999): 68-75. 80 | 81 | ```python 82 | ie.DSIHE() 83 | ``` 84 | 85 | #### MMBEBHE (Minimum Mean Brightness Error Histogram Equalization) 86 | 87 | Chen, Soong-Der, and Abd Rahman Ramli. 88 | 89 | Minimum mean brightness error bi-histogram equalization in contrast enhancement. 90 | 91 | IEEE transactions on Consumer Electronics 49, no. 4 (2003): 1310-1319. 92 | 93 | ```python 94 | ie.MMBEBHE() 95 | ``` 96 | 97 | #### RMSHE (Recursively Mean-Separate Histogram Equalization) 98 | 99 | Chen, Soong-Der, and Abd Rahman Ramli. 100 | 101 | Contrast enhancement using recursive mean-separate histogram equalization for scalable brightness preservation. 102 | 103 | IEEE Transactions on consumer Electronics 49, no. 4 (2003): 1301-1309. 104 | 105 | ```python 106 | ie.RMSHE(recursive) 107 | ``` 108 | 109 | #### BUBOHE (Bin Underflow and Bin Overflow Histogram Equalization) 110 | 111 | Yang, Seungjoon, Jae Hwan Oh, and Yungfun Park. 112 | 113 | Contrast enhancement using histogram equalization with bin underflow and bin overflow. 114 | 115 | In Proceedings 2003 International Conference on Image Processing (Cat. No. 03CH37429), vol. 1, pp. I-881. IEEE, 2003. 116 | 117 | ```python 118 | ie.BUBOHE(underflow, overflow) 119 | ``` 120 | 121 | #### BPHEME (Brightness Preserving Histogram Equalization with Maximum Entropy) 122 | 123 | Wang, Chao, and Zhongfu Ye. 124 | 125 | Brightness preserving histogram equalization with maximum entropy: a variational perspective. 126 | 127 | IEEE Transactions on Consumer Electronics 51, no. 4 (2005): 1326-1334. 128 | 129 | ```python 130 | ie.BPHEME() 131 | ``` 132 | 133 | #### RSIHE (Recursive Sub-Image Histogram Equalization) 134 | 135 | Sim, K. S., C. P. Tso, and Y. Y. Tan. 136 | 137 | Recursive sub-image histogram equalization applied to gray scale images. 138 | 139 | Pattern Recognition Letters 28, no. 10 (2007): 1209-1221. 140 | 141 | ```python 142 | ie.RSIHE(recursive) 143 | ``` 144 | 145 | #### WTHE (Weighted Thresholded Histogram Equalization) 146 | 147 | Wang, Qing, and Rabab K. Ward. 148 | 149 | Fast image/video contrast enhancement based on weighted thresholded histogram equalization. 150 | 151 | IEEE transactions on Consumer Electronics 53, no. 2 (2007): 757-764. 152 | 153 | ```python 154 | ie.WTHE(root, value, lower) 155 | ``` 156 | 157 | #### RSWHE (Recursive Separated and Weighted Histogram Equalization) 158 | 159 | Kim, Mary, and Min Gyo Chung. 160 | 161 | Recursively separated and weighted histogram equalization for brightness preservation and contrast enhancement. 162 | 163 | IEEE Transactions on Consumer Electronics 54, no. 3 (2008): 1389-1397. 164 | 165 | ```python 166 | ie.RSWHE(type, beta, recursive) 167 | ``` 168 | 169 | #### FHSABP (Flattest Histogram Specification with Accurate Brightness Preservation) 170 | 171 | Wang, C., J. Peng, and Z. Ye. 172 | 173 | Flattest histogram specification with accurate brightness preservation. 174 | 175 | IET Image Processing 2, no. 5 (2008): 249-262. 176 | 177 | ```python 178 | ie.FHSABP() 179 | ``` 180 | 181 | #### BHEPL (Bi-Histogram Equalization with a Plateau Limit) 182 | 183 | Ooi, Chen Hee, Nicholas Sia Pik Kong, and Haidi Ibrahim. 184 | 185 | Bi-histogram equalization with a plateau limit for digital image enhancement. 186 | 187 | IEEE transactions on consumer electronics 55, no. 4 (2009): 2072-2080. 188 | 189 | ```python 190 | ie.BHEPL() 191 | ``` 192 | 193 | #### RLBHE (Range Limited Bi-Histogram Equalization) 194 | 195 | Zuo, Chao, Qian Chen, and Xiubao Sui. 196 | 197 | Range limited bi-histogram equalization for image contrast enhancement. 198 | 199 | Optik 124, no. 5 (2013): 425-431. 200 | 201 | ```python 202 | ie.RLBHE() 203 | ``` 204 | 205 | ### Gamma Correction 206 | 207 | #### DCRGC (Dynamic Contrast Ratio Gamma Correction) 208 | 209 | Wang, Zhi-Guo, Zhi-Hu Liang, and Chun-Liang Liu. 210 | 211 | A real-time image processor with combining dynamic contrast ratio enhancement and inverse gamma correction for PDP. 212 | 213 | Displays 30, no. 3 (2009): 133-139. 214 | 215 | ```python 216 | ie.DCRGC(contrast_intensity, gamma) 217 | ``` 218 | 219 | #### AGCWD (Adaptive Gamma Correction with Weighting Distribution) 220 | 221 | Huang, Shih-Chia, Fan-Chieh Cheng, and Yi-Sheng Chiu. 222 | 223 | Efficient contrast enhancement using adaptive gamma correction with weighting distribution. 224 | 225 | IEEE transactions on image processing 22, no. 3 (2012): 1032-1041. 226 | 227 | ```python 228 | ie.AGCWD(alpha) 229 | ``` 230 | 231 | #### AGCCPF (Adaptive Gamma Correction Color Preserving Framework) 232 | 233 | Gupta, Bhupendra, and Mayank Tiwari. 234 | 235 | Minimum mean brightness error contrast enhancement of color images using adaptive gamma correction with color preserving framework. 236 | 237 | Optik 127, no. 4 (2016): 1671-1676. 238 | 239 | ```python 240 | ie.AGCCPF(alpha) 241 | ``` 242 | 243 | ### Other 244 | 245 | #### FLH (Fuzzy-Logic and Histogram) 246 | 247 | Raju, G., and Madhu S. Nair. 248 | 249 | A fast and efficient color image enhancement method based on fuzzy-logic and histogram. 250 | 251 | AEU-International Journal of electronics and communications 68, no. 3 (2014): 237-243. 252 | 253 | ```python 254 | ie.FLH(enhancement) 255 | ``` 256 | 257 | ## Quantitation 258 | 259 | Entry point to call quantitation functions. 260 | 261 | ```python 262 | from contrast_image import quantitation 263 | 264 | quantitation = Quantitation() 265 | ``` 266 | 267 | #### AMBE (Absolute Mean Brightness Error) 268 | 269 | ```python 270 | quantitatin.AMBE(input_image, output_image) 271 | ``` 272 | 273 | #### PSNR (Peak Signal to Noise Ratio) 274 | 275 | ```python 276 | quantitatin.PSNR(input_image, output_image) 277 | ``` 278 | 279 | #### Entropy 280 | 281 | ```python 282 | quantitatin.Entropy(image) 283 | ``` 284 | 285 | ## Contributing 286 | 287 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 288 | 289 | Please make sure to update tests as appropriate. 290 | 291 | ## License 292 | 293 | [MIT](https://choosealicense.com/licenses/mit/) 294 | -------------------------------------------------------------------------------- /image_enhancement/image_enhancement.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import numpy.ma as ma 4 | import cv2 as cv 5 | from .utils import Utils 6 | from .histogram import Histogram 7 | 8 | class IE: 9 | def __init__(self, image, color_space = 'HSV'): 10 | self.image = image 11 | self.color_space = color_space 12 | 13 | ######################################## 14 | # 15 | # Global histogram 16 | # 17 | ######################################## 18 | 19 | # Global Histogram Equalization 20 | def GHE(self): 21 | utils = Utils(self.image, self.color_space) 22 | image_gray = utils.image_gray() 23 | image_1d = image_gray.flatten() 24 | 25 | histogram = Histogram() 26 | LUT = histogram.histogram_equalization(image_1d) 27 | return utils.LUT_image(LUT) 28 | 29 | 30 | # Kim, Yeong-Taeg. 31 | # Contrast enhancement using brightness preserving bi-histogram equalization. 32 | # IEEE transactions on Consumer Electronics 43, no. 1 (1997): 1-8. 33 | # Brightness-preserving Bi-Histogram Equalization (BBHE) 34 | def BBHE(self): 35 | utils = Utils(self.image, self.color_space) 36 | image_gray = utils.image_gray() 37 | image_1d = image_gray.flatten() 38 | 39 | mean = np.mean(image_1d) 40 | mean = math.floor(mean) 41 | histogram = Histogram() 42 | LUT = histogram.histogram_equalization_threshold(image_1d, mean) 43 | return utils.LUT_image(LUT) 44 | 45 | 46 | # Kim, Yeong-Taeg. 47 | # Quantized bi-histogram equalization." 48 | # In 1997 IEEE International Conference on Acoustics, Speech, and Signal Processing, vol. 4, pp. 2797-2800. IEEE, 1997. 49 | # Quantized Bi-Histogram Equalization (QBHE) 50 | def QBHE(self, number_gray): 51 | utils = Utils(self.image, self.color_space) 52 | image_gray = utils.image_gray() 53 | image_1d = image_gray.flatten() 54 | 55 | mean = np.mean(image_1d) 56 | mean = math.floor(mean) 57 | 58 | number_divide = 256 / number_gray 59 | image_1d = image_1d / number_divide 60 | image_1d = image_1d.astype('uint8') 61 | image_1d = image_1d * number_divide 62 | image_1d = image_1d.astype('uint8') 63 | 64 | mean = round(round(mean / number_divide) * number_divide) 65 | 66 | histogram = Histogram() 67 | LUT = histogram.histogram_equalization_threshold(image_1d, mean) 68 | return utils.LUT_image(LUT) 69 | 70 | 71 | # Wang, Yu, Qian Chen, and Baeomin Zhang. 72 | # Image enhancement based on equal area dualistic sub-image histogram equalization method. 73 | # IEEE Transactions on Consumer Electronics 45, no. 1 (1999): 68-75. 74 | # Dualistic Sub-Image Histogram Equalization (DSIHE) 75 | def DSIHE(self): 76 | utils = Utils(self.image, self.color_space) 77 | image_gray = utils.image_gray() 78 | image_1d = image_gray.flatten() 79 | 80 | median = np.median(image_1d) 81 | median = math.floor(median) 82 | histogram = Histogram() 83 | LUT = histogram.histogram_equalization_threshold(image_1d, median) 84 | return utils.LUT_image(LUT) 85 | 86 | 87 | # Chen, Soong-Der, and Abd Rahman Ramli. 88 | # Minimum mean brightness error bi-histogram equalization in contrast enhancement. 89 | # IEEE transactions on Consumer Electronics 49, no. 4 (2003): 1310-1319. 90 | # Minimum Mean Brightness Error Histogram Equalization (MMBEBHE) 91 | def MMBEBHE(self): 92 | utils = Utils(self.image, self.color_space) 93 | image_gray = utils.image_gray() 94 | image_1d = image_gray.flatten() 95 | 96 | mbe = utils.minimum_mean_brightness_error(image_1d) 97 | histogram = Histogram() 98 | LUT = histogram.histogram_equalization_threshold(image_1d, mbe) 99 | return utils.LUT_image(LUT) 100 | 101 | 102 | # Chen, Soong-Der, and Abd Rahman Ramli. 103 | # Contrast enhancement using recursive mean-separate histogram equalization for scalable brightness preservation. 104 | # IEEE Transactions on consumer Electronics 49, no. 4 (2003): 1301-1309. 105 | # Recursive Mean-Separate Histogram Equalization (RMSHE) 106 | def RMSHE(self, recursive = 2): 107 | utils = Utils(self.image, self.color_space) 108 | image_gray = utils.image_gray() 109 | image_1d = image_gray.flatten() 110 | 111 | histogram = Histogram() 112 | LUT = histogram.histogram_equalization_recursively(image_1d, np.mean, recursive) 113 | return utils.LUT_image(LUT) 114 | 115 | 116 | # Yang, Seungjoon, Jae Hwan Oh, and Yungfun Park. 117 | # Contrast enhancement using histogram equalization with bin underflow and bin overflow. 118 | # In Proceedings 2003 International Conference on Image Processing (Cat. No. 03CH37429), vol. 1, pp. I-881. IEEE, 2003. 119 | # Bin Underflow and Bin Overflow Histogram Equalization (BUBOHE) 120 | def BUBOHE(self, underflow, overflow): 121 | utils = Utils(self.image, self.color_space) 122 | image_gray = utils.image_gray() 123 | image_1d = image_gray.flatten() 124 | 125 | histogram, _ = np.histogram(image_1d, 256, [0, 255]) 126 | histogram = histogram / len(image_1d) 127 | 128 | histogram[histogram > overflow] = overflow 129 | histogram[histogram < underflow] = underflow 130 | 131 | cdf = histogram.cumsum() 132 | LUT = np.array([0.0] * 256) 133 | 134 | for i in range(0, 256): 135 | LUT[i] = 255 * (cdf[i] - (cdf[i] / 255) * i) + i 136 | 137 | LUT = LUT.astype('uint8') 138 | return utils.LUT_image(LUT) 139 | 140 | # Wang, Chao, and Zhongfu Ye. 141 | # Brightness preserving histogram equalization with maximum entropy: a variational perspective. 142 | # IEEE Transactions on Consumer Electronics 51, no. 4 (2005): 1326-1334. 143 | # Brightness Preserving Histogram Equalization with Maximum Entropy (BPHEME) 144 | def BPHEME(self): 145 | utils = Utils(self.image, self.color_space) 146 | image_gray = utils.image_gray() 147 | image_1d = image_gray.flatten() 148 | 149 | mean = np.mean(image_1d) 150 | if mean == 127.5: 151 | cdf_output = np.array([mean] * 255) 152 | else: 153 | def maximum_histogram_entropy(x, mean): 154 | return (x * math.exp(x) - math.exp(x) + 1) / (x * (math.exp(x) - 1)) - mean 155 | 156 | def derivative_maximum_histogram_entropy(x): 157 | return (-math.exp(x) * (x * x + 2) + math.exp(2 * x) + 1) / ((math.exp(x) - 1) * (math.exp(x) - 1) * x * x) 158 | 159 | def maximum_cumulative_entropy(x): 160 | return np.array([(math.exp(x * (i / 255)) - 1) / (math.exp(x) - 1) for i in range(0, 256)]) 161 | 162 | scale_mean = mean / 255 163 | lamda = 1 164 | output_newton_method = maximum_histogram_entropy(lamda, scale_mean) 165 | 166 | while abs(output_newton_method) > 0.01: 167 | lamda = lamda - (output_newton_method / derivative_maximum_histogram_entropy(lamda)) 168 | output_newton_method = maximum_histogram_entropy(lamda, scale_mean) 169 | 170 | cdf_output = maximum_cumulative_entropy(lamda) 171 | 172 | pdf_input, _ = np.histogram(image_1d, 256, [0, 255]) 173 | cdf_input = pdf_input.cumsum() 174 | 175 | histogram = Histogram() 176 | LUT = histogram.cdf_matching(cdf_input, cdf_output) 177 | return utils.LUT_image(LUT) 178 | 179 | 180 | # Sim, K. S., C. P. Tso, and Y. Y. Tan. 181 | # Recursive sub-image histogram equalization applied to gray scale images. 182 | # Pattern Recognition Letters 28, no. 10 (2007): 1209-1221. 183 | # Recursive Sub-Image Histogram Equalization (RSIHE) 184 | def RSIHE(self, recursive = 2): 185 | utils = Utils(self.image, self.color_space) 186 | image_gray = utils.image_gray() 187 | image_1d = image_gray.flatten() 188 | 189 | histogram = Histogram() 190 | LUT = histogram.histogram_equalization_recursively(image_1d, np.median, recursive) 191 | return utils.LUT_image(LUT) 192 | 193 | 194 | # Wang, Qing, and Rabab K. Ward. 195 | # Fast image/video contrast enhancement based on weighted thresholded histogram equalization. 196 | # IEEE transactions on Consumer Electronics 53, no. 2 (2007): 757-764. 197 | # Weighted Thresholded Histogram Equalization (WTHE) 198 | def WTHE(self, root = 0.5, value = 0.5, lower = 0): 199 | utils = Utils(self.image, self.color_space) 200 | image_gray = utils.image_gray() 201 | image_1d = image_gray.flatten() 202 | length = len(image_1d) 203 | 204 | pdf, _ = np.histogram(image_1d, 256, [0, 255]) 205 | pdf = pdf / length 206 | 207 | if value < 1: 208 | upper = pdf.max() * value 209 | else: 210 | upper = pdf.max() 211 | 212 | weight_pdf = np.array([0.0] * 256) 213 | for i in range(0, 256): 214 | if pdf[i] < lower: 215 | weight_pdf[i] = 0 216 | elif pdf[i] < upper: 217 | weight_pdf[i] = upper * ((pdf[i] - lower) / (upper - lower)) ** root 218 | else: 219 | weight_pdf[i] = upper 220 | 221 | weight_pdf_sum = np.sum(weight_pdf) 222 | weight_pdf_scale = weight_pdf / weight_pdf_sum 223 | 224 | histogram = Histogram() 225 | LUT = histogram.sub_histogram_equalization(weight_pdf_scale) 226 | return utils.LUT_image(LUT) 227 | 228 | 229 | # Kim, Mary, and Min Gyo Chung. 230 | # Recursively separated and weighted histogram equalization for brightness preservation and contrast enhancement. 231 | # IEEE Transactions on Consumer Electronics 54, no. 3 (2008): 1389-1397. 232 | # Recursive Separated and Weighted Histogram Equalization (RSWHE) 233 | def RSWHE(self, type = 'mean', recursive = 2): 234 | utils = Utils(self.image, self.color_space) 235 | image_gray = utils.image_gray() 236 | image_1d = image_gray.flatten() 237 | length = len(image_1d) 238 | 239 | histogram = Histogram() 240 | if (type == 'mean'): 241 | histogram_segmentation = histogram.histogram_segmentation(image_1d, np.mean, recursive) 242 | elif (type == 'median'): 243 | histogram_segmentation = histogram.histogram_segmentation(image_1d, np.median, recursive) 244 | 245 | pdf, _ = np.histogram(image_1d, 256, [0, 255]) 246 | highest_probabilitiy = pdf.max() / length 247 | lowest_probability = pdf.min() / length 248 | 249 | image_mean = np.mean(image_1d) 250 | image_min = image_1d.min() 251 | image_max = image_1d.max() 252 | image_middle = (int(image_min) + int(image_max)) / 2 253 | beta = highest_probabilitiy * abs(image_mean - image_middle) / (image_max - image_min) 254 | 255 | histogram_weight = [] 256 | for sub_histogram in histogram_segmentation: 257 | sub_histogram_scale = sub_histogram / length 258 | alpha = np.sum(sub_histogram_scale) 259 | for i in range(0, len(sub_histogram_scale)): 260 | sub_histogram_scale[i] = highest_probabilitiy * ((sub_histogram_scale[i] - lowest_probability) / (highest_probabilitiy - lowest_probability)) ** alpha + beta 261 | 262 | histogram_weight += [sub_histogram_scale] 263 | 264 | histogram_weight_sum = 0 265 | for sub_histogram in histogram_weight: 266 | histogram_weight_sum += np.sum(sub_histogram) 267 | 268 | histogram_weight_scale = [] 269 | for sub_histogram in histogram_weight: 270 | sub_histogram = sub_histogram / histogram_weight_sum 271 | histogram_weight_scale += [sub_histogram] 272 | 273 | start = 0 274 | end = -1 275 | LUT = np.array([]) 276 | for sub_histogram in histogram_weight_scale: 277 | start = end + 1 278 | end = start + len(sub_histogram) - 1 279 | LUT = np.concatenate((LUT, histogram.sub_histogram_equalization(sub_histogram, start, end))) 280 | 281 | return utils.LUT_image(LUT) 282 | 283 | 284 | # Wang, C., J. Peng, and Z. Ye. 285 | # Flattest histogram specification with accurate brightness preservation. 286 | # IET Image Processing 2, no. 5 (2008): 249-262. 287 | # Flattest Histogram Specification with Accurate Brightness Preservation (FHSABP) 288 | def FHSABP(self): 289 | utils = Utils(self.image, self.color_space) 290 | image_gray = utils.image_gray() 291 | image_1d = image_gray.flatten() 292 | 293 | pdf_input, _ = np.histogram(image_1d, 256, [0, 255]) 294 | pdf_input = pdf_input / 1.0 295 | pdf_output = np.array([0.0] * 256) 296 | 297 | mean = np.mean(image_1d) 298 | if mean < 84.67: 299 | x_0 = 3 * math.floor(mean) + 1 300 | a = (-6 * x_0 + 12 * mean) / (x_0 * (x_0 + 1) * (x_0 + 2)) 301 | b = (4 * x_0 - 6 * mean + 2) / ((x_0 + 1) * (x_0 + 2)) 302 | 303 | for i in range(0, 256): 304 | pdf_output[i] = max(0, a * i + b) 305 | elif mean <= 170.33: 306 | a = (mean - 127.5) / 1398080 307 | b = (511 - 3 * mean) / 32896 308 | 309 | for i in range(0, 256): 310 | pdf_output[i] = a * i + b 311 | else: 312 | invert_mean = 255 - mean 313 | x_0 = 3 * math.floor(invert_mean) + 1 314 | a = (-6 * x_0 + 12 * invert_mean) / (x_0 * (x_0 + 1) * (x_0 + 2)) 315 | b = (4 * x_0 - 6 * invert_mean + 2) / ((x_0 + 1) * (x_0 + 2)) 316 | 317 | for i in range(0, 256): 318 | pdf_output[i] = max(0, a * i + b) 319 | 320 | histogram = Histogram() 321 | LUT = histogram.histogram_specification(pdf_input, pdf_output) 322 | return utils.LUT_image(LUT) 323 | 324 | 325 | # Ooi, Chen Hee, Nicholas Sia Pik Kong, and Haidi Ibrahim. 326 | # Bi-histogram equalization with a plateau limit for digital image enhancement. 327 | # IEEE transactions on consumer electronics 55, no. 4 (2009): 2072-2080. 328 | # Bi-Histogram Equalization with a Plateau Limit (BHEPL) 329 | def BHEPL(self): 330 | utils = Utils(self.image, self.color_space) 331 | image_gray = utils.image_gray() 332 | image_1d = image_gray.flatten() 333 | 334 | mean = np.mean(image_1d) 335 | mean = math.floor(mean) 336 | 337 | lower_filter = image_1d <= mean 338 | lower_1d = image_1d[lower_filter] 339 | 340 | upper_filter = image_1d > mean 341 | upper_1d = image_1d[upper_filter] 342 | 343 | lower_histogram, _ = np.histogram(lower_1d, mean + 1, [0, mean]) 344 | upper_histogram, _ = np.histogram(upper_1d, 255 - mean, [mean + 1, 255]) 345 | 346 | lower_plateau_limit = np.sum(lower_histogram) / (mean + 1) 347 | upper_plateau_limit = np.sum(upper_histogram) / (255 - mean) 348 | 349 | lower_histogram[lower_histogram > lower_plateau_limit] = lower_plateau_limit 350 | upper_histogram[upper_histogram > upper_plateau_limit] = upper_plateau_limit 351 | 352 | lower_histogram_sum = np.sum(lower_histogram) 353 | upper_Histogram_sum = np.sum(upper_histogram) 354 | 355 | lower_cdf = lower_histogram.cumsum() 356 | lower_cdf_mask = np.ma.masked_equal(lower_cdf, 0) 357 | 358 | # Scale cdf to [range_min, range_max] 359 | lower_scale_cdf_mask = ((lower_cdf_mask - 0.5 * lower_histogram - lower_cdf_mask.min()) * (mean - 0) / (lower_cdf_mask.max() - lower_cdf_mask.min())) + 0 360 | lower_LUT = np.ma.filled(lower_scale_cdf_mask, 0).astype('uint8') 361 | 362 | upper_cdf = upper_histogram.cumsum() 363 | upper_cdf_mask = np.ma.masked_equal(upper_cdf, 0) 364 | 365 | # Scale cdf to [range_min, range_max] 366 | upper_scale_cdf_mask = ((upper_cdf_mask - 0.5 * upper_histogram - upper_cdf_mask.min()) * (255 - mean - 1) / (upper_cdf_mask.max() - upper_cdf_mask.min())) + mean + 1 367 | upper_LUT = np.ma.filled(upper_scale_cdf_mask, 0).astype('uint8') 368 | 369 | LUT = np.concatenate((lower_LUT, upper_LUT)) 370 | 371 | return utils.LUT_image(LUT) 372 | 373 | 374 | # Zuo, Chao, Qian Chen, and Xiubao Sui. 375 | # Range limited bi-histogram equalization for image contrast enhancement. 376 | # Optik 124, no. 5 (2013): 425-431. 377 | # Range Limited Bi-Histogram Equalization (RLBHE) 378 | def RLBHE(self): 379 | utils = Utils(self.image, self.color_space) 380 | image_gray = utils.image_gray() 381 | image_1d = image_gray.flatten() 382 | 383 | otsu_threshold, _ = cv.threshold(image_gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU) 384 | otsu_threshold = math.floor(otsu_threshold) 385 | 386 | pdf, _ = np.histogram(image_1d, 256, [0, 255]) 387 | image_mean = 0 388 | lower_cumulative = 0 389 | for i in range(0, 256): 390 | image_mean += i * pdf[i] 391 | 392 | if i <= otsu_threshold: 393 | lower_cumulative += pdf[i] 394 | 395 | length = len(image_1d) 396 | image_mean /= length 397 | lower_cumulative /= length 398 | 399 | a = lower_cumulative 400 | b = 2 * image_mean - otsu_threshold - (1 - a) 401 | x_0 = 0 402 | x_l = 255 403 | for i in range(0, 500): 404 | temp = 2 * (a * x_0 + (1 - a) * x_l - b) 405 | x_0 -= temp * a 406 | x_l -= temp * (1 - a) 407 | 408 | if x_0 < 0: 409 | x_0 = 0 410 | elif x_0 > otsu_threshold: 411 | x_0 = otsu_threshold 412 | else: 413 | x_0 = math.floor(x_0) 414 | 415 | if x_l > 255: 416 | x_l = 255 417 | elif x_l <= otsu_threshold: 418 | x_l = otsu_threshold + 1 419 | else: 420 | x_l = math.floor(x_l) 421 | 422 | histogram = Histogram() 423 | LUT = histogram.histogram_equalization_threshold(image_1d, otsu_threshold, x_0, x_l) 424 | return utils.LUT_image(LUT) 425 | 426 | # Automatic Weighting Mean-separated Histogram Equalization 427 | # def AWMHE(image, color_space = 'HSV'): 428 | # image_gray, image_color = utils.image_gray(image, color_space) 429 | # image_1d = image_gray.flatten() 430 | 431 | # pdf, _ = np.histogram(image_1d, 256, [0, 255]) 432 | # cdf = pdf.cumsum() 433 | 434 | # print(histogram.histogram_weighting_mean(cdf)) 435 | 436 | ######################################## 437 | # 438 | # Adaptive histogram 439 | # 440 | ######################################## 441 | 442 | # Adaptive Histogram Equalization 443 | def AHE(self): 444 | return self.image 445 | 446 | # Non-Overlapped Sub-block Histogram Equalization 447 | def NOSHE(self): 448 | return self.image 449 | 450 | # Partially Overlapped Sub-block Histogram Equalization 451 | def POSHE(self): 452 | return self.image 453 | 454 | # Cascadede Multistep Binominal Filtering Histogram Equalization 455 | def CMBFHE(self): 456 | return self.image 457 | 458 | # Contrast Limited Adaptive Histogram Equalization 459 | def CLAHE(self): 460 | return self.image 461 | 462 | 463 | ######################################## 464 | # 465 | # Gamma Correction 466 | # 467 | ######################################## 468 | 469 | 470 | # Wang, Zhi-Guo, Zhi-Hu Liang, and Chun-Liang Liu. 471 | # A real-time image processor with combining dynamic contrast ratio enhancement and inverse gamma correction for PDP. 472 | # Displays 30, no. 3 (2009): 133-139. 473 | # Dynamic Contrast Ratio Gamma Correction (DCRGC) 474 | def DCRGC(self, contrast_intensity, gamma): 475 | utils = Utils(self.image, self.color_space) 476 | image_gray = utils.image_gray() 477 | image_1d = image_gray.flatten() 478 | 479 | gamma_reverse = [0.0] * 256 480 | for i in range(1, 256): 481 | gamma_reverse[i] = (i / 255) ** gamma 482 | 483 | normalized_foundation_histogram = np.array([0.0] * 256) 484 | for i in range(1, 256): 485 | normalized_foundation_histogram[i] = gamma_reverse[i] - gamma_reverse[i - 1] 486 | 487 | histogram, _ = np.histogram(image_1d, 256, [0, 255]) 488 | histogram = histogram / len(image_1d) 489 | 490 | normalized_combination_histogram = histogram * contrast_intensity + normalized_foundation_histogram * (1 - contrast_intensity) 491 | normalized_combination_cdf = normalized_combination_histogram.cumsum() 492 | 493 | LUT = normalized_combination_cdf * 255 494 | LUT = LUT.astype('uint8') 495 | 496 | return utils.LUT_image(LUT) 497 | 498 | 499 | # Huang, Shih-Chia, Fan-Chieh Cheng, and Yi-Sheng Chiu. 500 | # Efficient contrast enhancement using adaptive gamma correction with weighting distribution. 501 | # IEEE transactions on image processing 22, no. 3 (2012): 1032-1041. 502 | # Adaptive Gamma Correction with Weighting Distribution (AGCWD) 503 | def AGCWD(self, alpha = 0.5): 504 | utils = Utils(self.image, self.color_space) 505 | image_gray = utils.image_gray() 506 | image_1d = image_gray.flatten() 507 | length = len(image_1d) 508 | 509 | pdf, _ = np.histogram(image_1d, 256, [0, 255]) 510 | pdf = pdf / length 511 | highest_probabilitiy = pdf.max() 512 | lowest_probability = pdf.min() 513 | 514 | weight_distribution = highest_probabilitiy * ((pdf - lowest_probability) / (highest_probabilitiy - lowest_probability)) ** alpha 515 | weight_distribution_sum = np.sum(weight_distribution) 516 | 517 | weight_distribution_scale = weight_distribution / weight_distribution_sum 518 | 519 | LUT = np.array([0.0] * 256) 520 | for i in range(0, 256): 521 | LUT[i] = 255 * (i / 255) ** (1 - weight_distribution_scale[i]) 522 | LUT = LUT.astype('uint8') 523 | 524 | return utils.LUT_image(LUT) 525 | 526 | # Rahman, Shanto, Md Mostafijur Rahman, Mohammad Abdullah-Al-Wadud, Golam Dastegir Al-Quaderi, and Mohammad Shoyaib. 527 | # An adaptive gamma correction for image enhancement. 528 | # EURASIP Journal on Image and Video Processing 2016, no. 1 (2016): 1-13. 529 | # Adaptive Gamma Correction Image Enhancement (AGCIE) 530 | def AGCIE(self, contrast_threshold = 3): 531 | utils = Utils(self.image, self.color_space) 532 | image_gray = utils.image_gray() 533 | image_1d = image_gray.flatten() / 255 534 | 535 | mean = np.mean(image_1d) 536 | std = np.std(image_1d) 537 | LUT = np.arange(0, 256) / 255 538 | 539 | if std <= 1 / (4 * contrast_threshold): 540 | gamma = -math.log(std, 2) 541 | else: 542 | gamma = math.exp((1 - (mean + std))/2) 543 | 544 | if mean >= 0.5: 545 | LUT = 255 * (LUT ** gamma) 546 | else: 547 | for i in range(0, 256): 548 | LUT[i] = 255 * (LUT[i] ** gamma / (LUT[i] ** gamma + (1 - LUT[i] ** gamma) * mean ** gamma)) 549 | 550 | LUT = LUT.astype('uint8') 551 | 552 | return utils.LUT_image(LUT) 553 | 554 | # Gupta, Bhupendra, and Mayank Tiwari. 555 | # Minimum mean brightness error contrast enhancement of color images using adaptive gamma correction with color preserving framework. 556 | # Optik 127, no. 4 (2016): 1671-1676. 557 | # Adaptive Gamma Correction Color Preserving Framework (AGCCPF) 558 | def AGCCPF(self, alpha = 0.5): 559 | utils = Utils(self.image, self.color_space) 560 | image_gray = utils.image_gray() 561 | image_1d = image_gray.flatten() 562 | 563 | pdf, _ = np.histogram(image_1d, 256, [0, 255]) 564 | 565 | image_equalization = self.GHE() 566 | image_equalization_1d = image_equalization.flatten() 567 | 568 | pdf_equalization, _ = np.histogram(image_equalization_1d, 256, [0, 255]) 569 | 570 | smooth_pdf = 0.5 * pdf + 0.5 * pdf_equalization 571 | smooth_pdf_scale = smooth_pdf / np.sum(smooth_pdf) 572 | 573 | cdf = smooth_pdf_scale.cumsum() 574 | 575 | LUT = np.array([0.0] * 256) 576 | for i in range(0, 256): 577 | LUT[i] = 255 * (i / 255) ** (1 - cdf[i]) 578 | LUT = LUT.astype('uint8') 579 | 580 | return utils.LUT_image(LUT) 581 | ######################################## 582 | # 583 | # Genetic Algorithm 584 | # 585 | ######################################## 586 | 587 | # Saitoh, Fumihiko. 588 | # Image contrast enhancement using genetic algorithm. 589 | # In IEEE SMC'99 Conference Proceedings. 1999 IEEE International Conference on Systems, Man, and Cybernetics (Cat. No. 99CH37028), vol. 4, pp. 899-904. IEEE, 1999. 590 | # Saitoh Genetic Algorithm (SGA) 591 | 592 | # Hashemi, Sara, Soheila Kiani, Navid Noroozi, and Mohsen Ebrahimi Moghaddam. 593 | # An image contrast enhancement method based on genetic algorithm. 594 | # Pattern Recognition Letters 31, no. 13 (2010): 1816-1824. 595 | # Constrast Enhancement Based Genetic Algorithm (CEBGA) 596 | 597 | ######################################## 598 | # 599 | # Other 600 | # 601 | ######################################## 602 | 603 | 604 | # Raju, G., and Madhu S. Nair. 605 | # A fast and efficient color image enhancement method based on fuzzy-logic and histogram. 606 | # AEU-International Journal of electronics and communications 68, no. 3 (2014): 237-243. 607 | # Fuzzy-Logic and Histogram (FLH) 608 | def FLH(self, enhancement): 609 | utils = Utils(self.image, self.color_space) 610 | image_gray = utils.image_gray() 611 | image_1d = image_gray.flatten() 612 | 613 | histogram, _ = np.histogram(image_1d, 256, [0, 255]) 614 | sum_weight_histogram = 0 615 | for i in range(0, 256): 616 | sum_weight_histogram += i * histogram[i] 617 | 618 | control = sum_weight_histogram / np.sum(histogram) 619 | control = math.floor(control) 620 | 621 | lower_fuzzy = np.array([0.0] * (control)) 622 | upper_fuzzy = np.array([0.0] * (256 - control)) 623 | 624 | for i in range(0, control): 625 | lower_fuzzy[i] = i + (1 - control + i) * enhancement / control 626 | 627 | for i in range(control, 256): 628 | upper_fuzzy[i - control] = (255 - i) / (255 - control) + 255 - (255 - i) * enhancement / (255 - control) 629 | 630 | LUT = np.concatenate((lower_fuzzy, upper_fuzzy)) 631 | LUT[LUT < 0] = 0 632 | LUT = LUT.astype('uint8') 633 | 634 | return utils.LUT_image(LUT) 635 | 636 | # Celik, Turgay, and Tardi Tjahjadi. 637 | # Contextual and variational contrast enhancement. 638 | # IEEE Transactions on Image Processing 20, no. 12 (2011): 3431-3441. 639 | # Contextual and Variational Contrast (CVC) 640 | --------------------------------------------------------------------------------