IDL.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Sat Sep 15 13:42:30 2018
  4. @author: Giovanni Galizia
  5. collection of IDL commands
  6. and other useful snippets for the IDL_view->python translation
  7. """
  8. import logging
  9. from tkinter import filedialog
  10. import tkinter as tk
  11. import numpy as np
  12. import scipy.ndimage as sci
  13. #import skimage as ski
  14. import PIL as PIL
  15. from PIL import ImageDraw, ImageFont, ImageOps, Image
  16. import os
  17. import matplotlib.pyplot as plt
  18. from matplotlib import cm
  19. from matplotlib.colors import ListedColormap, LinearSegmentedColormap
  20. from matplotlib.colors import hsv_to_rgb
  21. #outFile = dialog_pickfile(/WRITE, path=flag[stg_odorReportPath],file='TIF_'+p1.experiment)
  22. def dialog_pickfile(path, default = '', write=True, defaultextension = 'tif'):
  23. #IDL: Result = DIALOG_PICKFILE( [, DEFAULT_EXTENSION=string] [, /DIRECTORY] [, DIALOG_PARENT=widget_id] [, DISPLAY_NAME=string] [, FILE=string] [, FILTER=string/string array] [, /FIX_FILTER] [, GET_PATH=variable] [, GROUP=widget_id] [, /MULTIPLE_FILES] [, /MUST_EXIST] [, /OVERWRITE_PROMPT] [, PATH=string] [, /READ | , /WRITE] [, RESOURCE_NAME=string] [, TITLE=string] )
  24. # path is given, file is output
  25. root = tk.Tk()
  26. ## root.outCanvasFile = filedialog.asksaveasfilename(mode='w', initialdir = flag.stg_odorReportPath, defaultextension=".tif")
  27. ## root.outCanvasFile = filedialog.asksaveasfilename(initialdir = 'C:/Users/Giovanni Galizia/')
  28. # root.outCanvasFile = filedialog.asksaveasfilename(initialdir = path)
  29. # if outCanvasFile is None: # asksaveasfile return `None` if dialog closed with "cancel".
  30. # outCanvasFile = default
  31. # path = 'C:/Users/Giovanni Galizia/'
  32. root.withdraw() # we don't want a full GUI, so keep the root window from appearing
  33. #root.focus_force()
  34. # root.lift()
  35. root.attributes("-topmost", True)
  36. # svg_filename = tkFileDialog.asksaveasfilename(title='SVG export filename',defaultextension = 'svg',initialdir = IDT_group_dir);
  37. if write:
  38. filename = filedialog.asksaveasfilename(parent = root, title='TIF export filename',defaultextension = 'tif',initialdir = path)
  39. else:
  40. filename = filedialog.askopenfilename(parent = root, title='Open existing file',defaultextension = defaultextension, initialdir = path)
  41. return filename
  42. # outside: open file like this:
  43. # file = open(name,'w')
  44. def bytarr(x,y):
  45. return np.zeros([x,y], dtype=np.uint8)
  46. def fltarr(x,y):
  47. return np.zeros([x,y], dtype=np.float64)
  48. def smooth(arrayND, filterSize):
  49. '''
  50. arrayND or any dimenstion, filterSize applied to all dimenstion
  51. in IDL it was a boxcar filter, here I use gaussian
  52. in IDL filterSize is a single value, here it can be a tuple
  53. '''
  54. return sci.gaussian_filter(arrayND, filterSize, mode='nearest')
  55. def xyouts(x,y, text, img, orientation=90, fill=255, align = 'left'):
  56. '''
  57. tries to replicate the IDL xyouts command, approximately
  58. align can be 'left' or 'right' or 'center', implemented by shifting the coordinates (corresponds to 0, 1, 0.5 in IDL)
  59. x, y are lower left corner of text box for horizontal text
  60. '''
  61. #; xyouts, NextPosition(0)+p1.format_x+border-2, NewSizeCanvas(1)-NextPosition(1)-p1.metadata.format_y, strTrim(string(fix(minimum*annotateFactor)),2), /device, ALIGNMENT=0, ORIENTATION=90
  62. #### analysis#; xyouts,
  63. # x coordinate: NextPosition(0)+p1.format_x+border-2,
  64. # y coordinate: NewSizeCanvas(1)-NextPosition(1)-p1.format_y,
  65. # text2write: strTrim(string(fix(minimum*annotateFactor)),2),
  66. # where to write: /device,
  67. # ALIGNMENT=0, #0 means left alignment
  68. # ORIENTATION=90 #90 means vertical going up
  69. #def add_vertical_text(x,y,text,img, fill):
  70. #adds text into img, vertically upwards
  71. width, height = img.size
  72. if x > width: print('IDL.xyouts - text appears to be outside x range')
  73. if y > height: print('IDL.xyouts - text appears to be outside y range')
  74. #img = img.rotate(-orientation) #rotate the original image
  75. #this does not work, because it rotates WITHIN the window
  76. # the coordinates are different due to the rotation
  77. if orientation == 90:
  78. img = img.transpose(Image.ROTATE_90)
  79. rot_x = y
  80. rot_y = width - x
  81. elif orientation == 0:
  82. rot_y = y
  83. rot_x = x
  84. else:
  85. print('IDL.xyouts: this value of rotation not implemented yet. If not 0,90,180,270, think hard')
  86. # x' = x*cos + y*sin
  87. # y' = -x*sin + y*cos
  88. # -- or as vectors --
  89. # x' cos sin x
  90. #( ) = ( ) * ( )
  91. # y' -sin cos y
  92. # but this does not work, because I do not know if to subtract negative values from x or from y
  93. # orientation = 180
  94. # r = np.deg2rad(orientation)
  95. # rot_x = x*np.cos(r) + y*np.sin(r)
  96. # rot_y = -x*np.sin(r) + y*np.cos(r)
  97. # print(rot_x, rot_y)
  98. # now write the text into this place
  99. draw = PIL.ImageDraw.Draw(img)
  100. # corect x axis if right alignement
  101. text_box_size = draw.textsize(text)
  102. if align.lower() == 'right':
  103. rot_x = rot_x - text_box_size[0]
  104. if align.lower() == 'center':
  105. text_box_size = draw.textsize(text)
  106. rot_x = rot_x - text_box_size[0]/2
  107. #coordinates are different from IDL, it seams - so shift the y by the text height
  108. rot_y = rot_y - text_box_size[1]
  109. #draw the text
  110. draw.text((rot_x, rot_y),text,fill=fill)#,font=font)
  111. #rotate back
  112. if orientation == 90:
  113. img = img.transpose(Image.ROTATE_270)
  114. return img #end xyouts
  115. def gio_get_filenames(extension, title):
  116. import tkinter as tk
  117. from tkinter.filedialog import askopenfilenames
  118. root = tk.Tk()
  119. root.withdraw() # so that windows closes after file chosen
  120. root.attributes('-topmost', True)
  121. filenames = askopenfilenames(
  122. parent=root,
  123. title=title,
  124. filetypes=[('settings files', extension), ('all files', '*')]
  125. ) # ask user to choose file
  126. return filenames
  127. def restore_maskframe(flag):
  128. areafilename = os.path.join(flag.STG_OdormaskPath,flag.STG_ReportTag) + '.Area'
  129. #os.path.isfile(areaFileName)
  130. if not(os.path.isfile(areafilename)):
  131. print('CalcSigAll3000.pro: AreaFileName does not exist :', areafilename)
  132. ## pick the right file name, to do.
  133. areafilename = gio_get_filenames('.Area', "Choose perimeter file .Area")[0] #only the first file name, if more were chosen
  134. # areaFileName = Dialog_Pickfile(Path=flag[stg_OdorMaskPath], get_Path = inPath, Filter='*.Area', title='Choose perimeter file!')
  135. # flag[stg_OdorMaskPath] = inpath
  136. from scipy.io.idl import readsav #command to read IDL files
  137. #temp = readsav(areaFileName, verbose=True) #reads IDL structure into temp. The Area file is in maskframe
  138. maskframe = readsav(areafilename).maskframe #only works because it was saved with the name maskFrame
  139. print('IDL.py: restored area file ',areafilename)
  140. return maskframe
  141. #bytscl(overviewframe, MIN=setminimum, MAX=setmaximum, TOP=!d.table_size)
  142. def bytscl(inf, MIN=0, MAX=255, TOP=255):
  143. inframe = inf.copy().astype('float')
  144. inframe = np.clip(inframe, MIN, MAX)
  145. inframe -= MIN
  146. inframe *= TOP/(MAX-MIN) #image *= (255.0/image.max())
  147. #inframe *= TOP/MAX #image *= (255.0/image.max())
  148. #astype does not round, but floors
  149. inframe = inframe + 0.5
  150. return inframe.astype('uint8')
  151. ## frame2 = rebin(frame2, (p1.format_x) * zoomfactor, p1.format_y * zoomfactor, sample = 1)
  152. def rebin(frame, newxsize, newysize, sample=1):
  153. from skimage.transform import resize
  154. #interp = 'bilinear'
  155. #if sample == 1: interp = 'nearest'
  156. #plt.imshow(resize(frame2, frame2.shape, mode='constant')).astype('uint8')
  157. print(newxsize, newysize)
  158. outframe = resize(frame, (newxsize,newysize), mode='constant')#, interp=interp)
  159. return outframe.astype('uint8')
  160. #write_tiff, outCanvasFile, TIFFCanvas, red=r, blue=b, green=g, xresol=A4resolution, yresol=A4resolution
  161. def write_tiff(outfile, MyArray, red, green, blue, xresol=100, yresol=100):
  162. """
  163. simulate the IDL write_tiff command, with only those options that I used in my view program
  164. writes an 8bit TIFF file, with the palette defined by red,blue,green
  165. input is array, a uint8 array
  166. """
  167. #21.8.2019 write tiff is only used for Canvas, so far, and the image is rotated.
  168. #fix: rotate the image here, and rotate it back in read_tiff
  169. MyArray = np.rot90(MyArray)
  170. #convert array into image
  171. mode = 'P' #(8-bit pixels, mapped to any other mode using a color palette
  172. img = PIL.Image.new(mode, MyArray.shape)
  173. img = PIL.Image.fromarray(MyArray) #creates an image into object window10
  174. # add palette
  175. #make sure colors are 8bit
  176. palette = palette_IDL2PIL(red,green,blue)
  177. img.putpalette(palette)
  178. #save to file
  179. img.save(outfile, dpi=(xresol, yresol))
  180. print('IDL.write_tiff: written 8bit tiff file to: ', outfile)
  181. return #nothing to give back
  182. #read_tiff(outCanvasFile, R, G, B)
  183. def read_tiff(filename):
  184. '''
  185. reads a tiff file, returns the array, and the red, green, blue palette
  186. emulates the IDL read_tiff with the options I used in view
  187. '''
  188. img = PIL.Image.open(filename)
  189. img_array = np.array(img)
  190. # rotate back, i.e. 3 times by 90 deg
  191. img_array = np.rot90(img_array,3)
  192. #here is the palette
  193. palette = img.getpalette()
  194. IDLpalette = palette_PIL2IDL(palette)
  195. # palette is a single list with R, G, B, R, G....
  196. return (img_array, IDLpalette)
  197. def palette_IDL2PIL(red,green,blue):
  198. red = red.astype('uint8')
  199. blue = blue.astype('uint8')
  200. green = green.astype('uint8')
  201. rgb = [red, green, blue]
  202. palette = [val for tup in zip(*rgb) for val in tup]
  203. return palette
  204. def palette_PIL2IDL(palette):
  205. red = palette[0::3]
  206. green = palette[1::3]
  207. blue = palette[2::3]
  208. return (red, green, blue)
  209. def palette_pyplot2PIL(pyplot_cm):
  210. ctP = pyplot_cm(np.linspace(0,1,256))
  211. #R is in ctP[:,0]
  212. R = ctP[:,0]*255
  213. G = ctP[:,1]*255
  214. B = ctP[:,2]*255
  215. return (R, G, B)
  216. def createPalette(SO_MV_colortable):
  217. """
  218. creates an RGBA palette (0-255) as tuple (r,g,b,a)
  219. extension of interpretation of SO_MV_colortable, allowing flags to have multiple expected types, FT_*Frame ->mv_*Frame
  220. based on the numbers that I used in IDL. Mostly based on DefineExplicitCt in the tools folder
  221. Most numbers not implemented yet - just translate as necessary
  222. """
  223. ##from IDL
  224. #;set 11: equal saturation, different hue
  225. #;define it in hsv system
  226. #s = replicate(1.0, 255)
  227. #v = replicate(1.0, 255)
  228. #h = 255-findgen(255) ;starting with blue, go to red (the entire circle is 360 deg, here we use 255 deg)
  229. #;load these values, get the corresponding r,g,b values
  230. #tvlct, h, s, v, /hsv
  231. #tvlct, r, g, b, /get
  232. #;define color table 11
  233. #modifyct, 11, 'HSVconst', r, g, b
  234. #from IDL, define 11
  235. hsv = np.zeros((256, 3))
  236. hsv[:, 0] = np.linspace(255/360, 0, 256) #in IDL, circle 360 - to to 255
  237. hsv[:, 1] = 1.
  238. hsv[:, 2] = 1. # np.linspace(0, 1, 512)[:, np.newaxis]
  239. rgba = np.ones((256, 4))
  240. rgba[:,0:3] = hsv_to_rgb(hsv) # transparency a fixed to 1
  241. #define color map 11
  242. # IDLcm11 = ListedColormap(rgba, name='HSVconst')
  243. rgba_11 = rgba.copy()
  244. #;into 12, set bottom white, top black
  245. #r1 = r & g1 = g & b1 = b
  246. #r1(0)=255 & g1(0)=255 & b1(0)=255
  247. #r1(255)=0 & g1(255)=0 & b1(255)=0
  248. #modifyct, 12, 'HSVconstWB', r1, g1, b1
  249. rgba[0,:] = [1,1,1,1]
  250. rgba[-1,:] = [0,0,0,1]
  251. # IDLcm12 = ListedColormap(rgba, name='HSVconstWB')
  252. rgba_12 = rgba.copy()
  253. #;into 13, set bottom black, top white
  254. rgba[-1,:] = [1,1,1,1]
  255. rgba[0,:] = [0,0,0,1]
  256. # IDLcm13 = ListedColormap(rgba, name='HSVconstBW')
  257. rgba_13 = rgba.copy()
  258. #;into 14, set center range centersize to gray
  259. #;left part via cyan to blue
  260. #;right part via yellow to red
  261. #;0 to black; 255 to white
  262. rgba[0,:] = [1,1,1,1] #r1(255)=255 & g1(255)=255 & b1(255)=255
  263. rgba[-1,:] = [0,0,0,1] #r1(*)=0 & g1(*)=0 & b1(*)=0
  264. centersize = 10
  265. grayValue = 180/255
  266. p1 = 64
  267. p2l = 128 - centersize
  268. p2r = 128 + centersize
  269. p3 = 192
  270. #;part from blue to cyan, range 0 to p1
  271. rgba[1:p1, 0] = 0 #r1(0:p1-1) = 0
  272. rgba[1:p1, 1] = np.linspace(0,1,p1-1) #g1(0:p1-1) = createLinearArray(0,255,p1)
  273. rgba[1:p1, 2] = 1 #b1(0:p1-1) = 255
  274. #;part from cyan to gray, range p1 to p2l
  275. rgba[p1:p2l,0] = np.linspace(0,grayValue,p2l-p1) #r1(p1:p2l-1) = createLinearArray(0 ,grayvalue, p2l-p1)
  276. rgba[p1:p2l,1] = np.linspace(1,grayValue,p2l-p1)#g1(p1:p2l-1) = createLinearArray(255,grayvalue, p2l-p1)
  277. rgba[p1:p2l,3] = np.linspace(1,grayValue,p2l-p1)#b1(p1:p2l-1) = createLinearArray(255,grayvalue, p2l-p1)
  278. #;part constant gray, range p2l to p2r
  279. rgba[p2l:p2r,0:3] = grayValue #r1(p2l:p2r-1) = grayvalue
  280. #g1(p2l:p2r-1) = grayvalue
  281. #b1(p2l:p2r-1) = grayvalue
  282. #;part from gray to yellow, range p2r to p3
  283. rgba[p2r:p3,0] = np.linspace(grayValue,1,p3-p2r) #r1(p2r:p3-1) = createLinearArray(grayvalue,255, p3-p2r)
  284. rgba[p2r:p3,1] = np.linspace(grayValue,1,p3-p2r)#g1(p2r:p3-1) = createLinearArray(grayvalue,255, p3-p2r)
  285. rgba[p2r:p3,2] = np.linspace(grayValue,0 ,p3-p2r)#b1(p2r:p3-1) = createLinearArray(grayvalue, 0, p3-p2r)
  286. #;part from yellow to red, range p3 to 2554
  287. rgba[p3:256,0] = 1 #r1(p3:254) = 255
  288. rgba[p3:256,1] = np.linspace(1,0,256-p3) #g1(p3:254) = createLinearArray( 255, 0, 255-p3)
  289. rgba[p3:256,2] = 0 #b1(p3:254) = 0
  290. #;rest bottom to black,top to white
  291. #r1(0)=0 & g1(0)=0 & b1(0)=0
  292. #r1(255)=255 & g1(255)=255 & b1(255)=255
  293. #modifyct, 14, 'GrayCenter', r1, g1, b1; for britons: GreyCentre
  294. #IDLcm14 = ListedColormap(rgba, name='GrayCenter')
  295. rgba_14 = rgba.copy()
  296. #
  297. #;into 15, use 14 above, set center range centersize to gray
  298. #;left part via cyan to blue
  299. #;right part via yellow to red
  300. #;0 to white; 255 to black
  301. #;rest bottom to white,top to black
  302. #r1(255)=0 & g1(255)=0 & b1(255)=0
  303. #r1(0)=255 & g1(0)=255 & b1(0)=255
  304. #modifyct, 15, 'GrayCenterBW', r1, g1, b1; for britons: GreyCentre
  305. #
  306. #
  307. #;make cyan-blue-*black*-red-yellow-white
  308. #
  309. #;position 0 is black
  310. #r(0) = 0 & g(0) = 0 & b(0) = 0
  311. #;positions 1 to 63 from cyan to (almost) blue
  312. #r(1:63)=0 & g(1:63) = byte(255*(1 - findgen(63)/63)) & b(1:63) = 255
  313. #;positions 64 to 127 from blue to (almost) black
  314. #r(64:127)=0 & g(64:127) = 0 & b(64:127) = byte(255*(1 - findgen(64)/64))
  315. #;positions 128 to 170 from black to (almost) red
  316. #r(128:170) = byte(255*(findgen(43)/43)) & g(128:170) = 0 & b(128:170) = 0
  317. #;position 171 to 213 from red to (almost) yellow
  318. #r(171:213) = 255 & g(171:213) = byte(255*(findgen(43)/43)) & b(171:213) = 0
  319. #;position 214 to 255 from yellow to white (divide by one less to get maximum in the array)
  320. #r(214:255) = 255 & g(214:255) = 255 & b(214:255) = byte(255*(findgen(42)/41))
  321. #modifyct, 36, 'cb_black_ryw', r, g, b
  322. #
  323. #
  324. #;make cyan-blue-*black*-red-yellow
  325. #
  326. #;position 0 is black
  327. #r(0) = 0 & g(0) = 0 & b(0) = 0
  328. #;positions 1 to 63 from cyan to (almost) blue
  329. #r(1:63)=0 & g(1:63) = byte(255*(1 - findgen(63)/63)) & b(1:63) = 255
  330. #;positions 64 to 127 from blue to (almost) black
  331. #r(64:127)=0 & g(64:127) = 0 & b(64:127) = byte(255*(1 - findgen(64)/64))
  332. #;positions 128 to 191 from black to (almost) red
  333. #r(128:191) = byte(255*(findgen(64)/64)) & g(128:191) = 0 & b(128:191) = 0
  334. #;position 192 to 255 from red to yellow
  335. #r(192:254) = 255 & g(192:254) = byte(255*(findgen(63)/62)) & b(192:254) = 0
  336. #;position 255 is white
  337. #r(255) = 255 & g(255) = 255 & b(255) = 255
  338. #
  339. #modifyct, 37, 'cb_black_ry', r, g, b
  340. #
  341. #
  342. #
  343. #
  344. #;make black-red-yellow-white table
  345. #;positions 0 to 84 from black to almost red
  346. #r(0:84) = byte(255*(findgen(85)/85)) & g(0:84) = 0 & b(0:84) = 0
  347. #;;position 85 to 170 from red to (almost) yellow
  348. #r(85:170) = 255 & g(85:170) = byte(255*(findgen(86)/86)) & b(85:170) = 0
  349. #;;position 170 to 255 from yellow to white (divide by one less to get maximum in the array)
  350. #r(170:255) = 255 & g(170:255) = 255 & b(170:255) = byte(255*(findgen(86)/85))
  351. #modifyct, 38, 'b_r_y_w', r, g, b
  352. #
  353. #
  354. #;make a greyscale, but with b/w inverted for toner-friendly printing
  355. #g = createLinearArray(0,255,255)
  356. #r = g
  357. #b = g
  358. #modifyct, 40, 'colorfriendly bw', r, g, b
  359. #;changed to 40 instead of 39, Jan 2010, with Daniel.
  360. #
  361. #
  362. #end ;procedure DefineExplicitCT
  363. if SO_MV_colortable == 11:
  364. logging.getLogger("VIEW").info('IDL.createpalette: returning palette 11')
  365. IDLcm11 = ListedColormap(rgba_11, name='HSVconst')
  366. return IDLcm11
  367. elif SO_MV_colortable == 12:
  368. logging.getLogger("VIEW").info('IDL.createpalette: returning palette 12')
  369. IDLcm12 = ListedColormap(rgba_12, name='HSVconstWB')
  370. return IDLcm12
  371. elif SO_MV_colortable == 13:
  372. logging.getLogger("VIEW").info('IDL.createpalette: returning palette 13')
  373. IDLcm13 = ListedColormap(rgba_13, name='HSVconstBW')
  374. return IDLcm13
  375. elif SO_MV_colortable == 14:
  376. logging.getLogger("VIEW").info('IDL.createpalette: returning palette 14')
  377. IDLcm14 = ListedColormap(rgba_14, name='GrayCenter')
  378. return IDLcm14
  379. else:
  380. raise NotImplementedError(
  381. f'IDL.createpalette: a palette for SO_MV_colortable={SO_MV_colortable} has not been defined yet in python package')
  382. ##debugging section
  383. if __name__ == "__main__":
  384. print('')
  385. #enter
  386. # outfile = 'dummytiff.tiff'
  387. # write_tiff(outfile, myImage, red, green, blue, 100, 100)
  388. # (i,p) = read_tiff(outfile)