processing.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. """
  2. Functions to quickly extract reduced information from image
  3. """
  4. from PIL import Image
  5. import random
  6. import cv2
  7. from skimage import transform, color
  8. from scipy import signal
  9. from numpy.linalg import svd
  10. from sklearn.decomposition import FastICA, IncrementalPCA
  11. import numpy as np
  12. import ipfml.metrics as metrics
  13. from ipfml.exceptions import NumpyShapeComparisonException
  14. import os
  15. def get_LAB_L_SVD(image):
  16. """Returns Singular values from LAB L Image information
  17. Args:
  18. image: PIL Image or Numpy array
  19. Returns:
  20. U, s, V information obtained from SVD compression using Lab
  21. Example:
  22. >>> from PIL import Image
  23. >>> from ipfml import processing
  24. >>> img = Image.open('./images/test_img.png')
  25. >>> U, s, V = processing.get_LAB_L_SVD(img)
  26. >>> U.shape
  27. (200, 200)
  28. >>> len(s)
  29. 200
  30. >>> V.shape
  31. (200, 200)
  32. """
  33. L = metrics.get_LAB_L(image)
  34. return metrics.get_SVD(L)
  35. def get_LAB_L_SVD_s(image):
  36. """Returns s (Singular values) SVD from L of LAB Image information
  37. Args:
  38. image: PIL Image or Numpy array
  39. Returns:
  40. vector of singular values
  41. Example:
  42. >>> from PIL import Image
  43. >>> from ipfml import processing
  44. >>> img = Image.open('./images/test_img.png')
  45. >>> s = processing.get_LAB_L_SVD_s(img)
  46. >>> len(s)
  47. 200
  48. """
  49. L = metrics.get_LAB_L(image)
  50. return metrics.get_SVD_s(L)
  51. def get_LAB_L_SVD_U(image):
  52. """Returns U SVD from L of LAB Image information
  53. Args:
  54. image: PIL Image or Numpy array
  55. Returns:
  56. U matrix of SVD compression
  57. Example:
  58. >>> from PIL import Image
  59. >>> from ipfml import processing
  60. >>> img = Image.open('./images/test_img.png')
  61. >>> U = processing.get_LAB_L_SVD_U(img)
  62. >>> U.shape
  63. (200, 200)
  64. """
  65. L = metrics.get_LAB_L(image)
  66. return metrics.get_SVD_U(L)
  67. def get_LAB_L_SVD_V(image):
  68. """Returns V SVD from L of LAB Image information
  69. Args:
  70. image: PIL Image or Numpy array
  71. Returns:
  72. V matrix of SVD compression
  73. Example:
  74. >>> from PIL import Image
  75. >>> from ipfml import processing
  76. >>> img = Image.open('./images/test_img.png')
  77. >>> V = processing.get_LAB_L_SVD_V(img)
  78. >>> V.shape
  79. (200, 200)
  80. """
  81. L = metrics.get_LAB_L(image)
  82. return metrics.get_SVD_V(L)
  83. def rgb_to_mscn(image):
  84. """Convert RGB Image into Mean Subtracted Contrast Normalized (MSCN)
  85. Args:
  86. image: 3D RGB image Numpy array or PIL RGB image
  87. Returns:
  88. 2D Numpy array with MSCN information
  89. Example:
  90. >>> from PIL import Image
  91. >>> from ipfml import processing
  92. >>> img = Image.open('./images/test_img.png')
  93. >>> img_mscn = processing.rgb_to_mscn(img)
  94. >>> img_mscn.shape
  95. (200, 200)
  96. """
  97. # check if PIL image or not
  98. img_arr = np.array(image)
  99. # convert rgb image to gray
  100. im = np.array(color.rgb2gray(img_arr) * 255, 'uint8')
  101. return metrics.gray_to_mscn(im)
  102. def rgb_to_grey_low_bits(image, nb_bits=4):
  103. """Convert RGB Image into grey image using only 4 low bits values
  104. Args:
  105. image: 3D RGB image Numpy array or PIL RGB image
  106. nb_bits: optional parameter which indicates the number of bits to keep (default 4)
  107. Returns:
  108. 2D Numpy array with low bits information kept
  109. Example:
  110. >>> from PIL import Image
  111. >>> from ipfml import processing
  112. >>> img = Image.open('./images/test_img.png')
  113. >>> low_bits_grey_img = processing.rgb_to_grey_low_bits(img, 5)
  114. >>> low_bits_grey_img.shape
  115. (200, 200)
  116. """
  117. img_arr = np.array(image)
  118. grey_block = np.array(color.rgb2gray(img_arr) * 255, 'uint8')
  119. return metrics.get_low_bits_img(grey_block, nb_bits)
  120. def rgb_to_LAB_L_low_bits(image, nb_bits=4):
  121. """Convert RGB Image into Lab L channel image using only 4 low bits values
  122. Args:
  123. image: 3D RGB image Numpy array or PIL RGB image
  124. nb_bits: optional parameter which indicates the number of bits to keep (default 4)
  125. Returns:
  126. 2D Numpy array with low bits information kept
  127. Example:
  128. >>> from PIL import Image
  129. >>> from ipfml import processing
  130. >>> img = Image.open('./images/test_img.png')
  131. >>> low_bits_Lab_l_img = processing.rgb_to_LAB_L_low_bits(img, 5)
  132. >>> low_bits_Lab_l_img.shape
  133. (200, 200)
  134. """
  135. L_block = np.asarray(metrics.get_LAB_L(image), 'uint8')
  136. return metrics.get_low_bits_img(L_block, nb_bits)
  137. def rgb_to_LAB_L_bits(image, interval):
  138. """Returns only bits from LAB L canal specified into the interval
  139. Args:
  140. image: image to convert using this interval of bits value to keep
  141. interval: (begin, end) of bits values
  142. Returns:
  143. 2D Numpy array with reduced values
  144. >>> from PIL import Image
  145. >>> from ipfml import processing
  146. >>> img = Image.open('./images/test_img.png')
  147. >>> bits_Lab_l_img = processing.rgb_to_LAB_L_bits(img, (2, 6))
  148. >>> bits_Lab_l_img.shape
  149. (200, 200)
  150. """
  151. L_block = np.asarray(metrics.get_LAB_L(image), 'uint8')
  152. return metrics.get_bits_img(L_block, interval)
  153. def divide_in_blocks(image, block_size, pil=True):
  154. '''Divide image into equal size blocks
  155. Args:
  156. image: PIL Image or Numpy array
  157. block: tuple (width, height) representing the size of each dimension of the block
  158. pil: block type returned as PIL Image (default True)
  159. Returns:
  160. list containing all 2D Numpy blocks (in RGB or not)
  161. Raises:
  162. ValueError: If `image_width` or `image_height` are not compatible to produce correct block sizes
  163. Example:
  164. >>> import numpy as np
  165. >>> from PIL import Image
  166. >>> from ipfml import processing
  167. >>> from ipfml import metrics
  168. >>> image_values = np.random.randint(255, size=(800, 800, 3))
  169. >>> blocks = divide_in_blocks(image_values, (20, 20))
  170. >>> len(blocks)
  171. 1600
  172. >>> blocks[0].width
  173. 20
  174. >>> blocks[0].height
  175. 20
  176. >>> img_l = Image.open('./images/test_img.png')
  177. >>> L = metrics.get_LAB_L(img_l)
  178. >>> blocks_L = divide_in_blocks(L, (100, 100))
  179. >>> len(blocks_L)
  180. 4
  181. >>> blocks_L[0].width
  182. 100
  183. '''
  184. blocks = []
  185. mode = 'RGB'
  186. # convert in Numpy array
  187. image_array = np.array(image)
  188. # check dimension of input image
  189. if image_array.ndim != 3:
  190. mode = 'L'
  191. image_width, image_height = image_array.shape
  192. else:
  193. image_width, image_height, _ = image_array.shape
  194. # check size compatibility
  195. width, height = block_size
  196. if (image_width % width != 0):
  197. raise ValueError("Width size issue, block size not compatible")
  198. if (image_height % height != 0):
  199. raise ValueError("Height size issue, block size not compatible")
  200. nb_block_width = image_width / width
  201. nb_block_height = image_height / height
  202. for i in range(int(nb_block_width)):
  203. begin_x = i * width
  204. for j in range(int(nb_block_height)):
  205. begin_y = j * height
  206. # getting sub block information
  207. current_block = image_array[begin_x:(begin_x + width), begin_y:(
  208. begin_y + height)]
  209. if pil:
  210. blocks.append(
  211. Image.fromarray(current_block.astype('uint8'), mode))
  212. else:
  213. blocks.append(current_block)
  214. return blocks
  215. def fusion_images(images, pil=True):
  216. '''Fusion array of images into single image
  217. Args:
  218. images: array of images (PIL Image or Numpy array)
  219. pil: block type returned as PIL Image (default True)
  220. Returns:
  221. merged image from array of images
  222. Raises:
  223. ValueError: if `images` is not an array or is empty
  224. NumpyShapeComparisonException: if `images` array contains images with different shapes
  225. Example:
  226. >>> import numpy as np
  227. >>> from ipfml import processing
  228. >>> image_values_1 = np.random.randint(255, size=(800, 800, 3))
  229. >>> image_values_2 = np.random.randint(255, size=(800, 800, 3))
  230. >>> merged_image = processing.fusion_images([image_values_1, image_values_2], pil=False)
  231. >>> merged_image.shape
  232. (800, 800, 3)
  233. '''
  234. mode = 'RGB'
  235. dim = 1
  236. if len(images) == 0:
  237. raise ValueError('Empty array of images provided...')
  238. # convert image in numpy array (perhaps not necessary)
  239. images = [np.asarray(img) for img in images]
  240. image_array = images[0]
  241. if image_array.ndim != 3:
  242. mode = 'L'
  243. width, height = image_array.shape
  244. else:
  245. width, height, dim = image_array.shape
  246. # raise exception if all images do not have same shape
  247. if not np.array([image_array.shape == a.shape for a in images]).all():
  248. raise NumpyShapeComparisonException()
  249. if dim == 1:
  250. image_mean = np.empty([width, height])
  251. else:
  252. image_mean = np.empty([width, height, dim])
  253. nb_images = len(images)
  254. # construction of mean image from rotation
  255. for i in range(width):
  256. for j in range(height):
  257. if dim == 1:
  258. grey_value = 0
  259. # for each image we merge pixel values
  260. for img in images:
  261. grey_value += img[i][j]
  262. image_mean[i][j] = grey_value / nb_images
  263. else:
  264. for k in range(dim):
  265. canal_value = 0
  266. # for each image we merge pixel values
  267. for img in images:
  268. canal_value += img[i][j][k]
  269. image_mean[i][j][k] = canal_value / nb_images
  270. image_mean = np.array(image_mean, 'uint8')
  271. if pil:
  272. return Image.fromarray(image_mean, mode)
  273. else:
  274. return image_mean
  275. def rotate_image(image, angle=90, pil=True):
  276. """Rotate image using specific angle
  277. Args:
  278. image: PIL Image or Numpy array
  279. angle: Angle value of the rotation
  280. pil: block type returned as PIL Image (default True)
  281. Returns:
  282. Image with rotation applied
  283. Example:
  284. >>> from PIL import Image
  285. >>> import numpy as np
  286. >>> from ipfml import processing
  287. >>> image_values = Image.open('./images/test_img.png')
  288. >>> rotated_image = processing.rotate_image(image_values, 90, pil=False)
  289. >>> rotated_image.shape
  290. (200, 200, 3)
  291. """
  292. mode = 'RGB'
  293. image_array = np.asarray(image)
  294. if image_array.ndim != 3:
  295. mode = 'L'
  296. rotated_image = np.array(
  297. transform.rotate(image_array, angle) * 255, 'uint8')
  298. if pil:
  299. return Image.fromarray(rotated_image, mode)
  300. else:
  301. return rotated_image
  302. def get_mscn_coefficients(image):
  303. """Compute the Mean Substracted Constrast Normalized coefficients of an image
  304. Args:
  305. image: PIL Image, Numpy array or path of image
  306. Returns:
  307. MSCN coefficients
  308. Raises:
  309. FileNotFoundError: If `image` is set as str path and image was not found
  310. ValueError: If `image` numpy shape are not correct
  311. Example:
  312. >>> from PIL import Image
  313. >>> import numpy as np
  314. >>> from ipfml import processing
  315. >>> image_values = Image.open('./images/test_img.png')
  316. >>> mscn_coefficients = processing.get_mscn_coefficients(image_values)
  317. >>> mscn_coefficients.shape
  318. (200, 200)
  319. """
  320. if isinstance(image, str):
  321. if os.path.exists(image):
  322. # open image directly as grey level image
  323. imdist = cv2.imread(image, 0)
  324. else:
  325. raise FileNotFoundError('Image not found in your system')
  326. elif isinstance(image, np.ndarray):
  327. # convert if necessary to grey level numpy array
  328. if image.ndim == 2:
  329. imdist = image
  330. if image.ndim == 3:
  331. imdist = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
  332. else:
  333. raise ValueError('Incorrect image shape')
  334. else:
  335. # if PIL Image
  336. image = np.asarray(image)
  337. if image.ndim == 2:
  338. imdist = image
  339. if image.ndim == 3:
  340. imdist = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
  341. else:
  342. raise ValueError('Incorrect image shape')
  343. imdist = imdist.astype(np.float64)
  344. imdist = imdist / 255.0
  345. # calculating MSCN coefficients
  346. mu = cv2.GaussianBlur(
  347. imdist, (7, 7), 7 / 6, borderType=cv2.BORDER_CONSTANT)
  348. mu_sq = mu * mu
  349. sigma = cv2.GaussianBlur(
  350. imdist * imdist, (7, 7), 7 / 6, borderType=cv2.BORDER_CONSTANT)
  351. sigma = np.sqrt(abs((sigma - mu_sq)))
  352. structdis = (imdist - mu) / (sigma + 1)
  353. return structdis
  354. def svd_reconstruction(image, interval):
  355. """Reconstruct an image from SVD compression using specific interval of Singular Values
  356. Args:
  357. image: PIL Image, Numpy array or path of 3D image
  358. interval: Interval used for reconstruction
  359. Returns:
  360. Reconstructed image
  361. Example:
  362. >>> from PIL import Image
  363. >>> import numpy as np
  364. >>> from ipfml import processing
  365. >>> image_values = Image.open('./images/test_img.png')
  366. >>> reconstructed_image = processing.svd_reconstruction(image_values, (100, 200))
  367. >>> reconstructed_image.shape
  368. (200, 200)
  369. """
  370. begin, end = interval
  371. lab_img = metrics.get_LAB_L(image)
  372. lab_img = np.array(lab_img, 'uint8')
  373. U, s, V = svd(lab_img, full_matrices=True)
  374. # reconstruction using specific interval
  375. smat = np.zeros((end - begin, end - begin), dtype=complex)
  376. smat[:, :] = np.diag(s[begin:end])
  377. output_img = np.dot(U[:, begin:end], np.dot(smat, V[begin:end, :]))
  378. return output_img
  379. def fast_ica_reconstruction(image, components):
  380. """Reconstruct an image from SVD compression using specific number of components to use
  381. Args:
  382. image: PIL Image, Numpy array or path of 3D image
  383. components: Number of components used for reconstruction
  384. Returns:
  385. Reconstructed image
  386. Example:
  387. >>> from PIL import Image
  388. >>> import numpy as np
  389. >>> from ipfml import processing
  390. >>> image_values = Image.open('./images/test_img.png')
  391. >>> reconstructed_image = processing.fast_ica_reconstruction(image_values, 25)
  392. >>> reconstructed_image.shape
  393. (200, 200)
  394. """
  395. lab_img = metrics.get_LAB_L(image)
  396. lab_img = np.array(lab_img, 'uint8')
  397. ica = FastICA(n_components=50)
  398. # run ICA on image
  399. ica.fit(lab_img)
  400. # reconstruct image with independent components
  401. image_ica = ica.fit_transform(lab_img)
  402. restored_image = ica.inverse_transform(image_ica)
  403. return restored_image
  404. def ipca_reconstruction(image, components, _batch_size=25):
  405. """Reconstruct an image from SVD compression using specific number of components to use and batch size
  406. Args:
  407. image: PIL Image, Numpy array or path of 3D image
  408. components: Number of components used for reconstruction
  409. batch_size: Batch size used for learn (default 25)
  410. Returns:
  411. Reconstructed image
  412. Example:
  413. >>> from PIL import Image
  414. >>> import numpy as np
  415. >>> from ipfml import processing
  416. >>> image_values = Image.open('./images/test_img.png')
  417. >>> reconstructed_image = processing.ipca_reconstruction(image_values, 20)
  418. >>> reconstructed_image.shape
  419. (200, 200)
  420. """
  421. lab_img = metrics.get_LAB_L(image)
  422. lab_img = np.array(lab_img, 'uint8')
  423. transformer = IncrementalPCA(
  424. n_components=components, batch_size=_batch_size)
  425. transformed_image = transformer.fit_transform(lab_img)
  426. restored_image = transformer.inverse_transform(transformed_image)
  427. return restored_image