processing.py 12 KB

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