tianmoucv.data.tianmoucData 源代码

import numpy as np
import cv2,sys
import torch
import math,time,subprocess
import torch.nn.functional as F
import torch.nn as nn

import os
flag = True
try:
    from tianmoucv.rdp_usb import rod_decoder_py as rdc
except:
    print("WARNING: no decoder found, try to compile it under ./rod_decoder_py")
    current_file_path = os.path.abspath(__file__)
    parent_folder_path = os.path.dirname(os.path.dirname(current_file_path))
    aim_path = os.path.join(parent_folder_path,'rdp_usb')
    os.chdir(aim_path)
    current_path = os.getcwd()
    print("Current Path:", current_path)
    subprocess.run(['sh', './compile_pybind.sh'])
    from tianmoucv.rdp_usb import rod_decoder_py as rdc
    print('compile decoder successfully')
    print('If you still get this message,please try:\n 1. run it in a python script (only once) \n 2. use source code install to see what happened')
  
    
#用于重建
from tianmoucv.proc.reconstruct import poisson_blending
from tianmoucv.proc.denoise import custom_round,conv_and_threshold,conv_and_threshold_1,denoise_defualt_args
from tianmoucv.isp import default_rgb_isp,upsample_cross_conv,SD2XY
from .tianmoucData_basic import TianmoucDataReader_basic
from .tianmoucDataSampleParser import TianmoucDataSampleParser
from .singlePathwayDecoder import multiple_rods_tmdat_to_npy_from_addr

[文档] class TianmoucDataReader(TianmoucDataReader_basic): ''' - TianmoucDataReader(version 0.3.7.4 in dev) - Data structure ├── dataset │ ├── matchkey(sample name) │ │ ├── cone │ │ ├── info.txt │ │ ├── xxx.tmdat │ │ ├── rod │ │ ├── info.txt │ │ ├── xxx.tmdat │ │ ├── cone1 │ │ ├── info.txt │ │ ├── xxx.tmdat │ │ ├── rod1 │ │ ├── info.txt │ │ ├── xxx.tmdat - @dataPath: - a path to a sample/dataset: string - a list of dataset path/sample: [string1, string2] - if the path is belong to a dataset/datasets or a list of samples, please use matchkey to index a certain sample, or set matchkey = None - if you use multiple camera, set camera_idx - @matchkey: - matchkey should be unified, is the only key to select data sample - matchkey is the folder name containing the cop/aop data folder - @N: number of continous samples, N=1 as default - the returned sample will contain (N+1) COP,and N*M+1 AOP - for example N=1, in 750@8bit, M=750/30=25, M = 25, a sample contain 2 COP and 26 AOP - @camera_idx: - camera_idx = 0 as default - in multi-camera system, use camera_idx to create a dataset fot certain camera - @MAXLEN: - the maximum read data length, set -1 to read all data, used only in training task [Output] create a dataset, the containings please refer to TianmoucDataReader.__getitem__() ''' def __init__(self,path, N=1, camera_idx= 0, aop_denoise = False, aop_denoise_args = None, showList=True, MAXLEN=-1, uniformSampler=False, matchkey=None, cachePath=None, ifcache = False, print_info=True, training=True, strict=False, use_data_parser = False, _correct_sd_upsampler = False, dark_level = 0): self.N = N self.print_info = print_info self.use_data_parser = use_data_parser super().__init__(path, showList=showList, MAXLEN=MAXLEN, uniformSampler = uniformSampler, camera_idx= camera_idx, matchkey=matchkey, cachePath=cachePath, ifcache = ifcache, print_info=print_info, training=training, strict = strict, dark_level = dark_level) # 调用父类的属性赋值方法 self.aop_denoise = aop_denoise self.aop_denoise_args = aop_denoise_args self._correct_sd_upsampler = _correct_sd_upsampler if self.aop_denoise: print('[tianmoucv Datareader]Customising your aop_denoise_args using tianmoucv.data.denoise_utils.denoise_defualt_args()') print('[tianmoucv Datareader]Better to provide:{\'TD\':[np.array]*2,\'SDL\':[np.array]*2*aop_cop_rate,\'SDR\':[np.array]*2*aop_cop_rate}') if self.aop_denoise_args.aop_dark_dict['TD'] is None: if self.aop_denoise_args.self_calibration: print('[tianmoucv Datareader] cannot find TD dark, use self calibration') self.aop_denoise_args.aop_dark_dict['TD'] = self.td_fpn_calibration_(Num=min(500,self.__len__()-1)) else: print('[tianmoucv Datareader] cannot find SDL dark, use ZERO') self.aop_denoise_args.aop_dark_dict['TD'] = [torch.zeros(160, 160) for _ in range(2)] if self.aop_denoise_args.aop_dark_dict['SDL'] is None: if self.aop_denoise_args.self_calibration: print('[tianmoucv Datareader] cannot find SDL dark, use self calibration') SDL_dark,SDR_dark = self.sd_fpn_calibration_(Num=min(500,self.__len__()-1)) self.aop_denoise_args.aop_dark_dict['SDL'] = SDL_dark self.aop_denoise_args.aop_dark_dict['SDR'] = SDR_dark else: print('[tianmoucv Datareader] cannot find SDL dark, use ZERO') self.aop_denoise_args.aop_dark_dict['SDL'] = [torch.zeros(160, 160) for _ in range(2)] self.aop_denoise_args.aop_dark_dict['SDR'] = [torch.zeros(160, 160) for _ in range(2)] self.choose_correct_fpn(thr_fpn=self.aop_denoise_args.thr_fpn) #判断奇偶帧 self.aop_denoise_args.print_info() self.denoise_function = aop_denoise_args.denoise_function self.denoise_function_args = aop_denoise_args.denoise_function_args print('[tianmoucv Datareader Warning] Doesn\'t support multiple keys for Denoise in this version') #你可以重写这个extration逻辑以获得更复杂的数据读取方法,例如抽帧等 def extraction(self,MAXLEN,uniformSampler): for key in self.fileDict: new_legalFileList = [] legalFileList = self.fileDict[key]['legalData'] #把相邻N个同步后的包合并为一个sample来读取 newsample_merge = dict([]) accum_count = 0 for sampleid in range(len(legalFileList)): sample_0 = legalFileList[sampleid] cone0 = sample_0['coneid'] if accum_count == 0: newsample_merge['sysTimeStamp'] = sample_0['sysTimeStamp'] newsample_merge['coneid'] = cone0 newsample_merge['rodid'] = sample_0['rodid'] newsample_merge[self.pathways[1]] = sample_0[self.pathways[1]] newsample_merge[self.pathways[0]] = sample_0[self.pathways[0]] newsample_merge['labels'] = sample_0['labels'] else: newsample_merge['coneid'] += cone0[1:] newsample_merge['rodid'] += sample_0['rodid'][1:] newsample_merge[self.pathways[1]] += sample_0[self.pathways[1]][1:] newsample_merge[self.pathways[0]] += sample_0[self.pathways[0]][1:] accum_count += 1 if accum_count == self.N: new_legalFileList.append(newsample_merge) newsample_merge = dict([]) accum_count =0 continue #拼接更多的sample,以待一起读取 if MAXLEN>0: if len(new_legalFileList)>MAXLEN: if uniformSampler: adptiverate = len(new_legalFileList)//MAXLEN new_legalFileList = [new_legalFileList[i] for i in range(0, adptiverate*MAXLEN, adptiverate)] else: new_legalFileList = new_legalFileList[:MAXLEN] self.fileDict[key]['legalData'] = new_legalFileList if self.print_info: print('[tianmoucv Datareader FULL]',key,'extracted length:',len(new_legalFileList)) #同理,你也可以通过修改这个函数获得更复杂的数据预处理手段
[文档] def packRead(self,idx,key,ifSync =True, needPreProcess = True): ''' use the decoder and isp preprocess to generate a paired (RGB,n*TSD) sample dict: - COP - COP is stored seprately as F0,F1,F2 ... FN+1, use string key to get them - COP's framerate is 30.3fps - i = 0~N - sample['Fi_without_isp'] = only demosaced frame data, without addtional isp 3*320*640, t=t_0+i*T - sample['Fi_HDR']: RGB+SD Blended HDR frame data, 3*320*640, t=t_0+i*T ms - sample['Fi']: preprocessed frame data, 3*320*640, t=t_0+i*T ms - AOP - AOP data is stored in a large tensor, [T,C,H,W] - channel 0 is TD, channel 1 is SDL, channel 2 is SDR - sample['tsdiff'] = TSD data upsample to (N+1)*3*320*640, from t=t_0 to t=t+ N*T ms (eg. T=33 in 757 @ 8 bit mode) - sample['rawDiff']: raw TSD data, (N+1)*3*160*160, from t=t_0 to t=t+ N*T ms (eg. T=33 in 757 @ 8 bit mode) - all tianmoucv API use sample['rawDiff'] for input, sample['tsdiff'] is used by some NN-based method - sample['meta']: - a dict - file path infomation - camera timestamps for each data - more other details - sample['labels']: - list of labels, if you have one - sample['sysTimeStamp']: unix system time stamp in us, use for multi-sensor sync, -1 if not supported - you can calculate Δt = sysTimeStamp1-sysTimeStamp2, unit is 'us' ''' sample = dict([]) metaInfo = dict([]) legalSample = self.fileDict[key]['legalData'][idx] conefilename = self.fileDict[key][self.pathways[1]] # only read first one, if you want to use dual camera you can read it again rodfilename = self.fileDict[key][self.pathways[0]] # only read first one, if you want to use dual camera you can read it again coneAddrs = legalSample[self.pathways[1]] rodAddrs = legalSample[self.pathways[0]] rgb_list = [] coneTimeStamp_list = [] raw_list = [] #read all rgb frames for i in range(self.N+1): #print('caddr:',coneAddrs[i]) frame,timestamp = self.readConeFast(conefilename,coneAddrs[i]) frame_raw = np.reshape(frame.copy(), (self.cone_height,self.cone_width)) frame = np.reshape(frame.astype(np.float32),(self.cone_height,self.cone_width)) raw_list.append(frame_raw) rgb_list.append(frame) coneTimeStamp_list.append(timestamp.astype(np.int64)) cone_id = legalSample['coneid'] rod_id = legalSample['rodid'] #prepare all meta infomation metaInfo['C_name'] = conefilename metaInfo['C_timestamp'] = coneTimeStamp_list metaInfo['C_idx'] = cone_id metaInfo['R_name'] = rodfilename metaInfo['R_idx'] = rod_id metaInfo['key'] = key metaInfo['sample_length'] = len(self.fileDict[key]['legalData']) itter = len(rodAddrs) #read all aop frames tsd = torch.zeros([3,itter,self.rod_height,self.rod_width]) tsd, rodTimeStamp = multiple_rods_tmdat_to_npy_from_addr(rodfilename, addr = rodAddrs[0] ,length=itter,print_info=False) tsd = torch.FloatTensor(tsd) metaInfo['R_timestamp'] = rodTimeStamp #metaInfo['R_timestamp'] = [] #for i in range(itter): # startAddr = rodAddrs[i] # sdl,sdr,td,rodTimeStamp = self.readRodFast(rodfilename,startAddr) # tsd[0,i,:,:] = torch.Tensor(td.astype(np.float32)).view(self.rod_height,self.rod_width) # tsd[1,i,:,:] = torch.Tensor(sdl.astype(np.float32)).view(self.rod_height,self.rod_width) # tsd[2,i,:,:] = torch.Tensor(sdr.astype(np.float32)).view(self.rod_height,self.rod_width) # metaInfo['R_timestamp'].append(rodTimeStamp.astype(np.int64)) #denoising if self.aop_denoise: tsd = self.denoise(tsd, rod_id, TD_dark=self.aop_denoise_args.aop_dark_dict['TD'], SD_dark_left=self.aop_denoise_args.aop_dark_dict['SDL'], SD_dark_right=self.aop_denoise_args.aop_dark_dict['SDR'], denoise_function = self.aop_denoise_args.denoise_function, denoise_function_args = self.aop_denoise_args.denoise_function_args) sample['rawDiff'] = tsd mingap = (itter-1)//self.N if needPreProcess: tsd_expand_original,txydiff_expand = self.tsd_preprocess(tsd) if self._correct_sd_upsampler: txydiff_resized = F.interpolate(txydiff_expand,(320,640),mode='bilinear') sample['txydiff'] = txydiff_resized else: tsdiff_resized = F.interpolate(tsd_expand_original,(320,640),mode='bilinear') sample['tsdiff'] = tsdiff_resized for i in range(self.N+1): frame,frame_without_isp = self.rgb_preprocess(rgb_list[i]) frame_raw = raw_list[i] sample['F'+str(i)+'_without_isp'] = frame_without_isp sample['F'+str(i)] = frame sample['F' + str(i)+"_raw"] = frame_raw SD_t = tsd[1:,mingap*i,...] sample['F'+str(i)+'_HDR'] = self.HDRRecon(SD_t / 128,frame) sample['meta'] = metaInfo sample['labels'] = legalSample['labels'] sample['sysTimeStamp'] = legalSample['sysTimeStamp'] dataRatio = self.fileDict[key]['dataRatio'] sample['dataRatio']= dataRatio #convert all numpy type to tensor for key in sample: value = sample[key] if isinstance(value,np.ndarray) and 'F' in key: sample[key] = torch.FloatTensor(value) return sample
def tsd_preprocess(self,tsdiff): #upsample_cross_conv: 老算法 tsd_expand_original = upsample_cross_conv(tsdiff)/128.0 Ix,Iy= (e/128.0 for e in SD2XY(tsdiff[1:,...].permute(1,0,2,3))) TXYD = torch.stack([tsd_expand_original[0,...],Ix,Iy],dim=0) return tsd_expand_original,TXYD def rgb_preprocess(self,F_raw): F,F_without_isp = default_rgb_isp(F_raw,blc=self.blc) return F,F_without_isp def __getitem__(self, index): ''' 定位在哪个sample里 ''' if index >= self.__len__(): print('[tianmoucv Datareader ERROR] INDEX OUT OF RANGE!') return None key,relativeIndex = self.locateSample(index) if key is None: print('[tianmoucv Datareader ERROR] No data Found for this key!') return None sample = self.packRead(relativeIndex, key) if self.use_data_parser: sample = TianmoucDataSampleParser(sample) return sample ################################################################################################################################## #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[暗噪声标定]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ###################################################################################################################################
[文档] def get_dark_noise(self): ''' inspect dark noise or calibrate dark noise data ''' return self.aop_denoise_args.aop_dark_dict
[文档] def choose_correct_fpn(self,thr_fpn=1,idx=0): ''' 空间噪声 ''' TD_dark = self.aop_denoise_args.aop_dark_dict['TD'] SDL_dark= self.aop_denoise_args.aop_dark_dict['SDL'] SDR_dark= self.aop_denoise_args.aop_dark_dict['SDR'] tsdiff,rodid = self.get_raw_tsdiff_(idx) for key in self.fileDict: dataRatio = self.fileDict[key]['dataRatio'] #判断奇偶帧匹配 cal = tsdiff[0, 0, ...] cal_0 = cal - TD_dark[0] cal_1 = cal - TD_dark[1] var_org = torch.var(cal) var_cal0 = torch.var(cal_0) var_cal1 = torch.var(cal_1) kernel = torch.ones((1, 1, 3, 3), dtype=torch.float32) / 9.0 match_tensor_0 = cal_0.unsqueeze(0).unsqueeze(0) smoothed_matrix = F.conv2d(match_tensor_0, kernel, padding=1) smoothed_matrix = smoothed_matrix.squeeze(0).squeeze(0) mask_0 = torch.abs(smoothed_matrix) >= thr_fpn match_tensor_1 = cal_1.unsqueeze(0).unsqueeze(0) smoothed_matrix = F.conv2d(match_tensor_1, kernel, padding=1) smoothed_matrix = smoothed_matrix.squeeze(0).squeeze(0) mask_1 = torch.abs(smoothed_matrix) >= thr_fpn mask_0 = mask_0.float() mask_1 = mask_1.float() var_0 = torch.var(mask_0) #dark0和tsdiff0的匹配 var_1 = torch.var(mask_1) #dark1和tsdiff1的匹配 TD_corrected_dark = [torch.zeros(160, 160) for _ in range(2)] SDL_corrected_dark = [torch.zeros(160, 160) for _ in range(2*dataRatio)] SDR_corrected_dark = [torch.zeros(160, 160) for _ in range(2*dataRatio)] if len(SDL_dark) < 2*dataRatio: print('[tianmoucv Datareader Warning] Dark Noise data\'s dataRatio does not match the data,use average-copy version(无法去横条纹)') SDL_dark = [torch.mean(torch.stack(SDL_dark[0:len(SDL_dark)//2],dim=0),dim=0),torch.mean(torch.stack(SDL_dark[len(SDL_dark)//2:],dim=0),dim=0)] SDL_dark = [SDL_dark[0]]*dataRatio + [SDL_dark[1]]*dataRatio SDR_dark = [torch.mean(torch.stack(SDR_dark[0:len(SDR_dark)//2],dim=0),dim=0),torch.mean(torch.stack(SDR_dark[len(SDR_dark)//2:],dim=0),dim=0)] SDR_dark = [SDR_dark[0]]*dataRatio + [SDR_dark[1]]*dataRatio #如果第一帧是偶数帧,并且第一帧更适配[0] #或者第一帧是偶数帧,并且第一帧更适配[1] if (var_cal0 < var_cal1 and rodid[0] % 2 ==0) or (var_cal0 > var_cal1 and rodid[0] % 2 ==1): TD_corrected_dark[1] = TD_dark[1] TD_corrected_dark[0] = TD_dark[0] for j in range(dataRatio): SDL_corrected_dark[j] = SDL_dark[j] SDL_corrected_dark[j+dataRatio] = SDL_dark[j+dataRatio] SDR_corrected_dark[j] = SDR_dark[j] SDR_corrected_dark[j+dataRatio] = SDR_dark[j+dataRatio] else: TD_corrected_dark[1] = TD_dark[0] TD_corrected_dark[0] = TD_dark[1] for j in range(dataRatio): SDL_corrected_dark[j] = SDL_dark[j+dataRatio] SDL_corrected_dark[j+dataRatio] = SDL_dark[j] SDR_corrected_dark[j] = SDR_dark[j+dataRatio] SDR_corrected_dark[j+dataRatio] = SDR_dark[j] #已经换好顺序的fpn-dark self.aop_denoise_args.aop_dark_dict['TD'] = TD_corrected_dark self.aop_denoise_args.aop_dark_dict['SDL'] = SDL_corrected_dark self.aop_denoise_args.aop_dark_dict['SDR'] = SDR_corrected_dark return TD_corrected_dark, SDL_corrected_dark, SDR_corrected_dark
[文档] def get_raw_tsdiff_(self, index): ''' 只拿原始rod数据和其绝对id,提升效率 ''' if index >= self.__len__(): print('[tianmoucv Datareader ERROR] INDEX OUT OF RANGE!') return None key,relativeIndex = self.locateSample(index) if key is None: print('[tianmoucv Datareader ERROR] No data Found for this key!') return None legalSample = self.fileDict[key]['legalData'][index] rodfilename = self.fileDict[key][self.pathways[0]] # only read first one, if you want to use dual camera you can read it again rodAddrs = legalSample[self.pathways[0]] rod_id = legalSample['rodid'] itter = len(rodAddrs) assert itter>=0 tsd = torch.zeros([3,itter,self.rod_height,self.rod_width]) for i in range(itter): startAddr = rodAddrs[i] sdl,sdr,td,rodTimeStamp = self.readRodFast(rodfilename,startAddr) tsd[0,i,:,:] = torch.Tensor(td.astype(np.float32)).view(self.rod_height,self.rod_width) tsd[1,i,:,:] = torch.Tensor(sdl.astype(np.float32)).view(self.rod_height,self.rod_width) tsd[2,i,:,:] = torch.Tensor(sdr.astype(np.float32)).view(self.rod_height,self.rod_width) return tsd,rod_id
[文档] def td_fpn_calibration_(self,Num=20): ''' 计算奇数帧和偶数帧TD空间噪声,Num为标定所用的index数量 ''' tsdiff_,_ = self.get_raw_tsdiff_(0) tsdiff = tsdiff_.clone() for key in self.fileDict: dataRatio = self.fileDict[key]['dataRatio'] TD_mean = [torch.zeros(160, 160) for _ in range(2)] TD_mean_odd = torch.zeros(160, 160) TD_mean_even = torch.zeros(160, 160) odd_count = 0 even_count = 0 for i in range(Num): tsdiff_,rod_id = self.get_raw_tsdiff_(i) #print('[>>>>>>>>>>>debug>>>>>>>>>>>>>>>]',rod_id) if tsdiff_ is None or tsdiff.shape[1] < dataRatio: print('[tianmoucv Datareader warninig] td_fpn_calibration_ lost data') continue tsdiff = tsdiff_.clone() for j in range(dataRatio): TD_0 = tsdiff[0, j, ...] if rod_id[j]%2==0:#偶数rod帧 TD_mean_even += TD_0 even_count += 1 if rod_id[j]%2==1:#偶数rod帧 TD_mean_odd += TD_0 odd_count += 1 TD_mean_even/= (even_count+1e-10) TD_mean_odd/= (odd_count+1e-10) TD_mean_odd = custom_round(TD_mean_odd) TD_mean_even = custom_round(TD_mean_even) TD_mean[0]=TD_mean_even TD_mean[1]=TD_mean_odd print('[tianmoucv Datareader] TD CALIB NUM:',odd_count,even_count) return TD_mean
[文档] def sd_fpn_calibration_(self,Num=20): ''' 计算奇数index帧和偶数index帧TD空间噪声,Num为标定所用的index数量 返回标定的SD_mean_left, SD_mean_right ''' tsdiff_,_ = self.get_raw_tsdiff_(0) tsdiff = tsdiff_.clone() for key in self.fileDict: dataRatio = self.fileDict[key]['dataRatio'] print('[tianmoucv Datareader warninig] use data ratio of the last key, if want to support multiple matkey, it may need to be modified') odd_count = 0 even_count = 0 SD_mean_left = [torch.zeros(160, 160) for _ in range(2*dataRatio)] SD_mean_right = [torch.zeros(160, 160) for _ in range(2*dataRatio)] for i in range(Num): tsdiff_,rod_id = self.get_raw_tsdiff_(i) if tsdiff_ is None or tsdiff.shape[1] < dataRatio: print('[tianmoucv Datareader warninig] sd_fpn_calibration_ lost data') continue tsdiff = tsdiff_.clone() if rod_id[0]%2==0:#偶数rod帧,考虑条纹噪声 even_count += 1 for j in range(dataRatio): SDL = tsdiff[1, j, ...] SDR = tsdiff[2, j, ...] SD_mean_left[j] += SDL SD_mean_right[j] += SDR else:#偶数rod帧,考虑条纹噪声 odd_count += 1 for j in range(dataRatio): SDL = tsdiff[1, j, ...] SDR = tsdiff[2, j, ...] SD_mean_left[j+dataRatio] += SDL SD_mean_right[j+dataRatio] += SDR for j in range(dataRatio): SD_mean_left[j] /= (even_count+1e-10) SD_mean_left[j] = custom_round(SD_mean_left[j]) SD_mean_right[j] /= (even_count+1e-10) SD_mean_right[j] = custom_round(SD_mean_right[j]) SD_mean_left[j+dataRatio] /= (odd_count+1e-10) SD_mean_left[j+dataRatio] = custom_round(SD_mean_left[j+dataRatio]) SD_mean_right[j+dataRatio] /= (odd_count+1e-10) SD_mean_right[j+dataRatio] = custom_round(SD_mean_right[j+dataRatio]) print('[tianmoucv Datareader] SD CALIB NUM:',odd_count,even_count) return SD_mean_left, SD_mean_right
def denoise(self, raw_tsd, rod_id, TD_dark=None, SD_dark_left=None, SD_dark_right=None, denoise_function = None, denoise_function_args = None): denoise_raw_tsd=torch.zeros(3, raw_tsd.shape[1], 160, 160) for key in self.fileDict: dataRatio = self.fileDict[key]['dataRatio'] #BLC for j in range(raw_tsd.shape[1]): if rod_id[j]%2 == 0: raw_tsd[0, j, ...] -= TD_dark[0] else: raw_tsd[0, j, ...] -= TD_dark[1] if rod_id[0]%2 == 0: raw_tsd[1, j, ...] -= SD_dark_left[j%dataRatio] raw_tsd[2, j, ...] -= SD_dark_right[j%dataRatio] else: raw_tsd[1, j, ...] -= SD_dark_left[j%dataRatio+dataRatio] raw_tsd[2, j, ...] -= SD_dark_right[j%dataRatio+dataRatio] #TD只去除空间噪声 denoise_raw_tsd[:, j, ...]=raw_tsd[:,j,...] #optional if denoise_function is None: return denoise_raw_tsd else: return denoise_function(raw_tsd,denoise_function_args)
################################################################################################################################## #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[暗噪声标定]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ###################################################################################################################################