ViewCalcData.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. import numpy as np
  2. import scipy.ndimage as sci
  3. import logging
  4. from view.python_core.areas import read_area_file
  5. # solution from https://stackoverflow.com/a/49480246
  6. from .bleach_correction import fitlogdecay, get_bleach_weights
  7. if __package__ is None or __package__ == '': # if Master_Hannah2018.py is importing this file
  8. pass
  9. else: # if we are importing this file from a package, e.g., using view.idl_translation_core.ViewOverview
  10. pass
  11. #import scipy.optimize as sco
  12. def CalcSigMaster(flag, p1):
  13. #PRO CalcSigMaster, raw, dark, p, method, relative_changes
  14. method1 = flag["LE_CalcMethod"] #keep this renaming for the lines below
  15. # if ((method1 gt 1000) AND (method1 lt 2000)) THEN method1= 1000
  16. # if ((method1 ge 3000) AND (method1 lt 4000)) THEN method1= 3000
  17. # if ((method1 ge 4000) AND (method1 lt 5000)) THEN method1= 4000
  18. # if ((method1 ge 5000) AND (method1 lt 6000)) THEN method1= 5000
  19. # if ((method1 ge 10000) AND (method1 lt 19000)) THEN method1=10000
  20. #
  21. #; Master Procedure to calculate relative changes from rawdata for all odors
  22. #; several different Methods can be used
  23. # addMeasurement = fix(flag[view_MultiExp])
  24. ################################################
  25. # these numbers do not match the IDL numbers any more
  26. if method1 == 0:
  27. p1 = CalcSigAll0(p1, flag) #absolute values / 1000, effectively raw data
  28. # in IDL, this was method 3
  29. elif method1 == 1:
  30. p1 = CalcSigAll1(p1, flag) #tbw
  31. # elif method1 == 2:
  32. # p1 = CalcSigAll2(p1, flag) #tbw
  33. elif method1 == 3: #simple deltaF, no thrills
  34. p1 = CalcSigAll3(p1, flag) #
  35. elif method1 == 4: #simple ratio, no thrills
  36. p1 = CalcSigAll4(p1, flag) #pure ratio, no thrills
  37. # in IDL, this was 15
  38. #9: BEGIN
  39. # ;calculates deltaF, F0 are initial frames to be set in program
  40. # ;includes median bleach correction
  41. # ;and baseline shift to inital values to be set in program
  42. # CalcSigAll9, raw, dark, p, relative_changes
  43. # END
  44. #10: BEGIN
  45. # ;calculates RATIOS for fura, and deltaF into air control memory
  46. # ;includes median bleach correction
  47. # ;and baseline shift to initial values, to be set in program
  48. # CalcSigAll10, raw, dark, p, relative_changes
  49. # END
  50. #11: BEGIN
  51. # ;calculates RATIOS for fura, and deltaF into air control memory
  52. # ;no baseline shift
  53. # CalcSigAll11, raw, dark, p, relative_changes
  54. # END
  55. #12: BEGIN
  56. # ;calculates RATIOS for fura into odour 2, and deltaF 340 into air control memory, deltaF 380 into odour 1
  57. # ;baseline shift
  58. # CalcSigAll12, raw, dark, p, relative_changes
  59. # END
  60. #13: BEGIN
  61. # ;calculates RATIOS for fura into odour 2, and deltaF 340 into air control memory, deltaF 380 into odour 1
  62. # ;baseline shift
  63. # CalcSigAll13, raw, dark, p, relative_changes
  64. # END
  65. #14: BEGIN
  66. # ;calculates RATIOS for fura into odour 2, and deltaF 340 into air control memory, deltaF 380 into odour 1
  67. # ;baseline shift
  68. # ;constant background correction
  69. # ;unsharp mask correction
  70. # CalcSigAll14, raw, dark, p, relative_changes
  71. # END
  72. #15: BEGIN
  73. # ;calculates RATIOS for fura into buffer 1, no other treatment/filter/shift/etc whatsoever
  74. # CalcSigAll15, raw, relative_changes
  75. # END
  76. #19: BEGIN
  77. # ;same as 9 but only central region 0.2-0.8 in x and y
  78. # ;calculates deltaF, F0 are initial frames to be set in program
  79. # ;includes median bleach correction
  80. # ;and baseline shift to inital values to be set in program
  81. # CalcSigAll9, raw, dark, p, relative_changes
  82. # END
  83. #29: BEGIN
  84. # ;same as 9 but left and right half frames are median corrected separatedly
  85. # CalcSigAll29, raw, dark, p, relative_changes
  86. # END
  87. #30: BEGIN
  88. # ;calculates FURA ratio and CaGr-deltaF signals from dual sets (Silke 2001)
  89. # CalcSigAll30, raw, dark, p, relative_changes
  90. # END
  91. #39: BEGIN
  92. # ;same as 9 but only AL perimeter is considered
  93. # CalcSigAll39, raw, dark, p, relative_changes
  94. # END
  95. #1000: BEGIN
  96. # ; method for substracting a smoothed background (smooth function in IDL)- test version
  97. # CalcSigAll1000, raw, dark, p, relative_changes, method
  98. # END
  99. elif (method1 >= 3000 & method1 < 6000):
  100. p1,flag = CalcSigAll3000(p1, flag) #tbw
  101. # ; calculates delta F / F (3000) or FURA (4000) or raw data (5000)
  102. # ; family of methods: first position after the 3 gives the following options
  103. # ; setting: 0 1 2 3 4 5 6 7 8 9
  104. # ;alPerimeter + + + + - - - - - - select central area or alperimeter for bleach corr
  105. # ;excludeStim - + - + - + - + - - exclude stimulus time + 3 sec from bleach corr
  106. # ;addNoise - - + + - - + + - - add high frequency noise to bleach corr
  107. # ; decimals and units give a radius in micrometers for correction of spread light
  108. # CalcSigAll3000, method
  109. #8000: BEGIN
  110. # CalcSigAll8000, method ;
  111. # ;8000 is for multi-focal short ratio measurements, collapses to one depth taking the maximum
  112. # END
  113. #9000: BEGIN
  114. # CalcSigAll9000, method ;
  115. # ;9000 is for multi-focal short ratio measurements, collapses to one depth taking the mean
  116. # END
  117. #10000: BEGIN
  118. # CalcSigAll10000, method ;10000 has the common blocks
  119. # ;10000 is for multi-focal short ratio measurements, with 3D correction and many sub-options
  120. # END
  121. else: print('ViewCalcData.CalcDataMaster: flag.CalcMethod not implemented')
  122. return
  123. def CalcSigAll3000(p1, flag):
  124. #settings values
  125. FlagRatioSetting = int(flag["LE_CalcMethod"] / 1000) # first digit. int is like floor
  126. RatioSetting = (FlagRatioSetting == 4) # is this a FURA measurement?
  127. startbackground = flag["LE_StartBackground"] # background ab frame ..-
  128. prestim_endbackground = flag["LE_PrestimEndBackground"] # background till n frames before stimulus
  129. subtractBeginning = (startbackground >= 0)
  130. #run CalcSigAll3000 for the single wavelength in p1.raw1
  131. p1, flag = CalcSigAll3000_raw(p1, flag) #run the full program on raw1
  132. # p1.sig is already the perfect calculation if not a ratio
  133. if RatioSetting: #fura measurement - so far we worked on raw1, i.e. 340
  134. # run the same thing for p1.raw2(380), therefore do some copying
  135. p2 = p1.copy() #make a flat copy into p2
  136. # raw1 is 340, raw2 is 380
  137. p2.raw1 = p2.raw2 #copy second wavelength into the first slot
  138. p2, flag = CalcSigAll3000_raw(p2, flag) #run the full program on raw2 (called raw1 here)
  139. # now calculate the ratio, put into p1.
  140. # 340/380 is p1/p2
  141. #but sig1 is deltaF/F, i.e. not F/F0, therefore add 1
  142. p1.sig1 = (p1.sig1 +1)/(p2.sig1 + 1) # times multiplyFactor for percentages, not defined in python
  143. #p1.raw1 = p2.raw1 #CalcSigAll3000 modifies raw1 also
  144. p1.metadata.bleachpar2 = p2.metadata.bleachpar #keep track of the bleach parameters
  145. # ;since this was the ratio of the deltaF, we have to multiply with the inverse of the backframes to get the true ratio
  146. # ;we did the delta F first because otherwise the log-bleach-correction would not work
  147. backBackFrame = p1.metadata.backframe/p2.metadata.backframe
  148. for i in range(p1.metadata.frames): #remove the effect of calculating F/F0
  149. p1.sig1[:,:,i] = p1.sig1[:,:,i] * backBackFrame
  150. print('ViewCalcData/CalcSigAll3000: calculated ratio based on method 4xyy ',flag["LE_CalcMethod"])
  151. #;subtract beginning
  152. if subtractBeginning:
  153. (startbackground, endbackground) = p1.metadata.background_frames
  154. backbackframe = np.mean(p1.sig1[:,:,startbackground:endbackground], axis=2) #takes the average for selected time frames
  155. for i in range(p1.metadata.frames):
  156. p1.sig1[:,:,i] -= backbackframe
  157. return p1, flag # no need to return p1, since it is modified anyway
  158. def CalcSigAll3000_raw(p1, flag):
  159. #;method 4xyy, where x is the setting (see table below), and y is the radius for scatter correction
  160. #3000 for deltaF, 4000 for Fura
  161. #5000 for raw data
  162. #
  163. #;fixed settings: need to go into flags, all of them!
  164. #
  165. #lightThresholdFlag =0 ;not well tested yet, implemented for 3D-2photon.Nov 1007
  166. #;switch OFF until exported to an external flag
  167. #
  168. noiseseconds = 1 #; high frequency cutoff in seconds for lamp noise reduction, when set
  169. #rawDataCopy = 0 ; puts raw data into odour set 0, if OFF puts deltaF/F of SLAVE into odour set 0
  170. #;set rawDataCopy to 2 to put bleach corrected raw data into raw1
  171. #;with new '5000' family of loading, do not use any more
  172. #; calculate nr of last frame that will be used for background average
  173. #multiplyfactor = 100.0 ; set to 100 to get percentage values
  174. lightmeanvalue = 1000.0 #; raw data are first scaled to this mean light value
  175. cutedge = 0.2 #; border area not to be considered for bleach correction when not using area
  176. bleachmean = 0 #; use mean over time and frames as trace to do the bleach correction with; else median
  177. rawdatadelete = flag["VIEW_DeleteRawData"] != 0 # ; deletes raw data at the end, saves disk space
  178. logexcludeseconds = flag["LELog_ExcludeSeconds"] #]) ; how many seconds after stimulus onset to exclude, when set
  179. bleachstartframe = flag["LE_BleachStartFrame"] #] ); set to 2 to exclude 2 frames at the beginning from log-fit
  180. # frames before bleachstartframe are ignored for bleach correction
  181. loginitialfactor = flag["LELog_InitialFactor"]#] ) ; weight factor for curve stretch before stimulus onset
  182. # frames after bleachstartframe and before stimulus-logexccludeseconds are valued with this weight
  183. # for example: curve 5 Hz, 100 Frames long, odor at frame 21
  184. # LogExcludeSeconds=5, BleachStartFrame=3, LogInitialFactor=2
  185. # LogFitting will be made using frames 3:20 and 46:99, but 3:20 will count double
  186. # prestim_endbackground (see below) is also used for bleach correction
  187. #variable settings
  188. flagratiosetting = int(flag["LE_CalcMethod"] / 1000) # first digit. int is like floor
  189. myMethod = int(flag["LE_CalcMethod"] / 100) % 10 #second digit. contains settings for bleach correction
  190. alperimeter = myMethod in [0,1,2,3] #;for 0,1,2,3
  191. excludestimulus= myMethod in [1,3,5,7] # for 1,3,5,7 (,9)
  192. addnoise = myMethod in [2,3,6,7] # for 2,3,6,7
  193. nobleach = myMethod in [9]
  194. airbleach = myMethod in [8]
  195. #AirBleachAddNoise = 0 ; local setting - option not implemented yet
  196. #;IF AirBleach THEN alPerimeter = 1 ;use this line for bleach in AL perimeter only
  197. #; setting: 0 1 2 3 4 5 6 7 8 9
  198. #;alPerimeter + + + + - - - - C - ; with 8 bleaching is in coordinates only (variable: CoorPerimeter = AirBleach)
  199. #;excludeStim - + - + - + - + - -
  200. #;addNoise - - + + - - + + - -
  201. #;no bleach - - - - - - - - - +
  202. #;air bleach - - - - - - - - + - ;correct with bleach parameters taken from air trial
  203. #;ratiosetting of 3: deltaF/F
  204. #;ratiosetting of 4: make ratio of odor buffer 0 / buffer1
  205. # ratiosetting = (FlagRatioSetting == 4) #not used because the fura is controlled from outside
  206. # in here, I only work on the main data raw1.
  207. returnrawdata = (flagratiosetting == 5)
  208. #returnrawdata returns the bleach corrected data to raw1
  209. #external settings
  210. shrinkfactor = flag["LE_ShrinkFaktor"]
  211. #Memory_3Dim = fix(flag[VIEW_No4Darray]) ; use arrays with only one odor-buffer to save space
  212. startbackground = max(flag["LE_StartBackground"],0) # background ab frame ..- at least 0
  213. # subtractbeginning = (startbackground >= 0)
  214. prestim_endbackground = flag["LE_PrestimEndBackground"] # background till n frames before stimulus
  215. #firstOdor = fix(flag[LE_FirstBuffer])
  216. # radius = flag.RM_Radius # necessary for coordinate analysis
  217. # the next lines not translated to Python, because they relate to the way the data was put into the sig variable
  218. # if RatioSetting THEN firstOdor = 0 ; 0 contains master
  219. #if (firstOdor eq -1) then begin ;settings as befor the introduction of LE_usefirstbuffer
  220. # IF (total(raw1(*,*,*,0)) eq 0) then firstOdor = 1 ELSE firstodor = 0
  221. #endIF
  222. (startbackground, endbackground) = p1.metadata.background_frames
  223. #SETTING should I do unsharpmask?
  224. smoothvalue = int(flag["LE_CalcMethod"] % 100) # last two digits, radius of filter in um
  225. unsharpmask = smoothvalue > 0
  226. if airbleach:
  227. print('ViewCalcData.CalcSigAll3000: airBleach not implemented yet in Python')
  228. # IF RatioSetting Then Begin
  229. # airBleach = 0
  230. # print, 'CalcSigAll3000: AirBleach not implemented with Ratio dyes'
  231. # endIF
  232. # IF (firstOdor gt 0) Then Begin
  233. # ;airBleach = 0
  234. # firstodor = 0
  235. # print, 'CalcSigAll3000: using AirBleach with flag[LE_FirstBuffer] greater 0 (???)'
  236. # endIF
  237. # IF (total(raw1(*,*,*,0)) eq 0) then begin
  238. # airBleach = 0
  239. # firstodor = 1
  240. # print, 'CalcSigAll3000: AirBleach not useful without data in buffer 0 (air is missing)'
  241. # endIF
  242. # IF AirBleachAddNoise THEN begin
  243. # print, '************CalcSigAll3000 - local flag set to addNoise with AirBleachAddNoise'
  244. # addNoise = 1
  245. # endIF
  246. #loadct, SO_MV_colortable
  247. #
  248. #;define variables
  249. #sig1 = fltarr(p1.format_x, p1.format_y, p1.frames, p1.odors+1)
  250. #backframe = fltarr(p1.format_x, p1.format_y)
  251. # Python: will use p1.sig1 and p1.backframe
  252. ###### start calculation: create maskframe, either via alperimeter, or via cutedge
  253. if alperimeter:
  254. # ;get selected AL area
  255. # if flag.RM_differentViews > 0: #not implemented for now -
  256. # ;special section Tilman
  257. # ;areaFileName = flag[stg_OdorMaskPath]+strmid(Flag[stg_ReportTag],0,14)+p1.viewLabel+' '+strmid(Flag[stg_ReportTag],14)+'.area'
  258. # ;print, 'CalcSigAll3000: special Tilman section for area file!!'
  259. # ;standard section
  260. # areafilename = flag.STG_OdormaskPath+flag.STG_ReportTag+str(p1.viewLabel)+'.Area'
  261. # else:
  262. # maskframe = IDL.restore_maskframe(flag) #restores maskframe from file
  263. try:
  264. area_filename = flag.get_existing_area_filepath()
  265. except FileNotFoundError as fnfe:
  266. maskframe = np.ones((p1.metadata.format_x, p1.metadata.format_y))
  267. else:
  268. maskframe = read_area_file(area_filename)
  269. # ;now AL perimeter is in variable maskframe
  270. #apply shrinkfactor
  271. if (shrinkfactor not in [0,1]): #both 0 and 1 mean 'no shrink'
  272. # copy from ViewLoadData.loadTILL
  273. shrink = 1/shrinkfactor # flag of 2 means half the size in each dimension
  274. import scipy.ndimage as scind
  275. # the command used in IDL for shrink was REBIN
  276. #raw1(*,*,i,1) = rebin(image(0:p1.format_x*shrinkFactor-1, 0:p1.format_y*shrinkFactor-1) ,p1.format_x, p1.format_y) ;
  277. # the following shrinks in x and y, not in t, with polinomial order 1.
  278. # "nearest" is for treatment at the borders
  279. maskframe = scind.zoom(maskframe,(shrink,shrink), mode='nearest')
  280. #check for consistent size,
  281. # ;safety check for unequal size
  282. # ;remove last row(s)/column(s) in case of original size incompatible with shrink factor
  283. # ;e.g. shrinkfactor 2 with format_x 173, limit mask to 0..172
  284. # maskFrame = maskFrame(0:p1.format_x*shrinkfactor-1,0:p1.format_y*shrinkfactor-1 )
  285. # ;resize maskFrame considering shrinkfactor
  286. # maskSize = size(maskFrame)
  287. # maskFrame = rebin(maskFrame,maskSize[1]/shrinkfactor,maskSize[2]/shrinkfactor)
  288. # maskSize = size(maskFrame)
  289. if (maskframe.shape != (p1.metadata.format_x,p1.metadata.format_y)): # not correct size
  290. print('ViewCalcData.Calcsigall3000: AREA file has wrong size - maybe make with binning unequal 1?')
  291. return #jump out of the program
  292. else: # there is no alPerimeter, i.e. no .Area file
  293. # ;select central region of AL for correction
  294. maskframe = np.zeros((p1.metadata.format_x, p1.metadata.format_y), dtype=int)
  295. maskframe[round(p1.metadata.format_x*cutedge):round(p1.metadata.format_x*(1-cutedge)),round(p1.metadata.format_y*cutedge):round(p1.metadata.format_y*(1-cutedge))]=1
  296. # maskFrame[p1.format_x*CutEdge:p1.format_x*(1-CutEdge),p1.format_y*CutEdge:p1.format_y*(1-CutEdge)]=1
  297. # ;now AL perimeter is in variable maskframe
  298. print('CalcSigAll3000 - reference area for F and for Bleach is centre with edges cut at ',cutedge)
  299. ######### CoorPerimeter #################
  300. # the following CoorPerimeter section appears not in use - not translated to python yet
  301. # I only found the flag in AirBleach.
  302. # The idea is: get the bleach function only from within the coordinates of .coor
  303. # easy to add if needed
  304. #;CoorPerimeter
  305. #;If CoorPerimeter is set, select the area delimited by coordinates for bleach correction maskframe
  306. #;section above becomes obsolete, because maskframe is overwritten
  307. #IF CoorPerimeter THEN begin
  308. # print, 'Using coordinates for bleach control (calcsigall3000)'
  309. # ;load coordinates
  310. # ;get file name of coordinate file (.coor)
  311. # ;Get Information about odours from external file
  312. # IF fix(flag[RM_differentViews]) THEN begin
  313. # GlomeruliListFile = flag[stg_OdorMaskPath]+flag[stg_ReportTag]+p1.viewLabel+'.coor'
  314. # endIF else begin
  315. # GlomeruliListFile = flag[stg_OdorMaskPath]+flag[stg_ReportTag]+'.coor'
  316. # endELSE
  317. # ;reportFile = flag[stg_OdorReportPath]+flag[stg_ReportTag]+'.expGlo'
  318. # shiftMaskeX = p1.shiftX
  319. # shiftMaskeY = p1.shiftY
  320. # ;empty maskframe
  321. # maskFrame = bytarr(p1.format_x, p1.format_y)
  322. # ;read coordinates
  323. # readListe, GlomeruliNum, liste, GlomeruliListFile, column4=separateLayers
  324. # glomerulinum = fix(glomerulinum)
  325. # ;got the liste of coordinates & identities
  326. # ;shiftMaskeX, shiftMaskeY
  327. # liste(0,*) = liste(0,*) + shiftMaskeX
  328. # liste(1,*) = liste(1,*) + shiftMaskeY
  329. # ;consider shrinkfactor
  330. # IF (shrinkFactor gt 1) then begin
  331. # liste(0,*) = liste(0,*)/shrinkFactor
  332. # liste(1,*) = liste(1,*)/shrinkFactor
  333. # endIF
  334. # ;now get infos about each coordinate
  335. # for glo=0, GlomeruliNum-1 do begin
  336. # x = liste(0,glo)
  337. # y = liste(1,glo)
  338. # xborders = [x-radius,x+radius]
  339. # IF ((xborders(0) lt 0) OR (xborders(1) gt (p1.format_x-1))) THEN begin
  340. # xborders = (xborders > 0 ) < (p1.format_x-1)
  341. # print, 'x coordinates clipped: ',x, xborders,' in glo ',glo
  342. # ENDif
  343. # yborders = [y-radius,y+radius]
  344. # IF ((yborders(0) lt 0) OR (yborders(1) gt (p1.format_y-1))) THEN begin
  345. # yborders = (yborders > 0 ) < (p1.format_y-1)
  346. # print, 'y coordinates clipped: ',y, yborders,' in glo ',glo
  347. # ENDif
  348. # ;now set this area to 1 in maskFrame
  349. # maskFrame(xborders(0):xborders(1),yborders(0):yborders(1))=1
  350. # endfor
  351. #endIF ;CoorPerimeter
  352. #positions = where(maskframe)
  353. #;positions contains the pixels to be considered for bleach correction
  354. # in python, do not use np.multiply(raw1,maskframe)
  355. #dividend = endbackground-startbackground+1 ;find how many frames for F
  356. #there are no multiple odors in Python, but there may be raw1 and raw2
  357. #
  358. #;first run through data for light scattering correction, and setting mean to 10000
  359. #for od = firstOdor, p1.odors do begin
  360. #
  361. # ;calculate F (float)
  362. # backframe = total(raw1(*,*,startbackground:endbackground,od),3,/DOUBLE) ;makes a sum of the third dimension (frames)
  363. # backframe = float(backframe) / dividend
  364. ############### background frame, corresponds to F0
  365. p1.metadata.backframe = np.mean(p1.raw1[:,:,startbackground:endbackground], axis=2)
  366. print('ViewCalcData/CalcSigAll3000_raw: background start/stop is frame: ',startbackground, endbackground)
  367. #takes the average for selected time frames
  368. #;calculate intensity correction, only within maskframe
  369. # backvalue = np.mean(np.multiply(p1.backframe,maskframe))
  370. backvalue = np.mean(p1.metadata.backframe[maskframe != 0])
  371. # backValue = mean(backframe(positions), /double) ; average light values in the backframe area
  372. if (backvalue == 0): backvalue = 1 #safety net to avoid dividing by 0
  373. backfactor = lightmeanvalue/backvalue
  374. # apply backFactor outside loop
  375. p1.sig1 = p1.raw1 * backfactor
  376. ########### now sig1 was created, as light intensity corrected copy of raw1
  377. # so from now on work on sig1
  378. # buffer[:,:,i] = sci.gaussian_filter(buffer[:,:,i], filterSpaceSize)
  379. #scipy.ndimage.filters.gaussian_filter(input, sigma, order=0, output=None, mode='reflect', cval=0.0, truncate=4.0)
  380. # ; correct data for scattered light
  381. if unsharpmask: #light scatter correction
  382. smoothfactor = flag["VIEW_ScatterLightFactor"]
  383. # adjust for pixel size - since smoothvalue is given in um
  384. smX = smoothvalue / p1.metadata.pixelsizex
  385. smY = smoothvalue / p1.metadata.pixelsizey
  386. smoothvalue = (smX+smY)/2.0
  387. if (smX != smY): print('unequal pixel size not implemented yet - averaging x and y value')
  388. print('Scattered light correction with ',smoothvalue, ' pixels for ',p1.metadata.pixelsizex*smoothvalue,' microns. Factor:', smoothfactor)
  389. print('CalcSigAll3000: a negative factor means scattered light correction, a positive factor a spatial high-pass filter')
  390. for i in range(0,p1.sig1.shape[2]): # go through frames
  391. # copy a frame, smooth it, copy back
  392. oneframe = p1.sig1[:,:,i]
  393. # for i= 0, p1.frames-1 do begin ;correct data first, write corrected data into final dest
  394. # ;dark-frame correction # dark frame deprecated
  395. # oneFrame = float(raw1(*,*,i,od) - dark1)
  396. # ;intensity correction # done outside the frame loop
  397. # oneFrame = oneFrame * backFactor
  398. # ;light scatter correction
  399. # IF unsharpMask THEN begin
  400. if smoothfactor > 0:
  401. # IF (SmoothFactor gt 0) THEN begin
  402. # oneFrame = oneFrame + SmoothFactor*(oneFrame - smooth(oneFrame, smoothValue, /EDGE_TRUNCATE))
  403. #I use sci.gaussian_filter for the IDL smooth command
  404. #mode='nearest' extends the last value as a constant
  405. oneframe = oneframe + smoothfactor*(oneframe - sci.gaussian_filter(oneframe, smoothvalue, mode='nearest'))
  406. # endIF
  407. if smoothfactor < 0:
  408. # IF (SmoothFactor lt 0) THEN begin
  409. # ;correction Jan 05: the upper line amplifies noise - in fact, rather than a scattered light correction
  410. # ;it is a spatial high-pass filter!
  411. # ;the next line smooths the unsharp image
  412. # ;for backward compatibility, this has to be indicated by a NEGATIVE smoothFactor
  413. # oneFrame = oneFrame - SmoothFactor*smooth((oneFrame - smooth(oneFrame, smoothValue, /EDGE_TRUNCATE)),smoothvalue, /edge_truncate)
  414. oneframe = oneframe - smoothfactor*sci.gaussian_filter((oneframe - sci.gaussian_filter(oneframe, smoothvalue, mode='nearest')), smoothvalue, mode='nearest')
  415. # endIF
  416. # endIF
  417. # ;write corrected data temp. into sig1
  418. # sig1(*,*,i, od) = oneFrame
  419. p1.sig1[:,:,i] = oneframe
  420. # endFOR
  421. #endfor ; odornr
  422. #;sig1 now contains the scatter-light corrected raw data, with mean at lightmeanvalue
  423. # copy into raw1, or delete raw1
  424. if rawdatadelete:
  425. p1.raw1 = 0 #save memory space
  426. else:
  427. p1.raw1 = p1.sig1.copy() # raw now contains scatter corrected "raw" data
  428. #IF rawDataDelete then begin
  429. # Raw1 = 0
  430. # print, 'CalcSigAll3000: raw data deleted'
  431. #endIF else begin
  432. # ;copy back into raw
  433. # raw1 = sig1 #why back into raw?
  434. #endELSE
  435. #;make a copy of this corrected data into the experimental foto
  436. #;only if the foto does not yet exist or is empty
  437. if not hasattr(p1, "foto1"):
  438. p1.foto1 = np.mean(p1.sig1[:, :, startbackground :endbackground], axis=2)
  439. elif np.all(p1.foto1 == 0):
  440. p1.foto1 = np.mean(p1.sig1[:, :, startbackground :endbackground], axis=2)
  441. ## lightThresholdFlag apparently was not implemented yet well
  442. #IF (lightThresholdFlag eq 1) then begin
  443. # ;apply light threshold to data
  444. # ;that is: where there is no dye, there cannot be a signal
  445. # ;threshold calculated from foto
  446. # lightThreshold = 800
  447. # sig1 = sig1 > lightThreshold
  448. #endIF ;flag
  449. #;calculate deltaF for each buffer
  450. p1.sig1 = calc_deltaF(p1.sig1, refRange=[startbackground, endbackground])
  451. #;run through deltaF data, bleach and noise correction for each 'odour', i.e. wavelength separately
  452. if nobleach:
  453. print('No logarighmic bleach correction in CalcSigAll3000')
  454. else:
  455. #$
  456. # ;bleach correction!
  457. # ;necessary data structures
  458. # ;excluded frames at the beginning are defined in fitlogdecay
  459. # correctArray = fltarr(p1.frames)
  460. # AllcorrectArray = fltarr(p1.odors+1,p1.frames)
  461. # AllOdourArray = fltarr(p1.odors+1,p1.frames)
  462. #calculate correctarray as mean or median
  463. #convert flag
  464. # - limit this to the mask
  465. # if bleachmean == 1:
  466. # correctarray = np.mean(p1.sig1, axis=(0,1))
  467. # else:
  468. # correctarray = np.median(p1.sig1, axis=(0,1))
  469. #not good in terms of syntax
  470. correctarray = np.zeros(p1.metadata.frames)
  471. positions = maskframe != 0
  472. if bleachmean == 1:
  473. for i in range(p1.metadata.frames):
  474. correctarray[i] = np.mean(p1.sig1[:,:,i][positions])
  475. else:
  476. for i in range(p1.metadata.frames):
  477. correctarray[i] = np.median(p1.sig1[:,:,i][positions])
  478. ##############NOW WORK ON THE BLEACH CORRECTION; that is: calculate the weight array.
  479. weights = get_bleach_weights(flags=flag, p1_metadata=p1_metadata, exclude_stimulus=excludestimulus)
  480. ############
  481. print(weights)
  482. print(prestim_endbackground, logexcludeseconds, p1.metadata.frequency, stimulus_on, loginitialfactor, bleachstartframe)
  483. if airbleach:
  484. print('CalcSigAll3000: ********* AirBleach not implemented in Python yet')
  485. # ;calculate log function only for AIR
  486. # IF (od eq 0) THEN fitlogdecay, correctArray, Airdetract, weights
  487. # detract = Airdetract
  488. # print, 'LogBleach done on reference measurement!'
  489. else:
  490. measurement_label = flag.get_default_measurement_label()
  491. (detract, opt_parms) = fitlogdecay(correctarray, weights, not flag["VIEW_batchmode"], measurement_label)
  492. p1.metadata.bleachpar = opt_parms
  493. print('CalcSigAll3000: Logbleach correction done')
  494. #IDL: pro FitLogDecay, lineIn, fittedOut, weights, verbose ;, noConverge
  495. if addnoise:
  496. # ;now add high frequency noise (lamp noise) to the fitted exponential
  497. # dummy = detract[0:BleachStartFrame] #;do not smooth before bleachStartFrame
  498. # $$ implement smooth in the next line, defined above
  499. # detract = detract + correctarray - smooth(correctarray, p1.frequency*noiseSeconds)
  500. #$$ or gaussian filter buffer[:,:,i] = sci.gaussian_filter(buffer[:,:,i], filterSpaceSize)
  501. detract = detract + correctarray - sci.gaussian_filter(correctarray, p1.metadata.frequency*noiseseconds)
  502. print('CalcSigAll3000: addnoise done')
  503. # detract[0:BleachStartFrame] = dummy[:]
  504. # ;subtract these values
  505. for i in range(p1.metadata.frames):
  506. p1.sig1[:,:,i] -= detract[i]
  507. # ;MONITOR the action taken
  508. # allcorrectArray(od,*)=detract(*) ;this is just for plotting the monitor
  509. # allOdourArray(od,*) =correctArray(*) ;this is just for plotting the monitor
  510. ##;calculate bleach corrected raw data and put it into raw
  511. # discontinued, because implemented in 5000 family with returnrawdata
  512. # if (rawdatacopy == 2):
  513. # for i in range(p1.frames-1):
  514. ## raw1(*,*,i,*) = (sig1(*,*,i,*) * backFrame(*,*,*))/multiplyfactor ;raw data
  515. # p1.raw1[:,:,i] = (p1.sig1[:,:,i] * p1.backframe[:,:])/multiplyfactor #
  516. # # this inverts the deltaF/F ;raw data
  517. ## endFor
  518. print('CalcSigAll3000: bleach corrected deltaF data in sig1')
  519. #;calculate bleach corrected raw data and put it into sig
  520. if returnrawdata:# setting is 5000
  521. for i in range(p1.metadata.frames): # do begin
  522. # do the inverse of deltaF/F
  523. p1.raw1[:,:,i] = ((p1.sig1[:,:,i]+1) * p1.metadata.backframe) # /multiplyfactor# ;raw data
  524. #this only works because backframe uses the same frames as deltaF/F
  525. print('CalcSigAll3000: bleach corrected raw data calculated')
  526. # else: #endIF ELSE begin
  527. # ;shift back to mean value of MultiplyFactor
  528. # ;'temporary' saves memory space
  529. # p1.sig1 += multiplyfactor
  530. # print('CalcSigAll3000: check this line - why would MultiplyFactor be added??')
  531. # in python, plot the curves not implemented (yet?)
  532. # ;plot the curves used in log-correction
  533. #
  534. # window, 16, xsize=512, ysize=512
  535. # minplot = floor(min([allcorrectarray(firstodor:p1.odors,*),allodourarray(firstodor:p1.odors,*)]))
  536. # maxplot = ceil(max([allcorrectarray(firstodor:p1.odors,*),allodourarray(firstodor:p1.odors,*)]))
  537. # ;minplot = 98.0
  538. # ;maxplot = 102.0
  539. # plot, correctarray(*), yrange=[minplot,maxplot], color=245, thick=3, ystyle=1, /nodata
  540. # ;plot weight values as curve and bottom line
  541. # oplot, minplot+weights(*), thick=2, color=255 & correctarray(*)=minplot & oplot, correctarray(*), thick=1, color=255
  542. # ;plot log fits
  543. # for od = firstOdor, p1.odors do begin
  544. # oplot, allcorrectArray(od,*), thick=3, color=240-30*od
  545. # oplot, allOdourArray(od,*), thick=1, color=240-30*od
  546. # end
  547. # ;plot stimulus bar
  548. # xaxis = FINDgen(p1.frames)
  549. # stimlow = fltarr(p1.frames)
  550. # stimlow(*)= minplot
  551. # oplot, xaxis(p1.stimulus_on:p1.stimulus_end), stimlow, thick=5, color = 245
  552. # ;second stimulus; if no second stimulus is given, this should be from 0 to 0
  553. # IF ((p1.stim2on gt 0) AND (p1.stim2off gt 0) AND (p1.stim2on lt p1.frames-1) AND (p1.stim2on lt p1.frames-1)) THEN begin
  554. # oplot, xaxis(p1.stim2on:p1.stim2off), stimlow, thick=5, color = 245
  555. # endIF
  556. #endELSE;nobleach
  557. return p1, flag # no return, p1 is modified already. Try return instead now (8/19)
  558. def calc_deltaF(imgData, refRange):
  559. '''
  560. Input: 3D matrix x,y,t, refRange
  561. calculates a crude deltaF.
  562. F0 is the entire movie (default), or the interval refRange (e.g. [14,21])
  563. :param refRange: list of length 2, frame numbers (indices) of the starting and ending frame of the range to use for
  564. baseline fluorescence calculation. Note that the ending frame is included in baseline calculation.
  565. '''
  566. referenceF = np.mean(imgData[:, :, refRange[0]: refRange[1] + 1], axis=2)
  567. dead_mask = referenceF == 0
  568. if np.any(dead_mask):
  569. # keep pixels that averages 0 over the reference range unmodified. these are most likely pixels dead at 0.
  570. referenceF[dead_mask] = 1
  571. logging.getLogger("VIEW").warning("F0 for some pixels were 0, not normalizing these pixels!")
  572. # convert to txy because framewise division is easier
  573. imgData_txy = np.moveaxis(imgData, source=-1, destination=0)
  574. deltaFdata_txy = (imgData_txy / referenceF) - 1
  575. # convert back
  576. deltaFdata = np.moveaxis(deltaFdata_txy, source=0, destination=-1)
  577. logging.getLogger("VIEW").info(
  578. f"Baseline fluorescence was calculated by averaging frames numbered {refRange[0]}-{refRange[1]}")
  579. return deltaFdata
  580. def CalcSigAll0(p1, flag):
  581. #pro CalcSigAll3, raw, dark, p, absolute_values
  582. #; Procedure not to calculate any signals
  583. #; Author: Giovanni, Jasdan
  584. #;parameters: (from loadexp.pro)
  585. #; raw : 4 dimensional array (x,y,frames,odors)
  586. #; odor=0: background/air
  587. #; odor=1... odors from experiment
  588. #; dark : darkframe of ccd camera
  589. #; p : parameterset
  590. #;results:
  591. #;relative_changes : 4 dimensional float array with signals for all odor and background
  592. #absolute_values = fltarr(p.format_x, p.format_y, p.frames, p.odors+1)
  593. #backframe = fltarr(p.format_x, p.format_y)
  594. #for odor = 0, p.odors do begin
  595. # backframe(*) = float(dark)
  596. # for i= 0, p.frames-1 do begin
  597. # absolute_values(*,*,i, odor) = raw(*,*,i,odor) - backframe(*,*)
  598. # endfor
  599. #endfor ; odornr
  600. #absolute_values = absolute_values / 1000
  601. p1.sig1 = p1.raw1.astype(float) / 1000
  602. print('ViewCalcData/CalcSigAll0: Signals calculated (method 0). sig1 = raw1/1000')
  603. #end ; of program
  604. return p1
  605. def CalcSigAll1(p1, flag):
  606. # pro CalcSigAll3, raw, dark, p, absolute_values
  607. # ; Procedure not to calculate any signals
  608. # ; Author: Giovanni, Jasdan
  609. # ;parameters: (from loadexp.pro)
  610. # ; raw : 4 dimensional array (x,y,frames,odors)
  611. # ; odor=0: background/air
  612. # ; odor=1... odors from experiment
  613. # ; dark : darkframe of ccd camera
  614. # ; p : parameterset
  615. # ;results:
  616. # ;relative_changes : 4 dimensional float array with signals for all odor and background
  617. # absolute_values = fltarr(p.format_x, p.format_y, p.frames, p.odors+1)
  618. # backframe = fltarr(p.format_x, p.format_y)
  619. # for odor = 0, p.odors do begin
  620. # backframe(*) = float(dark)
  621. # for i= 0, p.frames-1 do begin
  622. # absolute_values(*,*,i, odor) = raw(*,*,i,odor) - backframe(*,*)
  623. # endfor
  624. # endfor ; odornr
  625. # absolute_values = absolute_values / 1000
  626. p1.sig1 = p1.raw2.astype(float) / 1000
  627. print('ViewCalcData/CalcSigAll1: Signals calculated (method 0). sig1 = raw2/1000')
  628. # end ; of program
  629. return p1
  630. def CalcSigAll3(p1, flag):
  631. '''
  632. Calculate deltaF, with no thrills at all
  633. in IDL, this was ???
  634. '''
  635. p1.sig1 = calc_deltaF(p1.raw1, refRange=p1.metadata.background_frames)
  636. logging.getLogger("VIEW").info(
  637. f'CalcSigAll3: delta F with background: {p1.metadata.background_frames}')
  638. return p1
  639. def CalcSigAll4(p1, flag):
  640. '''
  641. Calculate the ratio, with no thrills at all
  642. in IDL, this was CalcSigAll15
  643. '''
  644. #pro CalcSigAll15, raw, relative_changes
  645. #deleteRaw = 1
  646. #multiplyFactor = 100 ; factor to multiply data with when making ratio
  647. #relative_changes = raw
  648. #IF deleteRAW then raw = 0
  649. #relative_changes = float(relative_changes)
  650. #;calculate RATIOS
  651. #relative_changes(*,*,*, 1) = (multiplyFactor*relative_changes(*,*,*, 0)) / relative_changes(*,*,*, 1)
  652. #relative_changes(*,*,*, 0) = 0 ;delete buffer 0
  653. #print, 'plain ratios in buffer 1'
  654. #end ; of program
  655. p1.sig1 = p1.raw1 / p1.raw2
  656. print('ViewCalcData/CalcSigAll4: Signals calculated (method 4). Fura, no thrills')
  657. return p1