image_processing.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. from PIL import Image
  2. from matplotlib import cm
  3. from skimage import color
  4. import numpy as np
  5. import ipfml.metrics as metrics
  6. import cv2
  7. def fig2data(fig):
  8. """
  9. @brief Convert a Matplotlib figure to a 3D numpy array with RGB channels and return it
  10. @param fig a matplotlib figure
  11. @return a numpy 3D array of RGB values
  12. """
  13. # draw the renderer
  14. fig.canvas.draw()
  15. # Get the RGBA buffer from the figure
  16. w,h = fig.canvas.get_width_height()
  17. buf = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8)
  18. buf.shape = (w, h, 3)
  19. # canvas.tostring_argb give pixmap in ARGB mode. Roll the ALPHA channel to have it in RGBA mode
  20. buf = np.roll(buf, 3, axis=2)
  21. return buf
  22. def fig2img(fig):
  23. """
  24. @brief Convert a Matplotlib figure to a PIL Image in RGB format and return it
  25. @param fig a matplotlib figure
  26. @return a Python Imaging Library (PIL) image : default size (480,640,3)
  27. """
  28. # put the figure pixmap into a numpy array
  29. buf = fig2data(fig)
  30. w, h, d = buf.shape
  31. return Image.frombytes("RGB", (w, h), buf.tostring())
  32. def get_LAB_L_SVD(image):
  33. """
  34. @brief Returns Singular values from LAB L Image information
  35. @param fig a matplotlib figure
  36. @return a Python Imaging Library (PIL) image : default size (480,640,3)
  37. Usage :
  38. >>> from PIL import Image
  39. >>> from ipfml import image_processing
  40. >>> img = Image.open('./images/test_img.png')
  41. >>> U, s, V = image_processing.get_LAB_L_SVD(img)
  42. >>> U.shape
  43. (200, 200)
  44. >>> len(s)
  45. 200
  46. >>> V.shape
  47. (200, 200)
  48. """
  49. L = metrics.get_LAB_L(image)
  50. return metrics.get_SVD(L)
  51. def get_LAB_L_SVD_s(image):
  52. """
  53. @brief Returns s (Singular values) SVD from L of LAB Image information
  54. @param PIL Image
  55. @return vector of singular values
  56. Usage :
  57. >>> from PIL import Image
  58. >>> from ipfml import image_processing
  59. >>> img = Image.open('./images/test_img.png')
  60. >>> s = image_processing.get_LAB_L_SVD_s(img)
  61. >>> len(s)
  62. 200
  63. """
  64. L = metrics.get_LAB_L(image)
  65. return metrics.get_SVD_s(L)
  66. def get_LAB_L_SVD_U(image):
  67. """
  68. @brief Returns U SVD from L of LAB Image information
  69. @param PIL Image
  70. @return vector of singular values
  71. Usage :
  72. >>> from PIL import Image
  73. >>> from ipfml import image_processing
  74. >>> img = Image.open('./images/test_img.png')
  75. >>> U = image_processing.get_LAB_L_SVD_U(img)
  76. >>> U.shape
  77. (200, 200)
  78. """
  79. L = metrics.get_LAB_L(image)
  80. return metrics.get_SVD_U(L)
  81. def get_LAB_L_SVD_V(image):
  82. """
  83. @brief Returns V SVD from L of LAB Image information
  84. @param PIL Image
  85. @return vector of singular values
  86. Usage :
  87. >>> from PIL import Image
  88. >>> from ipfml import image_processing
  89. >>> img = Image.open('./images/test_img.png')
  90. >>> V = image_processing.get_LAB_L_SVD_V(img)
  91. >>> V.shape
  92. (200, 200)
  93. """
  94. L = metrics.get_LAB_L(image)
  95. return metrics.get_SVD_V(L)
  96. def divide_in_blocks(image, block_size):
  97. '''
  98. @brief Divide image into equal size blocks
  99. @param img - PIL Image or numpy array
  100. @param block - tuple (width, height) representing the size of each dimension of the block
  101. @return list containing all PIL Image block (in RGB)
  102. Usage :
  103. >>> import numpy as np
  104. >>> from PIL import Image
  105. >>> from ipfml import image_processing
  106. >>> from ipfml import metrics
  107. >>> image_values = np.random.randint(255, size=(800, 800, 3))
  108. >>> blocks = divide_in_blocks(image_values, (20, 20))
  109. >>> len(blocks)
  110. 1600
  111. >>> blocks[0].width
  112. 20
  113. >>> blocks[0].height
  114. 20
  115. >>> img_l = Image.open('./images/test_img.png')
  116. >>> L = metrics.get_LAB_L(img_l)
  117. >>> blocks_L = divide_in_blocks(L, (100, 100))
  118. >>> len(blocks_L)
  119. 4
  120. >>> blocks_L[0].width
  121. 100
  122. '''
  123. blocks = []
  124. mode = 'RGB'
  125. # check input type (PIL Image or numpy array) and convert it if necessary
  126. if hasattr(image, 'filename'):
  127. image_array = np.array(image)
  128. else:
  129. image_array = image
  130. # check dimension of input image
  131. if image_array.ndim != 3:
  132. mode = 'L'
  133. image_width, image_height = image_array.shape
  134. else:
  135. image_width, image_height, _ = image_array.shape
  136. # check size compatibility
  137. width, height = block_size
  138. if(image_width % width != 0):
  139. raise "Width size issue, block size not compatible"
  140. if(image_height % height != 0):
  141. raise "Height size issue, block size not compatible"
  142. nb_block_width = image_width / width
  143. nb_block_height = image_height / height
  144. for i in range(int(nb_block_width)):
  145. begin_x = i * width
  146. for j in range(int(nb_block_height)):
  147. begin_y = j * height
  148. # getting sub block information
  149. current_block = image_array[begin_x:(begin_x + width), begin_y:(begin_y + height)]
  150. blocks.append(Image.fromarray(current_block.astype('uint8'), mode))
  151. return blocks
  152. def normalize_arr(arr):
  153. '''
  154. @brief Normalize data of 1D array shape
  155. @param array - array data of 1D shape
  156. Usage :
  157. >>> from ipfml import image_processing
  158. >>> import numpy as np
  159. >>> arr = np.arange(11)
  160. >>> arr_normalized = image_processing.normalize_arr(arr)
  161. >>> arr_normalized[1]
  162. 0.1
  163. '''
  164. output_arr = []
  165. max_value = max(arr)
  166. min_value = min(arr)
  167. for v in arr:
  168. output_arr.append((v - min_value) / (max_value - min_value))
  169. return output_arr
  170. def normalize_arr_with_range(arr, min, max):
  171. '''
  172. @brief Normalize data of 1D array shape
  173. @param array - array data of 1D shape
  174. Usage :
  175. >>> from ipfml import image_processing
  176. >>> import numpy as np
  177. >>> arr = np.arange(11)
  178. >>> arr_normalized = image_processing.normalize_arr_with_range(arr, 0, 20)
  179. >>> arr_normalized[1]
  180. 0.05
  181. '''
  182. output_arr = []
  183. for v in arr:
  184. output_arr.append((v - min) / (max - min))
  185. return output_arr
  186. def rgb_to_mscn(image):
  187. """
  188. @brief Convert RGB Image into Mean Subtracted Contrast Normalized (MSCN)
  189. @param 3D RGB image numpy array or PIL RGB image
  190. """
  191. # check if PIL image or not
  192. if hasattr(image, 'filename'):
  193. img_arr = np.array(image)
  194. else:
  195. img_arr = image
  196. im = np.array(color.rgb2gray(img_arr)*255, 'uint8')
  197. #im = cv2.imread(image.filename, 0) # read as gray scale
  198. blurred = cv2.GaussianBlur(im, (-3, 3), 1.166) # apply gaussian blur to the image
  199. blurred_sq = blurred * blurred
  200. sigma = cv2.GaussianBlur(im * im, (-3, 3), 1.166) # switch to -3, 3 (7, 7) before..
  201. sigma = (sigma - blurred_sq) ** 0.5
  202. sigma = sigma + 1.0/255 # to make sure the denominator doesn't give DivideByZero Exception
  203. structdis = (im - blurred)/sigma # final MSCN(i, j) image
  204. return structdis
  205. def segment_relation_in_block(block):
  206. """
  207. @brief Return betâ value to quantity relation between central segment and surrouding regions into block
  208. @param 2D numpy array
  209. """
  210. if block.ndim != 2:
  211. raise "Numpy array dimension is incorrect, expected 2."
  212. # getting middle information of numpy array
  213. x, y = block.shape
  214. if y < 4:
  215. raise "Block size too small needed at least (x, 4) shape"
  216. middle = int(y / 2)
  217. print(middle)
  218. # get central segments
  219. central_segments = block[:, middle-1:middle+1]
  220. # getting surrouding parts
  221. left_part = block[:, 0:middle-1]
  222. right_part = block[:, middle+1:]
  223. surrounding_parts = np.concatenate([left_part, right_part])
  224. std_cen = np.std(np.sort(central_segments.flatten()))
  225. std_sur = np.std(np.sort(surrounding_parts.flatten()))
  226. std_block = np.std(np.sort(block.flatten()))
  227. std_q = std_cen / std_sur
  228. # from article, it says that block if affected with noise if (std_block > 2 * beta)
  229. beta = abs(std_q - std_block) / max(std_q, std_block)
  230. return beta