image_processing.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. from PIL import Image
  2. from matplotlib import cm
  3. import random
  4. from skimage import color
  5. import numpy as np
  6. import ipfml.metrics as metrics
  7. import cv2
  8. from scipy import signal
  9. def get_LAB_L_SVD(image):
  10. """
  11. @brief Returns Singular values from LAB L Image information
  12. @param fig a matplotlib figure
  13. @return a Python Imaging Library (PIL) image : default size (480,640,3)
  14. Usage :
  15. >>> from PIL import Image
  16. >>> from ipfml import image_processing
  17. >>> img = Image.open('./images/test_img.png')
  18. >>> U, s, V = image_processing.get_LAB_L_SVD(img)
  19. >>> U.shape
  20. (200, 200)
  21. >>> len(s)
  22. 200
  23. >>> V.shape
  24. (200, 200)
  25. """
  26. L = metrics.get_LAB_L(image)
  27. return metrics.get_SVD(L)
  28. def get_LAB_L_SVD_s(image):
  29. """
  30. @brief Returns s (Singular values) SVD from L of LAB Image information
  31. @param PIL Image
  32. @return vector of singular values
  33. Usage :
  34. >>> from PIL import Image
  35. >>> from ipfml import image_processing
  36. >>> img = Image.open('./images/test_img.png')
  37. >>> s = image_processing.get_LAB_L_SVD_s(img)
  38. >>> len(s)
  39. 200
  40. """
  41. L = metrics.get_LAB_L(image)
  42. return metrics.get_SVD_s(L)
  43. def get_LAB_L_SVD_U(image):
  44. """
  45. @brief Returns U SVD from L of LAB Image information
  46. @param PIL Image
  47. @return vector of singular values
  48. Usage :
  49. >>> from PIL import Image
  50. >>> from ipfml import image_processing
  51. >>> img = Image.open('./images/test_img.png')
  52. >>> U = image_processing.get_LAB_L_SVD_U(img)
  53. >>> U.shape
  54. (200, 200)
  55. """
  56. L = metrics.get_LAB_L(image)
  57. return metrics.get_SVD_U(L)
  58. def get_LAB_L_SVD_V(image):
  59. """
  60. @brief Returns V SVD from L of LAB Image information
  61. @param PIL Image
  62. @return vector of singular values
  63. Usage :
  64. >>> from PIL import Image
  65. >>> from ipfml import image_processing
  66. >>> img = Image.open('./images/test_img.png')
  67. >>> V = image_processing.get_LAB_L_SVD_V(img)
  68. >>> V.shape
  69. (200, 200)
  70. """
  71. L = metrics.get_LAB_L(image)
  72. return metrics.get_SVD_V(L)
  73. def divide_in_blocks(image, block_size, pil=True):
  74. '''
  75. @brief Divide image into equal size blocks
  76. @param img - PIL Image or numpy array
  77. @param block - tuple (width, height) representing the size of each dimension of the block
  78. @param pil - kind block type (PIL by default or Numpy array)
  79. @return list containing all 2D numpy blocks (in RGB or not)
  80. Usage :
  81. >>> import numpy as np
  82. >>> from PIL import Image
  83. >>> from ipfml import image_processing
  84. >>> from ipfml import metrics
  85. >>> image_values = np.random.randint(255, size=(800, 800, 3))
  86. >>> blocks = divide_in_blocks(image_values, (20, 20))
  87. >>> len(blocks)
  88. 1600
  89. >>> blocks[0].width
  90. 20
  91. >>> blocks[0].height
  92. 20
  93. >>> img_l = Image.open('./images/test_img.png')
  94. >>> L = metrics.get_LAB_L(img_l)
  95. >>> blocks_L = divide_in_blocks(L, (100, 100))
  96. >>> len(blocks_L)
  97. 4
  98. >>> blocks_L[0].width
  99. 100
  100. '''
  101. blocks = []
  102. mode = 'RGB'
  103. # convert in numpy array
  104. image_array = np.array(image)
  105. # check dimension of input image
  106. if image_array.ndim != 3:
  107. mode = 'L'
  108. image_width, image_height = image_array.shape
  109. else:
  110. image_width, image_height, _ = image_array.shape
  111. # check size compatibility
  112. width, height = block_size
  113. if(image_width % width != 0):
  114. raise "Width size issue, block size not compatible"
  115. if(image_height % height != 0):
  116. raise "Height size issue, block size not compatible"
  117. nb_block_width = image_width / width
  118. nb_block_height = image_height / height
  119. for i in range(int(nb_block_width)):
  120. begin_x = i * width
  121. for j in range(int(nb_block_height)):
  122. begin_y = j * height
  123. # getting sub block information
  124. current_block = image_array[begin_x:(begin_x + width), begin_y:(begin_y + height)]
  125. if pil:
  126. blocks.append(Image.fromarray(current_block.astype('uint8'), mode))
  127. else:
  128. blocks.append(current_block)
  129. return blocks
  130. def normalize_arr(arr):
  131. '''
  132. @brief Normalize data of 1D array shape
  133. @param array - array data of 1D shape
  134. Usage :
  135. >>> from ipfml import image_processing
  136. >>> import numpy as np
  137. >>> arr = np.arange(11)
  138. >>> arr_normalized = image_processing.normalize_arr(arr)
  139. >>> arr_normalized[1]
  140. 0.1
  141. '''
  142. output_arr = []
  143. max_value = max(arr)
  144. min_value = min(arr)
  145. for v in arr:
  146. output_arr.append((v - min_value) / (max_value - min_value))
  147. return output_arr
  148. def normalize_arr_with_range(arr, min, max):
  149. '''
  150. @brief Normalize data of 1D array shape
  151. @param array - array data of 1D shape
  152. Usage :
  153. >>> from ipfml import image_processing
  154. >>> import numpy as np
  155. >>> arr = np.arange(11)
  156. >>> arr_normalized = image_processing.normalize_arr_with_range(arr, 0, 20)
  157. >>> arr_normalized[1]
  158. 0.05
  159. '''
  160. output_arr = []
  161. for v in arr:
  162. output_arr.append((v - min) / (max - min))
  163. return output_arr
  164. def normalize_2D_arr(arr):
  165. """
  166. @brief Return array normalize from its min and max values
  167. @param 2D numpy array
  168. Usage :
  169. >>> from PIL import Image
  170. >>> from ipfml import image_processing
  171. >>> img = Image.open('./images/test_img.png')
  172. >>> img_mscn = image_processing.rgb_to_mscn(img)
  173. >>> img_normalized = image_processing.normalize_2D_arr(img_mscn)
  174. >>> img_normalized.shape
  175. (200, 200)
  176. """
  177. # getting min and max value from 2D array
  178. max_value = arr.max(axis=1).max()
  179. min_value = arr.min(axis=1).min()
  180. # lambda computation to normalize
  181. g = lambda x : (x - min_value) / (max_value - min_value)
  182. f = np.vectorize(g)
  183. return f(arr)
  184. def rgb_to_mscn(image):
  185. """
  186. @brief Convert RGB Image into Mean Subtracted Contrast Normalized (MSCN)
  187. @param 3D RGB image numpy array or PIL RGB image
  188. Usage :
  189. >>> from PIL import Image
  190. >>> from ipfml import image_processing
  191. >>> img = Image.open('./images/test_img.png')
  192. >>> img_mscn = image_processing.rgb_to_mscn(img)
  193. >>> img_mscn.shape
  194. (200, 200)
  195. """
  196. # check if PIL image or not
  197. img_arr = np.array(image)
  198. # convert rgb image to gray
  199. im = np.array(color.rgb2gray(img_arr)*255, 'uint8')
  200. return metrics.gray_to_mscn(im)
  201. def rgb_to_grey_low_bits(image, bind=15):
  202. """
  203. @brief Convert RGB Image into grey image using only 4 low bits values
  204. @param 3D RGB image numpy array or PIL RGB image
  205. Usage :
  206. >>> from PIL import Image
  207. >>> from ipfml import image_processing
  208. >>> img = Image.open('./images/test_img.png')
  209. >>> low_bits_grey_img = image_processing.rgb_to_grey_low_bits(img)
  210. >>> low_bits_grey_img.shape
  211. (200, 200)
  212. """
  213. img_arr = np.array(image)
  214. grey_block = np.array(color.rgb2gray(img_arr)*255, 'uint8')
  215. return metrics.get_low_bits_img(grey_block, bind)
  216. def rgb_to_LAB_L_low_bits(image, bind=15):
  217. """
  218. @brief Convert RGB Image into Lab L channel image using only 4 low bits values
  219. @param 3D RGB image numpy array or PIL RGB image
  220. Usage :
  221. >>> from PIL import Image
  222. >>> from ipfml import image_processing
  223. >>> img = Image.open('./images/test_img.png')
  224. >>> low_bits_Lab_l_img = image_processing.rgb_to_LAB_L_low_bits(img)
  225. >>> low_bits_Lab_l_img.shape
  226. (200, 200)
  227. """
  228. L_block = np.asarray(metrics.get_LAB_L(image), 'uint8')
  229. return metrics.get_low_bits_img(L_block, bind)
  230. # TODO : Check this method too...
  231. def get_random_active_block(blocks, threshold = 0.1):
  232. """
  233. @brief Find an active block from blocks and return it (randomly way)
  234. @param 2D numpy array
  235. @param threshold 0.1 by default
  236. """
  237. active_blocks = []
  238. for id, block in enumerate(blocks):
  239. arr = np.asarray(block)
  240. variance = np.var(arr.flatten())
  241. if variance >= threshold:
  242. active_blocks.append(id)
  243. r_id = random.choice(active_blocks)
  244. return np.asarray(blocks[r_id])
  245. # TODO : check this method and check how to use active block
  246. def segment_relation_in_block(block, active_block):
  247. """
  248. @brief Return bêta value to quantity relation between central segment and surrouding regions into block
  249. @param 2D numpy array
  250. """
  251. if block.ndim != 2:
  252. raise "Numpy array dimension is incorrect, expected 2."
  253. # getting middle information of numpy array
  254. x, y = block.shape
  255. if y < 4:
  256. raise "Block size too small needed at least (x, 4) shape"
  257. middle = int(y / 2)
  258. # get central segments
  259. central_segments = block[:, middle-1:middle+1]
  260. # getting surrouding parts
  261. left_part = block[:, 0:middle-1]
  262. right_part = block[:, middle+1:]
  263. surrounding_parts = np.concatenate([left_part, right_part])
  264. std_sur = np.std(surrounding_parts.flatten())
  265. std_cen = np.std(central_segments.flatten())
  266. std_block = np.std(block.flatten())
  267. std_q = std_cen / std_sur
  268. # from article, it says that block if affected with noise if (std_block > 2 * beta)
  269. beta = abs(std_q - std_block) / max(std_q, std_block)
  270. return beta
  271. ### other way to compute MSCN :
  272. # TODO : Temp code, check to remove or use it
  273. def normalize_kernel(kernel):
  274. return kernel / np.sum(kernel)
  275. def gaussian_kernel2d(n, sigma):
  276. Y, X = np.indices((n, n)) - int(n/2)
  277. gaussian_kernel = 1 / (2 * np.pi * sigma ** 2) * np.exp(-(X ** 2 + Y ** 2) / (2 * sigma ** 2))
  278. return normalize_kernel(gaussian_kernel)
  279. def local_mean(image, kernel):
  280. return signal.convolve2d(image, kernel, 'same')
  281. def local_deviation(image, local_mean, kernel):
  282. "Vectorized approximation of local deviation"
  283. sigma = image ** 2
  284. sigma = signal.convolve2d(sigma, kernel, 'same')
  285. return np.sqrt(np.abs(local_mean ** 2 - sigma))
  286. def calculate_mscn_coefficients(image, kernel_size=6, sigma=7/6):
  287. # check if PIL image or not
  288. img_arr = np.array(image)
  289. C = 1/255
  290. kernel = gaussian_kernel2d(kernel_size, sigma=sigma)
  291. local_mean = signal.convolve2d(img_arr, kernel, 'same')
  292. local_var = local_deviation(img_arr, local_mean, kernel)
  293. return (img_arr - local_mean) / (local_var + C)