image_processing.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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. def rgb_to_LAB_L_bits(image, interval):
  231. """
  232. @brief Returns only bits from LAB L canal specified into the interval
  233. @param image to convert using this interval of bits value to keep
  234. @param interval (begin, end) of bits values
  235. @return Numpy array with reduced values
  236. >>> from PIL import Image
  237. >>> from ipfml import image_processing
  238. >>> img = Image.open('./images/test_img.png')
  239. >>> bits_Lab_l_img = image_processing.rgb_to_LAB_L_bits(img)
  240. >>> bits_Lab_l_img.shape
  241. (200, 200)
  242. """
  243. L_block = np.asarray(metrics.get_LAB_L(image), 'uint8')
  244. return metrics.get_bits_img(L_block, interval)
  245. # TODO : Check this method too...
  246. def get_random_active_block(blocks, threshold = 0.1):
  247. """
  248. @brief Find an active block from blocks and return it (randomly way)
  249. @param 2D numpy array
  250. @param threshold 0.1 by default
  251. """
  252. active_blocks = []
  253. for id, block in enumerate(blocks):
  254. arr = np.asarray(block)
  255. variance = np.var(arr.flatten())
  256. if variance >= threshold:
  257. active_blocks.append(id)
  258. r_id = random.choice(active_blocks)
  259. return np.asarray(blocks[r_id])
  260. # TODO : check this method and check how to use active block
  261. def segment_relation_in_block(block, active_block):
  262. """
  263. @brief Return bêta value to quantity relation between central segment and surrouding regions into block
  264. @param 2D numpy array
  265. """
  266. if block.ndim != 2:
  267. raise "Numpy array dimension is incorrect, expected 2."
  268. # getting middle information of numpy array
  269. x, y = block.shape
  270. if y < 4:
  271. raise "Block size too small needed at least (x, 4) shape"
  272. middle = int(y / 2)
  273. # get central segments
  274. central_segments = block[:, middle-1:middle+1]
  275. # getting surrouding parts
  276. left_part = block[:, 0:middle-1]
  277. right_part = block[:, middle+1:]
  278. surrounding_parts = np.concatenate([left_part, right_part])
  279. std_sur = np.std(surrounding_parts.flatten())
  280. std_cen = np.std(central_segments.flatten())
  281. std_block = np.std(block.flatten())
  282. std_q = std_cen / std_sur
  283. # from article, it says that block if affected with noise if (std_block > 2 * beta)
  284. beta = abs(std_q - std_block) / max(std_q, std_block)
  285. return beta
  286. ### other way to compute MSCN :
  287. # TODO : Temp code, check to remove or use it
  288. def normalize_kernel(kernel):
  289. return kernel / np.sum(kernel)
  290. def gaussian_kernel2d(n, sigma):
  291. Y, X = np.indices((n, n)) - int(n/2)
  292. gaussian_kernel = 1 / (2 * np.pi * sigma ** 2) * np.exp(-(X ** 2 + Y ** 2) / (2 * sigma ** 2))
  293. return normalize_kernel(gaussian_kernel)
  294. def local_mean(image, kernel):
  295. return signal.convolve2d(image, kernel, 'same')
  296. def local_deviation(image, local_mean, kernel):
  297. "Vectorized approximation of local deviation"
  298. sigma = image ** 2
  299. sigma = signal.convolve2d(sigma, kernel, 'same')
  300. return np.sqrt(np.abs(local_mean ** 2 - sigma))
  301. def calculate_mscn_coefficients(image, kernel_size=6, sigma=7/6):
  302. # check if PIL image or not
  303. img_arr = np.array(image)
  304. C = 1/255
  305. kernel = gaussian_kernel2d(kernel_size, sigma=sigma)
  306. local_mean = signal.convolve2d(img_arr, kernel, 'same')
  307. local_var = local_deviation(img_arr, local_mean, kernel)
  308. return (img_arr - local_mean) / (local_var + C)