├── README.md ├── TIPA_basic_run.ipynb ├── TIPA_library ├── main │ ├── data_preparation.py │ └── thermal_image_processing.py └── utils │ ├── compute_frequency_grid.py │ ├── gausswin.py │ ├── overlap_matrix.py │ ├── overlap_windows.py │ ├── pca_basic.py │ ├── rvs.py │ ├── simpleDRQ.py │ └── timshow.py ├── data └── readme.md └── figures ├── figure1.png ├── figure2.png ├── figure3.png └── readme.md /README.md: -------------------------------------------------------------------------------- 1 | # TIPA opensource toolkit project 2 | TIPA: Thermal Imaging-based Physiological and Affective computing 3 | 4 | Lead contributor: Dr. Youngjun Cho, Department of Computer Science, University College London (UCL) 5 | 6 | 7 | 8 | ## Brief guideline 9 | 10 | 1. Download Anaconda (latest version) - Python 3.7 (recommended) 11 | 12 | https://www.anaconda.com/distribution/ 13 | 14 | 15 | 2. Install basic libraries on the Conda console. 16 | 17 | conda install -c conda-forge opencv 18 | 19 | conda install scikit-learn 20 | 21 | pip install --upgrade numpy 22 | 23 | pip install --upgrade matplotlib 24 | 25 | pip install packaging 26 | 27 | conda install -c anaconda scipy 28 | 29 | 30 | * For your information 31 | 32 | print(python_version()) 33 | 34 | 3.7.3 35 | 36 | print(np.version.version) 37 | 38 | 1.16.4 39 | 40 | print(cv2.__version__) 41 | 42 | 3.4.2 43 | 44 | scipy (1.3.1) 45 | 46 | 3. Run "TIPA_basic_run.ipynb" on the Jupyter notebook 47 | 48 | You can find a basic instruction on the notebook. 49 | 50 | 51 | 52 | 53 | 54 | ## Key Reference 55 | [1] Youngjun Cho and Nadia Bianchi-Berthouze. 2019. Physiological and Affective Computing through Thermal Imaging: A Survey. arXiv:1908.10307 [cs], http://arxiv.org/abs/1908.10307 56 | 57 | ### Further Technical References 58 | [2] Cho, Y., Julier, S.J., Marquardt, N. and Bianchi-Berthouze, N., 2017. Robust tracking of respiratory rate in high-dynamic range scenes using mobile thermal imaging. Biomedical optics express, 8(10), pp.4480-4503. https://doi.org/10.1364/BOE.8.004480 59 | 60 | [3] Cho, Y., Julier, S.J. and Bianchi-Berthouze, N., 2019. Instant Stress: Detection of Perceived Mental Stress Through Smartphone Photoplethysmography and Thermal Imaging. JMIR mental health, 6(4), p.e10140. https://doi.org/10.2196/10140 61 | 62 | [4] Cho, Y., Bianchi-Berthouze, N. and Julier, S.J., 2017. DeepBreath: Deep learning of breathing patterns for automatic stress recognition using low-cost thermal imaging in unconstrained settings. In 2017 Seventh International Conference on Affective Computing and Intelligent Interaction (ACII) (pp. 456-463). IEEE. https://doi.org/10.1109/ACII.2017.8273639 63 | 64 | [5] Cho, Y., Bianchi-Berthouze, N., Marquardt, N. and Julier, S.J., 2018. Deep Thermal Imaging: Proximate Material Type Recognition in the Wild through Deep Learning of Spatial Surface Temperature Patterns. In Proceedings of the 2018 CHI Conference on Human Factors in Computing Systems, ACM. https://doi.org/10.1145/3173574.3173576 65 | -------------------------------------------------------------------------------- /TIPA_library/main/data_preparation.py: -------------------------------------------------------------------------------- 1 | # TIPA (Thermal Imaging-based Physiological and Affective computing) open-source project 2 | 3 | ## Author(s): Dr. Youngjun Cho*(Assistant Professor, UCL Computer Science) 4 | ## * http://youngjuncho.com 5 | 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | from ipywidgets import widgets 9 | from TIPA_library.main.thermal_image_processing import optimal_quantization_range as oq_range 10 | 11 | class data_preparation_TIPA_protocol: 12 | def __init__(self, file_name, known_height, known_width): 13 | self.frame_height = known_height 14 | self.frame_width = known_width 15 | self.file = np.fromfile(file_name, dtype='uint16') 16 | self.file = np.reshape(self.file, [-1, self.frame_height*self.frame_width + 4])# 17 | self.file = (np.transpose(self.file).astype('uint16')) 18 | 19 | self.tracked_matrix=[] 20 | self.thermal_matrix = self.get_thermal_matrix(self.file) 21 | self.time_stamp = self.get_time_stamp(self.file) 22 | self.time_stamp[0]=0 23 | 24 | def get_thermal_matrix(self, file): 25 | # assigning pixels to image-frame matrix 'img_frames' 26 | num_frames = np.size(file, 1) 27 | file_segment=file[0:self.frame_height*self.frame_width, 0:num_frames]; 28 | img_frames = np.reshape(file_segment[0:self.frame_height*self.frame_width, :], [self.frame_height, self.frame_width, num_frames]) 29 | #img_frames = np.reshape(file[0:320*240, :], [frame_height, frame_width, num_frames]) 30 | img_frames = (img_frames - 27315) / 100 # celcius 31 | return img_frames 32 | 33 | 34 | def get_time_stamp(self, file): 35 | # getting time array from the last two rows of read-in file 36 | pre_time_arr = file[self.frame_height*self.frame_width+2:, :] 37 | pre_time_arr_high = (np.round(pre_time_arr[:, :]) & 255) << 8 38 | pre_time_arr_low = np.round(pre_time_arr[:, :]) >> 8 39 | time_stamp = (pre_time_arr_high + pre_time_arr_low).astype('uint32') 40 | new_time_stamp = ((np.round(time_stamp[0, :]) << 16) + time_stamp[1, :]) / 1000 41 | return new_time_stamp 42 | 43 | 44 | # To show thermal 2D sequences interactively 45 | def interactive_imshow_cond(self, frame_number): 46 | plt.figure(2) 47 | plt.imshow(self.thermal_matrix[:,:,frame_number], cmap=plt.get_cmap('hot')) 48 | plt.colorbar() 49 | # plt.show() 50 | 51 | def interactive_imshow_cond2(self, frame_number, thermal_range): 52 | plt.figure(2) 53 | plt.imshow(self.thermal_matrix[:,:,frame_number], cmap=plt.get_cmap('hot'), vmin=thermal_range[0], vmax=thermal_range[1]) 54 | plt.colorbar() 55 | # plt.show() 56 | 57 | def interactive_imshow_cond3(self, frame_number): 58 | plt.figure(2) 59 | min_T, max_T=oq_range(self.thermal_matrix[:,:,frame_number]) 60 | plt.imshow(self.thermal_matrix[:,:,frame_number], cmap=plt.get_cmap('hot'), vmin=min_T, vmax=max_T) 61 | plt.colorbar() 62 | # plt.show() 63 | 64 | def interactive_imshow_cond4(self, frame_number): 65 | plt.figure(2) 66 | plt.imshow(self.tracked_matrix[:,:,frame_number], cmap=plt.get_cmap('hot')) 67 | plt.colorbar() 68 | # plt.show() 69 | 70 | class data_preparation_raw_matrix: 71 | def __init__(self, matrix, framerate): 72 | self.frame_height = matrix.shape[0] 73 | self.frame_width = matrix.shape[1] 74 | self.frame_length = matrix.shape[2] 75 | self.thermal_matrix = matrix 76 | self.time_stamp = np.arange(0,self.frame_length/framerate,1/framerate) -------------------------------------------------------------------------------- /TIPA_library/main/thermal_image_processing.py: -------------------------------------------------------------------------------- 1 | # TIPA (Thermal Imaging-based Physiological and Affective computing) open-source project 2 | 3 | ## Author(s): Dr. Youngjun Cho*(Assistant Professor, UCL Computer Science) 4 | ## * http://youngjuncho.com 5 | 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | import copy 9 | import cv2 10 | from packaging import version 11 | 12 | ''' Optimal Quantization: Optimal Quantization technique adaptively constructs a color mapping of absolute temperature to improve segmentation, classification and tracking 13 | 14 | Reference: Cho, Y., Julier, S. J., Marquardt, N., & Bianchi-Berthouze, N. (2017). Robust tracking of respiratory rate in high-dynamic range scenes using mobile thermal imaging. Biomedical optics express, 8(10), 4480-4503. 15 | ''' 16 | # output = optimal_quantization(mat, True) 17 | def optimal_quantization(t2d_data_original, print_mode = False): 18 | 19 | t2d_data= np.copy(t2d_data_original) 20 | 21 | vector_data= np.reshape(t2d_data, t2d_data.shape[0]*t2d_data.shape[1]) 22 | min_T=np.percentile(vector_data, 2.5) #2.5 percentile point 23 | max_T=np.percentile(vector_data, 97.5) #97.5 percentile point 24 | 25 | t2d_data[np.where(t2d_data > max_T)]=max_T 26 | t2d_data[np.where(t2d_data < min_T)]=min_T 27 | 28 | opt_T=min_T 29 | count=0 30 | while True: 31 | count+=1 32 | mean_back=np.mean(t2d_data[np.where(t2d_data <= opt_T)]) 33 | mean_obj=np.mean(t2d_data[np.where(t2d_data > opt_T)]) 34 | if np.abs(opt_T - (mean_back+mean_obj)/2 )<0.005: 35 | break; 36 | else: 37 | opt_T=(mean_back+mean_obj)/2 38 | 39 | min_T=opt_T 40 | 41 | if print_mode: 42 | print("optimal thermal range is [%f, %f]"%(min_T,max_T)) 43 | 44 | t2d_data[np.where(t2d_data < min_T)]=min_T 45 | 46 | quantized_t_img=255*(t2d_data-min_T)/(max_T-min_T) 47 | quantized_t_img = quantized_t_img.astype(np.uint8) 48 | 49 | return quantized_t_img 50 | 51 | def optimal_quantization_range(t2d_data): 52 | 53 | vector_data= np.reshape(t2d_data, t2d_data.shape[0]*t2d_data.shape[1]) 54 | min_T=np.percentile(vector_data, 2.5) #2.5 percentile point 55 | max_T=np.percentile(vector_data, 97.5) #97.5 percentile point 56 | 57 | t2d_data[np.where(t2d_data > max_T)]=max_T 58 | t2d_data[np.where(t2d_data < min_T)]=min_T 59 | 60 | opt_T=min_T 61 | count=0 62 | while True: 63 | count+=1 64 | mean_back=np.mean(t2d_data[np.where(t2d_data <= opt_T)]) 65 | mean_obj=np.mean(t2d_data[np.where(t2d_data > opt_T)]) 66 | if np.abs(opt_T - (mean_back+mean_obj)/2 )<0.005: 67 | break; 68 | else: 69 | opt_T=(mean_back+mean_obj)/2 70 | 71 | min_T=opt_T 72 | 73 | return min_T,max_T 74 | 75 | ''' 76 | This is the classical quantization method using fixed points (temperature range of interest). 77 | ''' 78 | # output = nonoptimal_quantization(mat, T1,Tk, True) 79 | def nonoptimal_quantization(t2d_data, min_T, max_T, print_mode = False): 80 | if print_mode: 81 | print("your fixed thermal range of interest is [%f, %f]"%(min_T,max_T)) 82 | 83 | quantized_t_img= np.copy(t2d_data) 84 | 85 | quantized_t_img[np.where(quantized_t_img > max_T)]=max_T 86 | quantized_t_img[np.where(quantized_t_img < min_T)]=min_T 87 | 88 | quantized_t_img=255*(quantized_t_img-min_T)/(max_T-min_T) 89 | quantized_t_img = quantized_t_img.astype(np.uint8) 90 | 91 | return quantized_t_img 92 | 93 | 94 | 95 | 96 | ''' Quantization-enabled thermal image tracking 97 | 98 | Reference [1]: Cho, Y., Julier, S. J., Marquardt, N., & Bianchi-Berthouze, N. (2017). Robust tracking of respiratory rate in high-dynamic range scenes using mobile thermal imaging. Biomedical optics express, 8(10), 4480-4503. 99 | Reference [2]: Cho, Y., Julier, S. J., & Bianchi-Berthouze, N. (2019). Instant Stress: Detection of Perceived Mental Stress Through Smartphone Photoplethysmography and Thermal Imaging. JMIR mental health, 6(4), e10140. 100 | Reference [3]: Cho, Y., Bianchi-Berthouze, N., Oliveira, M., Holloway, C., & Julier, S. (2019). Nose Heat: Exploring Stress-induced Nasal Thermal Variability through Mobile Thermal Imaging. In 2019 eighth International Conference on Affective Computing and Intelligent Interaction (ACII). IEEE. 101 | ''' 102 | 103 | ## eg. ROI_seq, tracked_img=thermal_tracker(data.thermal_matrix, 'MEDIANFLOW', False) 104 | def thermal_tracker(mat_3d, quantization_method='optimal', im_tracker_name = 'TLD', print_mode=False, nonfixed_ROI_size=True, img_viewer=True, min_T=28, max_T=38): 105 | 106 | # If using a version of openCV prior to 4.5.1, include all standard 107 | # trackers 108 | if version.parse(cv2.__version__) < version.parse("4.5.1"): 109 | trackers = {"TLD": cv2.TrackerTLD_create, 110 | "MEDIANFLOW": cv2.TrackerMedianFlow_create, 111 | "GOTURN": cv2.TrackerGOTURN_create, 112 | "MOSSE": cv2.TrackerMOSSE_create, 113 | "CSRT": cv2.TrackerCSRT_create, 114 | "BOOSTING": cv2.TrackerBoosting_create, 115 | "MIL": cv2.TrackerMIL_create, 116 | "KCF": cv2.TrackerKCF_create} 117 | # If using a more recent version of OpenCV include legacy trackers 118 | else: 119 | trackers = {"TLD": cv2.legacy.TrackerTLD_create, 120 | "MEDIANFLOW": cv2.legacy.TrackerMedianFlow_create, 121 | "GOTURN": cv2.TrackerGOTURN_create, 122 | "MOSSE": cv2.legacy.TrackerMOSSE_create, 123 | "CSRT": cv2.TrackerCSRT_create, 124 | "BOOSTING": cv2.legacy.TrackerBoosting_create, 125 | "MIL": cv2.TrackerMIL_create, 126 | "KCF": cv2.TrackerKCF_create} 127 | # If using a version of OpenCV newer than 4.5.2 then include the 128 | # DaSiamRPN tracker 129 | if version.parse(cv2.__version__) > version.parse("4.5.2"): 130 | trackers["DASIAMRPN"] = cv2.TrackerDaSiamRPN_create 131 | 132 | try: 133 | im_tracker = trackers[im_tracker_name]() 134 | except KeyError as E: 135 | print("Tracker could not be found, please check it is included in " 136 | "your version of OpenCV!") 137 | raise E 138 | greyscale = True 139 | if im_tracker_name in ["GOTURN", "BOOSTING", "DASIAMRPN"]: 140 | greyscale = False 141 | 142 | temp_mat = copy.deepcopy(mat_3d[:,:,0]) 143 | tracked_imgs = np.zeros(mat_3d.shape,np.uint8) 144 | 145 | if quantization_method == 'optimal': 146 | tracked_imgs[:,:,0] = optimal_quantization(temp_mat, print_mode) 147 | elif quantization_method == 'non-optimal': 148 | tracked_imgs[:,:,0] = nonoptimal_quantization(temp_mat, min_T, max_T, print_mode) 149 | 150 | 151 | # ROI selection 152 | mROI = np.zeros((4, mat_3d.shape[2]), dtype=int) 153 | mROI[:,0] = cv2.selectROI('ROI selection', tracked_imgs[:,:,0], False) 154 | # mROI.shape 155 | cv2.destroyWindow('ROI selection') 156 | 157 | # cv2.namedWindow('TrackViewer') 158 | 159 | # Initialization of a tracker 160 | 161 | if greyscale: 162 | out = im_tracker.init(tracked_imgs[:,:,0], tuple(mROI[:,0])) 163 | else: 164 | stacked_img = np.stack((tracked_imgs[:, :, 0],) * 3, axis=-1) 165 | out = im_tracker.init(stacked_img, tuple(mROI[:, 0])) 166 | 167 | 168 | for i in range(1,mat_3d.shape[2]): 169 | if np.mod(i,20)==0: 170 | print("frame: %d"%i) 171 | temp_mat = copy.deepcopy(mat_3d[:,:,i]) 172 | current_frame = np.zeros(tracked_imgs[:,:,0].shape) 173 | 174 | if quantization_method == 'optimal': 175 | current_frame = optimal_quantization(temp_mat, print_mode) 176 | elif quantization_method == 'non-optimal': 177 | current_frame = nonoptimal_quantization(temp_mat, min_T, max_T, print_mode) 178 | 179 | 180 | # Updating the tracker 181 | if nonfixed_ROI_size: 182 | if greyscale: 183 | result, mROI[:,i] = im_tracker.update(current_frame) 184 | else: 185 | stacked_img = np.stack((current_frame,) * 3, axis=-1) 186 | result, mROI[:, i] = im_tracker.update(stacked_img) 187 | else: 188 | if greyscale: 189 | result, bbox = im_tracker.update(current_frame) 190 | else: 191 | stacked_img = np.stack((current_frame,) * 3, axis=-1) 192 | result, bbox = im_tracker.update(stacked_img) 193 | 194 | mROI[2,i]=mROI[2,0] 195 | mROI[3,i]=mROI[3,0] 196 | mROI[0,i]=bbox[0]+np.round(bbox[2]/2) -np.round(mROI[2,0]/2) 197 | mROI[1,i]=bbox[1]+np.round(bbox[3]/2) -np.round(mROI[3,0]/2) 198 | 199 | 200 | # Visualise the tracked ROI 201 | if result==True: 202 | point1 = (int(mROI[0,i]), int(mROI[1,i])) 203 | point2 = (int(mROI[0,i] + mROI[2,i]), int(mROI[1,i] + mROI[3,i])) 204 | 205 | cv2.rectangle(current_frame, point1, point2, (255,255,0), 3, 0) 206 | tracked_imgs[:,:,i]=current_frame 207 | else : 208 | print("tracking error occurred at %d frame"%(i)) 209 | tracked_imgs[:,:,i]=current_frame 210 | 211 | if img_viewer: 212 | cv2.imshow('TrackViewer',current_frame) 213 | # Exit if ESC pressed 214 | k = cv2.waitKey(1) & 0xff 215 | if k == 27 : break 216 | 217 | # cv2.namedWindow('TrackViewer') 218 | # count=0 219 | # while True: 220 | # cv2.imshow('TrackViewer',tracked_imgs[:,:,count]) 221 | # if count=window_size: 20 | matrix_list.append(data[:,int(round(start+0.01)):int(round(end+0.01))]) 21 | start += overlap_rate1*window_size#start and end overlap 1 22 | end += overlap_rate1*window_size 23 | remain -= overlap_rate1*window_size 24 | return matrix_list 25 | -------------------------------------------------------------------------------- /TIPA_library/utils/overlap_windows.py: -------------------------------------------------------------------------------- 1 | ## Contributors 2 | ## Dr. Youngjun Cho (Assistant Professor, UCL Computer Science), Chang Liu (MSc in Machine Learning, UCL Computer Science) 3 | 4 | 5 | ''' 6 | Signal processing toolkits for Respiration Variability Spectrogram 7 | 8 | [Reference] 9 | Cho, Y., Bianchi-Berthouze, N. and Julier, S.J., 2017. 10 | DeepBreath: Deep learning of breathing patterns for automatic stress recognition using low-cost thermal imaging in unconstrained settings. In 2017 Seventh International Conference on Affective Computing and Intelligent Interaction (ACII) (pp. 456-463). IEEE. https://doi.org/10.1109/ACII.2017.8273639 11 | ''' 12 | 13 | def overlap_windows(data,overlap_rate,window_size): 14 | window_list = [] 15 | start = 0 16 | end = window_size 17 | remain_length = len(data) 18 | 19 | while remain_length>=window_size: 20 | window_list.append(data[int(round(start+0.01)):int(round(end+0.01))]) 21 | start += overlap_rate*window_size#start and end overlap 1 22 | end += overlap_rate*window_size 23 | remain_length -= overlap_rate*window_size 24 | return window_list 25 | -------------------------------------------------------------------------------- /TIPA_library/utils/pca_basic.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from sklearn.decomposition import PCA 3 | 4 | 5 | ''' This is an example code for PCA projection 6 | ''' 7 | def pca_basic(t2d_data_sequence): 8 | # pca_input: 2184*(240*320) 9 | 10 | t2d_data_height = t2d_data_sequence.shape[0] 11 | t2d_data_width = t2d_data_sequence.shape[1] 12 | t2d_data_sequence = t2d_data_sequence.reshape((t2d_data_height*t2d_data_width, np.size(t2d_data_sequence, 2))) 13 | t2d_data_sequence = np.transpose(t2d_data_sequence) 14 | 15 | # Eigenfaces 16 | output_pca = PCA(n_components=0.95) 17 | 18 | # PCA projection 19 | _ = output_pca.fit(t2d_data_sequence) 20 | t2d_data_sequence = output_pca.transform(t2d_data_sequence) 21 | 22 | # Eigenfaces 23 | eigen_faces = output_pca.components_ 24 | var_percent = output_pca.explained_variance_ratio_ 25 | 26 | return eigen_faces, var_percent -------------------------------------------------------------------------------- /TIPA_library/utils/rvs.py: -------------------------------------------------------------------------------- 1 | ## Contributors 2 | ## Dr. Youngjun Cho (Assistant Professor, UCL Computer Science), Chang Liu (MSc in Machine Learning, UCL Computer Science) 3 | ### currently, this codes needs to be debugged. 4 | 5 | 6 | import numpy as np 7 | from TIPA_library.utils import overlap_windows, overlap_matrix, gausswin, compute_frequency_grid 8 | from scipy import signal 9 | 10 | ''' 11 | Signal processing toolkits for Respiration Variability Spectrogram 12 | 13 | [Reference] 14 | Cho, Y., Bianchi-Berthouze, N. and Julier, S.J., 2017. 15 | DeepBreath: Deep learning of breathing patterns for automatic stress recognition using low-cost thermal imaging in unconstrained settings. In 2017 Seventh International Conference on Affective Computing and Intelligent Interaction (ACII) (pp. 456-463). IEEE. https://doi.org/10.1109/ACII.2017.8273639 16 | ''' 17 | # This code does have an issue with the limited resolution of outputs from scipy.signal.periodogram. (needs to improve) 18 | 19 | def rvs(Fss,x0):#t0: 20 | Tmax = 20 21 | step_length = Fss*1 22 | lag = Tmax*Fss 23 | window_list = overlap_windows.overlap_windows(x0,0.05,lag) 24 | #time_list = overlap_windows(t0,0.05,lag) 25 | s_PSD_1 = [] 26 | for i in range(len(window_list)): 27 | data_use = window_list[i] 28 | data_info = data_use.copy() 29 | #time_info = time_list[i] 30 | max_data = max(data_info) 31 | min_data = min(data_info) 32 | for j in range(len(data_info)): 33 | data_info[j] = (data_info[j]-min_data)/(max_data-min_data) 34 | 35 | filterN = 3 36 | Wn1=0.1 37 | Wn2=0.85 38 | Fn=Fss/2 39 | filter_b, filter_a = scipy.signal.ellip(filterN,1,2,[Wn1/Fn,Wn2/Fn],btype='bandpass') 40 | filtered_featurescaled_data = scipy.signal.lfilter(filter_b,filter_a,data_info,axis=0) 41 | w = gausswin.gausswin(5120) 42 | gaussian_final_window = np.array(filtered_featurescaled_data*(w.T),dtype='float64') 43 | ####### 44 | freq_index,freq_amplitude = scipy.signal.periodogram(gaussian_final_window,Fss) 45 | # freqs = compute_frequency_grid(oversampling=10) 46 | # ang_freqs = 2 * np.pi * freqs 47 | # #t = list(t) 48 | # freq_amplitude = scipy.signal.lombscargle(time_info,gaussian_final_window,ang_freqs) 49 | 50 | #STACK PSD IN SLIDING WNNDOW 51 | s_PSD_1.append(freq_amplitude) 52 | s_PSD_1 = np.array(s_PSD_1).T 53 | # ss_PSD_1=s_PSD_1[1:19,:] 54 | #return ss_PSD_1 55 | # seperate_data = overlap_matrix.overlap_matrix(ss_PSD_1,1/120,120) 56 | return s_PSD_1 -------------------------------------------------------------------------------- /TIPA_library/utils/simpleDRQ.py: -------------------------------------------------------------------------------- 1 | # TIPA (Thermal Imaging-based Physiological and Affective computing) open-source project 2 | 3 | ## Author(s): Dr. Youngjun Cho*(Assistant Professor, UCL Computer Science) 4 | ## * http://youngjuncho.com 5 | 6 | import numpy as np 7 | import cv2 8 | 9 | ''' 10 | Simple Dynamic-Range Quantisation. 11 | 12 | [Reference] 13 | Cho, Y., Bianchi-Berthouze, N., Marquardt, N. and Julier, S.J., 2018. Deep Thermal Imaging: Proximate Material Type Recognition in the Wild through Deep Learning of Spatial Surface Temperature Patterns. In Proceedings of the 2018 CHI Conference on Human Factors in Computing Systems, ACM. https://doi.org/10.1145/3173574.3173576 14 | ''' 15 | def simpleDRQ(t2d_data, desired_height, desired_width): 16 | 17 | (height, width) = t2d_data.shape 18 | 19 | x_start = int((height - width * 0.925) / 2 + 1) 20 | x_end = int((height + width * 0.925) / 2) 21 | y_start = int((width - width * 0.925) / 2 + 1) 22 | y_end = int((width + width * 0.925) / 2) 23 | t2d_data = t2d_data[x_start:x_end, y_start:y_end] 24 | 25 | [adjusted_h, adjusted_w] = t2d_data.shape 26 | 27 | data = np.zeros((leng, 60, 60)) 28 | 29 | 30 | temp_A = t2d_data 31 | mmin = temp_A.min() 32 | mmax = temp_A.max() 33 | for cho1 in range(0, adjusted_w): 34 | for cho2 in range(0, adjusted_h): 35 | temp_A[cho2, cho1] = (temp_A[cho2, cho1] - mmin) / (mmax - mmin) 36 | 37 | return cv2.resize(temp_A, (desired_height, desired_width)) 38 | -------------------------------------------------------------------------------- /TIPA_library/utils/timshow.py: -------------------------------------------------------------------------------- 1 | # TIPA (Thermal Imaging-based Physiological and Affective computing) open-source project 2 | 3 | ## Author(s): Dr. Youngjun Cho*(Assistant Professor, UCL Computer Science) 4 | ## * http://youngjuncho.com 5 | 6 | 7 | # from ipywidgets import interact, interactive, fixed, interact_manual 8 | # import ipywidgets as widgets 9 | import matplotlib.pyplot as plt 10 | 11 | ## for python jupyter users, type %matplotlib notebook before calling this. 12 | # Thermal image show. 13 | def timshow(matrix): 14 | fig= plt.figure() 15 | ax = fig.add_subplot(1,1,1) 16 | plt.imshow(matrix, cmap=plt.get_cmap('hot')) 17 | plt.colorbar() 18 | 19 | 20 | -------------------------------------------------------------------------------- /data/readme.md: -------------------------------------------------------------------------------- 1 | Place your data here. 2 | -------------------------------------------------------------------------------- /figures/figure1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepneuroscience/TIPA/c14bbd3355be38b5f8e48f9ba1497ac8aea62032/figures/figure1.png -------------------------------------------------------------------------------- /figures/figure2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepneuroscience/TIPA/c14bbd3355be38b5f8e48f9ba1497ac8aea62032/figures/figure2.png -------------------------------------------------------------------------------- /figures/figure3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepneuroscience/TIPA/c14bbd3355be38b5f8e48f9ba1497ac8aea62032/figures/figure3.png -------------------------------------------------------------------------------- /figures/readme.md: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------