From 264a7e47df3cc8ec86e35edacf61aa5a4b4a634a Mon Sep 17 00:00:00 2001 From: Rylan Larsen Date: Tue, 27 Aug 2019 15:28:57 -0700 Subject: [PATCH] Python 3 compatibility --- corticalmapping.egg-info/PKG-INFO | 20 + corticalmapping.egg-info/SOURCES.txt | 38 + corticalmapping.egg-info/dependency_links.txt | 1 + corticalmapping.egg-info/requires.txt | 8 + corticalmapping.egg-info/top_level.txt | 1 + corticalmapping/.gitignore | 58 + corticalmapping/DatabaseTools.py | 108 +- corticalmapping/HighLevel.py | 680 ++- corticalmapping/MotionCorrection.py | 336 ++ corticalmapping/NwbTools.py | 2270 +++------- corticalmapping/README.md | 6 + corticalmapping/RetinotopicMapping.py | 2504 ++++++----- corticalmapping/SingleCellAnalysis.py | 2294 ++--------- corticalmapping/VasculatureMapMatching.py | 34 +- corticalmapping/VisualStim.py | 234 +- corticalmapping/core/FileTools.py | 197 +- corticalmapping/core/ImageAnalysis.py | 580 +-- corticalmapping/core/PlottingTools.py | 111 +- corticalmapping/core/TimingAnalysis.py | 220 +- corticalmapping/core/tifffile.py | 3668 +++++++++++++++++ corticalmapping/ephys/KilosortWrapper.py | 36 +- corticalmapping/ephys/OpenEphysWrapper.py | 72 +- .../0000_delete_stas_and_repack.py | 71 - .../analysis_database/0010_regenerate_stas.py | 65 - .../analysis_database/0030_test_sigle_strf.py | 50 - .../0040_get_roi_metadata.py | 414 -- .../analysis_database/0041_get_plane_dfs.py | 427 -- .../analysis_database/0042_get_plane_meta.py | 32 - .../0050_generate_page_report.py | 50 - .../0060_single_page_report.py | 30 - .../analysis_database/0070_get_rf_maps.py | 194 - .../0080_plot_plane_rf_center.py | 262 -- .../00_old/analysis_database/h5repack.exe | Bin 1877504 -> 0 bytes .../000_reorganize_data.py | 95 - .../001_get_vasculature_map.py | 37 - .../010_motion_correction.py | 66 - .../020_downsample_from_server.py | 42 - .../030_get_movie_data.py | 65 - ...045_get_mmap_files_for_caiman_from_tiff.py | 54 - .../050_show_mmap_movie.py | 39 - .../060_caiman_segmentation.py | 116 - .../070_generate_nwb.py | 48 - .../071_add_vasmaps.py | 27 - .../080_add_sync_data.py | 23 - .../090_add_image_data.py | 52 - .../110_add_motion_correction_module.py | 56 - ...add_rois_and_traces_caiman_segmentation.py | 162 - ..._add_visual_stimuli_retinotopic_mapping.py | 15 - .../135_get_photodiode_onset_timestamps.py | 45 - .../140_analyze_analog_photodiode_onsets.py | 39 - .../140_analyze_digital_photodiode_onsets.py | 39 - .../150_get_STRFs.py | 96 - ...60_get_drifting_grating_response_tables.py | 18 - .../170_plot_STRFs.py | 61 - .../180_plot_zscore_RFs.py | 68 - .../190_plot_RF_contours.py | 112 - .../200_plot_dgc_response_all.py | 160 - .../210_plot_dgc_response_mean.py | 157 - .../220_plot_dgc_tuning_curves.py | 196 - .../within_plane_folder/040_get_cells_file.py | 87 - .../within_plane_folder/050_refine_cells.py | 173 - .../060_get_weighted_rois_and_surrounds.py | 122 - .../070_get_raw_center_and_surround_traces.py | 129 - .../090_get_neuropil_subtracted_traces.py | 101 - .../120_check_correlation.py | 99 - .../old/100_get_dff_traces.py | 22 - .../000_reorganize_data.py | 65 - .../001_get_vasculature_map.py | 37 - .../010_motion_correction.py | 46 - .../020_downsample_from_server.py | 45 - .../030_get_movie_data.py | 65 - ...045_get_mmap_files_for_caiman_from_tiff.py | 54 - .../050_show_mmap_movie.py | 39 - .../060_caiman_segmentation.py | 116 - .../070_generate_nwb.py | 48 - .../071_add_vasmaps.py | 36 - .../080_add_sync_data.py | 23 - .../090_add_image_data.py | 53 - .../110_add_motion_correction_module.py | 56 - ...add_rois_and_traces_caiman_segmentation.py | 162 - ..._add_visual_stimuli_retinotopic_mapping.py | 15 - .../135_get_photodiode_onset_timestamps.py | 45 - .../140_analyze_analog_photodiode_onsets.py | 39 - .../140_analyze_digital_photodiode_onsets.py | 39 - .../150_get_STRFs.py | 96 - ...60_get_drifting_grating_response_tables.py | 18 - .../170_plot_STRFs.py | 61 - .../180_plot_zscore_RFs.py | 68 - .../190_plot_RF_contours.py | 112 - .../200_plot_dgc_response_all.py | 160 - .../210_plot_dgc_response_mean.py | 157 - .../220_plot_dgc_tuning_curves.py | 196 - .../old/040_get_mmap_files_for_caiman.py | 64 - .../within_plane_folder/040_get_cells_file.py | 87 - .../within_plane_folder/050_refine_cells.py | 173 - .../060_get_weighted_rois_and_surrounds.py | 122 - .../070_get_raw_center_and_surround_traces.py | 129 - .../090_get_neuropil_subtracted_traces.py | 101 - .../120_check_correlation.py | 99 - .../old/100_get_dff_traces.py | 22 - .../000_reorganize_data.py | 30 - .../010_motion_correction.py | 53 - .../020_downsample.py | 21 - .../030_downsample_from_server.py | 30 - .../010_get_wf_vas_maps.py | 33 - .../020_rotate_2P_vas_maps.py | 38 - .../030_get_2p_vas_maps.py | 34 - .../040_reorganize_2P_movies.py | 61 - .../050_motion_correction.py | 41 - .../055_downsample_from_server.py | 34 - .../060_get_image_data.py | 40 - .../070_get_mmap_files_for_caiman_from_tif.py | 48 - .../090_show_mmap_movie.py | 35 - .../110_caiman_segmentation.py | 103 - .../120_get_cells_file.py | 87 - .../130_refine_cells.py | 173 - .../140_get_weighted_rois_and_surrounds.py | 122 - .../150_get_raw_center_and_surround_traces.py | 129 - .../160_get_neuropil_subtracted_traces.py | 101 - .../180_check_correlation.py | 99 - .../200_generate_nwb.py | 48 - .../210_add_vasmap.py | 38 - .../220_add_sync_data.py | 23 - .../230_add_image_data.py | 43 - .../240_add_motion_correction_module.py | 42 - .../250_get_photodiode_onset.py | 45 - ..._add_visual_stimuli_retinotopic_mapping.py | 15 - .../270_analyze_photodiode_onsets.py | 38 - ...add_rois_and_traces_caiman_segmentation.py | 154 - .../290_get_STRFs.py | 90 - .../310_plot_STRFs.py | 61 - .../320_plot_zscore_RFs.py | 68 - .../330_plot_RF_contours.py | 112 - .../old/120_get_cells_file.py | 76 - .../old/170_get_dff_traces.py | 22 - .../000_reorganized_data.py | 37 - .../005_remove_correction_files.py | 31 - .../010_motion_correction_zstack_caiman.py | 128 - .../020_get_images.py | 27 - .../030_motion_correction_cross_steps.py | 48 - .../040_get_fine_zstack.py | 53 - .../050_get_2p_vas_maps.py | 19 - .../060_get_wf_vas_maps.py | 35 - .../old/010_motion_correction.py | 49 - .../batch_analyzeFlashingCircle.py | 6 +- ...0_RetinotopicMappingAnalysisTemplate.ipynb | 0 ...1_RetinotopicMappingAnalysisTemplate.ipynb | 0 ...7_RetinotopicMappingAnalysisTemplate.ipynb | 0 ...pingAnalysisTemplate-ManualFileEntry.ipynb | 0 .../RetinotopicMapping/Thumbs.db | Bin 0 -> 74240 bytes .../batch_MarkPatches.py | 0 .../batch_PatchName.py | 4 +- .../batch_analyzeRetinotopicMapping_1.py | 18 +- .../batch_analyzeRetinotopicMapping_2.py | 0 .../batch_analyzeRetinotopicMapping_3.py | 0 .../analysis_database/0041_get_plane_dfs.py | 427 -- .../0042_get_plane_dfs_single.py | 432 -- .../analysis_database/0043_get_plane_meta.py | 40 - .../0044_reorganize_plane_meta.py | 19 - .../analysis_database/0070_get_rf_maps.py | 247 -- .../0075_get_rf_maps_axon.py | 257 -- .../0080_plot_plane_rf_center.py | 262 -- .../0090_group_boutons_into_axons.py | 70 - .../analysis_database/0100_add_axon_strf.py | 31 - .../analysis_database/0110_add_axon_dgcrm.py | 31 - .../0120_get_axon_plane_df.py | 413 -- .../analysis_database/0130_get_overall_csv.py | 54 - .../0140_plot_dire_retinotopy.py | 107 - .../0150_plot_ori_vs_rf_axis_DS.py | 139 - .../0160_plot_ori_vs_rf_axis_OS.py | 139 - .../0170_two_way_anova_dsi.py | 90 - .../0180_two_way_anova_rf_area.py | 79 - .../0190_get_axon_morphology.py | 54 - .../analysis_database/h5repack.exe | Bin 1877504 -> 0 bytes .../old/0041_get_plane_dfs.py | 427 -- .../old/0042_get_plane_dfs_single.py | 432 -- .../old/0043_get_plane_meta.py | 40 - .../old/0044_reorganize_plane_meta.py | 19 - .../analysis_database/old/0070_get_rf_maps.py | 247 -- .../old/0075_get_rf_maps_axon.py | 257 -- .../old/0080_plot_plane_rf_center.py | 262 -- .../old/0090_group_boutons_into_axons.py | 70 - .../old/0100_add_axon_strf.py | 31 - .../old/0110_add_axon_dgcrm.py | 31 - .../old/0120_get_axon_plane_df.py | 413 -- .../old/0130_get_overall_csv.py | 54 - .../old/0140_plot_dire_retinotopy.py | 107 - .../old/0150_plot_ori_vs_rf_axis_DS.py | 139 - .../old/0160_plot_ori_vs_rf_axis_OS.py | 139 - .../010_get_vasmap_2p.py | 67 - .../020_get_vasmap_wf_deepscope.py | 40 - .../030_get_vasmap_wf_sutter.py | 32 - .../01_get_thumbnail.py | 32 - .../02_get_section.py | 44 - ...nal)_check_deepscope_file_creation_time.py | 28 - ...000_(optional)_check_deepscope_filename.py | 25 - .../000_(optional)_plot_volume.py | 32 - .../000_reorganize_movie_2p_deepscope.py | 97 - .../000_reorganize_movie_2p_sutter.py | 98 - .../050_motion_correction.py | 71 - ...al)_reapply_motion_correction_deepscope.py | 44 - ...2_(optional)_downsample_corrected_files.py | 105 - .../055_downsample_from_server.py | 57 - .../060_get_image_data.py | 78 - .../070_get_hdf5_files_for_caiman_soma.py | 65 - .../070_get_mmap_files_for_caiman_bouton.py | 66 - .../110_caiman_segmentation_bouton.py | 126 - .../110_caiman_segmentation_soma.py | 109 - .../200_generate_nwb.py | 48 - .../analysis_pipeline_movie/210_add_vasmap.py | 48 - .../220_add_sync_data.py | 23 - ...optional)_check_2p_timestamps_deepscope.py | 27 - .../230_add_image_data.py | 69 - .../240_add_motion_correction_module.py | 61 - .../250_get_photodiode_onset.py | 45 - ..._add_visual_stimuli_retinotopic_mapping.py | 15 - .../270_analyze_photodiode_onsets.py | 43 - .../275_add_eyetracking.py | 68 - ...add_rois_and_traces_caiman_segmentation.py | 173 - .../300_get_response_tables.py | 24 - .../analysis_pipeline_movie/310_plot_STRFs.py | 65 - .../320_plot_zscore_RFs.py | 76 - .../330_plot_RF_contours.py | 113 - .../340_plot_dgc_response_all.py | 169 - .../350_plot_dgc_response_mean.py | 158 - .../360_plot_dgc_tuning_curves.py | 198 - .../batch_generate_nwb.bat | 16 - .../old/090_(optional)_show_mmap_movie.py | 35 - ...90_get_drifting_grating_response_tables.py | 18 - .../old/300_get_STRFs.py | 101 - .../old/310_plot_STRFs.py | 61 - .../old/320_plot_zscore_RFs.py | 72 - .../old/330_plot_RF_contours.py | 115 - .../120_get_cells_file_bouton.py | 87 - .../120_get_cells_file_soma.py | 81 - .../130_refine_cells_bouton.py | 173 - .../130_refine_cells_soma.py | 175 - .../135_generate_marked_avi.py | 132 - .../140_get_weighted_rois_and_surrounds.py | 127 - .../150_get_raw_center_and_surround_traces.py | 129 - .../160_get_neuropil_subtracted_traces.py | 120 - .../batch_processing_bouton.bat | 10 - .../batch_processing_soma.bat | 10 - .../000_reorganize_files.py | 54 - .../050_motion_correction_multichannel.py | 98 - .../060_apply_offsets_multichannel.py | 48 - .../065_get_uncorrected_zstack_from_server.py | 27 - .../070_get_zstack_from_server.py | 27 - .../080_remove_uncorrected_files.py | 26 - .../090_remove_corrected_files.py | 46 - .../095_split_channel.py | 18 - .../100_get_fine_zstack.py | 57 - .../110_rotate_zstack.py | 31 - .../120_get_depth_profile.py | 35 - .../130_combine_channels.py | 32 - .../old/caiman/000_reorganize_files.py | 55 - ...n_correction_zstack_caiman_multichannel.py | 107 - .../caiman/015_apply_offsets_multichannel.py | 51 - .../old/caiman/020_get_zstack_from_server.py | 26 - .../old/caiman/030_get_fine_zstack.py | 49 - .../old/caiman/040_combine_channels.py | 32 - .../old/caiman/050_get_2p_vas_maps.py | 37 - .../old/caiman/060_get_wf_vas_maps.py | 27 - .../old/deepscope/000_reorganize_files.py | 60 - .../old/scientifica/000_reorganize_files.py | 56 - .../sutter_regular_2p/000_reorganize_files.py | 55 - .../sutter_regular_2p/050_get_2p_vas_maps.py | 39 - .../sutter_regular_2p/060_get_wf_vas_maps.py | 31 - .../{00_old => }/batch_analyzeDetrend.py | 0 .../recording/batch_displayKSstimAllDir.py | 41 +- corticalmapping/setup.py | 74 + corticalmapping/test/data/test.hdf5 | Bin 12224 -> 12256 bytes corticalmapping/test/test_ImageAnalysis.py | 99 +- .../test/test_SingleCellAnalysis.py | 122 +- corticalmapping/test/test_TimingAnalysis.py | 18 +- 275 files changed, 7015 insertions(+), 27105 deletions(-) create mode 100644 corticalmapping.egg-info/PKG-INFO create mode 100644 corticalmapping.egg-info/SOURCES.txt create mode 100644 corticalmapping.egg-info/dependency_links.txt create mode 100644 corticalmapping.egg-info/requires.txt create mode 100644 corticalmapping.egg-info/top_level.txt create mode 100644 corticalmapping/.gitignore create mode 100644 corticalmapping/MotionCorrection.py create mode 100644 corticalmapping/README.md create mode 100644 corticalmapping/core/tifffile.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/analysis_database/0000_delete_stas_and_repack.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/analysis_database/0010_regenerate_stas.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/analysis_database/0030_test_sigle_strf.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/analysis_database/0040_get_roi_metadata.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/analysis_database/0041_get_plane_dfs.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/analysis_database/0042_get_plane_meta.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/analysis_database/0050_generate_page_report.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/analysis_database/0060_single_page_report.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/analysis_database/0070_get_rf_maps.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/analysis_database/0080_plot_plane_rf_center.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/analysis_database/h5repack.exe delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/000_reorganize_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/001_get_vasculature_map.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/010_motion_correction.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/020_downsample_from_server.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/030_get_movie_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/045_get_mmap_files_for_caiman_from_tiff.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/050_show_mmap_movie.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/060_caiman_segmentation.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/070_generate_nwb.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/071_add_vasmaps.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/080_add_sync_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/090_add_image_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/110_add_motion_correction_module.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/120_add_rois_and_traces_caiman_segmentation.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/130_add_visual_stimuli_retinotopic_mapping.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/135_get_photodiode_onset_timestamps.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/140_analyze_analog_photodiode_onsets.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/140_analyze_digital_photodiode_onsets.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/150_get_STRFs.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/160_get_drifting_grating_response_tables.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/170_plot_STRFs.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/180_plot_zscore_RFs.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/190_plot_RF_contours.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/200_plot_dgc_response_all.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/210_plot_dgc_response_mean.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/220_plot_dgc_tuning_curves.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/040_get_cells_file.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/050_refine_cells.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/060_get_weighted_rois_and_surrounds.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/070_get_raw_center_and_surround_traces.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/090_get_neuropil_subtracted_traces.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/120_check_correlation.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/old/100_get_dff_traces.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/000_reorganize_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/001_get_vasculature_map.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/010_motion_correction.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/020_downsample_from_server.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/030_get_movie_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/045_get_mmap_files_for_caiman_from_tiff.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/050_show_mmap_movie.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/060_caiman_segmentation.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/070_generate_nwb.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/071_add_vasmaps.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/080_add_sync_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/090_add_image_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/110_add_motion_correction_module.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/120_add_rois_and_traces_caiman_segmentation.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/130_add_visual_stimuli_retinotopic_mapping.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/135_get_photodiode_onset_timestamps.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/140_analyze_analog_photodiode_onsets.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/140_analyze_digital_photodiode_onsets.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/150_get_STRFs.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/160_get_drifting_grating_response_tables.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/170_plot_STRFs.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/180_plot_zscore_RFs.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/190_plot_RF_contours.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/200_plot_dgc_response_all.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/210_plot_dgc_response_mean.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/220_plot_dgc_tuning_curves.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/old/040_get_mmap_files_for_caiman.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/040_get_cells_file.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/050_refine_cells.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/060_get_weighted_rois_and_surrounds.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/070_get_raw_center_and_surround_traces.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/090_get_neuropil_subtracted_traces.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/120_check_correlation.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/old/100_get_dff_traces.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/000_reorganize_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/010_motion_correction.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/020_downsample.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/030_downsample_from_server.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/010_get_wf_vas_maps.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/020_rotate_2P_vas_maps.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/030_get_2p_vas_maps.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/040_reorganize_2P_movies.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/050_motion_correction.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/055_downsample_from_server.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/060_get_image_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/070_get_mmap_files_for_caiman_from_tif.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/090_show_mmap_movie.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/110_caiman_segmentation.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/120_get_cells_file.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/130_refine_cells.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/140_get_weighted_rois_and_surrounds.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/150_get_raw_center_and_surround_traces.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/160_get_neuropil_subtracted_traces.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/180_check_correlation.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/200_generate_nwb.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/210_add_vasmap.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/220_add_sync_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/230_add_image_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/240_add_motion_correction_module.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/250_get_photodiode_onset.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/260_add_visual_stimuli_retinotopic_mapping.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/270_analyze_photodiode_onsets.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/280_add_rois_and_traces_caiman_segmentation.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/290_get_STRFs.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/310_plot_STRFs.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/320_plot_zscore_RFs.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/330_plot_RF_contours.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/old/120_get_cells_file.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/old/170_get_dff_traces.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/000_reorganized_data.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/005_remove_correction_files.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/010_motion_correction_zstack_caiman.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/020_get_images.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/030_motion_correction_cross_steps.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/040_get_fine_zstack.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/050_get_2p_vas_maps.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/060_get_wf_vas_maps.py delete mode 100644 corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/old/010_motion_correction.py rename corticalmapping/scripts/post_recording/{00_old => }/FlashingCircle/batch_analyzeFlashingCircle.py (95%) rename corticalmapping/scripts/post_recording/{analysis_retinotopicmapping => RetinotopicMapping}/Retinotopic_Mapping_Analysis_Template/2015-10-30_RetinotopicMappingAnalysisTemplate.ipynb (100%) rename corticalmapping/scripts/post_recording/{analysis_retinotopicmapping => RetinotopicMapping}/Retinotopic_Mapping_Analysis_Template/2015-10-31_RetinotopicMappingAnalysisTemplate.ipynb (100%) rename corticalmapping/scripts/post_recording/{analysis_retinotopicmapping => RetinotopicMapping}/Retinotopic_Mapping_Analysis_Template/2015-11-17_RetinotopicMappingAnalysisTemplate.ipynb (100%) rename corticalmapping/scripts/post_recording/{analysis_retinotopicmapping => RetinotopicMapping}/Retinotopic_Mapping_Analysis_Template/2016-10-26_RetinotopicMappingAnalysisTemplate-ManualFileEntry.ipynb (100%) create mode 100644 corticalmapping/scripts/post_recording/RetinotopicMapping/Thumbs.db rename corticalmapping/scripts/post_recording/{analysis_retinotopicmapping => RetinotopicMapping}/batch_MarkPatches.py (100%) rename corticalmapping/scripts/post_recording/{analysis_retinotopicmapping => RetinotopicMapping}/batch_PatchName.py (91%) rename corticalmapping/scripts/post_recording/{analysis_retinotopicmapping => RetinotopicMapping}/batch_analyzeRetinotopicMapping_1.py (92%) rename corticalmapping/scripts/post_recording/{analysis_retinotopicmapping => RetinotopicMapping}/batch_analyzeRetinotopicMapping_2.py (100%) rename corticalmapping/scripts/post_recording/{analysis_retinotopicmapping => RetinotopicMapping}/batch_analyzeRetinotopicMapping_3.py (100%) delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0041_get_plane_dfs.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0042_get_plane_dfs_single.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0043_get_plane_meta.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0044_reorganize_plane_meta.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0070_get_rf_maps.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0075_get_rf_maps_axon.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0080_plot_plane_rf_center.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0090_group_boutons_into_axons.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0100_add_axon_strf.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0110_add_axon_dgcrm.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0120_get_axon_plane_df.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0130_get_overall_csv.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0140_plot_dire_retinotopy.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0150_plot_ori_vs_rf_axis_DS.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0160_plot_ori_vs_rf_axis_OS.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0170_two_way_anova_dsi.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0180_two_way_anova_rf_area.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/0190_get_axon_morphology.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/h5repack.exe delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0041_get_plane_dfs.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0042_get_plane_dfs_single.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0043_get_plane_meta.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0044_reorganize_plane_meta.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0070_get_rf_maps.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0075_get_rf_maps_axon.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0080_plot_plane_rf_center.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0090_group_boutons_into_axons.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0100_add_axon_strf.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0110_add_axon_dgcrm.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0120_get_axon_plane_df.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0130_get_overall_csv.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0140_plot_dire_retinotopy.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0150_plot_ori_vs_rf_axis_DS.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_database/old/0160_plot_ori_vs_rf_axis_OS.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/010_get_vasmap_2p.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/020_get_vasmap_wf_deepscope.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/030_get_vasmap_wf_sutter.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_histology_big_tiffs/01_get_thumbnail.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_histology_big_tiffs/02_get_section.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_check_deepscope_file_creation_time.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_check_deepscope_filename.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_plot_volume.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_reorganize_movie_2p_deepscope.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_reorganize_movie_2p_sutter.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/050_motion_correction.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/051_(optional)_reapply_motion_correction_deepscope.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/052_(optional)_downsample_corrected_files.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/055_downsample_from_server.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/060_get_image_data.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/070_get_hdf5_files_for_caiman_soma.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/070_get_mmap_files_for_caiman_bouton.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/110_caiman_segmentation_bouton.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/110_caiman_segmentation_soma.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/200_generate_nwb.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/210_add_vasmap.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/220_add_sync_data.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/221_(optional)_check_2p_timestamps_deepscope.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/230_add_image_data.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/240_add_motion_correction_module.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/250_get_photodiode_onset.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/260_add_visual_stimuli_retinotopic_mapping.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/270_analyze_photodiode_onsets.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/275_add_eyetracking.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/280_add_rois_and_traces_caiman_segmentation.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/300_get_response_tables.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/310_plot_STRFs.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/320_plot_zscore_RFs.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/330_plot_RF_contours.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/340_plot_dgc_response_all.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/350_plot_dgc_response_mean.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/360_plot_dgc_tuning_curves.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/batch_generate_nwb.bat delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/090_(optional)_show_mmap_movie.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/290_get_drifting_grating_response_tables.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/300_get_STRFs.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/310_plot_STRFs.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/320_plot_zscore_RFs.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/330_plot_RF_contours.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/120_get_cells_file_bouton.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/120_get_cells_file_soma.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/130_refine_cells_bouton.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/130_refine_cells_soma.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/135_generate_marked_avi.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/140_get_weighted_rois_and_surrounds.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/150_get_raw_center_and_surround_traces.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/160_get_neuropil_subtracted_traces.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/batch_processing_bouton.bat delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/batch_processing_soma.bat delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/000_reorganize_files.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/050_motion_correction_multichannel.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/060_apply_offsets_multichannel.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/065_get_uncorrected_zstack_from_server.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/070_get_zstack_from_server.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/080_remove_uncorrected_files.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/090_remove_corrected_files.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/095_split_channel.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/100_get_fine_zstack.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/110_rotate_zstack.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/120_get_depth_profile.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/130_combine_channels.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/000_reorganize_files.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/010_motion_correction_zstack_caiman_multichannel.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/015_apply_offsets_multichannel.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/020_get_zstack_from_server.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/030_get_fine_zstack.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/040_combine_channels.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/050_get_2p_vas_maps.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/060_get_wf_vas_maps.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/deepscope/000_reorganize_files.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/scientifica/000_reorganize_files.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/000_reorganize_files.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/050_get_2p_vas_maps.py delete mode 100644 corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/060_get_wf_vas_maps.py rename corticalmapping/scripts/post_recording/{00_old => }/batch_analyzeDetrend.py (100%) create mode 100644 corticalmapping/setup.py diff --git a/corticalmapping.egg-info/PKG-INFO b/corticalmapping.egg-info/PKG-INFO new file mode 100644 index 0000000..05a998d --- /dev/null +++ b/corticalmapping.egg-info/PKG-INFO @@ -0,0 +1,20 @@ +Metadata-Version: 2.1 +Name: corticalmapping +Version: 2.0.0 +Summary: cortical mapping tools +Home-page: http://stash.corp.alleninstitute.org/users/junz/repos/corticalmapping/ +Author: Jun Zhuang +Author-email: junz@alleninstitute.org +License: UNKNOWN +Description: corticalmapping + + by Jun Zhuang @ 2014 + + contains basic visual stimulation, imaging analysis, plotting, cell visual response properties analysis functionalities. + also contains a relatively mature code base of retinotopic mapping (visual stimulation and analysis) +Platform: any +Classifier: Programming Language :: Python +Classifier: Development Status :: 4 - Beta +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Provides-Extra: testing diff --git a/corticalmapping.egg-info/SOURCES.txt b/corticalmapping.egg-info/SOURCES.txt new file mode 100644 index 0000000..1eb2ef8 --- /dev/null +++ b/corticalmapping.egg-info/SOURCES.txt @@ -0,0 +1,38 @@ +README.md +setup.py +corticalmapping/CaimanTools.py +corticalmapping/CamstimTools.py +corticalmapping/DatabaseTools.py +corticalmapping/HighLevel.py +corticalmapping/MotionCorrection.py +corticalmapping/NwbTools.py +corticalmapping/ResponseAnalysis.py +corticalmapping/RetinotopicMapping.py +corticalmapping/SingleCellAnalysis.py +corticalmapping/VasculatureMapMatching.py +corticalmapping/VisualStim.py +corticalmapping/__init__.py +corticalmapping/setup.py +corticalmapping.egg-info/PKG-INFO +corticalmapping.egg-info/SOURCES.txt +corticalmapping.egg-info/dependency_links.txt +corticalmapping.egg-info/requires.txt +corticalmapping.egg-info/top_level.txt +corticalmapping/core/DataAnalysis.py +corticalmapping/core/FileTools.py +corticalmapping/core/ImageAnalysis.py +corticalmapping/core/PlottingTools.py +corticalmapping/core/TimingAnalysis.py +corticalmapping/core/__init__.py +corticalmapping/core/tifffile.py +corticalmapping/ephys/KilosortWrapper.py +corticalmapping/ephys/OpenEphysWrapper.py +corticalmapping/ephys/__init__.py +corticalmapping/ipython_lizard/__init__.py +corticalmapping/ipython_lizard/export_notebook_functions.py +corticalmapping/ipython_lizard/html_widgets.py +corticalmapping/ipython_lizard/ipython_filedialog.py +corticalmapping/ipython_lizard/patchplot_ipywidgets.py +corticalmapping/ipython_lizard/wrapped_retinotopic_mapping.py +corticalmapping/ipython_lizard/utils/__init__.py +corticalmapping/ipython_lizard/utils/progress_bar.py \ No newline at end of file diff --git a/corticalmapping.egg-info/dependency_links.txt b/corticalmapping.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/corticalmapping.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/corticalmapping.egg-info/requires.txt b/corticalmapping.egg-info/requires.txt new file mode 100644 index 0000000..23446c1 --- /dev/null +++ b/corticalmapping.egg-info/requires.txt @@ -0,0 +1,8 @@ +numpy +scipy +PyDAQmx +scikit-image +tifffile + +[testing] +pytest diff --git a/corticalmapping.egg-info/top_level.txt b/corticalmapping.egg-info/top_level.txt new file mode 100644 index 0000000..23ab74b --- /dev/null +++ b/corticalmapping.egg-info/top_level.txt @@ -0,0 +1 @@ +corticalmapping diff --git a/corticalmapping/.gitignore b/corticalmapping/.gitignore new file mode 100644 index 0000000..0be5d04 --- /dev/null +++ b/corticalmapping/.gitignore @@ -0,0 +1,58 @@ +# Created by .ignore support plugin (hsz.mobi) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +#*.iml +*.pyc + + +## Directory-based project format: +.idea/ +.cache/ + + +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + + +/corticalmapping/ipython_lizard/res/test_pkls +.ipynb_checkpoints + diff --git a/corticalmapping/DatabaseTools.py b/corticalmapping/DatabaseTools.py index afaa3a6..01abe68 100644 --- a/corticalmapping/DatabaseTools.py +++ b/corticalmapping/DatabaseTools.py @@ -1717,80 +1717,6 @@ def get_axon_dgcrm_from_clu_f(clu_f, plane_n, axon_n, trace_type): return dgcrm -def get_axon_morphology(clu_f, nwb_f, plane_n, axon_n): - - axon_morph = {} - - mc_grp = nwb_f['processing/motion_correction/MotionCorrection/{}/corrected'.format(plane_n)] - pixel_size = mc_grp['pixel_size'].value - # print(pixel_size) - pixel_size_mean = np.mean(pixel_size) - - bout_ns = clu_f['axons/{}'.format(axon_n)].value - # print(bout_ns) - bout_num = len(bout_ns) - axon_morph['bouton_num'] = bout_num - - if bout_num == 1: - axon_roi = get_roi(nwb_f=nwb_f, plane_n=plane_n, roi_n=bout_ns[0]) - else: - axon_roi = get_axon_roi_from_clu_f(clu_f=clu_f, axon_n=axon_n) - axon_roi = ia.WeightedROI(axon_roi.get_weighted_mask(), pixelSize=pixel_size, - pixelSizeUnit=mc_grp['pixel_size_unit'].value) - - # plt.imshow(axon_roi.get_binary_mask(), interpolation='nearest') - # plt.show() - - axon_morph['axon_row_range'] = (np.max(axon_roi.pixels[0]) - - np.min(axon_roi.pixels[0])) * pixel_size[0] * 1e6 - axon_morph['axon_col_range'] = (np.max(axon_roi.pixels[1]) - - np.min(axon_roi.pixels[1])) * pixel_size[1] * 1e6 - - axon_morph['axon_area'] = axon_roi.get_pixel_area() * 1e12 - - axon_qhull = spatial.ConvexHull(np.array(axon_roi.pixels).transpose()) - # print(axon_qhull.volume) - axon_morph['axon_qhull_area'] = axon_qhull.volume * axon_roi.pixelSizeX * axon_roi.pixelSizeY * 1e12 - - bout_rois = [] - for bout_n in bout_ns: - bout_rois.append(get_roi(nwb_f=nwb_f, plane_n=plane_n, roi_n=bout_n)) - - bout_areas = [r.get_pixel_area() for r in bout_rois] - bout_area_mean = np.mean(bout_areas) - axon_morph['bouton_area_mean'] = bout_area_mean * 1e12 - - if bout_num == 1: - bout_area_std = np.nan - else: - bout_area_std = np.std(bout_areas) - axon_morph['bouton_area_std'] = bout_area_std * 1e12 - - bout_coords = np.array([r.get_center() for r in bout_rois]) # [[y0, x0], [y1, x1], ... , [yn, xn]] - if bout_num == 1: - axon_morph['bouton_row_std'] = np.nan - axon_morph['bouton_col_std'] = np.nan - axon_morph['bouton_dis_mean'] = np.nan - axon_morph['bouton_dis_std'] = np.nan - axon_morph['bouton_dis_median'] = np.nan - axon_morph['bouton_dis_max'] = np.nan - else: - axon_morph['bouton_row_std'] = np.std(bout_coords[:, 0]) * pixel_size_mean * 1e6 - axon_morph['bouton_col_std'] = np.std(bout_coords[:, 1]) * pixel_size_mean * 1e6 - - bout_dis = spatial.distance.pdist(bout_coords) * pixel_size_mean - axon_morph['bouton_dis_mean'] = np.mean(bout_dis) * 1e6 - axon_morph['bouton_dis_median'] = np.median(bout_dis) * 1e6 - axon_morph['bouton_dis_max'] = np.max(bout_dis) * 1e6 - - if bout_num == 2: - axon_morph['bouton_dis_std'] = np.nan - else: - axon_morph['bouton_dis_std'] = np.std(bout_dis) * 1e6 - - return axon_morph - - def get_everything_from_axon(nwb_f, clu_f, plane_n, axon_n, params=ANALYSIS_PARAMS, verbose=False): """ @@ -3819,28 +3745,14 @@ def get_sta(arr, arr_ts, trigger_ts, frame_start, frame_end): if __name__ == '__main__': # =================================================================================================== - nwb_f = h5py.File(r"G:\bulk_LGN_database\nwbs\190221_M426525_110_repacked.nwb", 'r') - clu_f = h5py.File(r"G:\bulk_LGN_database\intermediate_results\bouton_clustering" - r"\AllStimuli_DistanceThr_1.30\190221_M426525_plane0_axon_grouping.hdf5", 'r') - plane_n = 'plane0' - axon_n = 'axon_0007' - axon_morph = get_axon_morphology(clu_f=clu_f, nwb_f=nwb_f, plane_n=plane_n, axon_n=axon_n) - - keys = axon_morph.keys() - keys.sort() - for key in keys: - print('{}: {}'.format(key, axon_morph[key])) - # =================================================================================================== - - # =================================================================================================== - # nwb_f = h5py.File(r"G:\bulk_LGN_database\nwbs\190404_M439939_110_repacked.nwb") - # uc_inds, _ = get_UC_ts_mask(nwb_f=nwb_f, plane_n='plane0') - # plt.plot(uc_inds) - # plt.show() - # - # dgc_spont_inds, _ = get_DGC_spont_ts_mask(nwb_f=nwb_f, plane_n='plane0') - # plt.plot(dgc_spont_inds) - # plt.show() + nwb_f = h5py.File(r"G:\bulk_LGN_database\nwbs\190404_M439939_110_repacked.nwb") + uc_inds, _ = get_UC_ts_mask(nwb_f=nwb_f, plane_n='plane0') + plt.plot(uc_inds) + plt.show() + + dgc_spont_inds, _ = get_DGC_spont_ts_mask(nwb_f=nwb_f, plane_n='plane0') + plt.plot(dgc_spont_inds) + plt.show() # =================================================================================================== # =================================================================================================== @@ -3894,6 +3806,4 @@ def get_sta(arr, arr_ts, trigger_ts, frame_start, frame_end): # plot_roi_retinotopy(coords_roi=coords_roi, coords_rf=coords_rf, ax_alt=ax_alt, ax_azi=ax_azi, # cmap='viridis', canvas_shape=(512, 512), edgecolors='#000000', linewidths=0.5) # plt.show() - # =================================================================================================== - - print('for debug ...') \ No newline at end of file + # =================================================================================================== \ No newline at end of file diff --git a/corticalmapping/HighLevel.py b/corticalmapping/HighLevel.py index cb118ac..2d79bfc 100644 --- a/corticalmapping/HighLevel.py +++ b/corticalmapping/HighLevel.py @@ -7,26 +7,157 @@ import itertools import pandas as pd import scipy.stats as stats -import scipy.ndimage as ni import scipy.sparse as sparse -import scipy.interpolate as ip +import scipy.ndimage as ni import matplotlib.pyplot as plt import tifffile as tf -from toolbox.misc import BinarySlicer -import allensdk_internal.brain_observatory.mask_set as mask_set +from toolbox.misc.slicer import BinarySlicer +#import allensdk_internal.brain_observatory.mask_set as mask_set import corticalmapping.core.ImageAnalysis as ia import corticalmapping.core.TimingAnalysis as ta import corticalmapping.core.PlottingTools as pt import corticalmapping.core.FileTools as ft -import corticalmapping.SingleCellAnalysis as sca +#import corticalmapping.SingleCellAnalysis as sca import corticalmapping.RetinotopicMapping as rm -try: - # from r_neuropil import NeuropilSubtract as NS - from allensdk.brain_observatory.r_neuropil import NeuropilSubtract as NS -except Exception as e: - print 'fail to import neural pil subtraction module ...' - print e +#try: +# # from r_neuropil import NeuropilSubtract as NS +# from allensdk.brain_observatory.r_neuropil import NeuropilSubtract as NS +#except Exception as e: + #print('fail to import neural pil subtraction module ...') + #print(e) + + + +def get_masks_from_caiman(spatial_com, dims, thr=0, thr_method='nrg', swap_dim=False): + """ + Gets masks of spatial components results generated the by the CaImAn segmentation + + this function is stripped out from the caiman.utils.visualization.get_contours(). only works for 2d spatial + components. + + Args: + spatial_com: np.ndarray or sparse matrix, mostly will be the caiman.source_extraction.cnmf.estimates.A + 2d Matrix of Spatial components, each row is a flattened pixel (order 'F'), each column + is a spatial component + dims: tuple of ints + Spatial dimensions of movie (row, col) + thr: scalar between 0 and 1 + Energy threshold for computing contours (default 0.9) + if thr_method is 'nrg': higher thr will make bigger hole inside the mask + if thr_method is 'max': (usually does not work very well), higher thr will make smaller mask + near the center. + thr_method: [optional] string + Method of thresholding: + 'max' sets to zero pixels that have value less than a fraction of the max value + 'nrg' keeps the pixels that contribute up to a specified fraction of the energy + swap_dim: if True, flattened 2d array will be reshaped by order 'C', otherwise with order 'F'. + Returns: + masks: 3d array, dtype=np.float, spatial component x row x col + """ + + if 'csc_matrix' not in str(type(spatial_com)): + spatial_com = sparse.csc_matrix(spatial_com) + + if len(spatial_com.shape) != 2: + raise ValueError('input "spatial_com" should be a 2d array or 2d sparse matrix.') + + n_mask = spatial_com.shape[1] + + if len(dims) != 2: + raise ValueError("input 'dims' should have two entries: (num_row, num_col).") + + if dims[0] * dims[1] != spatial_com.shape[0]: + raise ValueError("the product of dims[0] and dims[1] ({} x {}) should be equal to the first dimension " + "of the input 'spatial_com'.".format(dims[0], dims[1], spatial_com.shape[0])) + + masks = [] + + # # get the center of mass of neurons( patches ) + # cm = com(A, *dims) + + # for each patches + for i in range(n_mask): + # we compute the cumulative sum of the energy of the Ath component that has been ordered from least to highest + patch_data = spatial_com.data[spatial_com.indptr[i]:spatial_com.indptr[i + 1]] + indx = np.argsort(patch_data)[::-1] + if thr_method == 'nrg': + cumEn = np.cumsum(patch_data[indx] ** 2) + # we work with normalized values + cumEn /= cumEn[-1] + Bvec = np.ones(spatial_com.shape[0]) + # we put it in a similar matrix + Bvec[spatial_com.indices[spatial_com.indptr[i]:spatial_com.indptr[i + 1]][indx]] = cumEn + else: + if thr_method != 'max': + print('Unknown threshold method {}. should be either "max" or "nrg". ' + 'Choosing "max".'.format(thr_method)) + Bvec = np.zeros(spatial_com.shape[0]) + Bvec[spatial_com.indices[spatial_com.indptr[i]: + spatial_com.indptr[i + 1]]] = patch_data / patch_data.max() + if swap_dim: + Bmat = np.reshape(Bvec, dims, order='C') + mask = np.array(spatial_com[:, i].todense().reshape(dims, order='C')) + else: + Bmat = np.reshape(Bvec, dims, order='F') + mask = np.array(spatial_com[:, i].todense().reshape(dims, order='F')) + + Bmat[Bmat >= thr] = 1. + Bmat[Bmat < thr] = 0. + + masks.append(mask * Bmat) + + return np.array(masks) + + +def threshold_mask_by_energe(mask, sigma=1., thr_high=0.0, thr_low=0.1): + """ + threshold a weighted mask by reversed accumulative energy. Use this to treat masks spit out by caiman + segmentation. + :param mask: 2d array + :param sigma: float, 2d gaussian filter sigma + :param thr_high: float, 0 - 1, bigger thr_high will make bigger hole inside the roi + :param thr_low: float, 0 - 1, bigger thr_low will make smaller roi around the center + :return: 2d array thresholded mask + """ + + if len(mask.shape) != 2: + raise ValueError('input "mask" should be a 2d array.') + + if sigma is not None: + mask = ni.gaussian_filter(mask, sigma=sigma) + + mask = ia.array_nor(mask) + mask_s = mask.flatten() + + indx_low = np.argsort(mask_s) + cum_eng_low = np.cumsum(mask_s[indx_low] ** 2) + cum_eng_low /= cum_eng_low[-1] + mask_eng_low = np.ones(mask_s.shape, dtype=np.float) + mask_eng_low[indx_low] = cum_eng_low + mask_eng_low = mask_eng_low.reshape(mask.shape) + + indx_high = np.argsort(mask_s)[::-1] + cum_eng_high = np.cumsum(mask_s[indx_high] ** 2) + cum_eng_high /= cum_eng_high[-1] + mask_eng_high = np.ones(mask_s.shape, dtype=np.float) + mask_eng_high[indx_high] = cum_eng_high + mask_eng_high = mask_eng_high.reshape(mask.shape) + + mask_bin = np.ones(mask.shape) + + mask_bin[mask_eng_high < thr_high] = 0. + mask_bin[mask_eng_low < thr_low] = 0. + + mask_labeled, mask_num = ni.label(mask_bin, structure=[[1,1,1], [1,1,1], [1,1,1]]) + mask_dict = ia.get_masks(labeled=mask_labeled, keyPrefix='', labelLength=5) + + for key, value in mask_dict.items(): + mask_w = value * mask + mask_w = mask_w / np.amax(mask_w) + mask_dict[key] = ia.WeightedROI(mask_w) + + return mask_dict def translateMovieByVasculature(mov, parameterPath, matchingDecimation=2, referenceDecimation=2, verbose=True): @@ -46,17 +177,17 @@ def translateMovieByVasculature(mov, parameterPath, matchingDecimation=2, refere referenceDecimation = float(referenceDecimation) if matchingParams[ - 'Xoffset'] % matchingDecimation != 0: print 'Original Xoffset is not divisble by movDecimation. Taking the floor integer.' + 'Xoffset'] % matchingDecimation != 0: print('Original Xoffset is not divisble by movDecimation. Taking the floor integer.') if matchingParams[ - 'Yoffset'] % matchingDecimation != 0: print 'Original Yoffset is not divisble by movDecimation. Taking the floor integer.' + 'Yoffset'] % matchingDecimation != 0: print('Original Yoffset is not divisble by movDecimation. Taking the floor integer.') offset = [int(matchingParams['Xoffset'] / matchingDecimation), int(matchingParams['Yoffset'] / matchingDecimation)] if matchingParams[ - 'ReferenceMapHeight'] % matchingDecimation != 0: print 'Original ReferenceMapHeight is not divisble by movDecimation. Taking the floor integer.' + 'ReferenceMapHeight'] % matchingDecimation != 0: print('Original ReferenceMapHeight is not divisble by movDecimation. Taking the floor integer.') if matchingParams[ - 'ReferenceMapWidth'] % matchingDecimation != 0: print 'Original ReferenceMapWidth is not divisble by movDecimation. Taking the floor integer.' + 'ReferenceMapWidth'] % matchingDecimation != 0: print('Original ReferenceMapWidth is not divisble by movDecimation. Taking the floor integer.') outputShape = [int(matchingParams['ReferenceMapHeight'] / matchingDecimation), int(matchingParams['ReferenceMapHeight'] / matchingDecimation)] @@ -67,7 +198,7 @@ def translateMovieByVasculature(mov, parameterPath, matchingDecimation=2, refere if matchingDecimation / referenceDecimation != 1: movT = ia.rigid_transform_cv2(movT, zoom=matchingDecimation / referenceDecimation) - if verbose: print 'shape of output movie:', movT.shape + if verbose: print('shape of output movie:', movT.shape) return movT @@ -92,22 +223,22 @@ def translateHugeMovieByVasculature(inputPath, outputPath, parameterPath, output if outputDtype is None: outputDtype = inputMov.dtype.str - if len(inputMov.shape) != 3: raise ValueError, 'Input movie should be 3-d!' + if len(inputMov.shape) != 3: raise ValueError('Input movie should be 3-d!') frameNum = inputMov.shape[0] if outputPath[-4:] != '.npy': outputPath += '.npy' - if verbose: print '\nInput movie shape:', inputMov.shape + if verbose: print('\nInput movie shape:', inputMov.shape) chunkNum = frameNum // chunkLength if frameNum % chunkLength == 0: if verbose: - print 'Translating in chunks: ' + str(chunkNum) + ' x ' + str(chunkLength) + ' frame(s)' + print('Translating in chunks: ' + str(chunkNum) + ' x ' + str(chunkLength) + ' frame(s)') else: chunkNum += 1 - if verbose: print 'Translating in chunks: ' + str(chunkNum - 1) + ' x ' + str( - chunkLength) + ' frame(s)' + ' + ' + str(frameNum % chunkLength) + ' frame(s)' + if verbose: print('Translating in chunks: ' + str(chunkNum - 1) + ' x ' + str( + chunkLength) + ' frame(s)' + ' + ' + str(frameNum % chunkLength) + ' frame(s)') frameT1 = translateMovieByVasculature(inputMov[0, :, :], parameterPath=parameterPath, matchingDecimation=matchingDecimation, @@ -115,7 +246,7 @@ def translateHugeMovieByVasculature(inputPath, outputPath, parameterPath, output plt.imshow(frameT1, cmap='gray') plt.show() - if verbose: print 'Output movie shape:', (frameNum, frameT1.shape[0], frameT1.shape[1]), '\n' + if verbose: print('Output movie shape:', (frameNum, frameT1.shape[0], frameT1.shape[1]), '\n') with open(outputPath, 'wb') as f: np.lib.format.write_array_header_1_0(f, {'descr': outputDtype, 'fortran_order': False, @@ -126,8 +257,8 @@ def translateHugeMovieByVasculature(inputPath, outputPath, parameterPath, output indEnd = (i + 1) * chunkLength if indEnd > frameNum: indEnd = frameNum currMov = inputMov[indStart:indEnd, :, :] - if verbose: print 'Translating frame ' + str(indStart) + ' to frame ' + str(indEnd) + '.\t' + str( - i * 100. / chunkNum) + '%' + if verbose: print('Translating frame ' + str(indStart) + ' to frame ' + str(indEnd) + '.\t' + str( + i * 100. / chunkNum) + '%') currMovT = translateMovieByVasculature(currMov, parameterPath=parameterPath, matchingDecimation=matchingDecimation, referenceDecimation=referenceDecimation, verbose=False) @@ -147,38 +278,26 @@ def segmentPhotodiodeSignal(pd, digitizeThr=0.9, filterSize=0.01, segmentThr=0.0 :return: ''' - # plot_r = [2000., 3000.] - # - # plt.plot(pd[int(plot_r[0]*Fs):int(plot_r[1]*Fs)]) - # plt.title('pd raw') - # plt.show() - pdDigitized = np.array(pd) + # plt.plot(pdDigitized[0: 100 * 30000]) + # plt.show() - pdDigitized[pd < digitizeThr] = 0. + pdDigitized[pd < digitizeThr] = 0.; pdDigitized[pd >= digitizeThr] = 5. - - # plt.plot(pdDigitized[int(plot_r[0]*Fs):int(plot_r[1]*Fs)]) - # plt.title('pd digitized') + # plt.plot(pdDigitized[0: 100 * 30000]) # plt.show() filterDataPoint = int(filterSize * Fs) - # print filterDataPoint pdFiltered = ni.filters.gaussian_filter(pdDigitized, filterDataPoint) - - # plt.plot(pdFiltered[int(plot_r[0]*Fs):int(plot_r[1]*Fs)]) - # plt.title('pd filtered') - # plt.show() - pdFilteredDiff = np.diff(pdFiltered) pdFilteredDiff = np.hstack(([0], pdFilteredDiff)) pdSignal = np.multiply(pdDigitized, pdFilteredDiff) - - # plt.plot(pdSignal[int(plot_r[0]*Fs):int(plot_r[1]*Fs)]) - # plt.title('pd signal') + # plt.plot(pdSignal[0: 100 * 30000]) # plt.show() + # plt.plot(pdSignal[:1000000]) + # plt.show() displayOnsets = ta.get_onset_timeStamps(pdSignal, Fs, threshold=segmentThr, onsetType='raising') trueDisplayOnsets = [] @@ -191,13 +310,13 @@ def segmentPhotodiodeSignal(pd, digitizeThr=0.9, filterSize=0.01, segmentThr=0.0 trueDisplayOnsets.append(displayOnset) currOnset = displayOnset - print '\nNumber of photodiode onsets:', len(trueDisplayOnsets) + print('\nNumber of photodiode onsets:', len(trueDisplayOnsets)) if verbose: - print '\nDisplay onsets (sec):' - print '\n'.join([str(o) for o in trueDisplayOnsets]) + print('\nDisplay onsets (sec):') + print('\n'.join([str(o) for o in trueDisplayOnsets])) - print '\n' + print('\n') return np.array(trueDisplayOnsets) @@ -242,11 +361,11 @@ def findLogPath(date, # string if (dateTime[0:6] == date) and (mouseID in mouse) and (stimulus in stim) and (userID in user) and ( fileNumber == fileNum) and (ext == '.pkl'): logPathList.append(os.path.join(displayFolder, f)) - print '\n' + '\n'.join(logPathList) + '\n' + print('\n' + '\n'.join(logPathList) + '\n') if len(logPathList) == 0: - raise LookupError, 'Can not find visual display Log.' + raise LookupError('Can not find visual display Log.') elif len(logPathList) > 1: - raise LookupError, 'Find more than one visual display Log!' + raise LookupError('Find more than one visual display Log!') return logPathList[0] @@ -356,26 +475,34 @@ def analysisMappingDisplayLog(display_log): else: raise ValueError('log should be either dictionary or a path string!') + def convert(data): + if isinstance(data, bytes): return data.decode('ascii') + if isinstance(data, dict): return dict(map(convert, data.items())) + if isinstance(data, tuple): return map(convert, data) + return data + + log = convert(log) # convert bytestrings to strings (a py2to3 issue) + # check display order - if log['presentation']['displayOrder'] == -1: raise ValueError, 'Display order is -1 (should be 1)!' + if log['presentation']['displayOrder'] == -1: raise ValueError('Display order is -1 (should be 1)!') refreshRate = float(log['monitor']['refreshRate']) # check display visual frame interval interFrameInterval = np.mean(np.diff(log['presentation']['timeStamp'])) - if interFrameInterval > (1.01 / refreshRate): raise ValueError, 'Mean visual display too long: ' + str( - interFrameInterval) + 'sec' # check display - if interFrameInterval < (0.99 / refreshRate): raise ValueError, 'Mean visual display too short: ' + str( - interFrameInterval) + 'sec' # check display + if interFrameInterval > (1.01 / refreshRate): raise ValueError('Mean visual display too long: ' + str( + interFrameInterval) + 'sec') # check display + if interFrameInterval < (0.99 / refreshRate): raise ValueError('Mean visual display too short: ' + str( + interFrameInterval) + 'sec') # check display # get sweep start time relative to display onset try: startTime = -1 * log['stimulation']['preGapDur'] except KeyError: startTime = -1 * log['stimulation']['preGapFrameNum'] / log['monitor']['refreshRate'] - print 'Movie chunk start time relative to sweep onset:', startTime, 'sec' - displayInfo['B2U']['startTime'] = startTime + print('Movie chunk start time relative to sweep onset:', startTime, 'sec') + displayInfo['B2U']['startTime'] = startTime; displayInfo['U2B']['startTime'] = startTime - displayInfo['L2R']['startTime'] = startTime + displayInfo['L2R']['startTime'] = startTime; displayInfo['R2L']['startTime'] = startTime # get basic information @@ -383,44 +510,44 @@ def analysisMappingDisplayLog(display_log): displayIter = log['presentation']['displayIteration'] sweepTable = log['stimulation']['sweepTable'] dirList = [] - B2Uframes = [] - U2Bframes = [] - L2Rframes = [] + B2Uframes = []; + U2Bframes = []; + L2Rframes = []; R2Lframes = [] # parcel frames for each direction for frame in frames: currDir = frame[4] if currDir not in dirList: dirList.append(currDir) - if currDir == 'B2U': + if currDir == b'B2U': B2Uframes.append(frame) - elif currDir == 'U2B': + elif currDir == b'U2B': U2Bframes.append(frame) - elif currDir == 'L2R': + elif currDir == b'L2R': L2Rframes.append(frame) - elif currDir == 'R2L': + elif currDir == b'R2L': R2Lframes.append(frame) # get sweep order indices for each direction dirList = dirList * displayIter - displayInfo['B2U']['ind'] = [ind for ind, dir in enumerate(dirList) if dir == 'B2U'] - print 'B2U sweep order indices:', displayInfo['B2U']['ind'] - displayInfo['U2B']['ind'] = [ind for ind, dir in enumerate(dirList) if dir == 'U2B'] - print 'U2B sweep order indices:', displayInfo['U2B']['ind'] - displayInfo['L2R']['ind'] = [ind for ind, dir in enumerate(dirList) if dir == 'L2R'] - print 'L2R sweep order indices:', displayInfo['L2R']['ind'] - displayInfo['R2L']['ind'] = [ind for ind, dir in enumerate(dirList) if dir == 'R2L'] - print 'R2L sweep order indices:', displayInfo['R2L']['ind'] + displayInfo['B2U']['ind'] = [ind for ind, adir in enumerate(dirList) if adir == b'B2U'] + print('B2U sweep order indices:', displayInfo['B2U']['ind']) + displayInfo['U2B']['ind'] = [ind for ind, adir in enumerate(dirList) if adir == b'U2B'] + print('U2B sweep order indices:', displayInfo['U2B']['ind']) + displayInfo['L2R']['ind'] = [ind for ind, adir in enumerate(dirList) if adir == b'L2R'] + print('L2R sweep order indices:', displayInfo['L2R']['ind']) + displayInfo['R2L']['ind'] = [ind for ind, adir in enumerate(dirList) if adir == b'R2L'] + print('R2L sweep order indices:', displayInfo['R2L']['ind']) # get sweep duration for each direction displayInfo['B2U']['sweepDur'] = len(B2Uframes) / refreshRate - print 'Chunk duration for B2U sweeps:', displayInfo['B2U']['sweepDur'], 'sec' + print('Chunk duration for B2U sweeps:', displayInfo['B2U']['sweepDur'], 'sec') displayInfo['U2B']['sweepDur'] = len(U2Bframes) / refreshRate - print 'Chunk duration for U2B sweeps:', displayInfo['U2B']['sweepDur'], 'sec' + print('Chunk duration for U2B sweeps:', displayInfo['U2B']['sweepDur'], 'sec') displayInfo['L2R']['sweepDur'] = len(L2Rframes) / refreshRate - print 'Chunk duration for L2R sweeps:', displayInfo['L2R']['sweepDur'], 'sec' + print('Chunk duration for L2R sweeps:', displayInfo['L2R']['sweepDur'], 'sec') displayInfo['R2L']['sweepDur'] = len(R2Lframes) / refreshRate - print 'Chunk duration for R2L sweeps:', displayInfo['R2L']['sweepDur'], 'sec' + print('Chunk duration for R2L sweeps:', displayInfo['R2L']['sweepDur'], 'sec') # get phase position slopes and intercepts for each direction displayInfo['B2U']['slope'], displayInfo['B2U']['intercept'] = rm.getPhasePositionEquation2(B2Uframes, sweepTable) @@ -475,7 +602,7 @@ def analyzeSparseNoiseDisplayLog(logPath): def getAverageDfMovie(movPath, frameTS, onsetTimes, chunkDur, startTime=0., temporalDownSampleRate=1, - is_load_all=False): + is_load_all=True): ''' :param movPath: path to the image movie :param frameTS: the timestamps for each frame of the raw movie @@ -491,14 +618,14 @@ def getAverageDfMovie(movPath, frameTS, onsetTimes, chunkDur, startTime=0., temp elif temporalDownSampleRate > 1: frameTS_real = frameTS[::temporalDownSampleRate] else: - raise ValueError, 'temporal downsampling rate can not be less than 1!' + raise ValueError('temporal downsampling rate can not be less than 1!') if is_load_all: if movPath[-4:] == '.npy': try: mov = np.load(movPath) except ValueError: - print 'Cannot load the entire npy file into memroy. Trying BinarySlicer...' + print('Cannot load the entire npy file into memroy. Trying BinarySlicer...') mov = BinarySlicer(movPath) elif movPath[-4:] == '.tif': mov = tf.imread(movPath) @@ -537,7 +664,7 @@ def getAverageDfMovieFromH5Dataset(dset, frameTS, onsetTimes, chunkDur, startTim elif temporalDownSampleRate > 1: frameTS_real = frameTS[::temporalDownSampleRate] else: - raise ValueError, 'temporal downsampling rate can not be less than 1!' + raise ValueError('temporal downsampling rate can not be less than 1!') aveMov, n = ia.get_average_movie(dset, frameTS_real, onsetTimes + startTime, chunkDur, isReturnN=True) @@ -578,17 +705,17 @@ def getMappingMovies(movPath, frameTS, displayOnsets, displayInfo, temporalDownS elif FFTmode == 'valley': isReverse = True else: - raise LookupError, 'FFTmode should be either "peak" or "valley"!' + raise LookupError('FFTmode should be either "peak" or "valley"!') for dir in ['B2U', 'U2B', 'L2R', 'R2L']: - print '\nAnalyzing sweeps with direction:', dir + print('\nAnalyzing sweeps with direction:', dir) onsetInd = list(displayInfo[dir]['ind']) for ind in displayInfo[dir]['ind']: if ind >= len(displayOnsets): - print 'Visual Stimulation Direction:' + dir + ' index:' + str( - ind) + ' was not displayed. Remove from averageing.' + print('Visual Stimulation Direction:' + dir + ' index:' + str( + ind) + ' was not displayed. Remove from averageing.') onsetInd.remove(ind) aveMov, aveMovNor = getAverageDfMovie(movPath=movPath, @@ -599,6 +726,9 @@ def getMappingMovies(movPath, frameTS, displayOnsets, displayInfo, temporalDownS temporalDownSampleRate=temporalDownSampleRate, is_load_all=is_load_all) + print('aveMov.shape = ' + str(aveMov.shape)) + print('aveMovNor.shape = ' + str(aveMovNor.shape)) + if isRectify: aveMovNorRec = np.array(aveMovNor) aveMovNorRec[aveMovNorRec < 0.] = 0. @@ -651,7 +781,7 @@ def regression_detrend(mov, roi, verbose=True): """ if len(mov.shape) != 3: - raise (ValueError, 'Input movie should be 3-dimensional!') + raise ValueError roi = ia.WeightedROI(roi) trend = roi.get_weighted_trace(mov) @@ -664,7 +794,7 @@ def regression_detrend(mov, roi, verbose=True): n = 0 - for i, j in itertools.product(range(mov.shape[1]), range(mov.shape[2])): + for i, j in itertools.product(list(range(mov.shape[1])), list(range(mov.shape[2]))): pixel_trace = mov[:, i, j] slope, intercept, r_value, p_value, stderr = stats.linregress(trend, pixel_trace) slopes[i, j] = slope @@ -673,7 +803,7 @@ def regression_detrend(mov, roi, verbose=True): if verbose: if n % (pixel_num // 10) == 0: - print 'progress:', int(round(float(n) * 100 / pixel_num)), '%' + print('progress:', int(round(float(n) * 100 / pixel_num)), '%') n += 1 return mov_new, trend, slopes, rvalues @@ -715,7 +845,7 @@ def neural_pil_subtraction(trace_center, trace_surround, lam=0.05): ns.fit() # ns.fit_block_coordinate_desc() - return ns.r, ns.error, trace_center - (ns.r * trace_surround) + return ns.r, ns.error, trace_center - ns.r * trace_surround def get_lfp(trace, fs=30000., notch_base=60., notch_bandwidth=1., notch_harmonics=4, notch_order=2, @@ -770,7 +900,7 @@ def array_to_rois(input_folder, overlap_threshold=0.9, neuropil_limit=(5, 10), i center_masks.update(curr_masks) center_mask_array = [] - for mask in center_masks.values(): + for mask in list(center_masks.values()): center_mask_array.append(mask) center_mask_array = np.array(center_mask_array, dtype=np.uint8) @@ -779,7 +909,7 @@ def array_to_rois(input_folder, overlap_threshold=0.9, neuropil_limit=(5, 10), i duplicates = ms.detect_duplicates(overlap_threshold=overlap_threshold) # print 'number of duplicates:', len(duplicates) if len(duplicates) > 0: - inds = duplicates.keys() + inds = list(duplicates.keys()) center_mask_array = np.array([center_mask_array[i] for i in range(len(center_mask_array)) if i not in inds]) # removing unions @@ -787,7 +917,7 @@ def array_to_rois(input_folder, overlap_threshold=0.9, neuropil_limit=(5, 10), i unions = ms.detect_unions() # print 'number of unions:', len(unions) if len(unions) > 0: - inds = unions.keys() + inds = list(unions.keys()) center_mask_array = np.array([center_mask_array[i] for i in range(len(center_mask_array)) if i not in inds]) # get total mask @@ -888,7 +1018,7 @@ def concatenate_nwb_files(path_list, save_path, gap_dur=100., roi_path=None, is_ for i, curr_path in enumerate(path_list): - print('\n\nprocessing ' + curr_path + '...') + print(('\n\nprocessing ' + curr_path + '...')) curr_f = h5py.File(curr_path, 'r') @@ -913,7 +1043,7 @@ def concatenate_nwb_files(path_list, save_path, gap_dur=100., roi_path=None, is_ pixel_size_unit = None roi_grp = curr_f[roi_path] - for roi_n in roi_grp.keys(): + for roi_n in list(roi_grp.keys()): if roi_n[0: 4] == 'roi_' and roi_n != 'roi_list': roi_dict_center.update({roi_n: ia.ROI(roi_grp[roi_n]['img_mask'].value, pixelSize=pixel_size, pixelSizeUnit=pixel_size_unit)}) @@ -922,12 +1052,12 @@ def concatenate_nwb_files(path_list, save_path, gap_dur=100., roi_path=None, is_ pixelSizeUnit=pixel_size_unit)}) else: # print('avoid loading {}/{} as an roi.'.format(roi_path, roi_n)) - print('avoid loading {} as an roi.'.format(roi_n)) + print(('avoid loading {} as an roi.'.format(roi_n))) # check analog start time all_chs_grp = curr_f['acquisition/timeseries'] - for curr_chn, curr_ch_grp in all_chs_grp.items(): - if 'starting_time' in curr_ch_grp.keys(): + for curr_chn, curr_ch_grp in list(all_chs_grp.items()): + if 'starting_time' in list(curr_ch_grp.keys()): total_analog_sample_count = curr_ch_grp['num_samples'].value if curr_ch_grp['starting_time'].value != 0: raise ValueError('starting time of analog channel: {} is not 0.'.format(curr_chn)) @@ -972,7 +1102,7 @@ def concatenate_nwb_files(path_list, save_path, gap_dur=100., roi_path=None, is_ except Exception: pass for curr_unit_n in curr_unit_ns: - if curr_unit_n in curr_ug_dict.keys(): + if curr_unit_n in list(curr_ug_dict.keys()): curr_ug_dict[curr_unit_n].append(curr_ug_grp[curr_unit_n]['times'].value + next_start) else: curr_ug_dict[curr_unit_n] = [curr_ug_grp[curr_unit_n]['times'].value + next_start] @@ -995,7 +1125,7 @@ def concatenate_nwb_files(path_list, save_path, gap_dur=100., roi_path=None, is_ print('\nsaving rois ...') roi_grp = save_f.create_group('rois') if roi_dict_center: - for curr_roi_n_c, curr_roi_c in roi_dict_center.items(): + for curr_roi_n_c, curr_roi_c in list(roi_dict_center.items()): curr_roi_grp = roi_grp.create_group(curr_roi_n_c) curr_roi_grp_c = curr_roi_grp.create_group('center') curr_roi_c.to_h5_group(curr_roi_grp_c) @@ -1006,26 +1136,26 @@ def concatenate_nwb_files(path_list, save_path, gap_dur=100., roi_path=None, is_ curr_roi_grp_s = curr_roi_grp.create_group('surround') curr_roi_s.to_h5_group(curr_roi_grp_s) except Exception as e: - print('error in saving surrounding roi: {}. \nError message: {}'.format(curr_roi_n_c, e)) + print(('error in saving surrounding roi: {}. \nError message: {}'.format(curr_roi_n_c, e))) if analog_chs or is_save_running: print('\nsaving analog channels ...') - for curr_analog_n, curr_analog_trace_list in analog_dict.items(): - print curr_analog_n + for curr_analog_n, curr_analog_trace_list in list(analog_dict.items()): + print(curr_analog_n) curr_analog_trace_all = np.concatenate(curr_analog_trace_list, axis=0) save_f['analog_' + curr_analog_n] = curr_analog_trace_all if unit_groups: for unit_group in unit_groups: - print('\nsaving ephys units for unit group: {}'.format(unit_group)) + print(('\nsaving ephys units for unit group: {}'.format(unit_group))) save_units_grp = save_f.create_group(unit_group) - for unit_n, unit_ts in unit_dict[unit_group].items(): + for unit_n, unit_ts in list(unit_dict[unit_group].items()): save_unit_grp = save_units_grp.create_group(unit_n) save_unit_grp.create_dataset('timestamps', data=np.concatenate(unit_ts, axis=0)) if time_series_paths: print('\nsaving other time series ...') - for save_ts_n, save_ts_dict in time_series_dict.items(): + for save_ts_n, save_ts_dict in list(time_series_dict.items()): print(save_ts_n) save_ts_grp = save_f.create_group(save_ts_n) curr_ts_data = save_ts_dict['data'] @@ -1138,7 +1268,7 @@ def get_drifting_grating_dataframe(dgr_grp, sweep_dur): bl_bins = t <= 0 res_bins = (t > 0) & (t <= sweep_dur) - gratings = dgr_grp.keys() + gratings = list(dgr_grp.keys()) dg_df = pd.DataFrame(columns=['n', 'sf', 'tf', 'dir', 'con', 'radius', 'baseline', 'baseline_std', 'F0', 'F0_std', 'F1', 'F1_std', 'F2', 'F2_std']) @@ -1258,8 +1388,7 @@ def generate_strf_from_timestamps(unit_ts, squares_ts_grp, unit_n='', sta_start= :param unit_ts: 1d array, spike timestamps, should be monotonic increasing :param squares_ts_grp: h5py group object, containing the timestamps of each square displayed, this should be the - output of corticalmapping.NwbTools.RecordedFile.analyze_visual_stimuli_corticalmapping() - function + output of corticalmapping.NwbTools.RecordedFile.analyze_visual_stimuli() function :param unit_n: str, name of the unit :param sta_start: float, stimulus triggered average start time relative to stimulus onset :param sta_end: float, stimulus triggered average end time relative to stimulus onset @@ -1271,7 +1400,7 @@ def generate_strf_from_timestamps(unit_ts, squares_ts_grp, unit_n='', sta_start= t = np.arange((sta_start + bin_width / 2), (sta_end + bin_width / 2), bin_width) t = np.round(t * 100000) / 100000 - all_squares = squares_ts_grp.keys() + all_squares = list(squares_ts_grp.keys()) traces = [] locations = [] signs = [] @@ -1302,8 +1431,7 @@ def generate_strf_from_continuous(continuous, continuous_ts, squares_ts_grp, roi :param continuous_ts: 1d array, timestamp series for the continuous, monotonically increasing, should have same size as continuous :param squares_ts_grp: h5py group object, containing the timestamps of each square displayed, this should be the - output of corticalmapping.NwbTools.RecordedFile.analyze_visual_stimuli_corticalmapping() - function + output of corticalmapping.NwbTools.RecordedFile.analyze_visual_stimuli() function :param roi_n: str, name of the roi :param sta_start: float, stimulus triggered average start time relative to stimulus onset :param sta_end: float, stimulus triggered average end time relative to stimulus onset @@ -1316,7 +1444,7 @@ def generate_strf_from_continuous(continuous, continuous_ts, squares_ts_grp, roi chunk_frame_start = int(np.floor(sta_start / mean_frame_dur)) t = (np.arange(chunk_frame_dur) + chunk_frame_start) * mean_frame_dur - all_squares = squares_ts_grp.keys() + all_squares = list(squares_ts_grp.keys()) traces = [] # square x trial x t locations = [] @@ -1345,321 +1473,7 @@ def generate_strf_from_continuous(continuous, continuous_ts, squares_ts_grp, roi return sca.SpatialTemporalReceptiveField(locations, signs, traces, t, name=roi_n, trace_data_type='df/f') -def get_drifting_grating_response_nwb(nwb_path, plane_ns, grating_onsets_path, time_window): - """ - extract and response table for drifting_gratings from a nwb file. The response table will be saved in the /analysis - group. - - :param nwb_path: str, path to the nwb file - :param plane_ns: list of strings, plane names for multi-plane imaging - :param grating_onsets_path: str, hdf5 group path to the grating_onset timestamps - :param time_window: tuple/list of two floats, start and end time relative to grating onset - :return: None - """ - - def get_sta(arr, arr_ts, trigger_ts, frame_start, frame_end): - - sta_arr = [] - - for trig in trigger_ts: - trig_ind = ta.find_nearest(arr_ts, trig) - curr_sta = arr[:, (trig_ind + frame_start) : (trig_ind + frame_end)] - sta_arr.append(curr_sta.reshape((curr_sta.shape[0], 1, curr_sta.shape[1]))) - - sta_arr = np.concatenate(sta_arr, axis=1) - return sta_arr - - - if time_window[0] >= time_window[1]: - raise ValueError('time window should be from early time to late time.') - - nwb_f = h5py.File(nwb_path) - - res_grp = nwb_f['analysis'].create_group('response_table_drifting_grating') - - - grating_ns = nwb_f[grating_onsets_path].keys() - grating_ns.sort() - - for plane_n in plane_ns: - print(plane_n) - - res_grp_plane = res_grp.create_group(plane_n) - - trace_ts = nwb_f['processing/motion_correction/MotionCorrection/' + plane_n + '/corrected/timestamps'] - - traces = {} - traces['global_dff_center'] = nwb_f['processing/rois_and_traces_' + plane_n + '/DfOverF/dff_center/data'].value - traces['f_center_demixed'] = nwb_f['processing/rois_and_traces_' + plane_n + '/Fluorescence/f_center_demixed/data'].value - traces['f_center_raw'] = nwb_f['processing/rois_and_traces_' + plane_n + '/Fluorescence/f_center_raw/data'].value - traces['f_center_subtracted'] = nwb_f['processing/rois_and_traces_' + plane_n + '/Fluorescence/f_center_subtracted/data'].value - traces['f_surround_raw'] = nwb_f['processing/rois_and_traces_' + plane_n + '/Fluorescence/f_surround_raw/data'].value - - frame_dur = np.mean(np.diff(trace_ts)) - frame_start = int(time_window[0] // frame_dur) - frame_end = int(time_window[1] // frame_dur) - t_axis = np.arange(frame_end - frame_start) * frame_dur + time_window[0] - - res_grp_plane.attrs['sta_timestamps'] = t_axis - - for grating_n in grating_ns: - - onsets_grating_grp = nwb_f[grating_onsets_path + '/' + grating_n] - - curr_grating_grp = res_grp_plane.create_group(grating_n) - for key, value in onsets_grating_grp.items(): - if key not in ['data', 'num_samples', 'timestamps']: - curr_grating_grp.attrs[key] = value.value - curr_grating_grp.attrs['sta_traces_dimenstion'] = 'roi x trial x timepoint' - - grating_onsets = onsets_grating_grp['timestamps'].value - for trace_n, trace in traces.items(): - sta = get_sta(arr=trace, arr_ts=trace_ts, trigger_ts=grating_onsets, frame_start=frame_start, - frame_end=frame_end) - curr_grating_grp.create_dataset('sta_' + trace_n, data=sta) - - -def plot_roi_traces_three_planes(nwb_f, roi0=None, roi1=None, roi2=None, trace_type='f_center_raw'): - - f = plt.figure(figsize=(10, 10)) - - ax0_img = f.add_axes([0.04, 0.67, 0.32, 0.32]) - ax0_img.set_xticks([]) - ax0_img.set_yticks([]) - ax0_img.set_ylabel('plane0, {}'.format(roi0), fontsize=15) - bg0 = nwb_f['processing/rois_and_traces_plane0/ImageSegmentation/imaging_plane' \ - '/reference_images/max_projection/data'].value - bg0 = ia.array_nor(bg0) - ax0_img.imshow(bg0, vmin=0, vmax=0.8, cmap='gray', interpolation='nearest') - - ax0_trace0 = f.add_axes([0.37, 0.88, 0.62, 0.1]) - ax0_trace0.set_axis_off() - - ax0_trace1 = f.add_axes([0.37, 0.78, 0.62, 0.1]) - ax0_trace1.set_axis_off() - - ax0_trace2 = f.add_axes([0.37, 0.68, 0.62, 0.1]) - ax0_trace2.set_axis_off() - - if roi0 is not None: - roi0_mask = nwb_f['processing/rois_and_traces_plane0/ImageSegmentation/imaging_plane' \ - '/{}/img_mask'.format(roi0)].value - pt.plot_mask_borders(mask=roi0_mask, plotAxis=ax0_img, lw=0.5) - - roi0_ind = int(roi0[-4:]) - roi0_trace = nwb_f['processing/rois_and_traces_plane0/Fluorescence/{}' \ - '/data'.format(trace_type)][roi0_ind, :] - chunk_len = len(roi0_trace) // 3 - ax0_trace0.plot(roi0_trace[0:chunk_len]) - ax0_trace1.plot(roi0_trace[chunk_len:2*chunk_len]) - ax0_trace2.plot(roi0_trace[2*chunk_len:3*chunk_len]) - - ax1_img = f.add_axes([0.04, 0.34, 0.32, 0.32]) - ax1_img.set_xticks([]) - ax1_img.set_yticks([]) - ax1_img.set_ylabel('plane1, {}'.format(roi1), fontsize=15) - bg1 = nwb_f['processing/rois_and_traces_plane1/ImageSegmentation/imaging_plane' \ - '/reference_images/max_projection/data'].value - bg1 = ia.array_nor(bg1) - ax1_img.imshow(bg1, vmin=0, vmax=0.8, cmap='gray', interpolation='nearest') - - ax1_trace0 = f.add_axes([0.37, 0.55, 0.62, 0.1]) - ax1_trace0.set_axis_off() - - ax1_trace1 = f.add_axes([0.37, 0.45, 0.62, 0.1]) - ax1_trace1.set_axis_off() - - ax1_trace2 = f.add_axes([0.37, 0.35, 0.62, 0.1]) - ax1_trace2.set_axis_off() - - if roi1 is not None: - roi1_mask = nwb_f['processing/rois_and_traces_plane1/ImageSegmentation/imaging_plane' \ - '/{}/img_mask'.format(roi1)].value - pt.plot_mask_borders(mask=roi1_mask, plotAxis=ax1_img, lw=0.5) - - roi1_ind = int(roi1[-4:]) - roi1_trace = nwb_f['processing/rois_and_traces_plane1/Fluorescence/{}' \ - '/data'.format(trace_type)][roi1_ind, :] - chunk_len = len(roi1_trace) // 3 - ax1_trace0.plot(roi1_trace[0:chunk_len]) - ax1_trace1.plot(roi1_trace[chunk_len:2 * chunk_len]) - ax1_trace2.plot(roi1_trace[2 * chunk_len:3 * chunk_len]) - - ax2_img = f.add_axes([0.04, 0.01, 0.32, 0.32]) - ax2_img.set_xticks([]) - ax2_img.set_yticks([]) - ax2_img.set_ylabel('plane2, {}'.format(roi2), fontsize=15) - bg2 = nwb_f['processing/rois_and_traces_plane2/ImageSegmentation/imaging_plane' \ - '/reference_images/max_projection/data'].value - bg2 = ia.array_nor(bg2) - ax2_img.imshow(bg2, vmin=0, vmax=0.8, cmap='gray', interpolation='nearest') - - ax2_trace0 = f.add_axes([0.37, 0.22, 0.62, 0.1]) - ax2_trace0.set_axis_off() - - ax2_trace1 = f.add_axes([0.37, 0.12, 0.62, 0.1]) - ax2_trace1.set_axis_off() - - ax2_trace2 = f.add_axes([0.37, 0.02, 0.62, 0.1]) - ax2_trace2.set_axis_off() - - if roi2 is not None: - roi2_mask = nwb_f['processing/rois_and_traces_plane2/ImageSegmentation/imaging_plane' \ - '/{}/img_mask'.format(roi2)].value - pt.plot_mask_borders(mask=roi2_mask, plotAxis=ax2_img, lw=0.5) - - roi2_ind = int(roi2[-4:]) - roi2_trace = nwb_f['processing/rois_and_traces_plane2/Fluorescence/{}' \ - '/data'.format(trace_type)][roi2_ind, :] - chunk_len = len(roi2_trace) // 3 - ax2_trace0.plot(roi2_trace[0:chunk_len]) - ax2_trace1.plot(roi2_trace[chunk_len:2 * chunk_len]) - ax2_trace2.plot(roi2_trace[2 * chunk_len:3 * chunk_len]) - - # plt.show() - - return f - - -def get_masks_from_caiman(spatial_com, dims, thr=0, thr_method='nrg', swap_dim=False): - """ - Gets masks of spatial components results generated the by the CaImAn segmentation - - this function is stripped out from the caiman.utils.visualization.get_contours(). only works for 2d spatial - components. - - Args: - spatial_com: np.ndarray or sparse matrix, mostly will be the caiman.source_extraction.cnmf.estimates.A - 2d Matrix of Spatial components, each row is a flattened pixel (order 'F'), each column - is a spatial component - dims: tuple of ints - Spatial dimensions of movie (row, col) - thr: scalar between 0 and 1 - Energy threshold for computing contours (default 0.9) - if thr_method is 'nrg': higher thr will make bigger hole inside the mask - if thr_method is 'max': (usually does not work very well), higher thr will make smaller mask - near the center. - thr_method: [optional] string - Method of thresholding: - 'max' sets to zero pixels that have value less than a fraction of the max value - 'nrg' keeps the pixels that contribute up to a specified fraction of the energy - swap_dim: if True, flattened 2d array will be reshaped by order 'C', otherwise with order 'F'. - Returns: - masks: 3d array, dtype=np.float, spatial component x row x col - """ - - if 'csc_matrix' not in str(type(spatial_com)): - spatial_com = sparse.csc_matrix(spatial_com) - - if len(spatial_com.shape) != 2: - raise ValueError('input "spatial_com" should be a 2d array or 2d sparse matrix.') - - n_mask = spatial_com.shape[1] - - if len(dims) != 2: - raise ValueError("input 'dims' should have two entries: (num_row, num_col).") - - if dims[0] * dims[1] != spatial_com.shape[0]: - raise ValueError("the product of dims[0] and dims[1] ({} x {}) should be equal to the first dimension " - "of the input 'spatial_com'.".format(dims[0], dims[1], spatial_com.shape[0])) - - masks = [] - - # # get the center of mass of neurons( patches ) - # cm = com(A, *dims) - - # for each patches - for i in range(n_mask): - # we compute the cumulative sum of the energy of the Ath component that has been ordered from least to highest - patch_data = spatial_com.data[spatial_com.indptr[i]:spatial_com.indptr[i + 1]] - indx = np.argsort(patch_data)[::-1] - if thr_method == 'nrg': - cumEn = np.cumsum(patch_data[indx] ** 2) - # we work with normalized values - cumEn /= cumEn[-1] - Bvec = np.ones(spatial_com.shape[0]) - # we put it in a similar matrix - Bvec[spatial_com.indices[spatial_com.indptr[i]:spatial_com.indptr[i + 1]][indx]] = cumEn - else: - if thr_method != 'max': - print('Unknown threshold method {}. should be either "max" or "nrg". ' - 'Choosing "max".'.format(thr_method)) - Bvec = np.zeros(spatial_com.shape[0]) - Bvec[spatial_com.indices[spatial_com.indptr[i]: - spatial_com.indptr[i + 1]]] = patch_data / patch_data.max() - if swap_dim: - Bmat = np.reshape(Bvec, dims, order='C') - mask = np.array(spatial_com[:, i].todense().reshape(dims, order='C')) - else: - Bmat = np.reshape(Bvec, dims, order='F') - mask = np.array(spatial_com[:, i].todense().reshape(dims, order='F')) - - Bmat[Bmat >= thr] = 1. - Bmat[Bmat < thr] = 0. - - masks.append(mask * Bmat) - - return np.array(masks) - - -def threshold_mask_by_energy(mask, sigma=1., thr_high=0.0, thr_low=0.1): - """ - threshold a weighted mask by reversed accumulative energy. Use this to treat masks spit out by caiman - segmentation. - :param mask: 2d array - :param sigma: float, 2d gaussian filter sigma - :param thr_high: float, 0 - 1, bigger thr_high will make bigger hole inside the roi - :param thr_low: float, 0 - 1, bigger thr_low will make smaller roi around the center - :return: 2d array thresholded mask - """ - - if len(mask.shape) != 2: - raise ValueError('input "mask" should be a 2d array.') - - if sigma is not None: - mask = ni.gaussian_filter(mask, sigma=sigma) - - mask = ia.array_nor(mask) - mask_s = mask.flatten() - - indx_low = np.argsort(mask_s) - cum_eng_low = np.cumsum(mask_s[indx_low] ** 2) - cum_eng_low /= cum_eng_low[-1] - mask_eng_low = np.ones(mask_s.shape, dtype=np.float) - mask_eng_low[indx_low] = cum_eng_low - mask_eng_low = mask_eng_low.reshape(mask.shape) - - indx_high = np.argsort(mask_s)[::-1] - cum_eng_high = np.cumsum(mask_s[indx_high] ** 2) - cum_eng_high /= cum_eng_high[-1] - mask_eng_high = np.ones(mask_s.shape, dtype=np.float) - mask_eng_high[indx_high] = cum_eng_high - mask_eng_high = mask_eng_high.reshape(mask.shape) - - mask_bin = np.ones(mask.shape) - - mask_bin[mask_eng_high < thr_high] = 0. - mask_bin[mask_eng_low < thr_low] = 0. - - mask_labeled, mask_num = ni.label(mask_bin, structure=[[1,1,1], [1,1,1], [1,1,1]]) - mask_dict = ia.get_masks(labeled=mask_labeled, keyPrefix='', labelLength=5) - - for key, value in mask_dict.items(): - mask_w = value * mask - mask_w = mask_w / np.amax(mask_w) - mask_dict[key] = ia.WeightedROI(mask_w) - - return mask_dict - - if __name__ == '__main__': - - # =========================================================================== - nwb_f = h5py.File(r"Z:\chandelier_cell_project\M447219\2019-06-25-deepscope\190625_M447219_110.nwb", 'r') - plot_roi_traces_three_planes(nwb_f=nwb_f, roi0='roi_0000', roi1='roi_0004', roi2='roi_0003') - nwb_f.close() - # =========================================================================== - # =========================================================================== # dateRecorded = '150930' # mouseID = '187474' @@ -1841,14 +1655,12 @@ def threshold_mask_by_energy(mask, sigma=1., thr_high=0.0, thr_low=0.1): # =========================================================================== # =========================================================================== - # input_folder = r"\\aibsdata2\nc-ophys\CorticalMapping\IntrinsicImageData" \ - # r"\170404-M302706\2p_movies\for_segmentation\tempdir" - # c, s = array_to_rois(input_folder=input_folder, overlap_threshold=0.9, neuropil_limit=(5, 10), is_plot=True) - # print c.shape - # print s.shape + input_folder = r"\\aibsdata2\nc-ophys\CorticalMapping\IntrinsicImageData" \ + r"\170404-M302706\2p_movies\for_segmentation\tempdir" + c, s = array_to_rois(input_folder=input_folder, overlap_threshold=0.9, neuropil_limit=(5, 10), is_plot=True) + print(c.shape) + print(s.shape) # =========================================================================== - print 'for debug...' - - + print('for debug...') diff --git a/corticalmapping/MotionCorrection.py b/corticalmapping/MotionCorrection.py new file mode 100644 index 0000000..cf3869a --- /dev/null +++ b/corticalmapping/MotionCorrection.py @@ -0,0 +1,336 @@ +__author__ = 'junz' + +import os +import numpy as np +import tifffile as tf +from . import core.ImageAnalysis as ia +from . import core.FileTools as ft +import matplotlib.pyplot as plt + + +try: import cv2; from .core.ImageAnalysis import rigid_transform_cv2 as rigid_transform +except ImportError as e: print(e); from .core.ImageAnalysis import rigid_transform as rigid_transform + + +plt.ioff() + +def iamstupid(imgMat, imgRef, maxDisplacement=10, normFunc=ia.array_diff): + ''' + + align two images with rigid transformation + + :param imgRef: reference image + :param imgMat: matching image + :param maxDisplacement: maximum displacement, if single value, it will be apply to both rows and columns. if two + values, it will be [rowMaxDisplacement, columnMaxDisplacement] + :param normFunc: the function to calculate the distance between two images + :return: + : offSet: final offSet + : hitLimitFlag: the Flag to mark if the maxDisplacement limit was hit for row and column + ''' + + try: + rowMaxDisplacement = int(abs(maxDisplacement[0])) + columnMaxDisplacement = int(abs(maxDisplacement[1])) + except TypeError: rowMaxDisplacement = columnMaxDisplacement = int(abs(maxDisplacement)) + + #temporary code + # imgMat[imgMat<150]=0 + # imgRef[imgRef<150]=0 + # imgMat[imgMat>400]=400 + # imgRef[imgRef>400]=400 + + prevDis = normFunc(imgRef,imgMat) + prevOffset = [0, 0] + hitLimitFlag = [0, 0] + tryList = [[-1, 0], [0, -1], [1, 0], [0, 1]] + currDisList = np.array([normFunc(imgRef, rigid_transform(imgMat, offset=o, outputShape=imgRef.shape)) for o in tryList]) + currDis = np.min(currDisList) + minInd = np.where(currDisList == currDis)[0] + if len(minInd)>0: minInd = minInd[0] + currOffset = tryList[minInd] + + while currDis < prevDis: + prevDis = currDis; prevOffset = currOffset + + tryList = [] + if abs(prevOffset[0]) != rowMaxDisplacement: + if prevOffset[0] < 0: tryList.append([prevOffset[0]-1, prevOffset[1]]) + elif prevOffset[0] > 0: tryList.append([prevOffset[0]+1, prevOffset[1]]) + else: tryList += [[1, prevOffset[1]],[-1, prevOffset[1]]] + else: hitLimitFlag[0] = 1 + + if abs(prevOffset[1]) != columnMaxDisplacement: + if prevOffset[1] < 0: tryList.append([prevOffset[0], prevOffset[1]-1]) + elif prevOffset[1] > 0: tryList.append([prevOffset[0], prevOffset[1]+1]) + else: tryList += [[prevOffset[0], 1],[prevOffset[0], -1]] + else: hitLimitFlag[1] = 1 + + if len(tryList)>0: + currDisList = np.array([normFunc(imgRef, rigid_transform(imgMat, offset=o, outputShape=imgRef.shape)) for o in tryList]) + currDis = np.min(currDisList) + currOffset = tryList[np.where(currDisList == currDis)[0]] + else:break + + return np.array(prevOffset,dtype=np.int), hitLimitFlag + + +def getDistanceList(img, imgRef, normFunc=ia.array_diff, isPlot = False): + ''' + get the list of distances from each frame in img to the reference image, imgRef + normFunc is the function to calculate distance between two frames + ''' + + distanceList = np.zeros(img.shape[0]) + for i in range(img.shape[0]): + distanceList[i] = normFunc(img[i,:,:], imgRef) + if isPlot: + disMedian = np.median(distanceList) + disStd = np.std(distanceList) + f = plt.figure(figsize=(15,8)) + ax1 = f.add_subplot(211); ax1.plot(distanceList), ax1.set_ylim([0,200]);ax1.set_title('distance from mean for each frame') + ax2 = f.add_subplot(212); _ = ax2.hist(distanceList, bins=50, range=(disMedian-3*disStd,disMedian+3*disStd)); ax2.set_title('distribution of distances') + return distanceList, f + else: return distanceList + + +def alignSingleMovie(mov, imgRef, badFrameDistanceThr=100, maxDisplacement=10, normFunc=ia.array_diff, verbose=False, alignOrder=1): + ''' + align the frames in a single movie to the imgRef + + the frame with distance from mean projection larger than badFramdDistanceThr will not be used to update current + offset, nor will be included in calculation of new mean projection + + if order is 1: alignment goes from the first frame to the last + if order is -1: alignment goes from the last frame to the first, this is faster in the alignSingleMovieLoop function + + return: offsetList, alignedMov, meanFrame + ''' + + dataType = mov.dtype + currOffset = np.array([0,0]).astype(np.int) + offsetList = [] + alignedMov = np.empty(mov.shape,dtype=dataType) + validFrameNum = [] + + if alignOrder == 1: iterFrames = list(range(mov.shape[0])) + if alignOrder == -1: iterFrames = list(range(mov.shape[0]))[::-1] + + for i in iterFrames: + if normFunc(mov[i,:,:],imgRef)<=badFrameDistanceThr: + if np.array_equal(currOffset,np.array([0,0])):initCurrFrame = mov[i,:,:] + else: initCurrFrame = rigid_transform(mov[i, :, :], offset=currOffset, outputShape=imgRef.shape) + additionalOffset, hitFlag = iamstupid(initCurrFrame,imgRef,maxDisplacement=maxDisplacement,normFunc=normFunc) + currOffset = currOffset+additionalOffset + alignedMov[i,:,:] = rigid_transform(mov[i, :, :], offset=currOffset, outputShape=imgRef.shape) + offsetList.append(currOffset) + validFrameNum.append(i) + if verbose: + print('Frame'+ft.int2str(i,5)+'\tdistance:'+str(normFunc(mov[i,:,:],imgRef))+'\tgood Frame'+'\tOffset:'+str(currOffset)) + else: + alignedMov[i,:,:] = rigid_transform(mov[i, :, :], offset=currOffset, outputShape=imgRef.shape) + offsetList.append(currOffset) + if verbose: + print('Frame'+ft.int2str(i,5)+'\tdistance:'+str(normFunc(mov[i,:,:],imgRef))+'\tbad Frame'+'\tOffset:'+str(currOffset)) + + meanFrame = np.mean(alignedMov[np.array(validFrameNum),:,:],axis=0) + if alignOrder == -1: offsetList = offsetList[::-1] + return offsetList, alignedMov, meanFrame + + +def alignSingleMovieLoop(mov, iterations=2, badFrameDistanceThr=100, maxDisplacement=10, normFunc=ia.array_diff, verbose=False): + ''' + align a single movie with iterations, every time it will use mean frame from last iteration as imgRef + + For every iteration it calls MotionCorrection.alignSingleMovie function + + the imgRef for first iteration is the last frame of the movie + ''' + + if iterations < 1: raise ValueError('Iterations should be an integer larger than 0!') + + else: + offsetList, alignedMov, meanFrame = alignSingleMovie(mov,mov[-1,:,:],badFrameDistanceThr=badFrameDistanceThr,maxDisplacement=maxDisplacement,normFunc=normFunc,verbose=verbose,alignOrder=-1) + + if iterations == 1: + return offsetList, alignedMov, meanFrame + else: + allOffsetList = np.array(offsetList) + for i in range(iterations-1): + offsetList, alignedMov, meanFrame = alignSingleMovie(alignedMov,meanFrame,badFrameDistanceThr=badFrameDistanceThr,maxDisplacement=maxDisplacement,normFunc=normFunc,verbose=verbose) + allOffsetList += offsetList + return allOffsetList, alignedMov,meanFrame + + +def alignMultipleTiffs(paths, + iterations=2, + badFrameDistanceThr=100, + maxDisplacement=10, + normFunc=ia.array_diff, + verbose=True, + output=False, + saveFolder=None, + fileNameSurfix='corrected', + cameraBias=0): + ''' + motion correction of mulitiple tif file by using rigid plane transformation. Motion correction will be applied both + within and across tif files + + paths: paths of input tif files + iterations: number of iterations to perform motion correction8 + badFrameDistanceThr: the threshold of distance to define a good or bad frame, if a frame has distance from reference + framebigger than this value, it will be defined as bad frame, it will not be included in mean + frame calculation + normFunc: function to calculate distance between two frames. + options: corticalmapping.core.ImageAnalysis.array_diff (mean of absolute difference across all pixels) + corticalmapping.core.ImageAnalysis.distance (Frobenius distance or Euclidean norm) + + verbose: if True, print alignment information for each frame + output: if True, generate and save motion corrected tif files + saveFolder: if None, corrected files will be saved in the same folder of original data + fileNameSurfix: surfix of corrected file names + ''' + + if saveFolder is not None: + fileNameList = [os.path.split(p)[1] for p in paths] + if len(set(fileNameList)) 1: + print('\nPlotting distance distribution across mean frames of each file ...') + _, f = getDistanceList(meanFrames,meanFrames[0,:,:],normFunc=normFunc,isPlot=True) + f.suptitle('Distances across files'); plt.show() + print('Start alignment across files...') + fileOffset, allMeanFrames, aveMeanFrame = alignSingleMovieLoop(meanFrames,iterations=5,badFrameDistanceThr=65535,maxDisplacement=maxDisplacement,normFunc=normFunc,verbose=verbose) + print('Plotting mean frame of each file before and after cross file alignment ...') + tf.imshow(np.dstack((np.array(meanFrames), np.array(allMeanFrames))),photometric='miniswhite', cmap='gray'); plt.show() + + for i, path in enumerate(paths): + offsets[i] = offsets[i] + fileOffset[i,:] + print('Saving motion correction results for file:', path) + fileFolder, fileName = os.path.split(path) + newFileName = os.path.splitext(fileName)[0]+'_correction_results.pkl' + if saveFolder is not None: savePath = os.path.join(saveFolder,newFileName) + else: savePath = os.path.join(fileFolder,newFileName) + ft.saveFile(savePath,{'offset':offsets[i],'meanFrame':allMeanFrames[i,:,:].astype(np.float32),'path':path,'status':'cross_files'}) + print('End of cross file alignment.\n') + else: print('\nThere is only one file in the list. No need to align across files\n'); aveMeanFrame = meanFrames[0] + + if output: + for i, path in enumerate(paths): + print('Generating output image file for '+path) + fileFolder, fileName = os.path.split(path) + newFileName = ('_'+fileNameSurfix).join(os.path.splitext(fileName)) + if saveFolder is None: newPath = os.path.join(fileFolder,newFileName) + else: newPath = os.path.join(saveFolder,newFileName) + mov = tf.imread(path) + for j in range(mov.shape[0]): + if not np.array_equal(offsets[i][j,:], np.array([0,0])): + mov[j,:,:] = rigid_transform(mov[j, :, :], offset=offsets[i][j, :]) + tf.imsave(newPath, mov-cameraBias) + + return offsets, aveMeanFrame + + +if __name__=='__main__': + + #====================================================================================================== + # img_orig = tf.imread(r"C:\JunZhuang\labwork\data\python_temp_folder\motion_correction\original.tif") + # img_move = tf.imread(r"C:\JunZhuang\labwork\data\python_temp_folder\motion_correction\moved.tif") + # + # offset, _ = iamstupid(img_orig,img_move) + # assert(offset == [-4,-7]) + #====================================================================================================== + + + #====================================================================================================== + # imgPath = r'Z:\Jun\150610-M160809\KSStim_B2U_10Sweeps\KSStim_B2U_10sweeps_001_001.tif' + # imgPath = r"E:\data2\2015-06-11-python-2P-analysis-test\motion_correction_test\for_Jun\test_001.tif" + # img = tf.imread(imgPath) + # distanceList = getDistanceList(img,img[0,:,:],isPlot=True) + # #====================================================================================================== + + #====================================================================================================== + # imgPath = r"E:\data2\2015-06-11-python-2P-analysis-test\motion_correction_test\for_Jun\test_001.tif" + # img = tf.imread(imgPath) + # offset, _ = iamstupid(img[1,:,:],img[2,:,:]) + # print offset + #====================================================================================================== + + #====================================================================================================== + # imgPath = r"E:\data2\2015-06-11-python-2P-analysis-test\motion_correction_test\for_Jun\test_001.tif" + # img = tf.imread(imgPath) + # offsetList, alignedMov, meanFrame = alignSingleMovie(img,img[0,:,:],badFrameDistanceThr=110) + # + # tf.imshow(np.dstack((img,alignedMov)),cmap='gray') + # plt.show() + #====================================================================================================== + + #====================================================================================================== + # imgPath = r"E:\data2\2015-06-11-python-2P-analysis-test\motion_correction_test\for_Jun\test_001.tif" + # img = tf.imread(imgPath) + # offsetList, alignedMov, meanFrame = alignSingleMovie(img,img[0,:,:],badFrameDistanceThr=110) + # + # img2Path = r"E:\data2\2015-06-11-python-2P-analysis-test\motion_correction_test\for_Jun\test_002.tif" + # img2 = tf.imread(img2Path) + # offsetList2, alignedMov2, meanFrame2 = alignSingleMovie(img2,img2[0,:,:],badFrameDistanceThr=110) + # + # print np.hstack((offsetList,offsetList2)) + #====================================================================================================== + + #====================================================================================================== + # imgPath = r"E:\data2\2015-06-11-python-2P-analysis-test\motion_correction_test\for_Jun\test_001.tif" + # img = tf.imread(imgPath) + # offsetList, alignedMov, meanFrame = alignSingleMovieLoop(img,badFrameDistanceThr=110) + # print offsetList + #====================================================================================================== + + #====================================================================================================== + # imgPath = r"E:\data2\2015-06-11-python-2P-analysis-test\motion_correction_test\for_Jun\test_001.tif" + # img = tf.imread(imgPath) + # offsetList, alignedMov, meanFrame = alignSingleMovie(img,img[-1,:,:],badFrameDistanceThr=110,alignOrder=1) + # offsetList2, alignedMov2, meanFrame2 = alignSingleMovie(img,img[-1,:,:],badFrameDistanceThr=110,alignOrder=-1) + # print np.hstack((offsetList,offsetList2)) + # tf.imshow(np.dstack((img,alignedMov,alignedMov2)),cmap='gray') + # plt.show() + #====================================================================================================== + + #====================================================================================================== + # paths=[ + # r"E:\data2\2015-06-11-python-2P-analysis-test\motion_correction_test\for_Jun\test_001.tif", + # r"E:\data2\2015-06-11-python-2P-analysis-test\motion_correction_test\for_Jun\test_002.tif" + # ] + # offsets, meanFrame = alignMultipleTiffs(paths, + # iterations=2, + # badFrameDistanceThr=100, + # maxDisplacement=10, + # normFunc=ia.array_diff, + # verbose=False, + # output=True, + # saveFolder=None, + # fileNameSurfix='corrected', + # cameraBias=0) + # + # print offsets[0]-offsets[1] + #====================================================================================================== + + print('for debug...') \ No newline at end of file diff --git a/corticalmapping/NwbTools.py b/corticalmapping/NwbTools.py index 6d1e2be..f0742bb 100644 --- a/corticalmapping/NwbTools.py +++ b/corticalmapping/NwbTools.py @@ -8,58 +8,55 @@ import corticalmapping.core.FileTools as ft import corticalmapping.core.TimingAnalysis as ta import corticalmapping.core.PlottingTools as pt -import corticalmapping.CamstimTools as ct - try: from nwb.nwb import NWB except ImportError: - print 'no Allen Institute NWB API. get this from ' \ - 'http://stimash.corp.alleninstitute.org/projects/INF/repos/ainwb/browse' + print('no Allen Institute NWB API. get this from ' \ + 'http://stimash.corp.alleninstitute.org/projects/INF/repos/ainwb/browse') DEFAULT_GENERAL = { - 'session_id': '', - 'experimenter': '', - 'institution': 'Allen Institute for Brain Science', - # 'lab': '', - # 'related_publications': '', - 'notes': '', - 'experiment_description': '', - # 'data_collection': '', - 'stimulus': '', - # 'pharmacology': '', - # 'surgery': '', - # 'protocol': '', - 'subject': { - 'subject_id': '', - # 'description': '', - 'species': 'Mus musculus', - 'genotype': '', - 'sex': '', - 'age': '', - # 'weight': '', - }, - # 'virus': '', - # 'slices': '', - 'extracellular_ephys': { - 'electrode_map': '', - 'sampling_rate': 30000., - # 'electrode_group': [], - # 'impedance': [], - # 'filtering': [] - }, - 'optophysiology': { - # 'indicator': '', - # 'excitation_lambda': '', - # 'imaging_rate': '', - # 'location': '', - # 'device': '', - }, - # 'optogenetics': {}, - 'devices': {} -} + 'session_id': '', + 'experimenter': '', + 'institution': 'Allen Institute for Brain Science', + # 'lab': '', + # 'related_publications': '', + 'notes': '', + 'experiment_description': '', + # 'data_collection': '', + 'stimulus': '', + # 'pharmacology': '', + # 'surgery': '', + # 'protocol': '', + 'subject': { + 'subject_id': '', + # 'description': '', + 'species': 'Mus musculus', + 'genotype': '', + 'sex': '', + 'age': '', + # 'weight': '', + }, + # 'virus': '', + # 'slices': '', + 'extracellular_ephys': { + 'electrode_map': '', + 'sampling_rate': 30000., + # 'electrode_group': [], + # 'impedance': [], + # 'filtering': [] + }, + 'optophysiology': { + # 'indicator': '', + # 'excitation_lambda': '', + # 'imaging_rate': '', + # 'location': '', + # 'device': '', + }, + # 'optogenetics': {}, + 'devices': {} + } SPIKE_WAVEFORM_TIMEWINDOW = (-0.002, 0.002) - def plot_waveforms(waveforms, ch_locations=None, stds=None, waveforms_filtered=None, stds_filtered=None, f=None, ch_ns=None, axes_size=(0.2, 0.2), **kwargs): """ @@ -113,7 +110,7 @@ def plot_waveforms(waveforms, ch_locations=None, stds=None, waveforms_filtered=N curr_wf_f = waveforms_filtered[:, j] if stds_filtered is not None: curr_std_f = stds_filtered[:, j] - ax.fill_between(range(waveforms_filtered.shape[0]), curr_wf_f - curr_std_f, + ax.fill_between(list(range(waveforms_filtered.shape[0])), curr_wf_f - curr_std_f, curr_wf_f + curr_std_f, color='#888888', alpha=0.5, edgecolor='none') ax.plot(curr_wf_f, '-', color='#555555', label='filtered', **kwargs) @@ -121,8 +118,8 @@ def plot_waveforms(waveforms, ch_locations=None, stds=None, waveforms_filtered=N curr_wf = waveforms[:, j] if stds is not None: curr_std = stds[:, j] - ax.fill_between(range(waveforms.shape[0]), curr_wf - curr_std, curr_wf + curr_std, - color='#8888ff', alpha=0.5, edgecolor='none') + ax.fill_between(list(range(waveforms.shape[0])), curr_wf - curr_std, curr_wf + curr_std, + color='#8888ff',alpha=0.5, edgecolor='none') ax.plot(curr_wf, '-', color='#3333ff', label='unfiltered', **kwargs) # plot title @@ -151,16 +148,16 @@ def __init__(self, filename, is_manual_check=False, **kwargs): if is_manual_check: keyboard_input = '' while keyboard_input != 'y' and keyboard_input != 'n': - keyboard_input = raw_input('\nthe path "' + filename + '" already exists. Modify it? (y/n) \n') + keyboard_input = input('\nthe path "' + filename + '" already exists. Modify it? (y/n) \n') if keyboard_input == 'y': super(RecordedFile, self).__init__(filename=filename, modify=True, **kwargs) elif keyboard_input == 'n': raise IOError('file already exists.') else: - print('\nModifying existing nwb file: ' + filename) + print(('\nModifying existing nwb file: ' + filename)) super(RecordedFile, self).__init__(filename=filename, modify=True, **kwargs) else: - print('\nCreating a new nwb file: ' + filename) + print(('\nCreating a new nwb file: ' + filename)) super(RecordedFile, self).__init__(filename=filename, modify=False, **kwargs) def add_general(self, general=DEFAULT_GENERAL, is_overwrite=True): @@ -170,194 +167,6 @@ def add_general(self, general=DEFAULT_GENERAL, is_overwrite=True): slf = self.file_pointer ft.write_dictionary_to_h5group_recursively(target=slf['general'], source=general, is_overwrite=is_overwrite) - def add_sync_data(self, f_path, analog_downsample_rate=None, by_label=True, digital_labels=None, - analog_labels=None): - - sync_dict = ft.read_sync(f_path=f_path, analog_downsample_rate=analog_downsample_rate, - by_label=by_label, digital_labels=digital_labels, - analog_labels=analog_labels) - - # add digital channel - if 'digital_channels' in sync_dict.keys(): - - digital_channels = sync_dict['digital_channels'] - - # get channel names - for d_chn, d_ch in digital_channels.items(): - if ft.is_integer(d_chn): - curr_chn = 'digital_CH_' + ft.int2str(d_chn, 3) - else: - curr_chn = 'digital_' + d_chn - - curr_rise = d_ch['rise'] - ch_series_rise = self.create_timeseries('TimeSeries', curr_chn + '_rise', 'acquisition') - ch_series_rise.set_data([], unit='', conversion=np.nan, resolution=np.nan) - if len(curr_rise) == 0: - curr_rise = np.array([np.nan]) - ch_series_rise.set_time(curr_rise) - ch_series_rise.set_value('num_samples', 0) - else: - ch_series_rise.set_time(curr_rise) - ch_series_rise.set_description('timestamps of rise cross of digital channel: ' + curr_chn) - ch_series_rise.set_source('sync program') - ch_series_rise.set_comments('digital') - ch_series_rise.finalize() - - curr_fall = d_ch['fall'] - ch_series_fall = self.create_timeseries('TimeSeries', curr_chn + '_fall', 'acquisition') - ch_series_fall.set_data([], unit='', conversion=np.nan, resolution=np.nan) - if len(curr_fall) == 0: - curr_fall = np.array([np.nan]) - ch_series_fall.set_time(curr_fall) - ch_series_fall.set_value('num_samples', 0) - else: - ch_series_fall.set_time(curr_fall) - ch_series_fall.set_description('timestamps of fall cross of digital channel: ' + curr_chn) - ch_series_fall.set_source('sync program') - ch_series_fall.set_comments('digital') - ch_series_fall.finalize() - - # add analog channels - if 'analog_channels' in sync_dict.keys(): - - analog_channels = sync_dict['analog_channels'] - analog_fs = sync_dict['analog_sample_rate'] - - # get channel names - for a_chn, a_ch in analog_channels.items(): - if ft.is_integer(a_chn): - curr_chn = 'analog_CH_' + ft.int2str(a_chn, 3) - else: - curr_chn = 'analog_' + a_chn - - ch_series = self.create_timeseries('TimeSeries', curr_chn, 'acquisition') - ch_series.set_data(a_ch, unit='voltage', conversion=1., resolution=1.) - ch_series.set_time_by_rate(time_zero=0.0, rate=analog_fs) - ch_series.set_value('num_samples', len(a_ch)) - ch_series.set_comments('continuous') - ch_series.set_description('analog channel recorded by sync program') - ch_series.set_source('sync program') - ch_series.finalize() - - def add_acquisition_image(self, name, img, format='array', description=''): - """ - add arbitrarily recorded image into acquisition group, mostly surface vasculature image - :param name: - :param img: - :param format: - :param description: - :return: - """ - img_dset = self.file_pointer['acquisition/images'].create_dataset(name, data=img) - img_dset.attrs['format'] = format - img_dset.attrs['description'] = description - - def get_analog_data(self, ch_n): - """ - :param ch_n: string, analog channel name - :return: 1-d array, analog data, data * conversion - 1-d array, time stamps - """ - grp = self.file_pointer['acquisition/timeseries'][ch_n] - data = grp['data'].value - if not np.isnan(grp['data'].attrs['conversion']): - data = data.astype(np.float32) * grp['data'].attrs['conversion'] - if 'timestamps' in grp.keys(): - t = grp['timestamps'] - elif 'starting_time' in grp.keys(): - fs = grp['starting_time'].attrs['rate'] - sample_num = grp['num_samples'].value - t = np.arange(sample_num) / fs + grp['starting_time'].value - else: - raise ValueError('can not find timing information of channel:' + ch_n) - return data, t - - def _check_display_order(self, display_order=None): - """ - check display order make sure each presentation has a unique position, and move from increment order. - also check the given display_order is of the next number - """ - stimuli = self.file_pointer['stimulus/presentation'].keys() - - print('\nExisting visual stimuli:') - print('\n'.join(stimuli)) - - stimuli = [int(s[0:s.find('_')]) for s in stimuli] - stimuli.sort() - if stimuli != range(len(stimuli)): - raise ValueError('display order is not incremental.') - - if display_order is not None: - - if display_order != len(stimuli): - raise ValueError('input display order not the next display.') - - # ===========================photodiode related===================================================================== - def add_photodiode_onsets(self, photodiode_ch_path='acquisition/timeseries/photodiode', - digitizeThr=0.9, filterSize=0.01, segmentThr=0.01, smallestInterval=0.03, - expected_onsets_number=None): - """ - intermediate processing step for analysis of visual display. Containing the information about the onset of - photodiode signal. Timestamps are extracted from photodiode signal, should be aligned to the master clock. - extraction is done by corticalmapping.HighLevel.segmentPhotodiodeSignal() function. The raw signal - was first digitized by the digitize_threshold, then filtered by a gaussian fileter with filter_size. Then - the derivative of the filtered signal was calculated by numpy.diff. The derivative signal was then timed - with the digitized signal. Then the segmentation_threshold was used to detect rising edge of the resulting - signal. Any onset with interval from its previous onset smaller than smallest_interval will be discarded. - the resulting timestamps of photodiode onsets will be saved in 'analysis/photodiode_onsets' timeseries - - :param digitizeThr: float - :param filterSize: float - :param segmentThr: float - :param smallestInterval: float - :param expected_onsets_number: int, expected number of photodiode onsets, may extract from visual display - log. if extracted onset number does not match this number, the process will - be abort. If None, no such check will be performed. - :return: - """ - - pd_grp = self.file_pointer[photodiode_ch_path] - - fs = pd_grp['starting_time'].attrs['rate'] - - pd = pd_grp['data'].value * pd_grp['data'].attrs['conversion'] - - pd_onsets = hl.segmentPhotodiodeSignal(pd, digitizeThr=digitizeThr, filterSize=filterSize, - segmentThr=segmentThr, Fs=fs, smallestInterval=smallestInterval) - - if pd_onsets.shape[0] == 0: - return - - if expected_onsets_number is not None: - if len(pd_onsets) != expected_onsets_number: - raise ValueError('The number of photodiode onsets (' + str(len(pd_onsets)) + ') and the expected ' - 'number of sweeps ' + str( - expected_onsets_number) + ' do not match. Abort.') - - pd_ts = self.create_timeseries('TimeSeries', 'photodiode_onsets', modality='other') - pd_ts.set_time(pd_onsets) - pd_ts.set_data([], unit='', conversion=np.nan, resolution=np.nan) - pd_ts.set_description('intermediate processing step for analysis of visual display. ' - 'Containing the information about the onset of photodiode signal. Timestamps ' - 'are extracted from photodiode signal, should be aligned to the master clock.' - 'extraction is done by corticalmapping.HighLevel.segmentPhotodiodeSignal()' - 'function. The raw signal was first digitized by the digitize_threshold, then ' - 'filtered by a gaussian fileter with filter_size. Then the derivative of the filtered ' - 'signal was calculated by numpy.diff. The derivative signal was then timed with the ' - 'digitized signal. Then the segmentation_threshold was used to detect rising edge of ' - 'the resulting signal. Any onset with interval from its previous onset smaller than ' - 'smallest_interval will be discarded.') - pd_ts.set_path('/analysis/PhotodiodeOnsets') - pd_ts.set_value('digitize_threshold', digitizeThr) - pd_ts.set_value('fileter_size', filterSize) - pd_ts.set_value('segmentation_threshold', segmentThr) - pd_ts.set_value('smallest_interval', smallestInterval) - pd_ts.finalize() - - # ===========================photodiode related===================================================================== - - - # ===========================ephys related========================================================================== def add_open_ephys_data(self, folder, prefix, digital_channels=()): """ add open ephys raw data to self, in acquisition group, less useful, because the digital events needs to be @@ -369,7 +178,7 @@ def add_open_ephys_data(self, folder, prefix, digital_channels=()): """ output = oew.pack_folder_for_nwb(folder=folder, prefix=prefix, digital_channels=digital_channels) - for key, value in output.items(): + for key, value in list(output.items()): if 'CH' in key: # analog channel for electrode recording ch_ind = int(key[key.find('CH') + 2:]) @@ -407,10 +216,10 @@ def add_open_ephys_data(self, folder, prefix, digital_channels=()): else: # digital events - for key2, value2 in value.items(): + for key2, value2 in list(value.items()): ch_rise_ts = value2['rise'] - ch_series_rise = self.create_timeseries('TimeSeries', key2 + '_rise', 'acquisition') + ch_series_rise = self.create_timeseries('TimeSeries', key2+'_rise', 'acquisition') ch_series_rise.set_data([], unit='', conversion=np.nan, resolution=np.nan) if len(ch_rise_ts) == 0: ch_rise_ts = np.array([np.nan]) @@ -447,7 +256,7 @@ def add_open_ephys_continuous_data(self, folder, prefix): """ output = oew.pack_folder_for_nwb(folder=folder, prefix=prefix) - for key, value in output.items(): + for key, value in list(output.items()): if 'CH' in key: # analog channel for electrode recording ch_ind = int(key[key.find('CH') + 2:]) @@ -483,6 +292,53 @@ def add_open_ephys_continuous_data(self, folder, prefix): ch_series.set_source('open ephys') ch_series.finalize() + def add_acquisition_image(self, name, img, format='array', description=''): + """ + add arbitrarily recorded image into acquisition group, mostly surface vasculature image + :param name: + :param img: + :param format: + :param description: + :return: + """ + img_dset = self.file_pointer['acquisition/images'].create_dataset(name, data=img) + img_dset.attrs['format'] = format + img_dset.attrs['description'] = description + + def add_acquired_image_series_as_remote_link(self, name, image_file_path, dataset_path, timestamps, + description='', comments='', data_format='zyx', pixel_size=np.nan, + pixel_size_unit=''): + """ + add a required image series in to acquisition field as a link to an external hdf5 file. + :param name: str, name of the image series + :param image_file_path: str, the full file system path to the hdf5 file containing the raw image data + :param dataset_path: str, the path within the hdf5 file pointing to the raw data. the object should have at + least 3 attributes: 'conversion', resolution, unit + :param timestamps: 1-d array, the length of this array should be the same as number of frames in the image data + :param data_format: str, required field for ImageSeries object + :param pixel_size: array, size of pixel + :param pixel_size_unit: str, unit of pixel size + :return: + """ + + img_file = h5py.File(image_file_path) + img_data = img_file[dataset_path] + if timestamps.shape[0] != img_data.shape[0]: + raise ValueError('Number of frames does not equal to the length of timestamps!') + img_series = self.create_timeseries(ts_type='ImageSeries', name=name, modality='acquisition') + img_series.set_data_as_remote_link(image_file_path, dataset_path) + img_series.set_time(timestamps) + img_series.set_description(description) + img_series.set_comments(comments) + img_series.set_value('bits_per_pixel', img_data.dtype.itemsize * 8) + img_series.set_value('format', data_format) + img_series.set_value('dimension', img_data.shape) + img_series.set_value('image_file_path', image_file_path) + img_series.set_value('image_data_path_within_file', dataset_path) + img_series.set_value('pixel_size', pixel_size) + img_series.set_value('pixel_size_unit', pixel_size_unit) + img_series.finalize() + def add_phy_template_clusters(self, folder, module_name, ind_start=None, ind_end=None, is_add_artificial_unit=False, artificial_unit_firing_rate=2., spike_sorter=None): @@ -559,7 +415,7 @@ def add_phy_template_clusters(self, folder, module_name, ind_start=None, ind_end # create UnitTimes interface unit_times = mod.create_interface('UnitTimes') - for unit in spike_ind.keys(): + for unit in list(spike_ind.keys()): # get timestamps of current unit curr_ts = spike_ind[unit] @@ -657,10 +513,9 @@ def add_phy_template_clusters(self, folder, module_name, ind_start=None, ind_end unit_times.finalize() mod.finalize() - def add_external_LFP(self, traces, fs=30000., module_name=None, notch_base=60., notch_bandwidth=1., - notch_harmonics=4, + def add_external_LFP(self, traces, fs=30000., module_name=None, notch_base=60., notch_bandwidth=1., notch_harmonics=4, notch_order=2, lowpass_cutoff=300., lowpass_order=5, resolution=0, conversion=0, unit='', - comments='', source=''): + comments='', source=''): """ add LFP of raw arbitrary electrical traces into LFP module into /procession field. the trace will be filtered by corticalmapping.HighLevel.get_lfp() function. All filters are butterworth digital filters @@ -681,12 +536,12 @@ def add_external_LFP(self, traces, fs=30000., module_name=None, notch_base=60., :param source: str, interface source """ - if module_name is None or module_name == '': + if module_name is None or module_name=='': module_name = 'external_LFP' lfp = {} - for tn, trace in traces.items(): - curr_lfp = hl.get_lfp(trace, fs=fs, notch_base=notch_base, notch_bandwidth=notch_bandwidth, + for tn, trace in list(traces.items()): + curr_lfp = hl.get_lfp(trace,fs=fs, notch_base=notch_base, notch_bandwidth=notch_bandwidth, notch_harmonics=notch_harmonics, notch_order=notch_order, lowpass_cutoff=lowpass_cutoff, lowpass_order=lowpass_order) lfp.update({tn: curr_lfp}) @@ -695,9 +550,9 @@ def add_external_LFP(self, traces, fs=30000., module_name=None, notch_base=60., lfp_mod.set_description('LFP from external traces') lfp_interface = lfp_mod.create_interface('LFP') lfp_interface.set_value('description', 'LFP of raw arbitrary electrical traces. The traces were filtered by ' - 'corticalmapping.HighLevel.get_lfp() function. First, the powerline contamination at ' - 'multiplt harmonics were filtered out by a notch filter. Then the resulting traces were' - ' filtered by a lowpass filter. All filters are butterworth digital filters') + 'corticalmapping.HighLevel.get_lfp() function. First, the powerline contamination at ' + 'multiplt harmonics were filtered out by a notch filter. Then the resulting traces were' + ' filtered by a lowpass filter. All filters are butterworth digital filters') lfp_interface.set_value('comments', comments) lfp_interface.set_value('notch_base', notch_base) lfp_interface.set_value('notch_bandwidth', notch_bandwidth) @@ -706,7 +561,7 @@ def add_external_LFP(self, traces, fs=30000., module_name=None, notch_base=60., lfp_interface.set_value('lowpass_cutoff', lowpass_cutoff) lfp_interface.set_value('lowpass_order', lowpass_order) lfp_interface.set_source(source) - for tn, t_lfp in lfp.items(): + for tn, t_lfp in list(lfp.items()): curr_ts = self.create_timeseries('ElectricalSeries', tn, modality='other') curr_ts.set_data(t_lfp, conversion=conversion, resolution=resolution, unit=unit) curr_ts.set_time_by_rate(time_zero=0., rate=fs) @@ -738,7 +593,7 @@ def add_internal_LFP(self, continuous_channels, module_name=None, notch_base=60. :param source: str, interface source """ - if module_name is None or module_name == '': + if module_name is None or module_name=='': module_name = 'LFP' lfp_mod = self.create_module(module_name) @@ -759,7 +614,8 @@ def add_internal_LFP(self, continuous_channels, module_name=None, notch_base=60. lfp_interface.set_source(source) for channel in continuous_channels: - print '\n', channel, ': start adding LFP ...' + + print('\n', channel, ': start adding LFP ...') trace = self.file_pointer['acquisition/timeseries'][channel]['data'].value fs = self.file_pointer['acquisition/timeseries'][channel]['starting_time'].attrs['rate'] @@ -769,7 +625,7 @@ def add_internal_LFP(self, continuous_channels, module_name=None, notch_base=60. unit = self.file_pointer['acquisition/timeseries'][channel]['data'].attrs['unit'] ts_source = self.file_pointer['acquisition/timeseries'][channel].attrs['source'] - print channel, ': calculating LFP ...' + print(channel, ': calculating LFP ...') t_lfp = hl.get_lfp(trace, fs=fs, notch_base=notch_base, notch_bandwidth=notch_bandwidth, notch_harmonics=notch_harmonics, notch_order=notch_order, lowpass_cutoff=lowpass_cutoff, @@ -782,1190 +638,228 @@ def add_internal_LFP(self, continuous_channels, module_name=None, notch_base=60. curr_ts.set_value('electrode_idx', int(channel.split('_')[1])) curr_ts.set_source(ts_source) lfp_interface.add_timeseries(curr_ts) - print channel, ': finished adding LFP.' + print(channel, ': finished adding LFP.') lfp_interface.finalize() lfp_mod.finalize() - def plot_spike_waveforms(self, modulen, unitn, is_plot_filtered=False, fig=None, axes_size=(0.2, 0.2), **kwargs): + def add_visual_stimulation(self, log_path, display_order=0): """ - plot spike waveforms - - :param modulen: str, name of the module containing ephys recordings - :param unitn: str, name of ephys unit, should be in '/processing/ephys_units/UnitTimes' - :param is_plot_filtered: bool, plot unfiltered waveforms or not - :param channel_names: list of strs, channel names in continuous recordings, should be in '/acquisition/timeseries' - :param fig: matplotlib figure object - :param t_range: tuple of two floats, time range to plot along spike time stamps - :param kwargs: inputs to matplotlib.axes.plot() function - :return: fig + load visual stimulation given saved display log pickle file + :param log_path: the path to the display log generated by corticalmapping.VisualStim + :param display_order: int, in case there is more than one visual display in the file. + This value records the order of the displays + :return: """ - if modulen not in self.file_pointer['processing'].keys(): - raise LookupError('Can not find module for ephys recording: ' + modulen + '.') - - if unitn not in self.file_pointer['processing'][modulen]['UnitTimes'].keys(): - raise LookupError('Can not find ephys unit: ' + unitn + '.') + self._check_display_order(display_order) - ch_ns = self._get_channel_names() + log_dict = ft.loadFile(log_path) - unit_grp = self.file_pointer['processing'][modulen]['UnitTimes'][unitn] - waveforms = unit_grp['template'].value + stim_name = log_dict['stimulation']['stimName'] - if 'template_std' in unit_grp.keys(): - stds = unit_grp['template_std'].value - else: - stds = None + display_frames = log_dict['presentation']['displayFrames'] + time_stamps = log_dict['presentation']['timeStamp'] - if is_plot_filtered: - if 'template_filtered' in unit_grp.keys(): - waveforms_f = unit_grp['template_filtered'].value - if 'template_std_filtered' in unit_grp.keys(): - stds_f = unit_grp['template_std_filtered'].value - else: - stds_f = None - else: - print('can not find unfiltered spike waveforms for unit: ' + unitn) - waveforms_f = None - stds_f = None - else: - waveforms_f = None - stds_f = None + if len(display_frames) != len(time_stamps): + print(('\nWarning: {}'.format(log_path))) + print(('Unequal number of displayFrames ({}) and timeStamps ({}).'.format(len(display_frames), + len(time_stamps)))) - if 'channel_xpos' in self.file_pointer['processing'][modulen].keys(): - ch_xpos = self.file_pointer['processing'][modulen]['channel_xpos'] - ch_ypos = self.file_pointer['processing'][modulen]['channel_ypos'] - ch_locations = zip(ch_xpos, ch_ypos) + if stim_name == 'SparseNoise': + self._add_sparse_noise_stimulation(log_dict, display_order=display_order) + elif stim_name == 'FlashingCircle': + self._add_flashing_circle_stimulation(log_dict, display_order=display_order) + elif stim_name == 'UniformContrast': + self._add_uniform_contrast_stimulation(log_dict, display_order=display_order) + elif stim_name == 'DriftingGratingCircle': + self._add_drifting_grating_circle_stimulation(log_dict, display_order=display_order) + elif stim_name == 'KSstimAllDir': + self._add_drifting_checker_board_stimulation(log_dict, display_order=display_order) else: - ch_locations = None + raise ValueError('stimulation name {} unrecognizable!'.format(stim_name)) - fig = plot_waveforms(waveforms, ch_locations=ch_locations, stds=stds, waveforms_filtered=waveforms_f, - stds_filtered=stds_f, f=fig, ch_ns=ch_ns, axes_size=axes_size, **kwargs) + def add_visual_stimulations(self, log_paths): - fig.suptitle(self.file_pointer['identifier'].value + ' : ' + unitn) + exist_stimuli = list(self.file_pointer['stimulus/presentation'].keys()) - return fig + for i, log_path in enumerate(log_paths): + self.add_visual_stimulation(log_path, i + len(exist_stimuli)) - def generate_dat_file_for_kilosort(self, output_folder, output_name, ch_ns, is_filtered=True, cutoff_f_low=300., - cutoff_f_high=6000.): + @staticmethod + def _analyze_sparse_noise_frames(sn_grp): """ - generate .dat file for kilolsort: "https://github.com/cortex-lab/KiloSort", it is binary raw code, with - structure: ch0_t0, ch1_t0, ch2_t0, ...., chn_t0, ch0_t1, ch1_t1, ch2_t1, ..., chn_t1, ..., ch0_tm, ch1_tm, - ch2_tm, ..., chn_tm + analyze sparse noise display frames saved in '/stimulus/presentation', extract information about onset of + each displayed square: - :param output_folder: str, path to output directory - :param output_name: str, output file name, an extension of '.dat' will be automatically added. - :param ch_ns: list of strings, name of included analog channels - :param is_filtered: bool, if Ture, another .dat file with same size will be generated in the output folder. - this file will contain temporally filtered data (filter done by - corticalmapping.core.TimingAnalysis.butter_... functions). '_filtered' will be attached - to the filtered file name. - :param cutoff_f_low: float, low cutoff frequency, Hz. if None, it will be low-pass - :param cutoff_f_high: float, high cutoff frequency, Hz, if None, it will be high-pass - :return: None + return: all_squares: 2-d array, each line is a displayed square in sparse noise, each column is a feature of + a particular square, squares follow display order + data_format: str, description of the column structure of each square + description: str, + pooled_squares: dict, squares with same location and sign are pooled together. + keys: 'square_00000', 'square_00001', 'square_00002' ... each represents a unique + square. + values: dict, { + 'azi': , + 'alt': , + 'sign': , + 'onset_ind': list of indices of the appearances of current square in + in "all_squares", to be aligned with to photodiode onset + timestamps + } """ - save_path = os.path.join(output_folder, output_name + '.dat') - if os.path.isfile(save_path): - raise IOError('Output file already exists.') - - data_lst = [] - for ch_n in ch_ns: - data_lst.append(self.file_pointer['acquisition/timeseries'][ch_n]['data'].value) - - dtype = data_lst[0].dtype - data = np.array(data_lst, dtype=dtype).flatten(order='F') - data.tofile(save_path) - - if is_filtered: - - if cutoff_f_low is None and cutoff_f_high is None: - print ('both low cutoff frequency and high cutoff frequency are None. Do nothing.') - return - - save_path_f = os.path.join(output_folder, output_name + '_filtered.dat') - if os.path.isfile(save_path_f): - raise IOError('Output file for filtered data already existes.') - - fs = self.file_pointer['general/extracellular_ephys/sampling_rate'].value - data_lst_f = [] - for data_r in data_lst: - if cutoff_f_high is None: - data_lst_f.append(ta.butter_lowpass(data_r, fs=fs, cutoff=cutoff_f_low).astype(dtype)) - elif cutoff_f_low is None: - data_lst_f.append(ta.butter_highpass(data_r, fs=fs, cutoff=cutoff_f_high).astype(dtype)) - else: - data_lst_f.append(ta.butter_bandpass(data_r, - fs=fs, - cutoffs=(cutoff_f_low, cutoff_f_high)).astype(dtype)) - data_f = np.array(data_lst_f, dtype=dtype).flatten(order='F') - data_f.tofile(save_path_f) - - def _get_channel_names(self): - """ - :return: sorted list of channel names, each channel name should have prefix 'ch_' - """ - analog_chs = self.file_pointer['acquisition/timeseries'].keys() - channel_ns = [cn for cn in analog_chs if cn[0:3] == 'ch_'] - channel_ns.sort() - return channel_ns - - # ===========================ephys related========================================================================== - - - # ===========================2p movie related======================================================================= - def add_acquired_image_series_as_remote_link(self, name, image_file_path, dataset_path, timestamps, - description='', comments='', data_format='zyx', pixel_size=np.nan, - pixel_size_unit=''): - """ - add a required image series in to acquisition field as a link to an external hdf5 file. - :param name: str, name of the image series - :param image_file_path: str, the full file system path to the hdf5 file containing the raw image data - :param dataset_path: str, the path within the hdf5 file pointing to the raw data. the object should have at - least 3 attributes: 'conversion', resolution, unit - :param timestamps: 1-d array, the length of this array should be the same as number of frames in the image data - :param data_format: str, required field for ImageSeries object - :param pixel_size: array, size of pixel - :param pixel_size_unit: str, unit of pixel size - :return: - """ - - img_file = h5py.File(image_file_path) - img_data = img_file[dataset_path] - if timestamps.shape[0] != img_data.shape[0]: - raise ValueError('Number of frames does not equal to the length of timestamps!') - img_series = self.create_timeseries(ts_type='ImageSeries', name=name, modality='acquisition') - img_series.set_data_as_remote_link(image_file_path, dataset_path) - img_series.set_time(timestamps) - img_series.set_description(description) - img_series.set_comments(comments) - img_series.set_value('bits_per_pixel', img_data.dtype.itemsize * 8) - img_series.set_value('format', data_format) - img_series.set_value('dimension', img_data.shape) - img_series.set_value('image_file_path', image_file_path) - img_series.set_value('image_data_path_within_file', dataset_path) - img_series.set_value('pixel_size', pixel_size) - img_series.set_value('pixel_size_unit', pixel_size_unit) - img_series.finalize() - - def add_motion_correction_module(self, module_name, original_timeseries_path, corrected_file_path, - corrected_dataset_path, xy_translation_offsets, interface_name='MotionCorrection', - mean_projection=None, max_projection=None, description='', comments='', - source=''): - """ - add a motion corrected image series in to processing field as a module named 'motion_correction' and create a - link to an external hdf5 file which contains the images. - :param module_name: str, module name to be created - :param interface_name: str, interface name of the image series - :param original_timeseries_path: str, the path to the timeseries of the original images - :param corrected_file_path: str, the full file system path to the hdf5 file containing the raw image data - :param corrected_dataset_path: str, the path within the hdf5 file pointing to the motion corrected data. - the object should have at least 3 attributes: 'conversion', resolution, unit - :param xy_translation_offsets: 2d array with two columns, - :param mean_projection: 2d array, mean_projection of corrected image, if None, no dataset will be - created - :param max_projection: 2d array, max_projection of corrected image, if None, no dataset will be - created - :return: - """ - - orig = self.file_pointer[original_timeseries_path] - timestamps = orig['timestamps'].value - - img_file = h5py.File(corrected_file_path) - img_data = img_file[corrected_dataset_path] - if timestamps.shape[0] != img_data.shape[0]: - raise ValueError('Number of frames does not equal to the length of timestamps!') - - if xy_translation_offsets.shape[0] != timestamps.shape[0]: - raise ValueError('Number of offsets does not equal to the length of timestamps!') - - corrected = self.create_timeseries(ts_type='ImageSeries', name='corrected', modality='other') - corrected.set_data_as_remote_link(corrected_file_path, corrected_dataset_path) - corrected.set_time_as_link(original_timeseries_path) - corrected.set_description(description) - corrected.set_comments(comments) - corrected.set_source(source) - for value_n in orig.keys(): - if value_n not in ['image_data_path_within_file', 'image_file_path', 'data', 'timestamps']: - corrected.set_value(value_n, orig[value_n].value) - - xy_translation = self.create_timeseries(ts_type='TimeSeries', name='xy_translation', modality='other') - xy_translation.set_data(xy_translation_offsets, unit='pixel', conversion=np.nan, resolution=np.nan) - xy_translation.set_time_as_link(original_timeseries_path) - xy_translation.set_value('num_samples', xy_translation_offsets.shape[0]) - xy_translation.set_description('Time series of x, y shifts applied to create motion stabilized image series') - xy_translation.set_value('feature_description', ['x_motion', 'y_motion']) - - mc_mod = self.create_module(module_name) - mc_interf = mc_mod.create_interface("MotionCorrection") - mc_interf.add_corrected_image(interface_name, orig=original_timeseries_path, xy_translation=xy_translation, - corrected=corrected) - - if mean_projection is not None: - mc_interf.set_value('mean_projection', mean_projection) - - if max_projection is not None: - mc_interf.set_value('max_projection', max_projection) - - mc_interf.finalize() - mc_mod.finalize() - - def add_muliple_dataset_to_motion_correction_module(self, input_parameters, module_name='motion_correction', - temporal_downsample_rate=1): - """ - add multiple motion corrected datasets into a motion correction module. Designed for adding multiplane - imaging datasets at once. The motion correction module will contain multiple interfaces each corresponding - to one imaging plane. - - :param input_parameters: list of dictionaries, each dictionary in the list represents one imaging plane - the dictionary should contain the following keys: - 'field_name': str, name of the hdf5 group for the motion correction information - 'original_timeseries_path': str, the path to the timeseries of the original images - 'corrected_file_path': str, the full file system path to the hdf5 file - containing the corrected image data - 'corrected_dataset_path': str, the path within the hdf5 file pointing to the motion - corrected data. the object should have at least 3 - attributes: 'conversion', resolution and unit - 'xy_translation_offsets': 2d array with two columns - 'mean_projection': optional, 2d array, mean_projection of corrected image, - if not existing, no dataset will be created - 'max_projection': optional, 2d array, max_projection of corrected image, - if not existing, no dataset will be created - 'description': optional, str, if not existing, it will be set as '' - 'comments': optional, str, if not existing, it will be set as '' - 'source': optional, str, if not existing, it will be set as '' - :param module_name: str, module name to be created - :param temporal_downsample_rate: int, >0, in case the movie was motion corrected before temporal downsample, - use only a subset of offsets. - """ - - mc_mod = self.create_module(module_name) - mc_interf = mc_mod.create_interface('MotionCorrection') - - for mov_dict in input_parameters: - if 'description' not in mov_dict.keys(): - mov_dict['description'] = '' - - if 'comments' not in mov_dict.keys(): - mov_dict['comment'] = '' - - if 'source' not in mov_dict.keys(): - mov_dict['source'] = '' - - orig = self.file_pointer[mov_dict['original_timeseries_path']] - timestamps = orig['timestamps'].value - # print(timestamps.shape) - - img_file = h5py.File(mov_dict['corrected_file_path'], 'r') - img_data = img_file[mov_dict['corrected_dataset_path']] - # print(img_data.shape) - if timestamps.shape[0] != img_data.shape[0]: - raise ValueError('Number of frames does not equal to the length of timestamps!') - - offsets = mov_dict['xy_translation_offsets'] - offsets = offsets[::temporal_downsample_rate, :] - # print(offsets.shape) - if offsets.shape[0] != timestamps.shape[0]: - raise ValueError('Number of offsets does not equal to the length of timestamps!') - - corrected = self.create_timeseries(ts_type='ImageSeries', name='corrected', modality='other') - corrected.set_data_as_remote_link(mov_dict['corrected_file_path'], - mov_dict['corrected_dataset_path']) - corrected.set_time_as_link(mov_dict['original_timeseries_path']) - corrected.set_description(mov_dict['description']) - corrected.set_comments(mov_dict['comments']) - corrected.set_source(mov_dict['source']) - - if 'mean_projection' in mov_dict.keys() and mov_dict['mean_projection'] is not None: - corrected.set_value('mean_projection', mov_dict['mean_projection']) - - if 'max_projection' in mov_dict.keys() and mov_dict['max_projection'] is not None: - corrected.set_value('max_projection', mov_dict['max_projection']) - - for value_n in orig.keys(): - if value_n not in ['image_data_path_within_file', 'image_file_path', 'data', 'timestamps']: - corrected.set_value(value_n, orig[value_n].value) - - xy_translation = self.create_timeseries(ts_type='TimeSeries', name='xy_translation', modality='other') - xy_translation.set_data(offsets, unit='pixel', conversion=np.nan, - resolution=np.nan) - xy_translation.set_time_as_link(mov_dict['original_timeseries_path']) - xy_translation.set_value('num_samples', offsets.shape[0]) - xy_translation.set_description('Time series of x, y shifts applied to create motion ' - 'stabilized image series') - xy_translation.set_value('feature_description', ['x_motion', 'y_motion']) - - mc_interf.add_corrected_image(mov_dict['field_name'], orig=mov_dict['original_timeseries_path'], - xy_translation=xy_translation, - corrected=corrected) - - mc_interf.finalize() - mc_mod.finalize() - - # ===========================2p movie related======================================================================= - - - # ===========================camstim visual stimuli related========================================================= - def add_display_frame_ts_camstim(self, pkl_dict, max_mismatch=0.1, verbose=True, refresh_rate=60., - allowed_jitter=0.01): - - ts_pd_fall = self.file_pointer['acquisition/timeseries/digital_photodiode_fall/timestamps'].value - ts_display_rise = self.file_pointer['acquisition/timeseries/digital_vsync_visual_rise/timestamps'].value - - ts_display_real, display_lag = ct.align_visual_display_time(pkl_dict=pkl_dict, ts_pd_fall=ts_pd_fall, - ts_display_rise=ts_display_rise, - max_mismatch=max_mismatch, - verbose=verbose, refresh_rate=refresh_rate, - allowed_jitter=allowed_jitter) - - frame_ts = self.create_timeseries('TimeSeries', 'FrameTimestamps', modality='other') - frame_ts.set_time(ts_display_rise) - frame_ts.set_data([], unit='', conversion=np.nan, resolution=np.nan) - frame_ts.set_description('onset timestamps of each display frames after correction for display lag. ' - 'Used corticalmapping.HighLevel.align_visual_display_time() function to ' - 'calculate display lag.') - frame_ts.set_path('/processing/visual_display') - frame_ts.set_value('max_mismatch_sec', max_mismatch) - frame_ts.set_value('refresh_rate_hz', refresh_rate) - frame_ts.set_value('allowed_jitter_sec', allowed_jitter) - frame_ts.finalize() - - display_lag_ts = self.create_timeseries('TimeSeries', 'DisplayLag', modality='other') - display_lag_ts.set_time(display_lag[:, 0]) - display_lag_ts.set_data(display_lag[:, 1], unit='second', conversion=np.nan, resolution=np.nan) - display_lag_ts.set_path('/processing/visual_display') - display_lag_ts.set_value('mean_display_lag_sec', np.mean(display_lag[:, 1])) - display_lag_ts.finalize() - - def _add_drifting_grating_stimulation_camstim(self, stim_dict): - - dgts = self.create_timeseries(ts_type='TimeSeries', - name=stim_dict['stim_name'], - modality='stimulus') - - dgts.set_time(stim_dict['sweep_onset_frames']) - dgts.set_data(stim_dict['sweeps'], unit='', conversion=np.nan, resolution=np.nan) - dgts.set_source(stim_dict['source']) - dgts.set_comments(stim_dict['comments']) - dgts.set_description(stim_dict['description']) - for fn, fv in stim_dict.items(): - if fn not in ['sweep_onset_frames', 'sweeps', 'sources', 'comments', 'description']: - dgts.set_value(fn, fv) - dgts.finalize() - - def _add_locally_sparse_noise_stimulation_camstim(self, stim_dict): - - lsnts = self.create_timeseries(ts_type='TimeSeries', - name=stim_dict['stim_name'], - modality='stimulus') - lsnts.set_time(stim_dict['global_frame_ind']) - lsnts.set_data(stim_dict['probes'], unit='', conversion=np.nan, resolution=np.nan) - lsnts.set_source(stim_dict['source']) - lsnts.set_comments(stim_dict['comments']) - lsnts.set_description(stim_dict['description']) - for fn, fv in stim_dict.items(): - if fn not in ['probes', 'global_frame_ind', 'sources', 'comments', 'description']: - lsnts.set_value(fn, fv) - lsnts.finalize() - - def add_visual_stimuli_camstim(self, stim_dict_lst): - - for stim_dict in stim_dict_lst: - if stim_dict['stim_type'] == 'drifting_grating_camstim': - print('adding stimulus: {} to nwb.'.format(stim_dict['stim_name'])) - self._add_drifting_grating_stimulation_camstim(stim_dict=stim_dict) - elif stim_dict['stim_type'] == 'locally_sparse_noise_camstim': - print('adding stimulus: {} to nwb.'.format(stim_dict['stim_name'])) - self._add_locally_sparse_noise_stimulation_camstim(stim_dict=stim_dict) - else: - pass - - # ===========================camstim visual stimuli related========================================================= + if sn_grp['stim_name'].value != 'SparseNoise': + raise NameError('The input stimulus should be "SparseNoise".') + frames = sn_grp['data'].value + frames = [tuple(x) for x in frames] + dtype = [('isDisplay', int), ('azimuth', float), ('altitude', float), ('sign', int), ('isOnset', int)] + frames = np.array(frames, dtype=dtype) - # ===========================retinotopic_mapping visual stimuli related (indexed display)=========================== - def add_visual_display_log_retinotopic_mapping(self, stim_log): - """ - add visual display log into nwb. + all_squares = [] + for i in range(len(frames)): + if frames[i]['isDisplay'] == 1 and \ + (i == 0 or (frames[i - 1]['isOnset'] == -1 and frames[i]['isOnset'] == 1)): + all_squares.append(np.array((i, frames[i]['azimuth'], frames[i]['altitude'], frames[i]['sign']), + dtype=np.float32)) - :param stim_log: retinotopic_mapping.DisplayLogAnalysis.DisplayLogAnalyzer instance - :return: None - """ + all_squares = np.array(all_squares) - stim_dict = stim_log.get_stim_dict() - stim_ns = stim_dict.keys() - stim_ns.sort() - for stim_n in stim_ns: - curr_stim_dict = stim_dict[stim_n] - - print('\nadding {} to nwb ...'.format(stim_n)) - - if stim_n[-35:] == 'StimulusSeparatorRetinotopicMapping': - self._add_stimulus_separator_retinotopic_mapping(curr_stim_dict) - elif stim_n[-33:] == 'UniformContrastRetinotopicMapping': - self._add_uniform_contrast_retinotopic_mapping(curr_stim_dict) - elif stim_n[-32:] == 'FlashingCircleRetinotopicMapping': - self._add_flashing_circle_retinotopic_mapping(curr_stim_dict) - elif stim_n[-39:] == 'DriftingGratingCircleRetinotopicMapping': - self._add_drifting_grating_circle_retinotopic_mapping(curr_stim_dict) - elif stim_n[-37:] == 'StaticGratingCircleRetinotopicMapping': - self._add_static_grating_circle_retinotopic_mapping(curr_stim_dict) - elif stim_n[-30:] == '_SparseNoiseRetinotopicMapping': - self._add_sparse_noise_retinotopic_mapping(curr_stim_dict) - elif stim_n[-36:] == 'LocallySparseNoiseRetinotopicMapping': - self._add_locally_sparse_noise_retinotopic_mapping(curr_stim_dict) - elif stim_n[-30:] == 'StaticImagesRetinotopicMapping': - self._add_static_images_retinotopic_mapping(curr_stim_dict) - elif stim_n[-37:] == 'SinusoidalLuminanceRetinotopicMapping': - self._add_sinusoidal_luminance_retinotopic_mapping(curr_stim_dict) - else: - raise ValueError('Do not understand stimulus name: {}.'.format(stim_n)) + pooled_squares = {} + unique_squares = list(set([tuple(x[1:]) for x in all_squares])) + for i, unique_square in enumerate(unique_squares): + curr_square_n = 'square_' + ft.int2str(i, 5) + curr_azi = unique_square[0] + curr_alt = unique_square[1] + curr_sign = unique_square[2] + curr_onset_ind = [] + for j, give_square in enumerate(all_squares): + if np.array_equal(give_square[1:], unique_square): + curr_onset_ind.append(j) + pooled_squares.update({curr_square_n: {'azi': curr_azi, + 'alt': curr_alt, + 'sign': curr_sign, + 'onset_ind': curr_onset_ind}}) + all_squares = np.array(all_squares) + data_format = ['display frame indices for the onset of each square', 'azimuth of each square', + 'altitude of each square', 'sign of each square'] + description = 'TimeSeries of sparse noise square onsets. Stimulus generated by ' \ + 'corticalmapping.VisualStim.SparseNoise class.' + return all_squares, data_format, description, pooled_squares - def get_display_delay_retinotopic_mapping(self, stim_log, indicator_color_thr=0.5, ccg_t_range=(0., 0.1), - ccg_bins=100, is_plot=True, pd_onset_ts_path=None, - vsync_frame_ts_path=None): + @staticmethod + def _analyze_driftig_grating_frames(dg_grp): """ + analyze drifting grating display frames saved in '/stimulus/presentation', extract information about onset of + each displayed grating: - :param stim_log: retinotopic_mapping.DisplayLogAnalysis.DisplayLogAnalyzer instance - :param indicator_color_thr: float, [-1., 1.] - :param ccg_t_range: - :param ccg_bins: - :param is_plot: - :param pd_onset_ts_path: str, path to the timeseries of photodiode onsets in seconds - :return: + return: all_gratings: 2-d array, each line is a displayed square in sparse noise, each column is a feature of + a particular square, squares follow display order + data_format: str, description of the column structure of each grating + description: str, + pooled_squares: dict, gratings with same parameters are pooled together. + keys: 'grating_00000', 'grating_00001', 'grating_00002' ... each represents a unique + grating. + values: dict, { + 'sf': , + 'tf': , + 'direction': , + 'contrast': , + 'radius': , + 'azi': + 'alt': + 'onset_ind': list of indices of the appearances of current square in + in "all_squares", to be aligned with to photodiode onset + timestamps + } """ + if dg_grp['stim_name'].value != 'DriftingGratingCircle': + raise NameError('The input stimulus should be "DriftingGratingCircle".') - # get photodiode onset timestamps (after display) - if pd_onset_ts_path is None: - if 'acquisition/timeseries/digital_photodiode_rise' in self.file_pointer: - pd_ts_pd = self.file_pointer['acquisition/timeseries/digital_photodiode_rise/timestamps'].value - elif 'analysis/PhotodiodeOnsets' in self.file_pointer: - pd_ts_pd = self.file_pointer['analysis/PhotodiodeOnsets/timestamps'].value - else: - raise LookupError('Cannot find photodiode onset timeseries.') - else: - pd_ts_pd = self.file_pointer[pd_onset_ts_path + '/timestamps'].value - - # get vsync TTL timestamps for displayed frames - if vsync_frame_ts_path is None: - if 'acquisition/timeseries/digital_vsync_stim_rise' in self.file_pointer: - vsync_ts = self.file_pointer['acquisition/timeseries/digital_vsync_stim_rise/timestamps'].value - elif 'acquisition/timeseries/digital_vsync_visual_rise' in self.file_pointer: - vsync_ts = self.file_pointer['acquisition/timeseries/digital_vsync_visual_rise/timestamps'].value - else: - raise LookupError('Cannot find vsync TTL signal for displayed frames.') - else: - vsync_ts = self.file_pointer[vsync_frame_ts_path + '/timestamps'].value - - # check vsync_stim number and total frame number - print('\nnumber of total frames in log file: {}'.format(stim_log.num_frame_tot)) - print('number of vsync_stim TTL rise events: {}'.format(len(vsync_ts))) - if stim_log.num_frame_tot != len(vsync_ts): - raise ValueError('number of vsync_stim TTL rise events does not equal number of total frames in log file!') - - # get photodiode onset timestamps from vsync_stim (before display) - stim_dict = stim_log.get_stim_dict() - pd_onsets_seq = stim_log.analyze_photodiode_onsets_sequential(stim_dict=stim_dict, pd_thr=indicator_color_thr) - - pd_ts_vsync = [] - for pd_onset in pd_onsets_seq: - - if pd_onset['global_frame_ind'] < len(vsync_ts): - pd_ts_vsync.append(vsync_ts[pd_onset['global_frame_ind']]) - - - # calculate display delay as the weighted average of pd_ccg - print('Total number of detected photodiode onsets: {}'.format(len(pd_ts_pd))) - print('calculating photodiode cross-correlogram ...') - pd_ccg = ta.discrete_cross_correlation(pd_ts_vsync, pd_ts_pd, t_range=ccg_t_range, bins=ccg_bins, - isPlot=is_plot) - if is_plot: - plt.show() + frames = dg_grp['data'].value - display_delay = np.sum(pd_ccg[0] * pd_ccg[1]) / np.sum(pd_ccg[1]) - print('calculated display delay: {} second.'.format(display_delay)) - self.file_pointer['analysis/visual_display_delay_sec'] = display_delay + all_gratings = [] + for i in range(len(frames)): + if frames[i][8] == 1 and (i == 0 or (frames[i - 1][8] == -1)): + all_gratings.append(np.array((i, frames[i][2], frames[i][3], frames[i][4], frames[i][5], frames[i][6]), + dtype=np.float32)) - return display_delay + all_gratings = np.array(all_gratings) - def add_photodiode_onsets_combined_retinotopic_mapping(self, pd_onsets_com, display_delay, - vsync_frame_path='acquisition/timeseries/digital_vsync_stim_rise'): - """ - add combined photodiode onsets to self, currently the field is 'analysis/photodiode_onsets' + pooled_gratings = {} + unique_gratings = list(set([tuple(x[1:]) for x in all_gratings])) + for i, unique_grating in enumerate(unique_gratings): + curr_grating_n = 'grating_' + ft.int2str(i, 5) + curr_sf = unique_grating[0] + curr_tf = unique_grating[1] + curr_dir = unique_grating[2] + curr_con = unique_grating[3] + curr_r = unique_grating[4] + curr_onset_ind = [] + for j, given_grating in enumerate(all_gratings): + if np.array_equal(given_grating[1:], unique_grating): + curr_onset_ind.append(j) + pooled_gratings.update({curr_grating_n: {'sf': curr_sf, + 'tf': curr_tf, + 'dir': curr_dir, + 'con': curr_con, + 'r': curr_r, + 'onset_ind': curr_onset_ind}}) + data_format = ['display frame indices for the onset of each square', 'spatial frequency (cyc/deg)', + 'temporal frequency (Hz)', 'moving direction (arc)', 'contrast (%)', 'radius (deg)'] + description = 'TimeSeries of drifting grating circle onsets. Stimulus generated by ' \ + 'corticalmapping.VisualStim.SparseNoise class.' + return all_gratings, data_format, description, pooled_gratings - :param pd_onsets_com: dictionary, product of - retinotopic_mapping.DisplayLogAnalysis.DisplayLogAnalyzer.analyze_photodiode_onsets_combined() - function - :param display_delay: float, display delay in seconds - :param vsync_frame_path: str, hdf5 path to digital timeseries of digital_vsync_frame_rise - :return: None + @staticmethod + def _analyze_flashing_circle_frames(fc_grp): """ + analyze flashing circle display frames saved in '/stimulus/presentation', extract information about onset of + each displayed circle: - vsync_stim_ts = self.file_pointer[vsync_frame_path]['timestamps'].value + display_delay - - stim_ns = pd_onsets_com.keys() - stim_ns.sort() - - pd_grp = self.file_pointer['analysis'].create_group('photodiode_onsets') - for stim_n in stim_ns: - stim_grp = pd_grp.create_group(stim_n) - pd_onset_ns = pd_onsets_com[stim_n].keys() - pd_onset_ns.sort() - for pd_onset_n in pd_onset_ns: - pd_onset_grp = stim_grp.create_group(pd_onset_n) - pd_onset_grp['global_pd_onset_ind'] = pd_onsets_com[stim_n][pd_onset_n]['global_pd_onset_ind'] - pd_onset_grp['global_frame_ind'] = pd_onsets_com[stim_n][pd_onset_n]['global_frame_ind'] - - try: - pd_onset_grp['pd_onset_ts_sec'] = vsync_stim_ts[pd_onsets_com[stim_n][pd_onset_n]['global_frame_ind']] - except IndexError: - pd_onset_ts_sec = [] - for gfi in pd_onsets_com[stim_n][pd_onset_n]['global_frame_ind']: - if gfi < len(vsync_stim_ts): - pd_onset_ts_sec.append(vsync_stim_ts[gfi]) - pd_onset_grp['pd_onset_ts_sec'] = pd_onset_ts_sec - - - def get_drifting_grating_response_table_retinotopic_mapping(self, stim_name, time_window=(-1, 2.5)): - - def get_sta(arr, arr_ts, trigger_ts, frame_start, frame_end): - - sta_arr = [] - - for trig in trigger_ts: - trig_ind = ta.find_nearest(arr_ts, trig) - - if trig_ind + frame_end < arr.shape[1]: - curr_sta = arr[:, (trig_ind + frame_start): (trig_ind + frame_end)] - # print(curr_sta.shape) - sta_arr.append(curr_sta.reshape((curr_sta.shape[0], 1, curr_sta.shape[1]))) - - sta_arr = np.concatenate(sta_arr, axis=1) - return sta_arr - - if time_window[0] >= time_window[1]: - raise ValueError('time window should be from early time to late time.') - - grating_onsets_path = 'analysis/photodiode_onsets/{}'.format(stim_name) - grating_ns = self.file_pointer[grating_onsets_path].keys() - grating_ns.sort() - # print('\n'.join(grating_ns)) - - rois_and_traces_names = self.file_pointer['processing'].keys() - rois_and_traces_names = [n for n in rois_and_traces_names if n[0:15] == 'rois_and_traces'] - rois_and_traces_names.sort() - # print('\n'.join(rois_and_traces_paths)) - - res_grp = self.file_pointer['analysis'].create_group('response_table_{}'.format(stim_name)) - for curr_trace_name in rois_and_traces_names: - - print('\nadding drifting grating response table for {} ...'.format(curr_trace_name)) - - curr_plane_n = curr_trace_name[16:] - - res_grp_plane = res_grp.create_group(curr_plane_n) - - # get trace time stamps - trace_ts = self.file_pointer['processing/motion_correction/MotionCorrection' \ - '/{}/corrected/timestamps'.format(curr_plane_n)] - # get traces - traces = {} - if 'processing/{}/DfOverF/dff_center'.format(curr_trace_name) in self.file_pointer: - traces['global_dff_center'] = self.file_pointer[ - 'processing/{}/DfOverF/dff_center/data'.format(curr_trace_name)].value - if 'processing/{}/Fluorescence'.format(curr_trace_name) in self.file_pointer: - f_types = self.file_pointer['processing/{}/Fluorescence'.format(curr_trace_name)].keys() - for f_type in f_types: - traces[f_type] = self.file_pointer['processing/{}/Fluorescence/{}/data' - .format(curr_trace_name, f_type)].value - # print(traces.keys()) - - # frame_dur = np.mean(np.diff(trace_ts)) - # frame_start = int(time_window[0] // frame_dur) - # frame_end = int(time_window[1] // frame_dur) - # t_axis = np.arange(frame_end - frame_start) * frame_dur + time_window[0] - - frame_dur = np.mean(np.diff(trace_ts)) - frame_start = int(np.floor(time_window[0] / frame_dur)) - frame_end = int(np.ceil(time_window[1] / frame_dur)) - - t_axis = np.arange(frame_end - frame_start) * frame_dur + (frame_start * frame_dur) - res_grp_plane.attrs['sta_timestamps'] = t_axis - - for grating_n in grating_ns: - - onsets_grating_grp = self.file_pointer['{}/{}'.format(grating_onsets_path, grating_n)] - - curr_grating_grp = res_grp_plane.create_group(grating_n) - - grating_onsets = onsets_grating_grp['pd_onset_ts_sec'].value - - curr_grating_grp.attrs['global_trigger_timestamps'] = grating_onsets - curr_grating_grp.attrs['sta_traces_dimenstion'] = 'roi x trial x timepoint' - - for trace_n, trace in traces.items(): - sta = get_sta(arr=trace, arr_ts=trace_ts, trigger_ts=grating_onsets, frame_start=frame_start, - frame_end=frame_end) - curr_grating_grp.create_dataset('sta_' + trace_n, data=sta, compression='lzf') - - def get_spatial_temporal_receptive_field_retinotopic_mapping(self, stim_name, time_window=(-0.5, 2.), - verbose=True): - - def get_sta(arr, arr_ts, trigger_ts, frame_start, frame_end): - - sta_arr = [] - - for trig in trigger_ts: - trig_ind = ta.find_nearest(arr_ts, trig) - - if trig_ind + frame_end < arr.shape[1]: - curr_sta = arr[:, (trig_ind + frame_start): (trig_ind + frame_end)] - # print(curr_sta.shape) - sta_arr.append(curr_sta.reshape((curr_sta.shape[0], 1, curr_sta.shape[1]))) - - sta_arr = np.concatenate(sta_arr, axis=1) - return sta_arr - - if time_window[0] >= time_window[1]: - raise ValueError('time window should be from early time to late time.') - - probe_onsets_path = 'analysis/photodiode_onsets/{}'.format(stim_name) - probe_ns = self.file_pointer[probe_onsets_path].keys() - probe_ns.sort() - # print('\n'.join(probe_ns)) - - rois_and_traces_names = self.file_pointer['processing'].keys() - rois_and_traces_names = [n for n in rois_and_traces_names if n[0:15] == 'rois_and_traces'] - rois_and_traces_names.sort() - # print('\n'.join(rois_and_traces_paths)) - - strf_grp = self.file_pointer['analysis'].create_group('strf_{}'.format(stim_name)) - for curr_trace_name in rois_and_traces_names: - - if verbose: - print('\nadding strfs for {} ...'.format(curr_trace_name)) - - curr_plane_n = curr_trace_name[16:] - - strf_grp_plane = strf_grp.create_group(curr_plane_n) - - # get trace time stamps - trace_ts = self.file_pointer['processing/motion_correction/MotionCorrection' \ - '/{}/corrected/timestamps'.format(curr_plane_n)] - # get traces - traces = {} - if 'processing/{}/DfOverF/dff_center'.format(curr_trace_name) in self.file_pointer: - traces['global_dff_center'] = self.file_pointer[ - 'processing/{}/DfOverF/dff_center/data'.format(curr_trace_name)].value - if 'processing/{}/Fluorescence'.format(curr_trace_name) in self.file_pointer: - f_types = self.file_pointer['processing/{}/Fluorescence'.format(curr_trace_name)].keys() - for f_type in f_types: - traces[f_type] = self.file_pointer['processing/{}/Fluorescence/{}/data' - .format(curr_trace_name, f_type)].value - # print(traces.keys()) - - frame_dur = np.mean(np.diff(trace_ts)) - frame_start = int(np.floor(time_window[0] / frame_dur)) - frame_end = int(np.ceil(time_window[1] / frame_dur)) - t_axis = np.arange(frame_end - frame_start) * frame_dur + (frame_start * frame_dur) - # t_axis = np.arange(frame_end - frame_start) * frame_dur + time_window[0] - - strf_grp_plane.attrs['sta_timestamps'] = t_axis - - for probe_i, probe_n in enumerate(probe_ns): - - if verbose: - print('\tprocessing probe {} / {}'.format(probe_i+1, len(probe_ns))) - - onsets_probe_grp = self.file_pointer['{}/{}'.format(probe_onsets_path, probe_n)] - - curr_probe_grp = strf_grp_plane.create_group(probe_n) - - probe_onsets = onsets_probe_grp['pd_onset_ts_sec'].value - - curr_probe_grp['global_trigger_timestamps'] = h5py.SoftLink('/{}/{}/pd_onset_ts_sec' - .format(probe_onsets_path, probe_n)) - curr_probe_grp.attrs['sta_traces_dimenstion'] = 'roi x trial x timepoint' - - for trace_n, trace in traces.items(): - sta = get_sta(arr=trace, arr_ts=trace_ts, trigger_ts=probe_onsets, frame_start=frame_start, - frame_end=frame_end) - curr_probe_grp.create_dataset('sta_' + trace_n, data=sta, compression='lzf') - - def _add_stimulus_separator_retinotopic_mapping(self, ss_dict): - - stim_name = ss_dict['stim_name'] - - if stim_name[-35:] != 'StimulusSeparatorRetinotopicMapping': - raise ValueError('stimulus should be "StimulusSeparatorRetinotopicMapping" (StimulusSeparator from ' - 'retinotopic_mapping package). ') - - # add template - template_ts = self.create_timeseries('TimeSeries', stim_name, 'template') - template_ts.set_data(ss_dict['frames_unique'], unit='', conversion=np.nan, resolution=np.nan) - template_ts.set_value('num_samples', len(ss_dict['frames_unique'])) - template_ts.set_source(ss_dict['source']) - template_ts.finalize() - - # add stimulus - stim_ts = self.create_timeseries('IndexSeries', stim_name, 'stimulus') - stim_ts.set_time(ss_dict['timestamps'], dtype='u8') - stim_ts.set_data(ss_dict['index_to_display'], unit='frame', conversion=1, resolution=1, dtype='u4') - stim_ts.set_value_as_link('indexed_timeseries', '/stimulus/templates/{}'.format(stim_name)) - stim_ts.set_comments('The "timestamps" of this TimeSeries are indices (64-bit unsigned integer, hacked the ' - 'original ainwb code) referencing the entire display sequence. It should match hardware ' - 'vsync TTL (see "/acquisition/timeseries/digital_vsync_stim/rise"). The "data" of this ' - 'TimeSeries are indices referencing the frames template saved in the "indexed_timeseries" ' - 'field.') - stim_ts.set_description('stimulus separator displayed by retinotopic_mapping package') - stim_ts.set_source(ss_dict['source']) - for key in ['frame_config', 'stim_name', 'pregap_dur', 'postgap_dur', 'coordinate', 'background']: - stim_ts.set_value(key, ss_dict[key]) - stim_ts.set_value('indicator_on_frame_num', ss_dict['indicator_on_frame_num']) - stim_ts.set_value('indicator_off_frame_num', ss_dict['indicator_off_frame_num']) - stim_ts.set_value('cycle_num', ss_dict['cycle_num']) - - stim_ts.finalize() - - def _add_uniform_contrast_retinotopic_mapping(self, uc_dict): - stim_name = uc_dict['stim_name'] - - if stim_name[-33:] != 'UniformContrastRetinotopicMapping': - raise ValueError('stimulus should be "UniformContrastRetinotopicMapping" (UniformContrast from ' - 'retinotopic_mapping package). ') - - # add template - template_ts = self.create_timeseries('TimeSeries', stim_name, 'template') - template_ts.set_data(uc_dict['frames_unique'], unit='', conversion=np.nan, resolution=np.nan) - template_ts.set_value('num_samples', len(uc_dict['frames_unique'])) - template_ts.set_source(uc_dict['source']) - template_ts.finalize() - - # add stimulus - stim_ts = self.create_timeseries('IndexSeries', stim_name, 'stimulus') - stim_ts.set_time(uc_dict['timestamps'], dtype='u8') - stim_ts.set_data(uc_dict['index_to_display'], unit='frame', conversion=1, resolution=1, dtype='u4') - stim_ts.set_value_as_link('indexed_timeseries', '/stimulus/templates/{}'.format(stim_name)) - stim_ts.set_comments('The "timestamps" of this TimeSeries are indices (64-bit unsigned integer, hacked the ' - 'original ainwb code) referencing the entire display sequence. It should match hardware ' - 'vsync TTL (see "/acquisition/timeseries/digital_vsync_stim/rise"). The "data" of this ' - 'TimeSeries are indices referencing the frames template saved in the "indexed_timeseries" ' - 'field.') - stim_ts.set_description('uniform contrast displayed by retinotopic_mapping package') - stim_ts.set_source(uc_dict['source']) - for key in ['frame_config', 'stim_name', 'pregap_dur', 'postgap_dur', 'coordinate', 'background']: - stim_ts.set_value(key, uc_dict[key]) - stim_ts.set_value('duration', uc_dict['duration']) - stim_ts.set_value('color', uc_dict['color']) - stim_ts.finalize() - - def _add_flashing_circle_retinotopic_mapping(self, fc_dict): - stim_name = fc_dict['stim_name'] - - if stim_name[-32:] != 'FlashingCircleRetinotopicMapping': - raise ValueError('stimulus should be "FlashingCircleRetinotopicMapping" (FlashingCircle from ' - 'retinotopic_mapping package). ') - - # add template - template_ts = self.create_timeseries('TimeSeries', stim_name, 'template') - template_ts.set_data(fc_dict['frames_unique'], unit='', conversion=np.nan, resolution=np.nan) - template_ts.set_value('num_samples', len(fc_dict['frames_unique'])) - template_ts.set_source(fc_dict['source']) - template_ts.finalize() - - # add stimulus - stim_ts = self.create_timeseries('IndexSeries', stim_name, 'stimulus') - stim_ts.set_time(fc_dict['timestamps'], dtype='u8') - stim_ts.set_data(fc_dict['index_to_display'], unit='frame', conversion=1, resolution=1, dtype='u4') - stim_ts.set_value_as_link('indexed_timeseries', '/stimulus/templates/{}'.format(stim_name)) - stim_ts.set_comments('The "timestamps" of this TimeSeries are indices (64-bit unsigned integer, hacked the ' - 'original ainwb code) referencing the entire display sequence. It should match hardware ' - 'vsync TTL (see "/acquisition/timeseries/digital_vsync_stim/rise"). The "data" of this ' - 'TimeSeries are indices referencing the frames template saved in the "indexed_timeseries" ' - 'field.') - stim_ts.set_description('flashing circle displayed by retinotopic_mapping package') - stim_ts.set_source(fc_dict['source']) - for key in ['frame_config', 'stim_name', 'pregap_dur', 'postgap_dur', 'coordinate', 'background']: - stim_ts.set_value(key, fc_dict[key]) - stim_ts.set_value('is_smooth_edge', fc_dict['is_smooth_edge']) - stim_ts.set_value('smooth_width_ratio', fc_dict['smooth_width_ratio']) - stim_ts.set_value('center', fc_dict['center']) - stim_ts.set_value('radius', fc_dict['radius']) - stim_ts.set_value('flash_frame_num', fc_dict['flash_frame_num']) - stim_ts.set_value('midgap_dur', fc_dict['midgap_dur']) - stim_ts.set_value('iteration', fc_dict['iteration']) - stim_ts.set_value('color', fc_dict['color']) - stim_ts.finalize() - - def _add_drifting_grating_circle_retinotopic_mapping(self, dgc_dict): - stim_name = dgc_dict['stim_name'] - - if stim_name[-39:] != 'DriftingGratingCircleRetinotopicMapping': - raise ValueError('stimulus should be "DriftingGratingCircleRetinotopicMapping" (DriftingGratingCircle from ' - 'retinotopic_mapping package). ') - - # add template - template_ts = self.create_timeseries('TimeSeries', stim_name, 'template') - frames_unique = dgc_dict['frames_unique'] - frames_template = [] - for frame in frames_unique: - - # temporally fix a bug - if frame == (1, 1, 0., 0., 0., 0., 0., 1.): - frame = (1, 1, 0., 0., 0., 0., 0., 0., 1.) - - if frame == (1, 1, 0., 0., 0., 0., 0., 0.): - frame = (1, 1, 0., 0., 0., 0., 0., 0., 0.) - - curr_frame = np.array(frame) - # print(curr_frame) - curr_frame[curr_frame == None] = np.nan - frames_template.append(np.array(curr_frame, dtype=np.float32)) - frames_template = np.array(frames_template) - template_ts.set_data(frames_template, unit='', conversion=np.nan, resolution=np.nan) - template_ts.set_value('num_samples', frames_template.shape[0]) - template_ts.set_source(dgc_dict['source']) - template_ts.finalize() - - # add stimulus - stim_ts = self.create_timeseries('IndexSeries', stim_name, 'stimulus') - stim_ts.set_time(dgc_dict['timestamps'], dtype='u8') - stim_ts.set_data(dgc_dict['index_to_display'], unit='frame', conversion=1, resolution=1, dtype='u4') - stim_ts.set_value_as_link('indexed_timeseries', '/stimulus/templates/{}'.format(stim_name)) - stim_ts.set_comments('The "timestamps" of this TimeSeries are indices (64-bit unsigned integer, hacked the ' - 'original ainwb code) referencing the entire display sequence. It should match hardware ' - 'vsync TTL (see "/acquisition/timeseries/digital_vsync_stim/rise"). The "data" of this ' - 'TimeSeries are indices referencing the frames template saved in the "indexed_timeseries" ' - 'field.') - stim_ts.set_description('drifting grating circle displayed by retinotopic_mapping package') - stim_ts.set_source(dgc_dict['source']) - for key in ['frame_config', 'stim_name', 'pregap_dur', 'postgap_dur', 'coordinate', 'background']: - stim_ts.set_value(key, dgc_dict[key]) - stim_ts.set_value('is_smooth_edge', dgc_dict['is_smooth_edge']) - stim_ts.set_value('smooth_width_ratio', dgc_dict['smooth_width_ratio']) - stim_ts.set_value('center', dgc_dict['center']) - stim_ts.set_value('iteration', dgc_dict['iteration']) - stim_ts.set_value('dire_list', dgc_dict['dire_list']) - stim_ts.set_value('radius_list', dgc_dict['radius_list']) - stim_ts.set_value('con_list', dgc_dict['con_list']) - stim_ts.set_value('sf_list', dgc_dict['sf_list']) - stim_ts.set_value('tf_list', dgc_dict['tf_list']) - stim_ts.set_value('block_dur', dgc_dict['block_dur']) - stim_ts.set_value('midgap_dur', dgc_dict['midgap_dur']) - stim_ts.finalize() - - def _add_static_grating_circle_retinotopic_mapping(self, sgc_dict): - stim_name = sgc_dict['stim_name'] - - if stim_name[-37:] != 'StaticGratingCircleRetinotopicMapping': - raise ValueError('stimulus should be "StaticGratingCircleRetinotopicMapping" (StaticGratingCircle from ' - 'retinotopic_mapping package). ') - - # add template - template_ts = self.create_timeseries('TimeSeries', stim_name, 'template') - frames_unique = sgc_dict['frames_unique'] - frames_template = [] - for frame in frames_unique: - curr_frame = np.array(frame) - curr_frame[curr_frame == None] = np.nan - frames_template.append(np.array(curr_frame, dtype=np.float32)) - frames_template = np.array(frames_template) - template_ts.set_data(frames_template, unit='', conversion=np.nan, resolution=np.nan) - template_ts.set_value('num_samples', frames_template.shape[0]) - template_ts.set_source(sgc_dict['source']) - template_ts.finalize() - - # add stimulus - stim_ts = self.create_timeseries('IndexSeries', stim_name, 'stimulus') - stim_ts.set_time(sgc_dict['timestamps'], dtype='u8') - stim_ts.set_data(sgc_dict['index_to_display'], unit='frame', conversion=1, resolution=1, dtype='u4') - stim_ts.set_value_as_link('indexed_timeseries', '/stimulus/templates/{}'.format(stim_name)) - stim_ts.set_comments('The "timestamps" of this TimeSeries are indices (64-bit unsigned integer, hacked the ' - 'original ainwb code) referencing the entire display sequence. It should match hardware ' - 'vsync TTL (see "/acquisition/timeseries/digital_vsync_stim/rise"). The "data" of this ' - 'TimeSeries are indices referencing the frames template saved in the "indexed_timeseries" ' - 'field.') - stim_ts.set_description('static grating circle displayed by retinotopic_mapping package') - stim_ts.set_source(sgc_dict['source']) - for key in ['frame_config', 'stim_name', 'pregap_dur', 'postgap_dur', 'coordinate', 'background']: - stim_ts.set_value(key, sgc_dict[key]) - stim_ts.set_value('is_smooth_edge', sgc_dict['is_smooth_edge']) - stim_ts.set_value('smooth_width_ratio', sgc_dict['smooth_width_ratio']) - stim_ts.set_value('center', sgc_dict['center']) - stim_ts.set_value('iteration', sgc_dict['iteration']) - stim_ts.set_value('ori_list', sgc_dict['ori_list']) - stim_ts.set_value('radius_list', sgc_dict['radius_list']) - stim_ts.set_value('con_list', sgc_dict['con_list']) - stim_ts.set_value('sf_list', sgc_dict['sf_list']) - stim_ts.set_value('phase_list', sgc_dict['phase_list']) - stim_ts.set_value('display_dur', sgc_dict['display_dur']) - stim_ts.set_value('midgap_dur', sgc_dict['midgap_dur']) - stim_ts.finalize() - - def _add_sparse_noise_retinotopic_mapping(self, sn_dict): - stim_name = sn_dict['stim_name'] - - if stim_name[-30:] != '_SparseNoiseRetinotopicMapping': - raise ValueError('stimulus should be "SparseNoiseRetinotopicMapping" (SparseNoise from ' - 'retinotopic_mapping package). ') - - # add template - template_ts = self.create_timeseries('TimeSeries', stim_name, 'template') - frames_template = [] - probes = [] - for frame in sn_dict['frames_unique']: - - frames_template.append(np.array([frame[0], frame[3]], dtype=np.float32)) - - if frame[1] is None: - probes.append(np.array([np.nan, np.nan, np.nan], dtype=np.float32)) - else: - # print([frame[1][0], frame[1][1], frame[2]]) - probes.append(np.array([frame[1][0], frame[1][1], frame[2]], dtype=np.float32)) - - frames_template = np.array(frames_template) - probes = np.array(probes) - template_ts.set_data(frames_template, unit='', conversion=np.nan, resolution=np.nan) - template_ts.set_value('probes', probes) - template_ts.set_value('num_samples', frames_template.shape[0]) - template_ts.set_source(sn_dict['source']) - template_ts.set_description('The "data" field saved modified frame configuration: ' - '[is_display, indicator color)]. While the "probe" field saved modified probe ' - 'configuration: [altitude, azimuth, polarity]. These two fields have one-to-one ' - 'relationship. Together they define an unique display frame of sparse noise ' - 'stimulus. The order of these two fields should not be changed.') - template_ts.finalize() - - # add stimulus - stim_ts = self.create_timeseries('IndexSeries', stim_name, 'stimulus') - stim_ts.set_time(sn_dict['timestamps'], dtype='u8') - stim_ts.set_data(sn_dict['index_to_display'], unit='frame', conversion=1, resolution=1, dtype='u4') - stim_ts.set_value_as_link('indexed_timeseries', '/stimulus/templates/{}'.format(stim_name)) - stim_ts.set_comments('The "timestamps" of this TimeSeries are indices (64-bit unsigned integer, hacked the ' - 'original ainwb code) referencing the entire display sequence. It should match hardware ' - 'vsync TTL (see "/acquisition/timeseries/digital_vsync_stim/rise"). The "data" of this ' - 'TimeSeries are indices referencing the frames template saved in the "indexed_timeseries" ' - 'field.') - stim_ts.set_description('sparse noise displayed by retinotopic_mapping package') - stim_ts.set_source(sn_dict['source']) - for key in ['stim_name', 'pregap_dur', 'postgap_dur', 'coordinate', 'background']: - stim_ts.set_value(key, sn_dict[key]) - stim_ts.set_value('frame_config', ['is_display', 'indicator color[-1., 1.]']) # modified frame config - stim_ts.set_value('probe_config', ['altitude (deg)', 'azimuth (deg)', 'polarity']) # modified probe config - stim_ts.set_value('is_include_edge', sn_dict['is_include_edge']) - stim_ts.set_value('probe_frame_num', sn_dict['probe_frame_num']) - stim_ts.set_value('subregion', sn_dict['subregion']) - stim_ts.set_value('iteration', sn_dict['iteration']) - stim_ts.set_value('grid_space', sn_dict['grid_space']) - stim_ts.set_value('probe_orientation', sn_dict['probe_orientation']) - stim_ts.set_value('sign', sn_dict['sign']) - stim_ts.set_value('probe_size', sn_dict['probe_size']) - stim_ts.finalize() - - def _add_locally_sparse_noise_retinotopic_mapping(self, lsn_dict): - stim_name = lsn_dict['stim_name'] - - if stim_name[-36:] != 'LocallySparseNoiseRetinotopicMapping': - raise ValueError('stimulus should be "LocallySparseNoiseRetinotopicMapping" (LocallySparseNoise from ' - 'retinotopic_mapping package). ') - - # add template - template_ts = self.create_timeseries('TimeSeries', stim_name, 'template') - - max_probe_num = 0 # get max probe number in a single frame - for frame in lsn_dict['frames_unique']: - if frame[1] is not None: - max_probe_num = max([max_probe_num, len(frame[1])]) - - frames_template = np.empty((len(lsn_dict['frames_unique']), 2), dtype=np.float32) - probes = np.empty((len(lsn_dict['frames_unique']), max_probe_num, 3), dtype=np.float64) - probes[:] = np.nan - - for frame_ind, frame in enumerate(lsn_dict['frames_unique']): - - frames_template[frame_ind] = np.array([frame[0], frame[3]], dtype=np.float32) - - if frame[1] is not None: - for curr_probe_i, curr_probe in enumerate(frame[1]): - probes[frame_ind, curr_probe_i, :] = np.array([curr_probe], dtype=np.float64) - - template_ts.set_data(frames_template, unit='', conversion=np.nan, resolution=np.nan) - template_ts.set_value('probes', probes, dtype='float64') - template_ts.set_value('num_samples', frames_template.shape[0]) - template_ts.set_source(lsn_dict['source']) - template_ts.set_description('The "data" field saved modified frame configuration: ' - '[is_display, indicator color)]. While the "probe" field saved modified probe ' - 'configuration. It is a 3-d array with axis: [frame_num, probe_num, probe_info], ' - 'the probe info is specified as: [altitude, azimuth, polarity]. The frame ' - 'dimension of these two fields have one-to-one relationship. ' - 'Together they define an unique display frame of locally sparse noise stimulus. ' - 'The order of these two fields should not be changed.') - template_ts.finalize() - - # add stimulus - stim_ts = self.create_timeseries('IndexSeries', stim_name, 'stimulus') - stim_ts.set_time(lsn_dict['timestamps'], dtype='u8') - stim_ts.set_data(lsn_dict['index_to_display'], unit='frame', conversion=1, resolution=1, dtype='u4') - stim_ts.set_value_as_link('indexed_timeseries', '/stimulus/templates/{}'.format(stim_name)) - stim_ts.set_comments('The "timestamps" of this TimeSeries are indices (64-bit unsigned integer, hacked the ' - 'original ainwb code) referencing the entire display sequence. It should match hardware ' - 'vsync TTL (see "/acquisition/timeseries/digital_vsync_stim/rise"). The "data" of this ' - 'TimeSeries are indices referencing the frames template saved in the "indexed_timeseries" ' - 'field.') - stim_ts.set_description('locally sparse noise displayed by retinotopic_mapping package') - stim_ts.set_source(lsn_dict['source']) - for key in ['stim_name', 'pregap_dur', 'postgap_dur', 'coordinate', 'background']: - stim_ts.set_value(key, lsn_dict[key]) - stim_ts.set_value('frame_config', ['is_display', 'indicator color[-1., 1.]']) # modified frame config - stim_ts.set_value('probe_config', ['frame_num x probe_num x probe_info (altitude_deg, azimuth_deg, polarity)']) # modified probe config - stim_ts.set_value('is_include_edge', lsn_dict['is_include_edge']) - stim_ts.set_value('probe_frame_num', lsn_dict['probe_frame_num']) - stim_ts.set_value('subregion', lsn_dict['subregion']) - stim_ts.set_value('iteration', lsn_dict['iteration']) - stim_ts.set_value('grid_space', lsn_dict['grid_space']) - stim_ts.set_value('probe_orientation', lsn_dict['probe_orientation']) - stim_ts.set_value('sign', lsn_dict['sign']) - stim_ts.set_value('probe_size', lsn_dict['probe_size']) - stim_ts.set_value('min_distance', lsn_dict['min_distance']) - stim_ts.set_value('repeat', lsn_dict['repeat']) - stim_ts.finalize() - - def _add_static_images_retinotopic_mapping(self, si_dict): - stim_name = si_dict['stim_name'] - - if stim_name[-30:] != 'StaticImagesRetinotopicMapping': - raise ValueError('stimulus should be "StaticImagesRetinotopicMapping" (StaticImages from ' - 'retinotopic_mapping package). ') - - # add template - template_ts = self.create_timeseries('TimeSeries', stim_name, 'template') - frames_unique = si_dict['frames_unique'] - frames_template = [] - for frame in frames_unique: - curr_frame = np.array(frame) - curr_frame[curr_frame == None] = np.nan - frames_template.append(np.array(curr_frame, dtype=np.float32)) - frames_template = np.array(frames_template) - template_ts.set_data(frames_template, unit='', conversion=np.nan, resolution=np.nan) - template_ts.set_value('num_samples', frames_template.shape[0]) - template_ts.set_value('images_wrapped', si_dict['images_wrapped']) - template_ts.set_value('images_dewrapped', si_dict['images_dewrapped']) - template_ts.set_source(si_dict['source']) - template_ts.finalize() - - # add stimulus - stim_ts = self.create_timeseries('IndexSeries', stim_name, 'stimulus') - stim_ts.set_time(si_dict['timestamps'], dtype='u8') - stim_ts.set_data(si_dict['index_to_display'], unit='frame', conversion=1, resolution=1, dtype='u4') - stim_ts.set_value_as_link('indexed_timeseries', '/stimulus/templates/{}'.format(stim_name)) - stim_ts.set_comments('The "timestamps" of this TimeSeries are indices (64-bit unsigned integer, hacked the ' - 'original ainwb code) referencing the entire display sequence. It should match hardware ' - 'vsync TTL (see "/acquisition/timeseries/digital_vsync_stim/rise"). The "data" of this ' - 'TimeSeries are indices referencing the frames template saved in the "indexed_timeseries" ' - 'field.') - stim_ts.set_description('static images displayed by retinotopic_mapping package') - stim_ts.set_source(si_dict['source']) - for key in ['frame_config', 'stim_name', 'pregap_dur', 'postgap_dur', 'coordinate', 'background']: - stim_ts.set_value(key, si_dict[key]) - stim_ts.set_value('altitude_dewrapped', si_dict['altitude_dewrapped']) - stim_ts.set_value('azimuth_dewrapped', si_dict['azimuth_dewrapped']) - stim_ts.set_value('img_center', si_dict['img_center']) - stim_ts.set_value('midgap_dur', si_dict['midgap_dur']) - stim_ts.set_value('display_dur', si_dict['display_dur']) - stim_ts.set_value('iteration', si_dict['iteration']) - stim_ts.set_value('deg_per_pixel_azi', si_dict['deg_per_pixel_azi']) - stim_ts.set_value('deg_per_pixel_alt', si_dict['deg_per_pixel_alt']) - stim_ts.finalize() - - def _add_sinusoidal_luminance_retinotopic_mapping(self, sl_dict): - stim_name = sl_dict['stim_name'] - - if stim_name[-37:] != 'SinusoidalLuminanceRetinotopicMapping': - raise ValueError('stimulus should be "SinusoidalLuminanceRetinotopicMapping" (StaticImages from ' - 'retinotopic_mapping package). ') - - # add template - template_ts = self.create_timeseries('TimeSeries', stim_name, 'template') - frames_unique = sl_dict['frames_unique'] - frames_template = [] - for frame in frames_unique: - curr_frame = np.array(frame) - curr_frame[curr_frame == None] = np.nan - frames_template.append(np.array(curr_frame, dtype=np.float32)) - frames_template = np.array(frames_template) - template_ts.set_data(frames_template, unit='', conversion=np.nan, resolution=np.nan) - template_ts.set_value('num_samples', frames_template.shape[0]) - template_ts.set_source(sl_dict['source']) - template_ts.finalize() - - # add stimulus - stim_ts = self.create_timeseries('IndexSeries', stim_name, 'stimulus') - stim_ts.set_time(sl_dict['timestamps'], dtype='u8') - stim_ts.set_data(sl_dict['index_to_display'], unit='frame', conversion=1, resolution=1, dtype='u4') - stim_ts.set_value_as_link('indexed_timeseries', '/stimulus/templates/{}'.format(stim_name)) - stim_ts.set_comments('The "timestamps" of this TimeSeries are indices (64-bit unsigned integer, hacked the ' - 'original ainwb code) referencing the entire display sequence. It should match hardware ' - 'vsync TTL (see "/acquisition/timeseries/digital_vsync_stim/rise"). The "data" of this ' - 'TimeSeries are indices referencing the frames template saved in the "indexed_timeseries" ' - 'field.') - stim_ts.set_description('sinusoidal luminance displayed by retinotopic_mapping package') - stim_ts.set_source(sl_dict['source']) - for key in ['frame_config', 'stim_name', 'pregap_dur', 'postgap_dur', 'coordinate', 'background']: - stim_ts.set_value(key, sl_dict[key]) - stim_ts.set_value('cycle_num', sl_dict['cycle_num']) - stim_ts.set_value('max_level', sl_dict['max_level']) - stim_ts.set_value('start_phase', sl_dict['start_phase']) - stim_ts.set_value('midgap_dur', sl_dict['midgap_dur']) - stim_ts.set_value('frequency', sl_dict['frequency']) - stim_ts.finalize() - # ===========================retinotopic_mapping visual stimuli related (indexed display)=========================== - - - # ===========================corticalmapping visual stimuli related (non-indexed display)=========================== - def add_visual_stimulus_corticalmapping(self, log_path, display_order=0): - """ - load visual stimulation given saved display log pickle file - :param log_path: the path to the display log generated by corticalmapping.VisualStim - :param display_order: int, in case there is more than one visual display in the file. - This value records the order of the displays - :return: + return: all_circles: 2-d array, each line is the onset of displayed circle, each column is a feature of + that circle, circles follow the display order + data_format: str, description of the column structure of each circle + description: str, + pooled_circles: None """ - self._check_display_order(display_order) - log_dict = ft.loadFile(log_path) - - stim_name = log_dict['stimulation']['stimName'] + if fc_grp['stim_name'].value != 'FlashingCircle': + raise NameError('The input stimulus should be "FlashingCircle".') - display_frames = log_dict['presentation']['displayFrames'] - time_stamps = log_dict['presentation']['timeStamp'] + frames = fc_grp['data'][:, 0] + azi = fc_grp['center_azimuth_deg'].value + alt = fc_grp['center_altitude_deg'].value + color_c = fc_grp['center_color'].value + color_b = fc_grp['background_color'].value + radius = fc_grp['radius_deg'].value - if len(display_frames) != len(time_stamps): - print ('\nWarning: {}'.format(log_path)) - print('Unequal number of displayFrames ({}) and timeStamps ({}).'.format(len(display_frames), - len(time_stamps))) + all_cirlces = [] + for i in range(len(frames)): + if frames[i] == 1 and (i == 0 or (frames[i - 1] == 0)): + all_cirlces.append(np.array((i, azi, alt, color_c, color_b, radius), dtype=np.float32)) - if stim_name == 'SparseNoise': - self._add_sparse_noise_stimulus_corticalmapping(log_dict, display_order=display_order) - elif stim_name == 'FlashingCircle': - self._add_flashing_circle_stimulus_corticalmapping(log_dict, display_order=display_order) - elif stim_name == 'UniformContrast': - self._add_uniform_contrast_stimulus_corticalmapping(log_dict, display_order=display_order) - elif stim_name == 'DriftingGratingCircle': - self._add_drifting_grating_circle_stimulus_corticalmapping(log_dict, display_order=display_order) - elif stim_name == 'KSstimAllDir': - self._add_drifting_checker_board_stimulus_corticalmapping(log_dict, display_order=display_order) - else: - raise ValueError('stimulation name {} unrecognizable!'.format(stim_name)) + all_cirlces = np.array(all_cirlces) + data_format = ['display frame indices for the onset of each circle', 'center_azimuth_deg', + 'center_altitude_deg', 'center_color', 'background_color', 'radius_deg'] + description = 'TimeSeries of flashing circle onsets. Stimulus generated by ' \ + 'corticalmapping.VisualStim.SparseNoise class.' + return all_cirlces, data_format, description, None - def add_visual_stimuli_corticalmapping(self, log_paths): + @staticmethod + def _analyze_uniform_contrast_frames(uc_grp): - exist_stimuli = self.file_pointer['stimulus/presentation'].keys() + if uc_grp['stim_name'].value != 'UniformContrast': + raise NameError('The input stimulus should be "UniformContrast".') - for i, log_path in enumerate(log_paths): - self.add_visual_stimulus_corticalmapping(log_path, i + len(exist_stimuli)) + onset_array = np.array([]) + data_format = '' + description = 'TimeSeries of uniform contrast stimulus. No onset information. Stimulus generated by ' \ + 'corticalmapping.VisualStim.UniformContrast class.' + return onset_array, data_format, description, {} - def analyze_visual_stimuli_corticalmapping(self, onsets_ts=None): + def analyze_visual_stimuli(self, onsets_ts=None): """ add stimuli onset timestamps of all saved stimulus presentations to 'processing/stimulus_onsets' module @@ -1974,10 +868,10 @@ def analyze_visual_stimuli_corticalmapping(self, onsets_ts=None): """ if onsets_ts is None: - print 'input onsets_ts is None, try to use photodiode onsets as onsets_ts.' + print('input onsets_ts is None, try to use photodiode onsets as onsets_ts.') onsets_ts = self.file_pointer['processing/PhotodiodeOnsets/photodiode_onsets/timestamps'].value - stim_ns = self.file_pointer['stimulus/presentation'].keys() + stim_ns = list(self.file_pointer['stimulus/presentation'].keys()) stim_ns.sort() total_onsets = 0 @@ -1989,13 +883,13 @@ def analyze_visual_stimuli_corticalmapping(self, onsets_ts=None): curr_stim_grp = self.file_pointer['stimulus/presentation'][stim_n] if curr_stim_grp['stim_name'].value == 'SparseNoise': - _ = self._analyze_sparse_noise_frames_corticalmapping(curr_stim_grp) + _ = self._analyze_sparse_noise_frames(curr_stim_grp) elif curr_stim_grp['stim_name'].value == 'FlashingCircle': - _ = self._analyze_flashing_circle_frames_corticalmapping(curr_stim_grp) + _ = self._analyze_flashing_circle_frames(curr_stim_grp) elif curr_stim_grp['stim_name'].value == 'DriftingGratingCircle': - _ = self._analyze_driftig_grating_frames_corticalmapping(curr_stim_grp) + _ = self._analyze_driftig_grating_frames(curr_stim_grp) elif curr_stim_grp['stim_name'].value == 'UniformContrast': - _ = self._analyze_uniform_contrast_frames_corticalmapping(curr_stim_grp) + _ = self._analyze_uniform_contrast_frames(curr_stim_grp) else: raise LookupError('Do not understand stimulus type: {}.'.format(stim_n)) @@ -2013,13 +907,13 @@ def analyze_visual_stimuli_corticalmapping(self, onsets_ts=None): curr_stim_grp = self.file_pointer['stimulus/presentation'][stim_n] if curr_stim_grp['stim_name'].value == 'SparseNoise': - _ = self._analyze_sparse_noise_frames_corticalmapping(curr_stim_grp) + _ = self._analyze_sparse_noise_frames(curr_stim_grp) elif curr_stim_grp['stim_name'].value == 'FlashingCircle': - _ = self._analyze_flashing_circle_frames_corticalmapping(curr_stim_grp) + _ = self._analyze_flashing_circle_frames(curr_stim_grp) elif curr_stim_grp['stim_name'].value == 'DriftingGratingCircle': - _ = self._analyze_driftig_grating_frames_corticalmapping(curr_stim_grp) + _ = self._analyze_driftig_grating_frames(curr_stim_grp) elif curr_stim_grp['stim_name'].value == 'UniformContrast': - _ = self._analyze_uniform_contrast_frames_corticalmapping(curr_stim_grp) + _ = self._analyze_uniform_contrast_frames(curr_stim_grp) else: raise LookupError('Do not understand stimulus type: {}.'.format(stim_n)) @@ -2056,7 +950,7 @@ def analyze_visual_stimuli_corticalmapping(self, onsets_ts=None): curr_onset.set_value('sign', curr_stim_grp['sign'].value) curr_onset.set_value('subregion_deg', curr_stim_grp['subregion_deg'].value) curr_onset.finalize() - for curr_sn, curr_sd in pooled_onsets.items(): + for curr_sn, curr_sd in list(pooled_onsets.items()): curr_s_ts = self.create_timeseries('TimeSeries', curr_sn, modality='other') curr_s_ts.set_data([], unit='', conversion=np.nan, resolution=np.nan) curr_s_ts.set_time(curr_onset_ts[curr_sd['onset_ind']]) @@ -2077,7 +971,7 @@ def analyze_visual_stimuli_corticalmapping(self, onsets_ts=None): curr_onset.set_value('spatial_frequency_list', curr_stim_grp['spatial_frequency_list'].value) curr_onset.set_value('temporal_frequency_list', curr_stim_grp['temporal_frequency_list'].value) curr_onset.finalize() - for curr_gn, curr_gd in pooled_onsets.items(): + for curr_gn, curr_gd in list(pooled_onsets.items()): curr_g_ts = self.create_timeseries('TimeSeries', curr_gn, modality='other') curr_g_ts.set_data([], unit='', conversion=np.nan, resolution=np.nan) curr_g_ts.set_time(curr_onset_ts[curr_gd['onset_ind']]) @@ -2091,180 +985,288 @@ def analyze_visual_stimuli_corticalmapping(self, onsets_ts=None): curr_onset_start_ind = curr_onset_start_ind + curr_onset_arr.shape[0] - @staticmethod - def _analyze_sparse_noise_frames_corticalmapping(sn_grp): + def add_photodiode_onsets(self, digitizeThr=0.9, filterSize=0.01, segmentThr=0.01, smallestInterval=0.03, + expected_onsets_number=None): """ - analyze sparse noise display frames saved in '/stimulus/presentation', extract information about onset of - each displayed square: + intermediate processing step for analysis of visual display. Containing the information about the onset of + photodiode signal. Timestamps are extracted from photodiode signal, should be aligned to the master clock. + extraction is done by corticalmapping.HighLevel.segmentPhotodiodeSignal() function. The raw signal + was first digitized by the digitize_threshold, then filtered by a gaussian fileter with filter_size. Then + the derivative of the filtered signal was calculated by numpy.diff. The derivative signal was then timed + with the digitized signal. Then the segmentation_threshold was used to detect rising edge of the resulting + signal. Any onset with interval from its previous onset smaller than smallest_interval will be discarded. + the resulting timestamps of photodiode onsets will be saved in 'processing/photodiode_onsets' timeseries - return: all_squares: 2-d array, each line is a displayed square in sparse noise, each column is a feature of - a particular square, squares follow display order - data_format: str, description of the column structure of each square - description: str, - pooled_squares: dict, squares with same location and sign are pooled together. - keys: 'square_00000', 'square_00001', 'square_00002' ... each represents a unique - square. - values: dict, { - 'azi': , - 'alt': , - 'sign': , - 'onset_ind': list of indices of the appearances of current square in - in "all_squares", to be aligned with to photodiode onset - timestamps - } + :param digitizeThr: float + :param filterSize: float + :param segmentThr: float + :param smallestInterval: float + :param expected_onsets_number: int, expected number of photodiode onsets, may extract from visual display + log. if extracted onset number does not match this number, the process will + be abort. If None, no such check will be performed. + :return: + """ + fs = self.file_pointer['acquisition/timeseries/photodiode/starting_time'].attrs['rate'] + pd = self.file_pointer['acquisition/timeseries/photodiode/data'].value * \ + self.file_pointer['acquisition/timeseries/photodiode/data'].attrs['conversion'] + + pd_onsets = hl.segmentPhotodiodeSignal(pd, digitizeThr=digitizeThr, filterSize=filterSize, + segmentThr=segmentThr, Fs=fs, smallestInterval=smallestInterval) + + if pd_onsets.shape[0] == 0: + return + + if expected_onsets_number is not None: + if len(pd_onsets) != expected_onsets_number: + raise ValueError('The number of photodiode onsets (' + str(len(pd_onsets)) + ') and the expected ' + 'number of sweeps ' + str(expected_onsets_number) + ' do not match. Abort.') + + pd_ts = self.create_timeseries('TimeSeries', 'photodiode_onsets', modality='other') + pd_ts.set_time(pd_onsets) + pd_ts.set_data([], unit='', conversion=np.nan, resolution=np.nan) + pd_ts.set_description('intermediate processing step for analysis of visual display. ' + 'Containing the information about the onset of photodiode signal. Timestamps ' + 'are extracted from photodiode signal, should be aligned to the master clock.' + 'extraction is done by corticalmapping.HighLevel.segmentPhotodiodeSignal()' + 'function. The raw signal was first digitized by the digitize_threshold, then ' + 'filtered by a gaussian fileter with filter_size. Then the derivative of the filtered ' + 'signal was calculated by numpy.diff. The derivative signal was then timed with the ' + 'digitized signal. Then the segmentation_threshold was used to detect rising edge of ' + 'the resulting signal. Any onset with interval from its previous onset smaller than ' + 'smallest_interval will be discarded.') + pd_ts.set_path('/processing/PhotodiodeOnsets') + pd_ts.set_value('digitize_threshold', digitizeThr) + pd_ts.set_value('fileter_size', filterSize) + pd_ts.set_value('segmentation_threshold', segmentThr) + pd_ts.set_value('smallest_interval', smallestInterval) + pd_ts.finalize() + + def plot_spike_waveforms(self, modulen, unitn, is_plot_filtered=False, fig=None, axes_size=(0.2, 0.2), **kwargs): """ + plot spike waveforms + + :param modulen: str, name of the module containing ephys recordings + :param unitn: str, name of ephys unit, should be in '/processing/ephys_units/UnitTimes' + :param is_plot_filtered: bool, plot unfiltered waveforms or not + :param channel_names: list of strs, channel names in continuous recordings, should be in '/acquisition/timeseries' + :param fig: matplotlib figure object + :param t_range: tuple of two floats, time range to plot along spike time stamps + :param kwargs: inputs to matplotlib.axes.plot() function + :return: fig + """ + if modulen not in list(self.file_pointer['processing'].keys()): + raise LookupError('Can not find module for ephys recording: ' + modulen + '.') + + if unitn not in list(self.file_pointer['processing'][modulen]['UnitTimes'].keys()): + raise LookupError('Can not find ephys unit: ' + unitn + '.') + + ch_ns = self._get_channel_names() + + unit_grp = self.file_pointer['processing'][modulen]['UnitTimes'][unitn] + waveforms = unit_grp['template'].value + + if 'template_std' in list(unit_grp.keys()): + stds = unit_grp['template_std'].value + else: + stds = None + + if is_plot_filtered: + if 'template_filtered' in list(unit_grp.keys()): + waveforms_f = unit_grp['template_filtered'].value + if 'template_std_filtered' in list(unit_grp.keys()): + stds_f = unit_grp['template_std_filtered'].value + else: + stds_f = None + else: + print(('can not find unfiltered spike waveforms for unit: ' + unitn)) + waveforms_f = None + stds_f = None + else: + waveforms_f = None + stds_f = None + + if 'channel_xpos' in list(self.file_pointer['processing'][modulen].keys()): + ch_xpos = self.file_pointer['processing'][modulen]['channel_xpos'] + ch_ypos = self.file_pointer['processing'][modulen]['channel_ypos'] + ch_locations = list(zip(ch_xpos, ch_ypos)) + else: + ch_locations = None + + fig = plot_waveforms(waveforms, ch_locations=ch_locations, stds=stds, waveforms_filtered=waveforms_f, + stds_filtered=stds_f, f=fig, ch_ns=ch_ns, axes_size=axes_size, **kwargs) + + fig.suptitle(self.file_pointer['identifier'].value + ' : ' + unitn) + + return fig + + def add_motion_correction_module(self, module_name, original_timeseries_path, corrected_file_path, + corrected_dataset_path, xy_translation_offsets, interface_name='MotionCorrection', + mean_projection=None, max_projection=None, description='', comments='', + source=''): + """ + add a motion corrected image series in to processing field as a module named 'motion_correction' and create a + link to an external hdf5 file which contains the images. + :param module_name: str, module name to be created + :param interface_name: str, interface name of the image series + :param original_timeseries_path: str, the path to the timeseries of the original images + :param corrected_file_path: str, the full file system path to the hdf5 file containing the raw image data + :param corrected_dataset_path: str, the path within the hdf5 file pointing to the raw data. the object should have at + least 3 attributes: 'conversion', resolution, unit + :param xy_translation_offsets: 2d array with two columns, + :param mean_projection: 2d array, mean_projection of corrected image, if None, no dataset will be + created + :param max_projection: 2d array, max_projection of corrected image, if None, no dataset will be + created + :return: + """ + + orig = self.file_pointer[original_timeseries_path] + timestamps = orig['timestamps'].value + + img_file = h5py.File(corrected_file_path) + img_data = img_file[corrected_dataset_path] + if timestamps.shape[0] != img_data.shape[0]: + raise ValueError('Number of frames does not equal to the length of timestamps!') + + if xy_translation_offsets.shape[0] != timestamps.shape[0]: + raise ValueError('Number of offsets does not equal to the length of timestamps!') - if sn_grp['stim_name'].value != 'SparseNoise': - raise NameError('The input stimulus should be "SparseNoise".') + corrected = self.create_timeseries(ts_type='ImageSeries', name='corrected', modality='other') + corrected.set_data_as_remote_link(corrected_file_path, corrected_dataset_path) + corrected.set_time_as_link(original_timeseries_path) + corrected.set_description(description) + corrected.set_comments(comments) + corrected.set_source(source) + for value_n in list(orig.keys()): + if value_n not in ['image_data_path_within_file', 'image_file_path', 'data', 'timestamps']: + corrected.set_value(value_n, orig[value_n].value) - frames = sn_grp['data'].value - frames = [tuple(x) for x in frames] - dtype = [('isDisplay', int), ('azimuth', float), ('altitude', float), ('sign', int), ('isOnset', int)] - frames = np.array(frames, dtype=dtype) + xy_translation = self.create_timeseries(ts_type='TimeSeries', name='xy_translation', modality='other') + xy_translation.set_data(xy_translation_offsets, unit='pixel', conversion=np.nan, resolution=np.nan) + xy_translation.set_time_as_link(original_timeseries_path) + xy_translation.set_value('num_samples', xy_translation_offsets.shape[0]) + xy_translation.set_description('Time series of x, y shifts applied to create motion stabilized image series') + xy_translation.set_value('feature_description', ['x_motion', 'y_motion']) - all_squares = [] - for i in range(len(frames)): - if frames[i]['isDisplay'] == 1 and \ - (i == 0 or (frames[i - 1]['isOnset'] == -1 and frames[i]['isOnset'] == 1)): - all_squares.append(np.array((i, frames[i]['azimuth'], frames[i]['altitude'], frames[i]['sign']), - dtype=np.float32)) + mc_mod = self.create_module(module_name) + mc_interf = mc_mod.create_interface("MotionCorrection") + mc_interf.add_corrected_image(interface_name, orig=original_timeseries_path, xy_translation=xy_translation, + corrected=corrected) - all_squares = np.array(all_squares) + if mean_projection is not None: + mc_interf.set_value('mean_projection', mean_projection) - pooled_squares = {} - unique_squares = list(set([tuple(x[1:]) for x in all_squares])) - for i, unique_square in enumerate(unique_squares): - curr_square_n = 'square_' + ft.int2str(i, 5) - curr_azi = unique_square[0] - curr_alt = unique_square[1] - curr_sign = unique_square[2] - curr_onset_ind = [] - for j, give_square in enumerate(all_squares): - if np.array_equal(give_square[1:], unique_square): - curr_onset_ind.append(j) - pooled_squares.update({curr_square_n: {'azi': curr_azi, - 'alt': curr_alt, - 'sign': curr_sign, - 'onset_ind': curr_onset_ind}}) - all_squares = np.array(all_squares) - data_format = ['display frame indices for the onset of each square', 'azimuth of each square', - 'altitude of each square', 'sign of each square'] - description = 'TimeSeries of sparse noise square onsets. Stimulus generated by ' \ - 'corticalmapping.VisualStim.SparseNoise class.' - return all_squares, data_format, description, pooled_squares + if max_projection is not None: + mc_interf.set_value('max_projection', max_projection) - @staticmethod - def _analyze_driftig_grating_frames_corticalmapping(dg_grp): + mc_interf.finalize() + mc_mod.finalize() + + def generate_dat_file_for_kilosort(self, output_folder, output_name, ch_ns, is_filtered=True, cutoff_f_low=300., + cutoff_f_high=6000.): """ - analyze drifting grating display frames saved in '/stimulus/presentation', extract information about onset of - each displayed grating: + generate .dat file for kilolsort: "https://github.com/cortex-lab/KiloSort", it is binary raw code, with + structure: ch0_t0, ch1_t0, ch2_t0, ...., chn_t0, ch0_t1, ch1_t1, ch2_t1, ..., chn_t1, ..., ch0_tm, ch1_tm, + ch2_tm, ..., chn_tm - return: all_gratings: 2-d array, each line is a displayed square in sparse noise, each column is a feature of - a particular square, squares follow display order - data_format: str, description of the column structure of each grating - description: str, - pooled_squares: dict, gratings with same parameters are pooled together. - keys: 'grating_00000', 'grating_00001', 'grating_00002' ... each represents a unique - grating. - values: dict, { - 'sf': , - 'tf': , - 'direction': , - 'contrast': , - 'radius': , - 'azi': - 'alt': - 'onset_ind': list of indices of the appearances of current square in - in "all_squares", to be aligned with to photodiode onset - timestamps - } + :param output_folder: str, path to output directory + :param output_name: str, output file name, an extension of '.dat' will be automatically added. + :param ch_ns: list of strings, name of included analog channels + :param is_filtered: bool, if Ture, another .dat file with same size will be generated in the output folder. + this file will contain temporally filtered data (filter done by + corticalmapping.core.TimingAnalysis.butter_... functions). '_filtered' will be attached + to the filtered file name. + :param cutoff_f_low: float, low cutoff frequency, Hz. if None, it will be low-pass + :param cutoff_f_high: float, high cutoff frequency, Hz, if None, it will be high-pass + :return: None """ - if dg_grp['stim_name'].value != 'DriftingGratingCircle': - raise NameError('The input stimulus should be "DriftingGratingCircle".') - frames = dg_grp['data'].value + save_path = os.path.join(output_folder, output_name + '.dat') + if os.path.isfile(save_path): + raise IOError('Output file already exists.') - all_gratings = [] - for i in range(len(frames)): - if frames[i][8] == 1 and (i == 0 or (frames[i - 1][8] == -1)): - all_gratings.append(np.array((i, frames[i][2], frames[i][3], frames[i][4], frames[i][5], frames[i][6]), - dtype=np.float32)) + data_lst = [] + for ch_n in ch_ns: + data_lst.append(self.file_pointer['acquisition/timeseries'][ch_n]['data'].value) - all_gratings = np.array(all_gratings) + dtype = data_lst[0].dtype + data = np.array(data_lst, dtype=dtype).flatten(order='F') + data.tofile(save_path) - pooled_gratings = {} - unique_gratings = list(set([tuple(x[1:]) for x in all_gratings])) - for i, unique_grating in enumerate(unique_gratings): - curr_grating_n = 'grating_' + ft.int2str(i, 5) - curr_sf = unique_grating[0] - curr_tf = unique_grating[1] - curr_dir = unique_grating[2] - curr_con = unique_grating[3] - curr_r = unique_grating[4] - curr_onset_ind = [] - for j, given_grating in enumerate(all_gratings): - if np.array_equal(given_grating[1:], unique_grating): - curr_onset_ind.append(j) - pooled_gratings.update({curr_grating_n: {'sf': curr_sf, - 'tf': curr_tf, - 'dir': curr_dir, - 'con': curr_con, - 'r': curr_r, - 'onset_ind': curr_onset_ind}}) - data_format = ['display frame indices for the onset of each square', 'spatial frequency (cyc/deg)', - 'temporal frequency (Hz)', 'moving direction (arc)', 'contrast (%)', 'radius (deg)'] - description = 'TimeSeries of drifting grating circle onsets. Stimulus generated by ' \ - 'corticalmapping.VisualStim.SparseNoise class.' - return all_gratings, data_format, description, pooled_gratings + if is_filtered: - @staticmethod - def _analyze_flashing_circle_frames_corticalmapping(fc_grp): - """ - analyze flashing circle display frames saved in '/stimulus/presentation', extract information about onset of - each displayed circle: + if cutoff_f_low is None and cutoff_f_high is None: + print ('both low cutoff frequency and high cutoff frequency are None. Do nothing.') + return - return: all_circles: 2-d array, each line is the onset of displayed circle, each column is a feature of - that circle, circles follow the display order - data_format: str, description of the column structure of each circle - description: str, - pooled_circles: None - """ + save_path_f = os.path.join(output_folder, output_name + '_filtered.dat') + if os.path.isfile(save_path_f): + raise IOError('Output file for filtered data already existes.') - if fc_grp['stim_name'].value != 'FlashingCircle': - raise NameError('The input stimulus should be "FlashingCircle".') + fs = self.file_pointer['general/extracellular_ephys/sampling_rate'].value + data_lst_f = [] + for data_r in data_lst: + if cutoff_f_high is None: + data_lst_f.append(ta.butter_lowpass(data_r, fs=fs, cutoff=cutoff_f_low).astype(dtype)) + elif cutoff_f_low is None: + data_lst_f.append(ta.butter_highpass(data_r, fs=fs, cutoff=cutoff_f_high).astype(dtype)) + else: + data_lst_f.append(ta.butter_bandpass(data_r, + fs=fs, + cutoffs=(cutoff_f_low, cutoff_f_high)).astype(dtype)) + data_f = np.array(data_lst_f, dtype=dtype).flatten(order='F') + data_f.tofile(save_path_f) - frames = fc_grp['data'][:, 0] - azi = fc_grp['center_azimuth_deg'].value - alt = fc_grp['center_altitude_deg'].value - color_c = fc_grp['center_color'].value - color_b = fc_grp['background_color'].value - radius = fc_grp['radius_deg'].value + def _get_channel_names(self): + """ + :return: sorted list of channel names, each channel name should have prefix 'ch_' + """ + analog_chs = list(self.file_pointer['acquisition/timeseries'].keys()) + channel_ns = [cn for cn in analog_chs if cn[0:3] == 'ch_'] + channel_ns.sort() + return channel_ns - all_cirlces = [] - for i in range(len(frames)): - if frames[i] == 1 and (i == 0 or (frames[i - 1] == 0)): - all_cirlces.append(np.array((i, azi, alt, color_c, color_b, radius), dtype=np.float32)) + def get_analog_data(self, ch_n): + """ + :param ch_n: string, analog channel name + :return: 1-d array, analog data, data * conversion + 1-d array, time stamps + """ + grp = self.file_pointer['acquisition/timeseries'][ch_n] + data = grp['data'].value + if not np.isnan(grp['data'].attrs['conversion']): + data = data.astype(np.float32) * grp['data'].attrs['conversion'] + if 'timestamps' in list(grp.keys()): + t = grp['timestamps'] + elif 'starting_time' in list(grp.keys()): + fs = self.file_pointer['general/extracellular_ephys/sampling_rate'].value + sample_num = grp['num_samples'].value + t = np.arange(sample_num) / fs + grp['starting_time'].value + else: + raise ValueError('can not find timing information of channel:' + ch_n) + return data, t - all_cirlces = np.array(all_cirlces) - data_format = ['display frame indices for the onset of each circle', 'center_azimuth_deg', - 'center_altitude_deg', 'center_color', 'background_color', 'radius_deg'] - description = 'TimeSeries of flashing circle onsets. Stimulus generated by ' \ - 'corticalmapping.VisualStim.SparseNoise class.' - return all_cirlces, data_format, description, None + def _check_display_order(self, display_order=None): + """ + check display order make sure each presentation has a unique position, and move from increment order. + also check the given display_order is of the next number + """ + stimuli = list(self.file_pointer['stimulus/presentation'].keys()) - @staticmethod - def _analyze_uniform_contrast_frames_corticalmapping(uc_grp): + print('\nExisting visual stimuli:') + print(('\n'.join(stimuli))) - if uc_grp['stim_name'].value != 'UniformContrast': - raise NameError('The input stimulus should be "UniformContrast".') + stimuli = [int(s[0:s.find('_')]) for s in stimuli] + stimuli.sort() + if stimuli != list(range(len(stimuli))): + raise ValueError('display order is not incremental.') - onset_array = np.array([]) - data_format = '' - description = 'TimeSeries of uniform contrast stimulus. No onset information. Stimulus generated by ' \ - 'corticalmapping.VisualStim.UniformContrast class.' - return onset_array, data_format, description, {} + if display_order is not None: - def _add_sparse_noise_stimulus_corticalmapping(self, log_dict, display_order): + if display_order != len(stimuli): + raise ValueError('input display order not the next display.') + + def _add_sparse_noise_stimulation(self, log_dict, display_order): stim_name = log_dict['stimulation']['stimName'] @@ -2312,7 +1314,7 @@ def _add_sparse_noise_stimulus_corticalmapping(self, log_dict, display_order): 'corticalmapping.VisualStim.DisplaySequence for display') stim.finalize() - def _add_flashing_circle_stimulus_corticalmapping(self, log_dict, display_order): + def _add_flashing_circle_stimulation(self, log_dict, display_order): stim_name = log_dict['stimulation']['stimName'] @@ -2352,7 +1354,7 @@ def _add_flashing_circle_stimulus_corticalmapping(self, log_dict, display_order) stim.set_value('iteration', log_dict['stimulation']['iteration']) stim.finalize() - def _add_uniform_contrast_stimulus_corticalmapping(self, log_dict, display_order): + def _add_uniform_contrast_stimulation(self, log_dict, display_order): stim_name = log_dict['stimulation']['stimName'] @@ -2383,7 +1385,7 @@ def _add_uniform_contrast_stimulus_corticalmapping(self, log_dict, display_order stim.set_value('stim_name', log_dict['stimulation']['stimName']) stim.finalize() - def _add_drifting_grating_circle_stimulus_corticalmapping(self, log_dict, display_order): + def _add_drifting_grating_circle_stimulation(self, log_dict, display_order): stim_name = log_dict['stimulation']['stimName'] @@ -2401,9 +1403,9 @@ def _add_drifting_grating_circle_stimulus_corticalmapping(self, log_dict, displa 'stimulus') stim.set_time(time_stamps) stim.set_data(frame_array, unit='', conversion=np.nan, resolution=np.nan) - stim.set_comments('the timestamps of displayed frames (saved in data) are referenced to the start of ' - 'this particular display, not the master time clock. For more useful timestamps, check ' - '"/processing" for aligned photodiode onset timestamps.') + stim.set_comments('the timestamps of displayed frames (saved in data) are referenced to the start of' + 'this particular display, not the master time clock. For more useful timestamps, check' + '/processing for aligned photodiode onset timestamps.') stim.set_description('data formatting: [isDisplay (0:gap; 1:display), ' 'firstFrameInCycle (first frame in cycle:1, rest display frames: 0), ' 'spatialFrequency (cyc/deg), ' @@ -2435,7 +1437,7 @@ def _add_drifting_grating_circle_stimulus_corticalmapping(self, log_dict, displa stim.set_value('background_color', log_dict['stimulation']['background']) stim.finalize() - def _add_drifting_checker_board_stimulus_corticalmapping(self, log_dict, display_order): + def _add_drifting_checker_board_stimulation(self, log_dict, display_order): stim_name = log_dict['stimulation']['stimName'] @@ -2474,8 +1476,7 @@ def _add_drifting_checker_board_stimulus_corticalmapping(self, log_dict, display 'indicatorColor (for photodiode, from -1 to 1)]. ' 'direction (B2U: 0, U2B: 1, L2R: 2, R2L: 3), ' 'for gap frames, the 2ed to 3th elements should be np.nan.') - stim.set_value('data_formatting', - ['isDisplay', 'squarePolarity', 'sweepIndex', 'indicatorColor', 'sweepDirection']) + stim.set_value('data_formatting', ['isDisplay', 'squarePolarity', 'sweepIndex', 'indicatorColor', 'sweepDirection']) stim.set_source('corticalmapping.VisualStim.KSstimAllDir for stimulus; ' 'corticalmapping.VisualStim.DisplaySequence for display') stim.set_value('background_color', log_dict['stimulation']['background']) @@ -2490,7 +1491,7 @@ def _add_drifting_checker_board_stimulus_corticalmapping(self, log_dict, display display_grp.attrs['description'] = 'This group saves the useful infomation about the retiotopic mapping visual' \ 'stimulation (drifting checker board sweeps in all directions). Generated ' \ 'by the corticalmapping.HighLevel.analysisMappingDisplayLog() function.' - for direction, value in display_info.items(): + for direction, value in list(display_info.items()): dir_grp = display_grp.create_group(direction) dir_grp.attrs['description'] = 'group containing the relative information about all sweeps in a particular' \ 'sweep direction. B: bottom, U: up, L: nasal, R: temporal (for stimulus to' \ @@ -2506,95 +1507,38 @@ def _add_drifting_checker_board_stimulus_corticalmapping(self, log_dict, display 'degree = phase * slope + intercept' equ_dset.attrs['data_format'] = ['slope', 'intercept'] - # ===========================corticalmapping visual stimuli related (non-indexed display)=========================== + def add_sync_data(self): + # not for now + pass - - # ============================================eye tracking related================================================== - def add_eyetracking_data(self, ts_path='', pupil_x=None, pupil_y=None, pupil_area=None, module_name='eye_tracking', - unit='unknown', side='leftright_unknown', comments='', description='', source='', - pupil_shape=None, pupil_shape_meta=None): + def add_kilosort_clusters(self, folder, module_name, ind_start=None, ind_end=None): """ - add eyetrackin data as a module named 'eye_tracking' - :param ts_path: str, timestamp path in the nwb file - :param pupil_x: 1-d array, horizontal position of pupil center - :param pupil_y: 1-d array, vertical position of pupil center - :param pupil_area: 1-d array, area of detected pupil - :param module_name: str, module name to be created - :param unit: str, the unit of pupil_x, pupil_y, the unit of pupil_area should be ^2 - :param side: str, side of the eye, 'left' or 'right' - :param comments: str - :param description: str - :param source: str + expects spike clusters.npy, spike_templates.npy, and spike_times.npy in the folder. use only for the direct outputs of kilosort, + that haven't been modified with phy-template. + :param folder: :return: """ - if ts_path not in self.file_pointer['acquisition/timeseries'].keys(): - print('Cannot find field "{}" in "acquisition/timeseries".'.format(ts_path)) - return - else: - ts = self.file_pointer['acquisition/timeseries'][ts_path]['timestamps'].value - - ts_num = len(ts) - print('number of eyet racking timestamps: {}'.format(ts.shape)) - - ts_num_min = ts_num + # if ind_start == None: + # ind_start = 0 + # + # if ind_end == None: + # ind_end = self.file_pointer['acquisition/timeseries/photodiode/num_samples'].value + # + # if ind_start >= ind_end: + # raise ValueError('ind_end should be larger than ind_start.') + # + # spike_clusters = np.load(os.path.join(folder, 'spike_clusters.npy')) + # spike_templates = np.load(os.path.join(folder, 'spike_templates.npy')) + # spikes_times = np.load(os.path.join(folder, 'spike_times.npy')) + # templates = np.load(os.path.join(folder, 'templates.npy')) - if pupil_x is not None: - if pupil_x.shape[0] != ts_num: - print('length of pupil_x ({}) is different from the number' - ' of timestamps ({}).'.format(pupil_x.shape[0], ts_num)) - ts_num_min = min([ts_num_min, pupil_x.shape[0]]) - - if pupil_y is not None: - if pupil_y.shape[0] != ts_num: - print('length of pupil_y ({}) is different from the number' - ' of timestamps ({}).'.format(pupil_area.shape[0], ts_num)) - ts_num_min = min([ts_num_min, pupil_y.shape[0]]) - - if pupil_area is not None: - if pupil_area.shape[0] != ts_num: - print('length of pupil_area ({}) is different from the number' - ' of timestamps ({}).'.format(pupil_area.shape[0], ts_num)) - ts_num_min = min([ts_num_min, pupil_area.shape[0]]) - - ts_to_add = ts[0:ts_num_min] - - pupil_series = self.create_timeseries('TimeSeries', name='eyetracking', modality='other') - pupil_series.set_data([], unit='', conversion=np.nan, resolution=np.nan) - pupil_series.set_time(ts_to_add) - - if pupil_x is not None: - pupil_series.set_value('pupil_x', pupil_x[0:ts_num_min]) - - if pupil_y is not None: - pupil_series.set_value('pupil_y', pupil_y[0:ts_num_min]) - - if pupil_area is not None: - pupil_series.set_value('pupil_area', pupil_area[0:ts_num_min]) - - if pupil_shape is not None: - pupil_series.set_value('pupil_shape', pupil_shape[0:ts_num_min, :]) - - if pupil_shape_meta is not None: - pupil_series.set_value('pupil_shape_meta', pupil_shape_meta) - - pupil_series.set_value('unit', 'pupil_x: {}; pupil_y: {}; pupil_area: {} ^ 2'.format(unit, unit, unit)) - pupil_series.set_value('side', side) - pupil_series.set_comments(comments) - pupil_series.set_description(description) - pupil_series.set_source(source) - - et_mod = self.create_module('{}_{}'.format(module_name, side)) - et_interf = et_mod.create_interface("PupilTracking") - et_interf.add_timeseries(pupil_series) - pupil_series.finalize() - et_interf.finalize() - et_mod.finalize() - - # ============================================eye tracking related================================================== + # not for now + pass if __name__ == '__main__': + # ========================================================================================================= # tmp_path = r"E:\data\python_temp_folder\test.nwb" # open_ephys_folder = r"E:\data\2016-07-19-160719-M256896\100_spontaneous_2016-07-19_09-45-06_Jun" @@ -2638,7 +1582,7 @@ def add_eyetracking_data(self, ts_path='', pupil_x=None, pupil_y=None, pupil_are # log_path = r"E:\data\2016-06-29-160610-M240652-Ephys\101_160610172256-SparseNoise-M240652-Jun-0-" \ # r"notTriggered-complete.pkl" # rf = RecordedFile(tmp_path) - # rf.add_visual_stimulation_corticalmapping(log_path) + # rf.add_visual_stimulation(log_path) # rf.close() # ========================================================================================================= @@ -2647,7 +1591,7 @@ def add_eyetracking_data(self, ts_path='', pupil_x=None, pupil_y=None, pupil_are # log_path = r"\\aibsdata2\nc-ophys\CorticalMapping\IntrinsicImageData\161017-M274376-FlashingCircle" \ # r"\161017162026-FlashingCircle-M274376-Sahar-101-Triggered-complete.pkl" # rf = RecordedFile(tmp_path) - # rf.add_visual_stimulation_corticalmapping(log_path, display_order=1) + # rf.add_visual_stimulation(log_path, display_order=1) # rf.close() # ========================================================================================================= @@ -2656,7 +1600,7 @@ def add_eyetracking_data(self, ts_path='', pupil_x=None, pupil_y=None, pupil_are # log_paths = [r"\\aibsdata2\nc-ophys\CorticalMapping\IntrinsicImageData\161017-M274376-FlashingCircle\161017162026-FlashingCircle-M274376-Sahar-101-Triggered-complete.pkl", # r"E:\data\2016-06-29-160610-M240652-Ephys\101_160610172256-SparseNoise-M240652-Jun-0-notTriggered-complete.pkl",] # rf = RecordedFile(tmp_path) - # rf.add_visual_stimuli_corticalmapping(log_paths) + # rf.add_visual_stimulations(log_paths) # rf.close() # ========================================================================================================= @@ -2664,7 +1608,7 @@ def add_eyetracking_data(self, ts_path='', pupil_x=None, pupil_y=None, pupil_are # tmp_path = r"E:\data\python_temp_folder\test.nwb" # log_paths = [r"C:\data\sequence_display_log\161018164347-UniformContrast-MTest-Jun-255-notTriggered-complete.pkl"] # rf = RecordedFile(tmp_path) - # rf.add_visual_stimuli_corticalmapping(log_paths) + # rf.add_visual_stimulations(log_paths) # rf.close() # ========================================================================================================= @@ -2673,7 +1617,7 @@ def add_eyetracking_data(self, ts_path='', pupil_x=None, pupil_y=None, pupil_are # # log_paths = [r"C:\data\sequence_display_log\160205131514-ObliqueKSstimAllDir-MTest-Jun-255-notTriggered-incomplete.pkl"] # log_paths = [r"C:\data\sequence_display_log\161018174812-DriftingGratingCircle-MTest-Jun-255-notTriggered-complete.pkl"] # rf = RecordedFile(tmp_path) - # rf.add_visual_stimuli_corticalmapping(log_paths) + # rf.add_visual_stimulations(log_paths) # rf.close() # ========================================================================================================= @@ -2696,16 +1640,18 @@ def add_eyetracking_data(self, ts_path='', pupil_x=None, pupil_y=None, pupil_are # ========================================================================================================= # ========================================================================================================= - # rf = RecordedFile(r"D:\data2\thalamocortical_project\method_development\2017-02-25-ephys-software-development" - # r"\test_folder\170302_M292070_100_SparseNoise.nwb") - # unit = 'unit_00065' - # wfs = rf.file_pointer['processing/tetrode/UnitTimes'][unit]['template'].value - # stds = rf.file_pointer['processing/tetrode/UnitTimes'][unit]['template_std'].value - # x_pos = rf.file_pointer['processing/tetrode/channel_xpos'].value - # y_pos = rf.file_pointer['processing/tetrode/channel_ypos'].value - # rf.close() - # plot_waveforms(wfs, zip(x_pos, y_pos), stds, axes_size=(0.3, 0.3)) - # plt.show() + rf = RecordedFile(r"D:\data2\thalamocortical_project\method_development\2017-02-25-ephys-software-development" + r"\test_folder\170302_M292070_100_SparseNoise.nwb") + unit = 'unit_00065' + wfs = rf.file_pointer['processing/tetrode/UnitTimes'][unit]['template'].value + stds = rf.file_pointer['processing/tetrode/UnitTimes'][unit]['template_std'].value + x_pos = rf.file_pointer['processing/tetrode/channel_xpos'].value + y_pos = rf.file_pointer['processing/tetrode/channel_ypos'].value + rf.close() + plot_waveforms(wfs, list(zip(x_pos, y_pos)), stds, axes_size=(0.3, 0.3)) + plt.show() # ========================================================================================================= - print('for debug ...') + + + print('for debug ...') \ No newline at end of file diff --git a/corticalmapping/README.md b/corticalmapping/README.md new file mode 100644 index 0000000..18c9e1b --- /dev/null +++ b/corticalmapping/README.md @@ -0,0 +1,6 @@ +corticalmapping + +by Jun Zhuang @ 2014 + +contains basic visual stimulation, imaging analysis, plotting, cell visual response properties analysis functionalities. +also contains a relatively mature code base of retinotopic mapping (visual stimulation and analysis) \ No newline at end of file diff --git a/corticalmapping/RetinotopicMapping.py b/corticalmapping/RetinotopicMapping.py index 5a77143..fcea62f 100644 --- a/corticalmapping/RetinotopicMapping.py +++ b/corticalmapping/RetinotopicMapping.py @@ -11,19 +11,15 @@ from operator import itemgetter import skimage.morphology as sm import skimage.transform as tsfm - +import cv2 import matplotlib.colors as col from matplotlib import cm -import core.FileTools as ft -import core.ImageAnalysis as ia -import core.PlottingTools as pt -import tifffile as tf +import corticalmapping.core.FileTools as ft +import corticalmapping.core.ImageAnalysis as ia +import corticalmapping.core.PlottingTools as pt +import imaging_behavior.core.tifffile as tf -try: - import cv2 -except ImportError as e: - print e def loadTrial(trialPath): @@ -38,22 +34,20 @@ def loadTrial(trialPath): except KeyError: traces = [] - trial = RetinotopicMappingTrial(mouseID=trialDict['mouseID'], # str, mouseID - dateRecorded=trialDict['dateRecorded'], # int, date recorded, yearmonthday - trialNum=trialDict['trialNum'], # str, number of the trail on that day - mouseType=trialDict['mouseType'], # str, mouse Genotype - visualStimType=trialDict['visualStimType'], # str, stimulation type - visualStimBackground=trialDict['visualStimBackground'], - # str, background of visual stimulation - imageExposureTime=trialDict['imageExposureTime'], - # float, exposure time of image file - altPosMap=trialDict['altPosMap'], # altitude position map - aziPosMap=trialDict['aziPosMap'], # azimuth position map - altPowerMap=trialDict['altPowerMap'], # altitude power map - aziPowerMap=trialDict['aziPowerMap'], # azimuth power map - vasculatureMap=trialDict['vasculatureMap'], # vasculature map - params=trialDict['params'], - isAnesthetized=trialDict['isAnesthetized']) + trial = RetinotopicMappingTrial(mouseID = trialDict['mouseID'], # str, mouseID + dateRecorded = trialDict['dateRecorded'], # int, date recorded, yearmonthday + trialNum = trialDict['trialNum'], # str, number of the trail on that day + mouseType = trialDict['mouseType'], # str, mouse Genotype + visualStimType = trialDict['visualStimType'], # str, stimulation type + visualStimBackground = trialDict['visualStimBackground'], # str, background of visual stimulation + imageExposureTime = trialDict['imageExposureTime'], # float, exposure time of image file + altPosMap = trialDict['altPosMap'], # altitude position map + aziPosMap = trialDict['aziPosMap'], # azimuth position map + altPowerMap = trialDict['altPowerMap'], # altitude power map + aziPowerMap = trialDict['aziPowerMap'], # azimuth power map + vasculatureMap = trialDict['vasculatureMap'], # vasculature map + params = trialDict['params'], + isAnesthetized = trialDict['isAnesthetized']) try: trial.altPosMapf = trialDict['altPosMapf'] @@ -76,28 +70,22 @@ def loadTrial(trialPath): pass try: - if isinstance(trialDict['finalPatches'].values()[0], dict): + if isinstance(list(trialDict['finalPatches'].values())[0],dict): trial.finalPatches = {} - for area, patchDict in trialDict['finalPatches'].iteritems(): - try: - trial.finalPatches.update({area: Patch(patchDict['array'], patchDict['sign'])}) - except KeyError: - trial.finalPatches.update({area: Patch(patchDict['sparseArray'], patchDict['sign'])}) - else: - pass + for area,patchDict in trialDict['finalPatches'].items(): + try:trial.finalPatches.update({area:Patch(patchDict['array'],patchDict['sign'])}) + except KeyError:trial.finalPatches.update({area:Patch(patchDict['sparseArray'],patchDict['sign'])}) + else: pass except KeyError: pass try: - if isinstance(trialDict['finalPatchesMarked'].values()[0], dict): + if isinstance(list(trialDict['finalPatchesMarked'].values())[0],dict): trial.finalPatchesMarked = {} - for area, patchDict in trialDict['finalPatchesMarked'].iteritems(): - try: - trial.finalPatchesMarked.update({area: Patch(patchDict['array'], patchDict['sign'])}) - except KeyError: - trial.finalPatchesMarked.update({area: Patch(patchDict['sparseArray'], patchDict['sign'])}) - else: - pass + for area,patchDict in trialDict['finalPatchesMarked'].items(): + try:trial.finalPatchesMarked.update({area:Patch(patchDict['array'],patchDict['sign'])}) + except KeyError:trial.finalPatchesMarked.update({area:Patch(patchDict['sparseArray'],patchDict['sign'])}) + else: pass except KeyError: pass @@ -139,13 +127,13 @@ def findPhaseIndex(trace, harmonic=1): :return: index of the peak of the given harmonic ''' traceF = np.fft.fft(trace) - angle = (-1 * np.angle(traceF[harmonic])) % (2 * np.pi) - index = len(trace) * angle / (2 * np.pi) + angle = (-1*np.angle(traceF[harmonic]))%(2*np.pi) + index = len(trace) * angle / (2*np.pi) return index -def generatePhaseMap(movie, cycles=1, isReverse=False, isFilter=False, sigma=3., isplot=False): +def generatePhaseMap(movie,cycles = 1,isReverse = False,isFilter = False,sigma = 3.,isplot = False): ''' generating phase map of a 3-d movie, on the frequency defined by cycles. @@ -164,36 +152,36 @@ def generatePhaseMap(movie, cycles=1, isReverse=False, isFilter=False, sigma=3., if isReverse: movie = np.amax(movie) - movie - spectrumMovie = np.fft.fft(movie, axis=0) + spectrumMovie = np.fft.fft(movie,axis=0) - # generate power movie + #generate power movie powerMovie = (np.abs(spectrumMovie) * 2.) / np.size(movie, 0) - powerMap = np.abs(powerMovie[cycles, :, :]) + powerMap = np.abs(powerMovie[cycles,:,:]) - # generate phase movie + #generate phase movie phaseMovie = np.angle(spectrumMovie) - # phaseMap = phaseMovie[cycles,:,:] - phaseMap = -1 * phaseMovie[cycles, :, :] + #phaseMap = phaseMovie[cycles,:,:] + phaseMap = -1 * phaseMovie[cycles,:,:] - # remove pixels with not enough power in the ideal frequency + #remove pixels with not enough power in the ideal frequency if isFilter == True: - meanPower = np.mean(powerMovie, axis=0) - stdPower = np.std(powerMovie, axis=0) + meanPower = np.mean(powerMovie, axis = 0) + stdPower = np.std(powerMovie, axis = 0) for i in np.arange(powerMap.shape[0]): for j in np.arange(powerMap.shape[1]): - if powerMap[i, j] < meanPower[i, j] + sigma * stdPower[i, j]: - phaseMap[i, j] = np.nan + if powerMap[i,j] < meanPower[i,j] + sigma * stdPower[i,j]: + phaseMap[i,j] = np.nan if isplot == True: plt.figure() plotMap = 180 * (phaseMap / np.pi) - plt.imshow(plotMap, cmap='hsv', aspect='equal') + plt.imshow(plotMap,aspect='equal') plt.colorbar() - return phaseMap % (2 * np.pi), powerMap # value from -pi to pi + return phaseMap % (2*np.pi), powerMap #value from -pi to pi -def generatePhaseMap2(movie, cycles, isReverse=False, isPlot=False): +def generatePhaseMap2(movie,cycles,isReverse = False,isPlot = False): ''' generating phase map of a 3-d movie, on the frequency defined by cycles. the movie should have the same length of 'cycles' number of cycles. @@ -203,14 +191,14 @@ def generatePhaseMap2(movie, cycles, isReverse=False, isPlot=False): spectrumMovie = np.fft.fft(movie, axis=0) - # generate power movie + #generate power movie powerMovie = (np.abs(spectrumMovie) * 2.) / np.size(movie, 0) - powerMap = np.abs(powerMovie[cycles, :, :]) + powerMap = np.abs(powerMovie[cycles,:,:]) - # generate phase movie + #generate phase movie phaseMovie = np.angle(spectrumMovie) - # phaseMap = phaseMovie[cycles,:,:] - phaseMap = -1 * phaseMovie[cycles, :, :] + #phaseMap = phaseMovie[cycles,:,:] + phaseMap = -1 * phaseMovie[cycles,:,:] phaseMap = phaseMap % (2 * np.pi) if isPlot == True: @@ -218,15 +206,13 @@ def generatePhaseMap2(movie, cycles, isReverse=False, isPlot=False): plotMap = 180 * (phaseMap / np.pi) plt.imshow(plotMap, aspect='equal', - cmap='hsv', - vmax=360, - vmin=0, - interpolation='nearest') + cmap = 'hsv', + vmax = 360, + vmin = 0, + interpolation = 'nearest') plt.colorbar() return phaseMap, powerMap - - # # def generatePhaseMap3(movie, filter_size=None, isReverse=False, isPlot=False): # ''' @@ -267,14 +253,14 @@ def generatePhaseMap2(movie, cycles, isReverse=False, isPlot=False): # return phaseMap, powerMap -def getPhase(trace, cycles, isReverse=False): +def getPhase(trace,cycles,isReverse = False): ''' return phase and power of a certain trace, the trace should have 'cycles' number of cycles the returned phase can be plugged direct into equation generated by getPhasePositionEquation() function to calculate retinotopic location of a pertical trace ''' - if trace.ndim != 1: raise ValueError, 'input trace should be a 1-d array!' + if trace.ndim != 1: raise ValueError('input trace should be a 1-d array!') if isReverse: trace = np.amax(trace) - trace @@ -292,36 +278,36 @@ def phasePosition(phaseMap, displayLog): log ''' - if not ('KSstim' in displayLog['stimulation']['stimName']): - raise TypeError, 'The stimulation is not KSstim!' + if not('KSstim' in displayLog['stimulation']['stimName']): + raise TypeError('The stimulation is not KSstim!') sweepTable = displayLog['stimulation']['sweepTable'] frames = displayLog['stimulation']['frames'] - phase = np.linspace(0, 2 * np.pi, len(frames), endpoint=False) + phase = np.linspace(0,2*np.pi,len(frames),endpoint=False) phaseIndexStart = np.nan phaseIndexEnd = np.nan phaseStart = np.nan phaseEnd = np.nan - for i in range(0, len(frames) - 1): - if (frames[i][2] == None) & (frames[i + 1][2] != None): - phaseStart = phase[i + 1] - phaseIndexStart = int(i + 1) - # print phaseIndexStart, phaseStart + for i in range(0,len(frames)-1): + if (frames[i][2] == None) & (frames[i+1][2] != None): + phaseStart = phase[i+1] + phaseIndexStart = int(i+1) + #print phaseIndexStart, phaseStart - if (frames[i][2] != None) & (frames[i + 1][2] == None): + if (frames[i][2] != None) & (frames[i+1][2] == None): phaseEnd = phase[i] phaseIndexEnd = int(i) - # print phaseIndexEnd, phaseEnd + #print phaseIndexEnd, phaseEnd if np.isnan(phaseIndexStart): - print 'no gap in the front.' + print('no gap in the front.') phaseIndexStart = 0 phaseStart = phase[0] if np.isnan(phaseIndexEnd): - print 'no gap in the end.' + print('no gap in the end.') phaseIndexEnd = len(phase) phaseEnd = phase[-1] @@ -330,7 +316,7 @@ def phasePosition(phaseMap, displayLog): for i, framei in enumerate(frames): if framei[2] != None: - position[i] = (sweepTable[framei[2]][1] + sweepTable[framei[2]][2]) / 2 + position[i] = (sweepTable[framei[2]][1]+sweepTable[framei[2]][2])/2 stiDirection = displayLog['stimulation']['direction'] @@ -338,17 +324,17 @@ def phasePosition(phaseMap, displayLog): position = position[::-1] stiDirection = stiDirection[::-1] - print '\nStimulus direction:', stiDirection + print('\nStimulus direction:', stiDirection) - slope, intercept, r_value, p_value, stderr = stats.linregress(phase[phaseIndexStart:(phaseIndexEnd + 1)], - position[phaseIndexStart:(phaseIndexEnd + 1)]) + slope, intercept, r_value, p_value, stderr = stats.linregress(phase[phaseIndexStart:(phaseIndexEnd+1)], + position[phaseIndexStart:(phaseIndexEnd+1)]) positionMap = phaseMap * slope + intercept - # print slope, intercept, phaseMap[74,66], phaseMap2[74,66], positionMap[74,66] + #print slope, intercept, phaseMap[74,66], phaseMap2[74,66], positionMap[74,66] - # positionMap[phaseMapphaseEnd] = np.nan + #positionMap[phaseMapphaseEnd] = np.nan return positionMap @@ -359,41 +345,40 @@ def getPhasePositionEquation(displayLog): display of KSStim ''' - if not ('KSstim' in displayLog['stimulation']['stimName']): - raise TypeError, 'The stimulation is not KSstim!' + if not('KSstim' in displayLog['stimulation']['stimName']): + raise TypeError('The stimulation is not KSstim!') sweepTable = displayLog['stimulation']['sweepTable'] frames = displayLog['stimulation']['frames'] - phase = np.linspace(0, 2 * np.pi, len(frames), endpoint=False) + phase = np.linspace(0,2*np.pi,len(frames),endpoint=False) phaseIndexStart = np.nan phaseIndexEnd = np.nan - for i in range(0, len(frames) - 1): - if (frames[i][2] == None) & (frames[i + 1][2] != None): phaseIndexStart = int(i + 1) - if (frames[i][2] != None) & (frames[i + 1][2] == None): phaseIndexEnd = int(i) + for i in range(0,len(frames)-1): + if (frames[i][2] == None) & (frames[i+1][2] != None):phaseIndexStart = int(i+1) + if (frames[i][2] != None) & (frames[i+1][2] == None):phaseIndexEnd = int(i) - if np.isnan(phaseIndexStart): print 'no gap in the front.'; phaseIndexStart = 0 + if np.isnan(phaseIndexStart):print('no gap in the front.'); phaseIndexStart = 0 - if np.isnan(phaseIndexEnd): print 'no gap in the end.'; phaseIndexEnd = len(phase) + if np.isnan(phaseIndexEnd):print('no gap in the end.'); phaseIndexEnd = len(phase) position = np.zeros(len(frames)) position[:] = np.nan for i, framei in enumerate(frames): if framei[2] is not None: - position[i] = (sweepTable[framei[2]][1] + sweepTable[framei[2]][2]) / 2 + position[i] = (sweepTable[framei[2]][1]+sweepTable[framei[2]][2])/2 stiDirection = displayLog['stimulation']['direction'] if displayLog['presentation']['displayOrder'] == -1: - position = position[::-1]; - stiDirection = stiDirection[::-1] + position = position[::-1]; stiDirection = stiDirection[::-1] # print '\nStimulus direction:', stiDirection - slope, intercept, r_value, p_value, stderr = stats.linregress(phase[phaseIndexStart:(phaseIndexEnd + 1)], - position[phaseIndexStart:(phaseIndexEnd + 1)]) + slope, intercept, r_value, p_value, stderr = stats.linregress(phase[phaseIndexStart:(phaseIndexEnd+1)], + position[phaseIndexStart:(phaseIndexEnd+1)]) # print 'slope: \t'+str(slope) # print 'intercept: \t'+str(intercept) @@ -411,28 +396,28 @@ def getPhasePositionEquation2(frames, sweepTable): Assuming displayOrder is 1 when display this stimulus ''' - phase = np.linspace(0, 2 * np.pi, len(frames), endpoint=False) + phase = np.linspace(0,2*np.pi,len(frames),endpoint=False) phaseIndexStart = np.nan phaseIndexEnd = np.nan - for i in range(0, len(frames) - 1): - if (frames[i][2] == None) & (frames[i + 1][2] != None): phaseIndexStart = int(i + 1) - if (frames[i][2] != None) & (frames[i + 1][2] == None): phaseIndexEnd = int(i) + for i in range(0,len(frames)-1): + if (frames[i][2] == None) & (frames[i+1][2] != None):phaseIndexStart = int(i+1) + if (frames[i][2] != None) & (frames[i+1][2] == None):phaseIndexEnd = int(i) - if np.isnan(phaseIndexStart): print 'no gap in the front.'; phaseIndexStart = 0 - if np.isnan(phaseIndexEnd): print 'no gap in the end.'; phaseIndexEnd = len(phase) + if np.isnan(phaseIndexStart):print('no gap in the front.'); phaseIndexStart = 0 + if np.isnan(phaseIndexEnd):print('no gap in the end.'); phaseIndexEnd = len(phase) position = np.zeros(len(frames)) position[:] = np.nan for i, framei in enumerate(frames): if framei[2] is not None: - position[i] = (sweepTable[framei[2]][1] + sweepTable[framei[2]][2]) / 2 + position[i] = (sweepTable[framei[2]][1]+sweepTable[framei[2]][2])/2 # print '\nStimulus direction:', stiDirection - slope, intercept, r_value, p_value, stderr = stats.linregress(phase[phaseIndexStart:(phaseIndexEnd + 1)], - position[phaseIndexStart:(phaseIndexEnd + 1)]) + slope, intercept, r_value, p_value, stderr = stats.linregress(phase[phaseIndexStart:(phaseIndexEnd+1)], + position[phaseIndexStart:(phaseIndexEnd+1)]) # print 'slope: \t'+str(slope) # print 'intercept: \t'+str(intercept) @@ -443,13 +428,13 @@ def getPhasePositionEquation2(frames, sweepTable): return slope, intercept -def visualSignMap(phasemap1, phasemap2): +def visualSignMap(phasemap1,phasemap2): ''' calculate visual sign map from two orthogonally oriented phase maps ''' if phasemap1.shape != phasemap2.shape: - raise LookupError, "'phasemap1' and 'phasemap2' should have same size." + raise LookupError("'phasemap1' and 'phasemap2' should have same size.") gradmap1 = np.gradient(phasemap1) gradmap2 = np.gradient(phasemap2) @@ -463,96 +448,98 @@ def visualSignMap(phasemap1, phasemap2): graddir2 = np.zeros(np.shape(gradmap2[0])) # gradmag2 = np.zeros(np.shape(gradmap2[0])) - for i in range(phasemap1.shape[0]): + for i in range(phasemap1.shape[0]): for j in range(phasemap2.shape[1]): - graddir1[i, j] = math.atan2(gradmap1[1][i, j], gradmap1[0][i, j]) - graddir2[i, j] = math.atan2(gradmap2[1][i, j], gradmap2[0][i, j]) + + graddir1[i,j] = math.atan2(gradmap1[1][i,j],gradmap1[0][i,j]) + graddir2[i,j] = math.atan2(gradmap2[1][i,j],gradmap2[0][i,j]) # gradmag1[i,j] = np.sqrt((gradmap1[1][i,j]**2)+(gradmap1[0][i,j]**2)) # gradmag2[i,j] = np.sqrt((gradmap2[1][i,j]**2)+(gradmap2[0][i,j]**2)) - vdiff = np.multiply(np.exp(1j * graddir1), np.exp(-1j * graddir2)) + vdiff = np.multiply(np.exp(1j * graddir1),np.exp(-1j * graddir2)) areamap = np.sin(np.angle(vdiff)) return areamap -def dilationPatches(rawPatches, smallPatchThr=5, borderWidth=1): # pixel width of the border after dilation +def dilationPatches(rawPatches,smallPatchThr = 5,borderWidth = 1): #pixel width of the border after dilation ''' dilation patched in a given area untill the border between them are as narrow as defined by 'borderWidth'. ''' - # get patch borders + #get patch borders total_area = sm.convex_hull_image(rawPatches) patchBorder = np.multiply(-1 * (rawPatches - 1), total_area) - # thinning patch borders + #thinning patch borders patchBorder = sm.skeletonize(patchBorder) - # thicking patch borders + #thicking patch borders if borderWidth > 1: - patchBorder = ni.binary_dilation(patchBorder, iterations=borderWidth - 1).astype(np.int) + patchBorder = ni.binary_dilation(patchBorder, iterations = borderWidth - 1).astype(np.int) - # genertating new patches + #genertating new patches newPatches = np.multiply(-1 * (patchBorder - 1), total_area) - # removing small edges + #removing small edges labeledPatches, patchNum = ni.label(newPatches) - for i in xrange(1, patchNum + 1): + for i in range(1, patchNum + 1): currPatch = np.array(labeledPatches) currPatch[currPatch != i] = 0 currPatch = currPatch / i if (np.sum(np.multiply(currPatch, rawPatches)[:]) == 0) or (np.sum(currPatch[:]) < smallPatchThr): - # revCurrPatch = -1 * (currPatch - 1) - # newPatches = np.multiply(newPatches, revCurrPatch) + #revCurrPatch = -1 * (currPatch - 1) + #newPatches = np.multiply(newPatches, revCurrPatch) newPatches[currPatch == 1] = 0 else: currPatch = ni.binary_closing(currPatch, - structure=np.ones((borderWidth + 2, borderWidth + 2))).astype(np.int) + structure = np.ones((borderWidth+2,borderWidth+2))).astype(np.int) newPatches[currPatch == 1] = 1 return newPatches -def dilationPatches2(rawPatches, dilationIter=20, borderWidth=1): # pixel width of the border after dilation +def dilationPatches2(rawPatches,dilationIter = 20,borderWidth = 1): #pixel width of the border after dilation ''' dilation patched in a given area untill the border between them are as narrow as defined by 'borderWidth'. ''' - total_area = ni.binary_dilation(rawPatches, iterations=dilationIter).astype(np.int) + + total_area = ni.binary_dilation(rawPatches, iterations = dilationIter).astype(np.int) patchBorder = total_area - rawPatches - # thinning patch borders + #thinning patch borders patchBorder = sm.skeletonize(patchBorder) - # thickening patch borders + #thickening patch borders if borderWidth > 1: - patchBorder = ni.binary_dilation(patchBorder, iterations=borderWidth - 1).astype(np.int) + patchBorder = ni.binary_dilation(patchBorder, iterations = borderWidth - 1).astype(np.int) - # genertating new patches + #genertating new patches newPatches = np.multiply(-1 * (patchBorder - 1), total_area) - # removing small edges + #removing small edges labeledPatches, patchNum = ni.label(newPatches) - newPatches2 = np.zeros(newPatches.shape, dtype=np.int) + newPatches2 = np.zeros(newPatches.shape, dtype = np.int) - for i in xrange(1, patchNum + 1): - currPatch = np.zeros(labeledPatches.shape, dtype=np.int) + for i in range(1, patchNum + 1): + currPatch = np.zeros(labeledPatches.shape, dtype = np.int) currPatch[labeledPatches == i] = 1 currPatch[labeledPatches != i] = 0 if (np.sum(np.multiply(currPatch, rawPatches)[:]) > 0): - # currPatch = ni.binary_closing(currPatch, - # structure = np.ones((borderWidth+2,borderWidth+2))).astype(np.int) +# currPatch = ni.binary_closing(currPatch, +# structure = np.ones((borderWidth+2,borderWidth+2))).astype(np.int) newPatches2[currPatch == 1] = 1 return newPatches2 @@ -566,21 +553,21 @@ def labelPatches(patchmap, signMap, connectivity=4): labeledPatches, patchNum = ni.label(patchmap) - # list of area of every patch, first column: patch label, second column: area - patchArea = np.zeros((patchNum, 2), dtype=np.int) + #list of area of every patch, first column: patch label, second column: area + patchArea = np.zeros((patchNum,2),dtype=np.int) - for i in range(1, patchNum + 1): - currPatch = np.zeros(labeledPatches.shape, dtype=np.int) + for i in range(1, patchNum+1): + currPatch = np.zeros(labeledPatches.shape, dtype = np.int) currPatch[labeledPatches == i] = 1 currPatch[labeledPatches != i] = 0 - patchArea[i - 1] = [i, np.sum(currPatch[:])] + patchArea[i-1] = [i, np.sum(currPatch[:])] - # sort patches by the area, from largest to the smallest - sortArea = patchArea[patchArea[:, 1].argsort(axis=0)][::-1, :] + #sort patches by the area, from largest to the smallest + sortArea=patchArea[patchArea[:,1].argsort(axis=0)][::-1,:] patches = {} - for i, ind in enumerate(sortArea[:, 0]): - currPatch = np.zeros(labeledPatches.shape, dtype=np.int) + for i, ind in enumerate(sortArea[:,0]): + currPatch = np.zeros(labeledPatches.shape, dtype = np.int) currPatch[labeledPatches == ind] = 1 currPatch[labeledPatches != ind] = 0 currSignPatch = np.multiply(currPatch, signMap) @@ -590,19 +577,19 @@ def labelPatches(patchmap, signMap, connectivity=4): elif np.sum(currSignPatch[:]) < 0: currSign = -1 else: - raise LookupError, 'This patch has no visual Sign!!' + raise LookupError('This patch has no visual Sign!!') patchname = 'patch' + ft.int2str(i, 2) - patches.update({patchname: Patch(currPatch, currSign)}) + patches.update({patchname : Patch(currPatch, currSign)}) return patches def phaseFilter(phaseMap, - filterType='Gaussian', # 'Gaussian' of 'uniform' - filterSize=3, - isPositive=True): # if Ture return phase [0 2pi], if False return phase [-pi, pi] + filterType = 'Gaussian', # 'Gaussian' of 'uniform' + filterSize = 3, + isPositive = True): #if Ture return phase [0 2pi], if False return phase [-pi, pi] ''' smooth phaseMap in a circular fashion ''' @@ -641,8 +628,8 @@ def plotVisualSpace(visualSpace, altAxis, aziAxis, tickSpace=10, plotAxis=None, pt.plot_mask_borders(visualSpace, plotAxis=ax, color=lineColor, borderWidth=lineWidth) ax.set_aspect('equal') - altTickInds = range(len(altAxis))[::tickSpace] - aziTickInds = range(len(aziAxis))[::tickSpace] + altTickInds = list(range(len(altAxis)))[::tickSpace] + aziTickInds = list(range(len(aziAxis)))[::tickSpace] altTickLabels = [str(int(round(altAxis[altTickInd]))) for altTickInd in altTickInds] aziTickLabels = [str(int(round(aziAxis[aziTickInd]))) for aziTickInd in aziTickInds] @@ -656,6 +643,7 @@ def plotVisualSpace(visualSpace, altAxis, aziAxis, tickSpace=10, plotAxis=None, def localMin(eccMap, binSize): + ''' find local minimum of eccenticity map (in degree), with binning by binSize in degree @@ -677,51 +665,53 @@ def localMin(eccMap, binSize): marker, NumOfMin = ni.measurements.label(marker) i = i + 1 - # if NumOfMin == 1: - # print 'Only one local minumum was found!!!' - # elif NumOfMin == 0: - # print 'No local minumum was found!!!' - # else: - # print str(NumOfMin) + ' local minuma were found!!!' - # - # if NumOfMin > 1: - # plt.figure() - # plt.imshow(marker,vmin=np.amin(marker), vmax=np.amax(marker),cmap='jet',interpolation='nearest') - # plt.colorbar() - # plt.title('marker from local min') +# if NumOfMin == 1: +# print 'Only one local minumum was found!!!' +# elif NumOfMin == 0: +# print 'No local minumum was found!!!' +# else: +# print str(NumOfMin) + ' local minuma were found!!!' +# +# if NumOfMin > 1: +# plt.figure() +# plt.imshow(marker,vmin=np.amin(marker), vmax=np.amax(marker),cmap='jet',interpolation='nearest') +# plt.colorbar() +# plt.title('marker from local min') return marker -def adjacentPairs(patches, borderWidth=2): +def adjacentPairs(patches,borderWidth = 2): + ''' return all the patch pairs with same visual sign and sharing border ''' - keyList = patches.keys() + keyList = list(patches.keys()) pairKeyList = [] for pair in combinations(keyList, 2): patch1 = patches[pair[0]] patch2 = patches[pair[1]] - if (ia.is_adjacent(patch1.array, patch2.array, borderWidth=borderWidth)) and (patch1.sign == patch2.sign): + if (ia.is_adjacent(patch1.array, patch2.array, borderWidth = borderWidth)) and (patch1.sign == patch2.sign): + pairKeyList.append(pair) return pairKeyList -def mergePatches(array1, array2, borderWidth=2): +def mergePatches(array1, array2, borderWidth = 2): ''' merge two binary patches with borderWidth no greater than borderWidth ''' sp = array1 + array2 - spc = ni.binary_closing(sp, iterations=(borderWidth)).astype(np.int8) + spc = ni.binary_closing(sp, iterations = (borderWidth)).astype(np.int8) _, patchNum = ni.measurements.label(spc) if patchNum > 1: - raise LookupError, 'this two patches are too far apart!!!' + raise LookupError('this two patches are too far apart!!!') else: return spc @@ -743,20 +733,20 @@ def eccentricityMap(altMap, aziMap, altCenter, aziCenter): eccMap = np.zeros(altMap.shape) eccMap[:] = np.nan - # for i in xrange(altMap.shape[0]): - # for j in xrange(altMap.shape[1]): - # alt = altMap2[i,j] - # azi = aziMap2[i,j] - # eccMap[i,j] = np.arctan(np.sqrt(np.tan(alt-altCenter2)**2 + ((np.tan(azi-aziCenter2)**2)/(np.cos(alt-altCenter2)**2)))) +# for i in xrange(altMap.shape[0]): +# for j in xrange(altMap.shape[1]): +# alt = altMap2[i,j] +# azi = aziMap2[i,j] +# eccMap[i,j] = np.arctan(np.sqrt(np.tan(alt-altCenter2)**2 + ((np.tan(azi-aziCenter2)**2)/(np.cos(alt-altCenter2)**2)))) eccMap = np.arctan( - np.sqrt( - np.square(np.tan(altMap2 - altCenter2)) - + - np.square(np.tan(aziMap2 - aziCenter2)) / np.square(np.cos(altMap2 - altCenter2)) - ) - ) - - eccMap = eccMap * 180 / np.pi + np.sqrt( + np.square(np.tan(altMap2-altCenter2)) + + + np.square(np.tan(aziMap2-aziCenter2))/np.square(np.cos(altMap2-altCenter2)) + ) + ) + + eccMap = eccMap*180 / np.pi return eccMap @@ -768,51 +758,51 @@ def sortPatches(patchDict): patches = [] newPatchDict = {} - for key, value in patchDict.iteritems(): - patches.append((value, value.getArea())) + for key, value in patchDict.items(): + patches.append((value,value.getArea())) - patches = sorted(patches, key=lambda a: a[1], reverse=True) + patches = sorted(patches, key=lambda a:a[1], reverse=True) for i, item in enumerate(patches): + patchName = 'patch' + ft.int2str(i + 1, 2) - newPatchDict.update({patchName: item[0]}) + newPatchDict.update({patchName:item[0]}) return newPatchDict -def plotPatches(patches, plotaxis=None, zoom=1, alpha=0.5, markersize=5): +def plotPatches(patches,plotaxis = None,zoom = 1,alpha = 0.5,markersize = 5): ''' plot a patches in a patch dictionary ''' if plotaxis == None: f = plt.figure() - plotaxis = f.add_axes([1, 1, 1, 1]) + plotaxis = f.add_axes([1,1,1,1]) imageHandle = {} - for key, value in patches.iteritems(): + for key, value in patches.items(): if zoom > 1: - currPatch = Patch(ni.zoom(value.array, zoom, order=0), value.sign) + currPatch = Patch(ni.zoom(value.array, zoom, order = 0),value.sign) else: currPatch = value - h = plotaxis.imshow(currPatch.getSignedMask(), cmap='jet', vmax=1, vmin=-1, interpolation='nearest', - alpha=alpha) - plotaxis.plot(currPatch.getCenter()[1], currPatch.getCenter()[0], '.k', markersize=markersize * zoom) - imageHandle.update({'handle_' + key: h}) + h = plotaxis.imshow(currPatch.getSignedMask(),vmax=1,vmin=-1,interpolation='nearest',alpha=alpha) + plotaxis.plot(currPatch.getCenter()[1], currPatch.getCenter()[0],'.k', markersize = markersize * zoom) + imageHandle.update({'handle_'+key:h}) - plotaxis.set_xlim([0, currPatch.array.shape[1] - 1]) - plotaxis.set_ylim([currPatch.array.shape[0] - 1, 0]) + plotaxis.set_xlim([0, currPatch.array.shape[1]-1]) + plotaxis.set_ylim([currPatch.array.shape[0]-1, 0]) # plotaxis.set_axis_off() return imageHandle -def plotPatchBorders(patches, plotaxis=None, borderWidth=2, color='#ff0000', zoom=1, isPlotCenter=True, isCenter=True, - rotationAngle=0): # rotation of map in degrees, counter-clockwise +def plotPatchBorders(patches,plotaxis = None,borderWidth = 2,color='#ff0000',zoom = 1,isPlotCenter = True,isCenter = True, + rotationAngle = 0 ):# rotation of map in degrees, counter-clockwise - # generating plot axis + #generating plot axis if plotaxis == None: f = plt.figure() plotaxis = f.add_subplot(111) @@ -822,41 +812,41 @@ def plotPatchBorders(patches, plotaxis=None, borderWidth=2, color='#ff0000', zoo borderArray = [] - # initiating center and area + #initiating center and area center = None area = 0 - for key, value in patches.iteritems(): + for key, value in patches.items(): if zoom > 1: - currPatch = Patch(ni.zoom(value.array, zoom, order=0), value.sign) + currPatch = Patch(ni.zoom(value.array, zoom, order = 0),value.sign) currBorderWidth = borderWidth * zoom else: currPatch = value currBorderWidth = borderWidth - # updating center + #updating center currArea = currPatch.getArea() currCenter = currPatch.getCenter() if currArea > area: center = currCenter area = np.int(currArea) - # print 'currArea:', currArea, ' currCenter:', currCenter, ' center:', center + #print 'currArea:', currArea, ' currCenter:', currCenter, ' center:', center - # generating border array for the current patch - currBorder = currPatch.getBorder(borderWidth=currBorderWidth) + #generating border array for the current patch + currBorder = currPatch.getBorder(borderWidth = currBorderWidth) - # adding center of current patches to the border array + #adding center of current patches to the border array if isPlotCenter: - currBorder[currCenter[0] - currBorderWidth - 1:currCenter[0] + currBorderWidth + 1, - currCenter[1] - currBorderWidth - 1:currCenter[1] + currBorderWidth + 1] = 1 + currBorder[currCenter[0]-currBorderWidth-1:currCenter[0]+currBorderWidth+1, + currCenter[1]-currBorderWidth-1:currCenter[1]+currBorderWidth+1] = 1 currBorder[np.isnan(currBorder)] = 0 borderArray.append(currBorder) - # binarize border array - borderArray = np.sum(np.array(borderArray), axis=0) + #binarize border array + borderArray = np.sum(np.array(borderArray),axis=0) borderArray[borderArray >= 1] = 1 # centering and expanding border array @@ -879,43 +869,45 @@ def plotPatchBorders(patches, plotaxis=None, borderWidth=2, color='#ff0000', zoo expandE = maxDis - center[1] expandW = maxDis - (borderArray.shape[1] - center[1]) - borderArray = np.concatenate((np.zeros((expandN, borderArray.shape[1])), borderArray), axis=0) - borderArray = np.concatenate((borderArray, np.zeros((expandS, borderArray.shape[1]))), axis=0) - borderArray = np.concatenate((np.zeros((borderArray.shape[0], expandE)), borderArray), axis=1) - borderArray = np.concatenate((borderArray, np.zeros((borderArray.shape[0], expandW))), axis=1) + borderArray = np.concatenate((np.zeros((expandN,borderArray.shape[1])),borderArray),axis = 0) + borderArray = np.concatenate((borderArray,np.zeros((expandS,borderArray.shape[1]))),axis = 0) + borderArray = np.concatenate((np.zeros((borderArray.shape[0],expandE)),borderArray),axis = 1) + borderArray = np.concatenate((borderArray,np.zeros((borderArray.shape[0],expandW))),axis = 1) + # rotating border array borderArrayR = tsfm.rotate(borderArray, rotationAngle) - # binarize rotated border array - borderArrayR[borderArrayR > 0] = 1 + #binarize rotated border array + borderArrayR[borderArrayR > 0]=1 - # thinning rotated border array - # borderArrayR = sm.binary_opening(borderArrayR,np.array([[0,1,0],[1,1,1],[0,1,0]])) + #thinning rotated border array + #borderArrayR = sm.binary_opening(borderArrayR,np.array([[0,1,0],[1,1,1],[0,1,0]])) borderArrayR = sm.skeletonize(borderArrayR) - # dilating rotated border array + #dilating rotated border array borderArrayR = sm.binary_dilation(borderArrayR, sm.square(currBorderWidth)) - # clear unwanted pixels + #clear unwanted pixels borderR = np.array(borderArrayR).astype(np.float32) borderR[borderArrayR == 0] = np.nan - # plotting - imageHandle = plotaxis.imshow(borderR, vmin=0, vmax=1, cmap='temp', interpolation='nearest') + #plotting + imageHandle = plotaxis.imshow(borderR, vmin=0, vmax=1, cmap='temp', interpolation = 'nearest') return imageHandle def plotPatchBorders2(patches, - plotAxis=None, - plotSize=None, # size of plotting area - borderWidth=2, - zoom=1, - centerPatch=1, - rotationAngle=0, # rotation of map in degrees, counter-clockwise - markerSize=2, # size of center dot - closingIteration=None # open iteration for patch borders + plotAxis = None, + plotSize = None, # size of plotting area + borderWidth = 2, + zoom = 1, + centerPatch = 1, + rotationAngle = 0, # rotation of map in degrees, counter-clockwise + markerSize = 2, # size of center dot + closingIteration = None # open iteration for patch borders ): + ''' plot rotated and centered patch borders @@ -925,17 +917,19 @@ def plotPatchBorders2(patches, ... ''' - # generating plot axis + #generating plot axis if plotAxis == None: f = plt.figure() plotAxis = f.add_subplot(111) - # generating list for plotting - # for each patch: first item: center, second item: area, third item: patch array, forth item: sign - forPlotting = [] + #generating list for plotting + #for each patch: first item: center, second item: area, third item: patch array, forth item: sign + forPlotting=[] + + for key, value in patches.items(): - for key, value in patches.iteritems(): - currPatch = Patch(ni.zoom(value.array, zoom, order=0), value.sign) + + currPatch = Patch(ni.zoom(value.array, zoom, order = 0),value.sign) forPlotting.append([currPatch.getCenter(), currPatch.getArea(), @@ -946,13 +940,13 @@ def plotPatchBorders2(patches, forPlotting = sorted(forPlotting, key=lambda a: a[1], reverse=True) # get the plotting center - center = forPlotting[centerPatch - 1][0] + center = forPlotting[centerPatch-1][0] # width and height of original plot width = forPlotting[0][2].shape[1] height = forPlotting[0][2].shape[0] - # coordinate of four corners + #coordinate of four corners NW = np.array([0, 0]) NE = np.array([0, width]) SW = np.array([height, 0]) @@ -973,55 +967,54 @@ def plotPatchBorders2(patches, for ind, value in enumerate(forPlotting): - # expanding border map for each patch - value[2] = np.concatenate((np.zeros((expandN, value[2].shape[1])), value[2]), axis=0) - value[2] = np.concatenate((value[2], np.zeros((expandS, value[2].shape[1]))), axis=0) - value[2] = np.concatenate((np.zeros((value[2].shape[0], expandE)), value[2]), axis=1) - value[2] = np.concatenate((value[2], np.zeros((value[2].shape[0], expandW))), axis=1) + #expanding border map for each patch + value[2] = np.concatenate((np.zeros((expandN,value[2].shape[1])),value[2]),axis = 0) + value[2] = np.concatenate((value[2],np.zeros((expandS,value[2].shape[1]))),axis = 0) + value[2] = np.concatenate((np.zeros((value[2].shape[0],expandE)),value[2]),axis = 1) + value[2] = np.concatenate((value[2],np.zeros((value[2].shape[0],expandW))),axis = 1) - value[2][value[2] == 0] = np.nan + value[2][value[2]==0] = np.nan - # rotate border map for each patch + #rotate border map for each patch value[2] = tsfm.rotate(value[2], rotationAngle) # #binarize current border map # value[2][value[2]<0.9]=np.nan # value[2][value[2]>=0.9]=1 - # ploting current border + #ploting current border if value[3] == -1: - pt.plot_mask(value[2], plotAxis=plotAxis, color='#0000ff', borderWidth=borderWidth, - closingIteration=closingIteration) + pt.plot_mask(value[2], plotAxis=plotAxis, color='#0000ff', borderWidth = borderWidth, closingIteration = closingIteration) elif value[3] == 1: - pt.plot_mask(value[2], plotAxis=plotAxis, color='#ff0000', borderWidth=borderWidth, - closingIteration=closingIteration) + pt.plot_mask(value[2], plotAxis=plotAxis, color='#ff0000', borderWidth = borderWidth, closingIteration = closingIteration) # expanding center coordinate for each patch value[0][0] = value[0][0] + expandN value[0][1] = value[0][1] + expandE - # rotate center coordinate for each patch + #rotate center coordinate for each patch x = value[0][1] - maxDis y = maxDis - value[0][0] - xx = x * np.cos(rotationAngle * np.pi / 180) - y * np.sin(rotationAngle * np.pi / 180) - yy = y * np.cos(rotationAngle * np.pi / 180) + x * np.sin(rotationAngle * np.pi / 180) + xx = x*np.cos(rotationAngle*np.pi/180) - y*np.sin(rotationAngle*np.pi/180) + yy = y*np.cos(rotationAngle*np.pi/180) + x*np.sin(rotationAngle*np.pi/180) value[0][0] = int(np.round(maxDis - yy)) value[0][1] = int(np.round(maxDis + xx)) - # ploting current center + #ploting current center if value[3] == -1: - plotAxis.plot(value[0][1], value[0][0], '.b', markersize=markerSize) + plotAxis.plot(value[0][1],value[0][0], '.b', markersize = markerSize) elif value[3] == 1: - plotAxis.plot(value[0][1], value[0][0], '.r', markersize=markerSize) + plotAxis.plot(value[0][1],value[0][0], '.r', markersize = markerSize) + if plotSize: - plotAxis.set_xlim([maxDis - plotSize / 2, maxDis + plotSize / 2]) - plotAxis.set_ylim([maxDis + plotSize / 2, maxDis - plotSize / 2]) + plotAxis.set_xlim([maxDis-plotSize/2, maxDis+plotSize/2]) + plotAxis.set_ylim([maxDis+plotSize/2, maxDis-plotSize/2]) else: - plotAxis.set_xlim([0, 2 * maxDis]) - plotAxis.set_ylim([2 * maxDis, 0]) + plotAxis.set_xlim([0,2*maxDis]) + plotAxis.set_ylim([2*maxDis,0]) plotAxis.get_xaxis().set_visible(False) plotAxis.get_yaxis().set_visible(False) @@ -1031,14 +1024,14 @@ def plotPatchBorders2(patches, def plotPatchBorders3(patches, altPosMap, aziPosMap, - plotAxis=None, - plotSize=None, # size of plotting area - borderWidth=2, - zoom=1, - centerPatchKey='patch01', # center at the largest patch by default - markerSize=2, # size of center dot - closingIteration=None, # open iteration for patch borders - arrowLength=10 # length of arrow of gradiant + plotAxis = None, + plotSize = None, # size of plotting area + borderWidth = 2, + zoom = 1, + centerPatchKey = 'patch01', # center at the largest patch by default + markerSize = 2, # size of center dot + closingIteration = None, # open iteration for patch borders + arrowLength = 10 # length of arrow of gradiant ): ''' plot patch border centered and rotated by a certain patch defined by 'centerPatch' @@ -1046,17 +1039,18 @@ def plotPatchBorders3(patches, also plot vetors of altitude gradiant and azimuth gradiant ''' - # generating plot axis + + #generating plot axis if plotAxis == None: f = plt.figure() plotAxis = f.add_subplot(111) - # calculat rotation angle and center + #calculat rotation angle and center try: centerPatchObj = patches[centerPatchKey] except KeyError: - area = [] - for key, value in patches.iteritems(): + area=[] + for key, value in patches.items(): area.append([key, value.getArea()]) area = sorted(area, key=lambda a: a[1], reverse=True) @@ -1072,18 +1066,19 @@ def plotPatchBorders3(patches, aziGradMapX = np.sum(aziGradMap[0] * centerPatchObj.array) aziGradMapY = np.sum(aziGradMap[1] * centerPatchObj.array) - rotationAngle = -(np.arctan2(-aziGradMapX, aziGradMapY) % (2 * np.pi)) * 180 / np.pi + rotationAngle = -(np.arctan2(-aziGradMapX,aziGradMapY)%(2*np.pi))*180/np.pi # rotationAngle = 0 # print (np.arctan2(-altGradMapX,altGradMapY)%(2*np.pi))*180/np.pi - zoomedCenterPatch = Patch(ni.zoom(centerPatchObj.array, zoom, order=0), centerPatchObj.sign) + zoomedCenterPatch = Patch(ni.zoom(centerPatchObj.array, zoom, order = 0),centerPatchObj.sign) center = zoomedCenterPatch.getCenter() + # width and height of original plot width = zoomedCenterPatch.array.shape[1] height = zoomedCenterPatch.array.shape[0] - # coordinate of four corners + #coordinate of four corners NW = np.array([0, 0]) NE = np.array([0, width]) SW = np.array([height, 0]) @@ -1102,61 +1097,59 @@ def plotPatchBorders3(patches, expandE = maxDis - center[1] expandW = maxDis - (width - center[1]) - for key, currPatch in patches.iteritems(): + for key, currPatch in patches.items(): - zoomedArray = ni.zoom(currPatch.array, zoom, order=0) + zoomedArray = ni.zoom(currPatch.array, zoom, order = 0) - # expanding border map for each patch - zoomedArray = np.concatenate((np.zeros((expandN, zoomedArray.shape[1])), zoomedArray), axis=0) - zoomedArray = np.concatenate((zoomedArray, np.zeros((expandS, zoomedArray.shape[1]))), axis=0) - zoomedArray = np.concatenate((np.zeros((zoomedArray.shape[0], expandE)), zoomedArray), axis=1) - zoomedArray = np.concatenate((zoomedArray, np.zeros((zoomedArray.shape[0], expandW))), axis=1) + #expanding border map for each patch + zoomedArray = np.concatenate((np.zeros((expandN,zoomedArray.shape[1])),zoomedArray),axis = 0) + zoomedArray = np.concatenate((zoomedArray,np.zeros((expandS,zoomedArray.shape[1]))),axis = 0) + zoomedArray = np.concatenate((np.zeros((zoomedArray.shape[0],expandE)),zoomedArray),axis = 1) + zoomedArray = np.concatenate((zoomedArray,np.zeros((zoomedArray.shape[0],expandW))),axis = 1) - # rotate border map for each patch + #rotate border map for each patch zoomedArray = tsfm.rotate(zoomedArray, rotationAngle) - # get center - zoomedCenter = np.round(np.mean(np.argwhere(zoomedArray).astype(np.float32), axis=0)).astype(np.int) + #get center + zoomedCenter = np.round(np.mean(np.argwhere(zoomedArray).astype(np.float32),axis=0)).astype(np.int) - # binarize current border map - zoomedArray[zoomedArray < 0.9] = np.nan - zoomedArray[zoomedArray >= 0.9] = 1 - # ploting current border + #binarize current border map + zoomedArray[zoomedArray<0.9]=np.nan + zoomedArray[zoomedArray>=0.9]=1 + + #ploting current border if currPatch.sign == -1: - pt.plot_mask(zoomedArray, plotAxis=plotAxis, color='#0000ff', borderWidth=borderWidth, - closingIteration=closingIteration) - plotAxis.plot(zoomedCenter[1], zoomedCenter[0], '.b', markersize=markerSize) + pt.plot_mask(zoomedArray, plotAxis=plotAxis, color='#0000ff', borderWidth = borderWidth, closingIteration = closingIteration) + plotAxis.plot(zoomedCenter[1],zoomedCenter[0], '.b', markersize = markerSize) elif currPatch.sign == 1: - pt.plot_mask(zoomedArray, plotAxis=plotAxis, color='#ff0000', borderWidth=borderWidth, - closingIteration=closingIteration) - plotAxis.plot(zoomedCenter[1], zoomedCenter[0], '.r', markersize=markerSize) + pt.plot_mask(zoomedArray, plotAxis=plotAxis, color='#ff0000', borderWidth = borderWidth, closingIteration = closingIteration) + plotAxis.plot(zoomedCenter[1],zoomedCenter[0], '.r', markersize = markerSize) - # get gradiant vectors for current patch + #get gradiant vectors for current patch currAltGradMapX = np.sum(altGradMap[0] * currPatch.array) currAltGradMapY = np.sum(altGradMap[1] * currPatch.array) - currAltAngle = np.arctan2(-currAltGradMapX, currAltGradMapY) % (2 * np.pi) + (rotationAngle * np.pi / 180) + currAltAngle = np.arctan2(-currAltGradMapX,currAltGradMapY)%(2*np.pi)+(rotationAngle*np.pi/180) currAziGradMapX = np.sum(aziGradMap[0] * currPatch.array) currAziGradMapY = np.sum(aziGradMap[1] * currPatch.array) - currAziAngle = np.arctan2(-currAziGradMapX, currAziGradMapY) % (2 * np.pi) + (rotationAngle * np.pi / 180) + currAziAngle = np.arctan2(-currAziGradMapX,currAziGradMapY)%(2*np.pi)+(rotationAngle*np.pi/180) # if key == centerPatchKey: # print currAltAngle*180/np.pi # print np.sin(currAltAngle) # print np.cos(currAltAngle) - # plotting arrow for the current patch - plotAxis.arrow(zoomedCenter[1], zoomedCenter[0], arrowLength * zoom * np.cos(currAltAngle), - -arrowLength * zoom * np.sin(currAltAngle), color='#ff00ff', linewidth=2, width=0.5) - plotAxis.arrow(zoomedCenter[1], zoomedCenter[0], arrowLength * zoom * np.cos(currAziAngle), - -arrowLength * zoom * np.sin(currAziAngle), color='#00ffff', linewidth=2, width=0.5) + #plotting arrow for the current patch + plotAxis.arrow(zoomedCenter[1],zoomedCenter[0],arrowLength*zoom*np.cos(currAltAngle),-arrowLength*zoom*np.sin(currAltAngle),color='#ff00ff',linewidth=2,width=0.5) + plotAxis.arrow(zoomedCenter[1],zoomedCenter[0],arrowLength*zoom*np.cos(currAziAngle),-arrowLength*zoom*np.sin(currAziAngle),color='#00ffff',linewidth=2,width=0.5) + if plotSize: - plotAxis.set_xlim([maxDis - plotSize * zoom / 2, maxDis + plotSize * zoom / 2]) - plotAxis.set_ylim([maxDis + plotSize * zoom / 2, maxDis - plotSize * zoom / 2]) + plotAxis.set_xlim([maxDis-plotSize*zoom/2, maxDis+plotSize*zoom/2]) + plotAxis.set_ylim([maxDis+plotSize*zoom/2, maxDis-plotSize*zoom/2]) else: - plotAxis.set_xlim([0, 2 * maxDis]) - plotAxis.set_ylim([2 * maxDis, 0]) + plotAxis.set_xlim([0,2*maxDis]) + plotAxis.set_ylim([2*maxDis,0]) plotAxis.get_xaxis().set_visible(False) plotAxis.get_yaxis().set_visible(False) @@ -1167,22 +1160,23 @@ def plotPairedPatches(patch1, altMap, aziMap, title, - pixelSize=1, - closeIter=None): - visualSpace1, _, _ = patch1.getVisualSpace(altMap=altMap, - aziMap=aziMap, - pixelSize=pixelSize, - closeIter=closeIter) + pixelSize = 1, + closeIter = None): + + visualSpace1, _, _ = patch1.getVisualSpace(altMap = altMap, + aziMap = aziMap, + pixelSize = pixelSize, + closeIter = closeIter) area1 = np.sum(visualSpace1[:]) * (pixelSize ** 2) - visualSpace2, _, _ = patch2.getVisualSpace(altMap=altMap, - aziMap=aziMap, - pixelSize=pixelSize, - closeIter=closeIter) + visualSpace2, _, _ = patch2.getVisualSpace(altMap = altMap, + aziMap = aziMap, + pixelSize = pixelSize, + closeIter = closeIter) area2 = np.sum(visualSpace2[:]) * (pixelSize ** 2) - visualSpace1 = np.array(visualSpace1, dtype=np.float32) - visualSpace2 = np.array(visualSpace2, dtype=np.float32) + visualSpace1 = np.array(visualSpace1, dtype = np.float32) + visualSpace2 = np.array(visualSpace2, dtype = np.float32) visualSpace1[visualSpace1 == 0] = np.nan visualSpace2[visualSpace2 == 0] = np.nan @@ -1190,13 +1184,13 @@ def plotPairedPatches(patch1, f = plt.figure() f.suptitle(title) f_121 = f.add_subplot(121) - patchPlot1 = f_121.imshow(patch1.getMask(), cmap='jet', interpolation='nearest', alpha=0.5, vmax=2, vmin=1) - patchPlot2 = f_121.imshow(patch2.getMask() * 2, cmap='jet', interpolation='nearest', alpha=0.5, vmax=2, vmin=1) + patchPlot1 = f_121.imshow(patch1.getMask(), interpolation = 'nearest', alpha = 0.5, vmax=2, vmin=1) + patchPlot2 = f_121.imshow(patch2.getMask()*2, interpolation = 'nearest', alpha = 0.5, vmax=2, vmin=1) f_121.set_title('patch1: blue, patch2: red') f_122 = f.add_subplot(122) - areaPlot1 = f_122.imshow(visualSpace1, cmap='jet', interpolation='nearest', alpha=0.5, vmax=2, vmin=1) - areaPlot2 = f_122.imshow(visualSpace2 * 2, cmap='jet', interpolation='nearest', alpha=0.5, vmax=2, vmin=1) + areaPlot1 = f_122.imshow(visualSpace1, interpolation = 'nearest', alpha = 0.5, vmax=2, vmin=1) + areaPlot2 = f_122.imshow(visualSpace2*2, interpolation = 'nearest', alpha = 0.5, vmax=2, vmin=1) f_122.set_title('area1: %.1f, area2: %.1f (deg^2)' % (area1, area2)) f_122.invert_yaxis() @@ -1204,8 +1198,8 @@ def plotPairedPatches(patch1, # reorganize visual space axis label altRange = np.array([np.amin(altMap), np.amax(altMap)]) aziRange = np.array([np.amin(aziMap), np.amax(aziMap)]) - xlist = np.arange(aziRange[0], aziRange[1], pixelSize) - ylist = np.arange(altRange[0], altRange[1], pixelSize) + xlist = np.arange(aziRange[0],aziRange[1],pixelSize) + ylist = np.arange(altRange[0],altRange[1],pixelSize) xtick = [] xticklabel = [] @@ -1216,7 +1210,7 @@ def plotPairedPatches(patch1, xticklabel.append(str(int(np.floor(xlist[i])))) i = int(i + 9 / pixelSize) else: - i = i + 1 + i=i+1 ytick = [] yticklabel = [] @@ -1227,50 +1221,50 @@ def plotPairedPatches(patch1, yticklabel.append(str(int(np.floor(ylist[i])))) i = int(i + 9 / pixelSize) else: - i = i + 1 + i=i+1 f_122.set_xticks(xtick) f_122.set_xticklabels(xticklabel) f_122.set_yticks(ytick) f_122.set_yticklabels(yticklabel) - def getPatchDict(patch): - return {'sparseArray': patch.sparseArray, 'sign': patch.sign} + return {'sparseArray':patch.sparseArray,'sign':patch.sign} class RetinotopicMappingTrial(object): + def __init__(self, - mouseID, # str, mouseID - dateRecorded, # int, date recorded, yearmonthday - trialNum, # str, number of the trail on that day - mouseType, # str, mouse Genotype - visualStimType, # str, stimulation type - visualStimBackground, # str, background of visual stimulation - imageExposureTime, # float, exposure time of image file - altPosMap, # altitude position map - aziPosMap, # azimuth position map - altPowerMap, # altitude power map - aziPowerMap, # azimuth power map - vasculatureMap, # vasculature map - params={ - 'phaseMapFilterSigma': 1, - 'signMapFilterSigma': 10, - 'signMapThr': 0.3, - 'eccMapFilterSigma': 5., - 'splitLocalMinCutStep': 10., - 'mergeOverlapThr': 0.05, - 'closeIter': 3, - 'openIter': 3, - 'dilationIter': 20, - 'borderWidth': 1, - 'smallPatchThr': 200, - 'visualSpacePixelSize': 0.5, - 'visualSpaceCloseIter': 15, - 'splitOverlapThr': 1.1 - }, - isAnesthetized=False, + mouseID, # str, mouseID + dateRecorded, # int, date recorded, yearmonthday + trialNum, # str, number of the trail on that day + mouseType, # str, mouse Genotype + visualStimType, # str, stimulation type + visualStimBackground, # str, background of visual stimulation + imageExposureTime, # float, exposure time of image file + altPosMap, # altitude position map + aziPosMap, # azimuth position map + altPowerMap, # altitude power map + aziPowerMap, # azimuth power map + vasculatureMap, # vasculature map + params ={ + 'phaseMapFilterSigma':1, + 'signMapFilterSigma':10, + 'signMapThr':0.3, + 'eccMapFilterSigma':5., + 'splitLocalMinCutStep':10., + 'mergeOverlapThr':0.05, + 'closeIter':3, + 'openIter':3, + 'dilationIter':20, + 'borderWidth':1, + 'smallPatchThr':200, + 'visualSpacePixelSize':0.5, + 'visualSpaceCloseIter':15, + 'splitOverlapThr':1.1 + }, + isAnesthetized = False, ): self.mouseID = mouseID @@ -1289,14 +1283,15 @@ def __init__(self, self.isAnesthetized = isAnesthetized + def getName(self): - trialName = str(self.dateRecorded) + \ - '_M' + str(self.mouseID) + \ + trialName = str(self.dateRecorded)+\ + '_M'+str(self.mouseID)+\ '_Trial' + str(self.trialNum) - # '_'+self.mouseType.split('-')[0]+';'+self.mouseType.split(';')[-1][0:4] - # '_' + str(self.visualStimType)+\ - # '_' + str(self.visualStimBackground) + #'_'+self.mouseType.split('-')[0]+';'+self.mouseType.split(';')[-1][0:4] + #'_' + str(self.visualStimType)+\ + #'_' + str(self.visualStimBackground) # if self.isAnesthetized: # trialName += '_Anesth' @@ -1305,24 +1300,23 @@ def getName(self): return trialName - def _getSignMap(self, isReverse=False, isPlot=False, isFixedRange=True): - altPosMapf = ni.filters.gaussian_filter(self.altPosMap, - self.params['phaseMapFilterSigma']) - aziPosMapf = ni.filters.gaussian_filter(self.aziPosMap, - self.params['phaseMapFilterSigma']) + def _getSignMap(self, isReverse = False, isPlot = False, isFixedRange = True): + + altPosMapf = ni.filters.gaussian_filter(self.altPosMap, + self.params['phaseMapFilterSigma']) + aziPosMapf = ni.filters.gaussian_filter(self.aziPosMap, + self.params['phaseMapFilterSigma']) if self.altPowerMap is not None: - altPowerMapf = ni.filters.gaussian_filter(self.altPowerMap, - self.params['phaseMapFilterSigma']) - else: - altPowerMapf = None + altPowerMapf = ni.filters.gaussian_filter(self.altPowerMap, + self.params['phaseMapFilterSigma']) + else: altPowerMapf = None if self.aziPowerMap is not None: - aziPowerMapf = ni.filters.gaussian_filter(self.aziPowerMap, - self.params['phaseMapFilterSigma']) - else: - aziPowerMapf = None + aziPowerMapf = ni.filters.gaussian_filter(self.aziPowerMap, + self.params['phaseMapFilterSigma']) + else: aziPowerMapf = None signMap = visualSignMap(altPosMapf, aziPosMapf) @@ -1332,20 +1326,16 @@ def _getSignMap(self, isReverse=False, isPlot=False, isFixedRange=True): self.params['signMapFilterSigma']) if isPlot: - f1 = plt.figure(figsize=(18, 9)) + f1=plt.figure(figsize=(18,9)) f1_231 = f1.add_subplot(231) - if isFixedRange: - currfig = f1_231.imshow(self.altPosMap, vmin=-40, vmax=60, cmap='hsv', interpolation='nearest') - else: - currfig = f1_231.imshow(self.altPosMap, cmap='hsv', interpolation='nearest') + if isFixedRange: currfig = f1_231.imshow(self.altPosMap, vmin=-40, vmax=60, cmap='hsv', interpolation='nearest') + else: currfig = f1_231.imshow(self.altPosMap, cmap='hsv', interpolation='nearest') f1.colorbar(currfig) f1_231.set_axis_off() f1_231.set_title('alt position') f1_232 = f1.add_subplot(232) - if isFixedRange: - currfig = f1_232.imshow(self.aziPosMap, vmin=-0, vmax=120, cmap='hsv', interpolation='nearest') - else: - currfig = f1_232.imshow(self.aziPosMap, cmap='hsv', interpolation='nearest') + if isFixedRange: currfig = f1_232.imshow(self.aziPosMap, vmin=-0, vmax=120, cmap='hsv', interpolation='nearest') + else: currfig = f1_232.imshow(self.aziPosMap, cmap='hsv', interpolation='nearest') f1.colorbar(currfig) f1_232.set_axis_off() f1_232.set_title('azi position') @@ -1355,18 +1345,14 @@ def _getSignMap(self, isReverse=False, isPlot=False, isFixedRange=True): f1_233.set_axis_off() f1_233.set_title('sign map') f1_234 = f1.add_subplot(234) - if isFixedRange: - currfig = f1_234.imshow(altPosMapf, vmin=-40, vmax=60, cmap='hsv', interpolation='nearest') - else: - currfig = f1_234.imshow(altPosMapf, cmap='hsv', interpolation='nearest') + if isFixedRange: currfig = f1_234.imshow(altPosMapf, vmin=-40, vmax=60, cmap='hsv', interpolation='nearest') + else: currfig = f1_234.imshow(altPosMapf, cmap='hsv', interpolation='nearest') f1.colorbar(currfig) f1_234.set_axis_off() f1_234.set_title('alt position filtered') f1_235 = f1.add_subplot(235) - if isFixedRange: - currfig = f1_235.imshow(aziPosMapf, vmin=0, vmax=120, cmap='hsv', interpolation='nearest') - else: - currfig = f1_235.imshow(aziPosMapf, cmap='hsv', interpolation='nearest') + if isFixedRange: currfig = f1_235.imshow(aziPosMapf, vmin=0, vmax=120, cmap='hsv', interpolation='nearest') + else: currfig = f1_235.imshow(aziPosMapf, cmap='hsv', interpolation='nearest') f1.colorbar(currfig) plt.axis('off') f1_235.set_title('azi position filtered') @@ -1376,22 +1362,21 @@ def _getSignMap(self, isReverse=False, isPlot=False, isFixedRange=True): plt.axis('off') f1_236.set_title('sign map filtered') - f2 = plt.figure(figsize=(12, 4)) + f2=plt.figure(figsize=(12,4)) f2_121 = f2.add_subplot(121) if altPowerMapf is not None: - currfig = f2_121.imshow(ia.array_nor(self.altPowerMap), cmap='hot', vmin=0, vmax=1, - interpolation='nearest') + currfig = f2_121.imshow(ia.array_nor(self.altPowerMap), cmap ='hot', vmin = 0, vmax=1, interpolation='nearest') f2.colorbar(currfig) f2_121.set_title('alt power map') f2_121.set_axis_off() f2_122 = f2.add_subplot(122) if aziPowerMapf is not None: - currfig = f2_122.imshow(ia.array_nor(self.aziPowerMap), cmap='hot', vmin=0, vmax=1, - interpolation='nearest') + currfig = f2_122.imshow(ia.array_nor(self.aziPowerMap), cmap ='hot', vmin = 0, vmax=1, interpolation='nearest') f2.colorbar(currfig) f2_122.set_title('azi power map') f2_122.set_axis_off() + self.altPosMapf = altPosMapf self.aziPosMapf = aziPosMapf self.altPowerMapf = altPowerMapf @@ -1401,7 +1386,8 @@ def _getSignMap(self, isReverse=False, isPlot=False, isFixedRange=True): return altPosMapf, aziPosMapf, altPowerMapf, aziPowerMapf, signMap, signMapf - def _getRawPatchMap(self, isPlot=False): + + def _getRawPatchMap(self, isPlot = False): if not hasattr(self, 'signMapf'): _ = self._getSignMap() @@ -1411,20 +1397,21 @@ def _getRawPatchMap(self, isPlot=False): openIter = self.params['openIter'] closeIter = self.params['closeIter'] - # thresholding filtered signmap + #thresholding filtered signmap patchmap = np.zeros(signMapf.shape) patchmap[signMapf >= signMapThr] = 1 patchmap[signMapf <= -1 * signMapThr] = 1 patchmap[(signMapf < signMapThr) & (signMapf > -1 * signMapThr)] = 0 - patchmap = ni.binary_opening(np.abs(patchmap), iterations=openIter).astype(np.int) + patchmap = ni.binary_opening(np.abs(patchmap), iterations = openIter).astype(np.int) patches, patchNum = ni.label(patchmap) - # closing each patch, then put them together - patchmap2 = np.zeros(patchmap.shape).astype(np.int) + + #closing each patch, then put them together + patchmap2=np.zeros(patchmap.shape).astype(np.int) for i in range(patchNum): currPatch = np.zeros(patches.shape).astype(np.int) - currPatch[patches == i + 1] = 1 - currPatch = ni.binary_closing(currPatch, iterations=closeIter).astype(np.int) + currPatch[patches==i+1]=1 + currPatch = ni.binary_closing(currPatch, iterations = closeIter).astype(np.int) patchmap2 = patchmap2 + currPatch if isPlot: @@ -1438,7 +1425,8 @@ def _getRawPatchMap(self, isPlot=False): return patchmap2 - def _getRawPatches(self, isPlot=False): + + def _getRawPatches(self, isPlot = False): if not hasattr(self, 'rawPatchMap'): _ = self._getRawPatchMap() @@ -1452,22 +1440,22 @@ def _getRawPatches(self, isPlot=False): patchMapDilated = dilationPatches2(rawPatchMap, dilationIter=dilationIter, borderWidth=borderWidth) - # generate raw patch dictionary - rawPatches = labelPatches(patchMapDilated, signMapf, connectivity=4) + #generate raw patch dictionary + rawPatches = labelPatches(patchMapDilated, signMapf, connectivity = 4) rawPatches2 = dict(rawPatches) - # remove small patches - for key, value in rawPatches2.iteritems(): + #remove small patches + for key, value in rawPatches2.items(): if (value.getArea() < smallPatchThr): rawPatches.pop(key) - # remove isolated Patches + #remove isolated Patches rawPatches2 = dict(rawPatches) - for key in rawPatches2.iterkeys(): + for key in rawPatches2.keys(): isTouching = 0 - for key2 in rawPatches2.iterkeys(): + for key2 in rawPatches2.keys(): if key != key2: - if rawPatches2[key].isTouching(rawPatches2[key2], borderWidth * 2): + if rawPatches2[key].isTouching(rawPatches2[key2], borderWidth*2): isTouching = 1 break @@ -1484,10 +1472,10 @@ def _getRawPatches(self, isPlot=False): f = plt.figure() f_axis = f.add_subplot(111) try: - f_axis.imshow(vasculatureMap, cmap='gray', interpolation='nearest') + f_axis.imshow(vasculatureMap, cmap = 'gray', interpolation = 'nearest') except: pass - _ = plotPatches(rawPatches, plotaxis=f_axis, zoom=zoom) + _ = plotPatches(rawPatches, plotaxis = f_axis, zoom = zoom) f_axis.set_title('raw patches') plt.gca().set_axis_off() del _ @@ -1496,7 +1484,8 @@ def _getRawPatches(self, isPlot=False): return rawPatches - def _getDeterminantMap(self, isPlot=False): + + def _getDeterminantMap(self, isPlot = False): if not hasattr(self, 'altPosMapf') or not hasattr(self, 'aziPosMapf'): _ = self._getSignMap() @@ -1510,12 +1499,13 @@ def _getDeterminantMap(self, isPlot=False): detMap = np.array([[gradAltMap[0], gradAltMap[1]], [gradAziMap[0], gradAziMap[1]]]) - detMap = detMap.transpose(2, 3, 0, 1) + detMap = detMap.transpose(2,3,0,1) detMap = np.abs(np.linalg.det(detMap)) + if isPlot: plt.figure() - plt.imshow(detMap, vmin=0, vmax=1, cmap='hsv', interpolation='nearest') + plt.imshow(detMap, vmin = 0, vmax = 1,cmap='hsv', interpolation='nearest') plt.colorbar() plt.title('determinant map') plt.gca().set_axis_off() @@ -1524,7 +1514,8 @@ def _getDeterminantMap(self, isPlot=False): return detMap - def _getEccentricityMap(self, isPlot=False): + + def _getEccentricityMap(self, isPlot = False): if not hasattr(self, 'rawPatches'): _ = self._getRawPatches() @@ -1539,17 +1530,18 @@ def _getEccentricityMap(self, isPlot=False): eccMap[:] = np.nan eccMapf[:] = np.nan - for key, value in patches.iteritems(): - patchAltC, patchAziC = value.getPixelVisualCenter(altPosMapf, aziPosMapf) + for key, value in patches.items(): + + patchAltC, patchAziC = value.getPixelVisualCenter(altPosMapf,aziPosMapf) patchEccMap = eccentricityMap(altPosMapf, aziPosMapf, patchAltC, patchAziC) - patchEccMapf = ni.filters.uniform_filter(patchEccMap, eccMapFilterSigma) + patchEccMapf = ni.filters.uniform_filter(patchEccMap, eccMapFilterSigma) eccMap[value.array == 1] = patchEccMap[value.array == 1] eccMapf[value.array == 1] = patchEccMapf[value.array == 1] if isPlot: plt.figure() - plt.imshow(eccMapf, cmap='hsv', interpolation='nearest') + plt.imshow(eccMapf, interpolation='nearest') plt.colorbar() plt.title('filtered eccentricity map') plt.gca().set_axis_off() @@ -1559,7 +1551,8 @@ def _getEccentricityMap(self, isPlot=False): return eccMap, eccMapf - def _splitPatches(self, isPlot=False): + + def _splitPatches(self,isPlot = False): if not hasattr(self, 'eccentricityMapf'): _ = self._getEccentricityMap() @@ -1569,7 +1562,7 @@ def _splitPatches(self, isPlot=False): altPosMapf = self.altPosMapf aziPosMapf = self.aziPosMapf - # eccMap = self.eccentricityMapf +# eccMap = self.eccentricityMapf eccMapf = self.eccentricityMapf patches = dict(self.rawPatches) detMap = self.determinantMap @@ -1583,17 +1576,17 @@ def _splitPatches(self, isPlot=False): overlapPatches = [] newPatchesDict = {} - for key, value in patches.iteritems(): + for key, value in patches.items(): visualSpace, _, _ = value.getVisualSpace(altPosMapf, aziPosMapf, - pixelSize=visualSpacePixelSize, - closeIter=visualSpaceCloseIter) + pixelSize = visualSpacePixelSize, + closeIter = visualSpaceCloseIter) AU = np.sum(visualSpace[:]) * (visualSpacePixelSize ** 2) AS = value.getSigmaArea(detMap) - print key, 'AU=' + str(AU), ' AS=' + str(AS), ' ratio=' + str(AS / AU) + print(key, 'AU='+str(AU), ' AS='+str(AS), ' ratio='+str(AS/AU)) - if AS / AU >= splitOverlapThr: + if AS/AU >= splitOverlapThr: patchEccMapf = eccMapf * value.getMask() patchEccMapf[value.array == 0] = np.nan @@ -1602,54 +1595,52 @@ def _splitPatches(self, isPlot=False): NumOfMin = np.amax(minMarker) if NumOfMin == 1: - print 'Only one local minumum was found!!!' + print('Only one local minumum was found!!!') elif NumOfMin == 0: - print 'No local minumum was found!!!' + print('No local minumum was found!!!') else: - print str(NumOfMin) + ' local minuma were found!!!' + print(str(NumOfMin) + ' local minuma were found!!!') overlapPatches.append(key) newPatches = value.split2(patchEccMapf, - patchName=key, - cutStep=splitLocalMinCutStep, - borderWidth=borderWidth, - isplot=False) + patchName = key, + cutStep = splitLocalMinCutStep, + borderWidth = borderWidth, + isplot = False) - # plotting splitted patches + #plotting splitted patches if len(newPatches) > 1: - f = plt.figure() + f=plt.figure() f121 = f.add_subplot(121) f121.set_title(key) f122 = f.add_subplot(122) f122.set_title('visual space') currPatchValue = 0 - for key2, value2 in newPatches.iteritems(): + for key2, value2 in newPatches.items(): currPatchValue += 1 currArray = np.array(value2.array, dtype=np.float32) - currArray[currArray == 0] = np.nan - currArray[currArray == 1] = currPatchValue - f121.imshow(currArray, cmap='jet', interpolation='nearest', vmin=0, - vmax=len(newPatches.keys())) + currArray[currArray==0]=np.nan + currArray[currArray==1]=currPatchValue + f121.imshow(currArray,interpolation='nearest', vmin=0, vmax=len(list(newPatches.keys()))) f121.set_axis_off() currVisualSpace, _, _ = value2.getVisualSpace(altPosMapf, aziPosMapf, - pixelSize=visualSpacePixelSize, - closeIter=visualSpaceCloseIter) - currVisualSpace = currVisualSpace.astype(np.float32) - currVisualSpace[currVisualSpace == 0] = np.nan - currVisualSpace[currVisualSpace == 1] = currPatchValue - f122.imshow(currVisualSpace, cmap='jet', interpolation='nearest', alpha=0.5, vmin=0, - vmax=len(newPatches.keys())) + pixelSize = visualSpacePixelSize, + closeIter = visualSpaceCloseIter) + currVisualSpace=currVisualSpace.astype(np.float32) + currVisualSpace[currVisualSpace==0]=np.nan + currVisualSpace[currVisualSpace==1]=currPatchValue + f122.imshow(currVisualSpace,interpolation='nearest',alpha=0.5, vmin=0, vmax=len(list(newPatches.keys()))) - xlabel = np.arange(-20, 120, visualSpacePixelSize) - ylabel = np.arange(60, -40, -visualSpacePixelSize) + xlabel = np.arange(-20,120,visualSpacePixelSize) + ylabel = np.arange(60,-40,-visualSpacePixelSize) - indSpace = int(10. / visualSpacePixelSize) + indSpace = int(10./visualSpacePixelSize) - xtickInd = range(0, len(xlabel), indSpace) - ytickInd = range(0, len(ylabel), indSpace) + xtickInd = list(range(0,len(xlabel),indSpace)) + ytickInd = list(range(0,len(ylabel),indSpace)) xtickLabel = [str(int(xlabel[x])) for x in xtickInd] ytickLabel = [str(int(ylabel[y])) for y in ytickInd] @@ -1671,21 +1662,23 @@ def _splitPatches(self, isPlot=False): zoom = self.vasculatureMap.shape[0] / self.altPosMap.shape[0] except: zoom = 1 - f2 = plt.figure() - f2_111 = f2.add_subplot(111) + f2=plt.figure() + f2_111=f2.add_subplot(111) try: - f2_111.imshow(self.vasculatureMap, cmap='gray', interpolation='nearest') + f2_111.imshow(self.vasculatureMap, cmap = 'gray', interpolation = 'nearest') except: pass - h = plotPatches(patches, plotaxis=f2_111, zoom=zoom) + h = plotPatches(patches, plotaxis = f2_111, zoom = zoom) f2_111.set_axis_off() f2_111.set_title('patches after split') f2_111.set_axis_off() + self.patchesAfterSplit = patches return patches + def _mergePatches(self, isPlot=False): if not hasattr(self, 'patchesAfterSplit'): @@ -1701,7 +1694,7 @@ def _mergePatches(self, isPlot=False): mergeOverlapThr = self.params['mergeOverlapThr'] smallPatchThr = self.params['smallPatchThr'] - # merging non-overlaping patches + #merging non-overlaping patches mergeIter = 1 # pairs of patches that meet the criterion of merging @@ -1715,75 +1708,62 @@ def _mergePatches(self, isPlot=False): while (mergeIter == 1) or (len(mergePairs) > 0): - print 'merge iteration: ' + str(mergeIter) + print('merge iteration: ' + str(mergeIter)) mergePairs = [] - # get adjacent pairs - adjPairs = adjacentPairs(patches, borderWidth=borderWidth + 1) + #get adjacent pairs + adjPairs = adjacentPairs(patches, borderWidth = borderWidth+1) - for ind, pair in enumerate(adjPairs): # for every adjacent pair + for ind, pair in enumerate(adjPairs): #for every adjacent pair patch1 = patches[pair[0]] patch2 = patches[pair[1]] try: - # merge these two patches - currMergedPatch = Patch(mergePatches(patch1.array, patch2.array, borderWidth=borderWidth), - sign=patch1.sign) + #merge these two patches + currMergedPatch = Patch(mergePatches(patch1.array,patch2.array,borderWidth=borderWidth), + sign = patch1.sign) - # calculate unique area of the merged patch + #calculate unique area of the merged patch visualSpace, _, _ = currMergedPatch.getVisualSpace(altPosMapf, aziPosMapf, - pixelSize=visualSpacePixelSize, - closeIter=visualSpaceCloseIter) + pixelSize = visualSpacePixelSize, + closeIter = visualSpaceCloseIter) AU = np.sum(visualSpace[:]) * (visualSpacePixelSize ** 2) - # calculate the visual space and unique area of the first patch - visualSpace1, _, _ = patch1.getVisualSpace(altPosMapf, - aziPosMapf, - pixelSize=visualSpacePixelSize, - closeIter=visualSpaceCloseIter) - visualSpace1 = visualSpace1.astype(np.uint8) + #calculate the visual space and unique area of the first patch + visualSpace1, _, _ =patch1.getVisualSpace(altPosMapf, + aziPosMapf, + pixelSize = visualSpacePixelSize, + closeIter = visualSpaceCloseIter) AU1 = np.sum(visualSpace1[:]) * (visualSpacePixelSize ** 2) - # calculate the visual space and unique area of the second patch - visualSpace2, _, _ = patch2.getVisualSpace(altPosMapf, - aziPosMapf, - pixelSize=visualSpacePixelSize, - closeIter=visualSpaceCloseIter) - visualSpace2 = visualSpace2.astype(np.uint8) + #calculate the visual space and unique area of the second patch + visualSpace2, _, _ =patch2.getVisualSpace(altPosMapf, + aziPosMapf, + pixelSize = visualSpacePixelSize, + closeIter = visualSpaceCloseIter) AU2 = np.sum(visualSpace2[:]) * (visualSpacePixelSize ** 2) - # calculate the overlapping area of these two patches + #calculate the overlapping area of these two patches sumSpace = visualSpace1 + visualSpace2 - overlapSpace = np.zeros(sumSpace.shape, dtype=np.int) + overlapSpace = np.zeros(sumSpace.shape, dtype = np.int) overlapSpace[sumSpace == 2] = 1 - - # f = plt.figure() - # ax1 = f.add_subplot(141) - # ax1.imshow(visualSpace1) - # ax2 = f.add_subplot(142) - # ax2.imshow(visualSpace2) - # ax3 = f.add_subplot(143) - # ax3.imshow(sumSpace) - # ax4 = f.add_subplot(144) - # ax4.imshow(overlapSpace) - # plt.show() - Aoverlap = np.sum(overlapSpace[:]) * (visualSpacePixelSize ** 2) - # calculate the ratio of overlaping area to the unique area of each patch + #calculate the ratio of overlaping area to the unique area of each patch overlapRatio1 = Aoverlap / AU1 overlapRatio2 = Aoverlap / AU2 - # if both ratios are small than merge overlap threshold definded at the beginning of the file + #if both ratios are small than merge overlap threshold definded at the beginning of the file if (overlapRatio1 <= mergeOverlapThr) and (overlapRatio2 <= mergeOverlapThr): - # put this pair and related information to mergePairs list + + #put this pair and related information to mergePairs list mergePairs.append([pair[0], pair[1], currMergedPatch, - np.max([overlapRatio1, overlapRatio2]), + np.max([overlapRatio1,overlapRatio2]), (-1 * AU)]) del visualSpace1, visualSpace2, AU1, AU2, sumSpace, overlapSpace, Aoverlap @@ -1796,41 +1776,42 @@ def _mergePatches(self, isPlot=False): del patch1, patch2 if len(mergePairs) > 0: - # for each identified patch pair to merge sort them with the sum of two - # overlap ratios, from smallest to biggest and then sort them with the - # unique area of merged patches from biggest to smallest - mergePairs.sort(key=itemgetter(3, 4)) + #for each identified patch pair to merge sort them with the sum of two + #overlap ratios, from smallest to biggest and then sort them with the + #unique area of merged patches from biggest to smallest + mergePairs.sort(key = itemgetter(3,4)) - for ind, value in enumerate(mergePairs): # for each of these pairs + for ind, value in enumerate(mergePairs): #for each of these pairs patch1 = value[0] patch2 = value[1] # if both of these two patches are still in the 'patches' dictionary - if (patch1 in patches.keys()) and (patch2 in patches.keys()): - # plot these patches and their visual space + if (patch1 in list(patches.keys())) and (patch2 in list(patches.keys())): + + #plot these patches and their visual space plotPairedPatches(patches[patch1], patches[patch2], altPosMapf, aziPosMapf, - title='merge iteation:' + str( - mergeIter) + ' patch1:' + patch1 + ' patch2:' + patch2, - pixelSize=visualSpacePixelSize, - closeIter=visualSpaceCloseIter) + title = 'merge iteation:'+str(mergeIter)+' patch1:'+patch1+' patch2:'+patch2, + pixelSize = visualSpacePixelSize, + closeIter = visualSpaceCloseIter) - # remove these two patches from the 'patches' dictionary + #remove these two patches from the 'patches' dictionary patches.pop(patch1) patches.pop(patch2) - # add merged patches into the 'patches' dictionare - patches.update({patch1 + '+' + patch2[5:]: value[2]}) + #add merged patches into the 'patches' dictionare + patches.update({patch1+'+'+patch2[5:]:value[2]}) - print 'merging: ' + patch1 + ' & ' + patch2 + ', overlap ratio: ' + str(value[3]) + print('merging: '+patch1+' & '+patch2 + ', overlap ratio: ' + str(value[3])) mergeIter = mergeIter + 1 - # remove small patches + + #remove small patches patches2 = dict(patches) - for key, value in patches2.iteritems(): + for key, value in patches2.items(): if (value.getArea() < smallPatchThr): patches.pop(key) @@ -1845,13 +1826,13 @@ def _mergePatches(self, isPlot=False): zoom = self.vasculatureMap.shape[0] / self.altPosMap.shape[0] except: zoom = 1 - f = plt.figure() - f111 = f.add_subplot(111) + f=plt.figure() + f111=f.add_subplot(111) try: - f111.imshow(self.vasculatureMap, cmap='gray', interpolation='nearest') + f111.imshow(self.vasculatureMap, cmap = 'gray', interpolation = 'nearest') except: pass - h = plotPatches(finalPatches, plotaxis=f111, zoom=zoom) + h = plotPatches(finalPatches, plotaxis = f111, zoom = zoom) f111.set_axis_off() f111.set_title('final Patches') @@ -1859,26 +1840,28 @@ def _mergePatches(self, isPlot=False): return patches, finalPatches + def getTraces(self, moviePath, - resampleFrequency=10, # at which frequency the traces are resampled - centerPatch='patch01', # the patch to get ROI - ROIcenters=([30., 0.], [60., 0.], [90., 0.]), # visual space centers of ROIs - ROIsearchRange=0.5, # range to search pixels in ROI - ROIsize=10, # ROI size (pixel) - ROIcolor=('#ff0000', '#00ff00', '#0000ff'), # color for each ROI - isPlot=False, + resampleFrequency = 10, # at which frequency the traces are resampled + centerPatch = 'patch01', # the patch to get ROI + ROIcenters = ([30.,0.],[60.,0.],[90.,0.]), # visual space centers of ROIs + ROIsearchRange = 0.5, #range to search pixels in ROI + ROIsize = 10, # ROI size (pixel) + ROIcolor = ('#ff0000','#00ff00','#0000ff'),#color for each ROI + isPlot = False, ): + if not hasattr(self, 'finalPatches'): self.processTrial() if type(ROIsearchRange) == int or type(ROIsearchRange) == float: - ROIsearchRanges = [] + ROIsearchRanges=[] for i in range(len(ROIcenters)): ROIsearchRanges.append(ROIsearchRange) elif type(ROIsearchRange) == list: - ROIsearchRanges = ROIsearchRange + ROIsearchRanges = ROIsearchRange if not hasattr(self, 'altPosMapf'): self._getSignMap() @@ -1894,29 +1877,25 @@ def getTraces(self, for i, ROIcenter in enumerate(ROIcenters): try: - altIndex = np.logical_and(altPosMapf > ROIcenter[1] - ROIsearchRanges[i], - altPosMapf < ROIcenter[1] + ROIsearchRanges[i]) - aziIndex = np.logical_and(aziPosMapf > ROIcenter[0] - ROIsearchRanges[i], - aziPosMapf < ROIcenter[0] + ROIsearchRanges[i]) + altIndex = np.logical_and(altPosMapf > ROIcenter[1]-ROIsearchRanges[i], altPosMapf < ROIcenter[1]+ROIsearchRanges[i]) + aziIndex = np.logical_and(aziPosMapf > ROIcenter[0]-ROIsearchRanges[i], aziPosMapf < ROIcenter[0]+ROIsearchRanges[i]) index = np.logical_and(altIndex, aziIndex) index = np.where(np.logical_and(index, centerPatchArray) == True) - pos = int(len(index[0]) / 2) - print ROIcenter, index - print 'ROI' + str(i) + ' center: [' + str(index[0][pos]) + ',' + str( - index[1][pos]) + ']; visual space: [' + str(altPosMapf[index[0][pos], index[1][pos]]) + ',' + str( - aziPosMapf[index[0][pos], index[1][pos]]) + ']' + pos = int(len(index[0])/2) + print(ROIcenter, index) + print('ROI'+str(i)+' center: ['+str(index[0][pos])+','+str(index[1][pos])+']; visual space: ['+str(altPosMapf[index[0][pos],index[1][pos]])+','+str(aziPosMapf[index[0][pos],index[1][pos]])+']') mask = ia.generate_rectangle_mask(mov, (index[0][pos], index[1][pos]), ROIsize, ROIsize) trace = ia.get_trace(mov, mask) t = np.arange(len(trace)) * self.imageExposureTime traceP = findPhaseIndex(trace) * self.imageExposureTime - t2, y2 = ia.resample(t, trace, 1. / resampleFrequency) + t2, y2 = ia.resample(t, trace, 1./resampleFrequency) traces.append({'position': ROIcenter, 'mask': mask, - 'trace': [t2, y2], + 'trace': [t2,y2], 'tracePhaseTime': traceP, - 'ROIcolor': ROIcolor[i]}) + 'ROIcolor':ROIcolor[i]}) except: pass @@ -1928,34 +1907,33 @@ def getTraces(self, except: zoom = 1 - f = plt.figure(figsize=(15, 5)) - ax1 = f.add_axes([0.1, 0.1, 0.2, 0.8]) - ax2 = f.add_axes([0.4, 0.1, 0.5, 0.8]) + f = plt.figure(figsize=(15,5)) + ax1 = f.add_axes([0.1,0.1,0.2,0.8]) + ax2 = f.add_axes([0.4,0.1,0.5,0.8]) try: - ax1.imshow(vasculatureMap, cmap='gray', interpolation='nearest', aspect='equal') + ax1.imshow(vasculatureMap, cmap='gray', interpolation='nearest',aspect='equal') except: pass ROIlegend = [] for i in range(len(traces)): try: - pt.plot_mask(traces[i]['mask'], plotAxis=ax1, borderWidth=5, zoom=zoom, color=traces[i]['ROIcolor']) - currT = traces[i]['trace'][0] - currTrace = traces[i]['trace'][1] - ax2.plot(currT, currTrace - np.mean(currTrace), '-', color=traces[i]['ROIcolor'], lw=2) - ROIlegend.append( - ['center:' + str(traces[i]['position']) + ', baseline:' + '{0:.0f}'.format(np.mean(currTrace))]) + pt.plot_mask(traces[i]['mask'], plotAxis = ax1, borderWidth=5, zoom=zoom, color=traces[i]['ROIcolor']) + currT=traces[i]['trace'][0] + currTrace=traces[i]['trace'][1] + ax2.plot(currT,currTrace-np.mean(currTrace), '-', color=traces[i]['ROIcolor'], lw=2) + ROIlegend.append(['center:'+str(traces[i]['position'])+', baseline:' + '{0:.0f}'.format(np.mean(currTrace))]) except: pass - ax2.legend(ROIlegend, prop={'size': 8}) - ymin = np.floor(ax2.yaxis.get_data_interval()[0] / 10) * 10 - ymax = np.ceil(ax2.yaxis.get_data_interval()[1] / 10) * 10 + ax2.legend(ROIlegend,prop={'size':8}) + + ymin = np.floor(ax2.yaxis.get_data_interval()[0]/10)*10 + ymax = np.ceil(ax2.yaxis.get_data_interval()[1]/10)*10 for i in range(len(traces)): try: - ax2.plot([traces[i]['tracePhaseTime'], traces[i]['tracePhaseTime']], [ymin, ymax], '--', - color=traces[i]['ROIcolor'], lw=2) + ax2.plot([traces[i]['tracePhaseTime'],traces[i]['tracePhaseTime']],[ymin,ymax],'--',color=traces[i]['ROIcolor'],lw=2) except: pass @@ -1966,91 +1944,62 @@ def getTraces(self, return traces + def cleanMaps(self): - try: - del self.altPosMapf - except AttributeError: - pass + try:del self.altPosMapf + except AttributeError:pass - try: - del self.aziPosMapf - except AttributeError: - pass + try:del self.aziPosMapf + except AttributeError:pass - try: - del self.altPowerMapf - except AttributeError: - pass + try:del self.altPowerMapf + except AttributeError:pass - try: - del self.aziPowerMapf - except AttributeError: - pass + try:del self.aziPowerMapf + except AttributeError:pass - try: - del self.signMap - except AttributeError: - pass + try:del self.signMap + except AttributeError:pass - try: - del self.signMapf - except AttributeError: - pass + try:del self.signMapf + except AttributeError:pass - try: - del self.rawPatchMap - except AttributeError: - pass + try:del self.rawPatchMap + except AttributeError:pass - try: - del self.rawPatches - except AttributeError: - pass + try:del self.rawPatches + except AttributeError:pass - try: - del self.eccentricityMap - except AttributeError: - pass + try:del self.eccentricityMap + except AttributeError:pass - try: - del self.eccentricityMapf - except AttributeError: - pass + try:del self.eccentricityMapf + except AttributeError:pass - try: - del self.determinantMap - except AttributeError: - pass + try:del self.determinantMap + except AttributeError:pass - try: - del self.patchesAfterSplit - except AttributeError: - pass + try:del self.patchesAfterSplit + except AttributeError:pass - try: - del self.patchesAfterMerge - except AttributeError: - pass + try:del self.patchesAfterMerge + except AttributeError:pass - try: - del self.finalPatches - except AttributeError: - pass + try:del self.finalPatches + except AttributeError:pass + + try:del self.finalPatchesMarked + except AttributeError:pass - try: - del self.finalPatchesMarked - except AttributeError: - pass def cleanTraces(self): - try: - del self.traces - except AttributeError: - pass + try:del self.traces + except AttributeError:pass + - def processTrial(self, isPlot=False): + def processTrial(self, isPlot = False): self.cleanMaps() _ = self._getSignMap(isPlot=isPlot) if isPlot: plt.show() @@ -2067,15 +2016,16 @@ def processTrial(self, isPlot=False): _ = self._mergePatches(isPlot=isPlot) if isPlot: plt.show() + def refresh(self, - moviePath=None, - imageExposureTime=None, - centerPatch='patch01', - ROIcenters=[[30, 0], [60, 0], [90, 0]], # visual space centers of ROIs - ROIsearchRange=0.5, - ROIsize=10, # ROI size (pixel) - ROIcolor=['#ff0000', '#00ff00', '#0000ff'], # color for each ROI - isPlot=False): + moviePath = None, + imageExposureTime = None, + centerPatch = 'patch01', + ROIcenters = [[30,0],[60,0],[90,0]], # visual space centers of ROIs + ROIsearchRange = 0.5, + ROIsize = 10, # ROI size (pixel) + ROIcolor = ['#ff0000','#00ff00','#0000ff'], #color for each ROI + isPlot = False): self.cleanSelf() @@ -2088,108 +2038,112 @@ def refresh(self, self._mergePatches(isPlot=isPlot) try: - _ = self.getTraces(moviePath=moviePath, - imageExposureTime=imageExposureTime, - centerPatch=centerPatch, - ROIcenters=ROIcenters, - ROIsearchRange=ROIsearchRange, - ROIsize=ROIsize, - ROIcolor=ROIcolor, - isPlot=isPlot) + _ = self.getTraces(moviePath = moviePath, + imageExposureTime = imageExposureTime, + centerPatch = centerPatch, + ROIcenters = ROIcenters, + ROIsearchRange = ROIsearchRange, + ROIsize = ROIsize, + ROIcolor = ROIcolor, + isPlot = isPlot) except: pass + def generateTrialDict(self, - keysToRetain=('altPosMap', - 'aziPosMap', - 'signMap', - 'altPosMapf', - 'aziPosMapf', - 'signMapf', - 'rawPatchMap', - 'eccentricityMapf', - 'finalPatches', - 'finalPatchesMarked', - 'mouseID', - 'dateRecorded', - 'trialNum', - 'mouseType', - 'visualStimType', - 'visualStimBackground', - 'imageExposureTime', - 'altPowerMap', - 'altPowerMapf', - 'aziPowerMap', - 'aziPowerMapf', - 'vasculatureMap', - 'params', - 'isAnesthetized' - ) + keysToRetain = ('altPosMap', + 'aziPosMap', + 'signMap', + 'altPosMapf', + 'aziPosMapf', + 'signMapf', + 'rawPatchMap', + 'eccentricityMapf', + 'finalPatches', + 'finalPatchesMarked', + 'mouseID', + 'dateRecorded', + 'trialNum', + 'mouseType', + 'visualStimType', + 'visualStimBackground', + 'imageExposureTime', + 'altPowerMap', + 'altPowerMapf', + 'aziPowerMap', + 'aziPowerMapf', + 'vasculatureMap', + 'params', + 'isAnesthetized' + ) ): + trialDict = {} keysLeft = list(keysToRetain) - for key in self.__dict__.iterkeys(): + for key in self.__dict__.keys(): if key in keysToRetain: - if key == 'finalPatches': + if key=='finalPatches': finalPatches = {} - for area, patch in self.finalPatches.iteritems(): - finalPatches.update({area: getPatchDict(patch)}) - trialDict.update({'finalPatches': finalPatches}) + for area,patch in self.finalPatches.items(): + finalPatches.update({area:getPatchDict(patch)}) + trialDict.update({'finalPatches':finalPatches}) keysLeft.remove('finalPatches') elif key == 'finalPathcesMarked': finalPatchesMarked = {} - for area, patch in self.finalPathcesMarked.iteritems(): - finalPatchesMarked.update({area: getPatchDict(patch)}) - trialDict.update({'finalPatchesMarked': finalPatchesMarked}) + for area,patch in self.finalPathcesMarked.items(): + finalPatchesMarked.update({area:getPatchDict(patch)}) + trialDict.update({'finalPatchesMarked':finalPatchesMarked}) keysLeft.remove('finalPatchesMarked') else: try: - trialDict.update({key: self.__dict__[key]}) + trialDict.update({key:self.__dict__[key]}) keysLeft.remove(key) except AttributeError: pass if keysLeft: - print 'Can not find wanted key(s): ' + str(keysLeft) + print('Can not find wanted key(s): ' + str(keysLeft)) return trialDict + def generatePosOverlay(self): if (not hasattr(self, 'altPosMapf')) or (not hasattr(self, 'aziPosMapf')): self._getSignMap() - vasMap = self.vasculatureMap + vasMap=self.vasculatureMap altPosMap = self.altPosMapf aziPosMap = self.aziPosMapf - zoom = vasMap.shape[0] / altPosMap.shape[0] + zoom = vasMap.shape[0]/altPosMap.shape[0] - altPosMap = ni.zoom(altPosMap, zoom) - aziPosMap = ni.zoom(aziPosMap, zoom) + altPosMap = ni.zoom(altPosMap,zoom) + aziPosMap = ni.zoom(aziPosMap,zoom) - f = plt.figure(figsize=(20, 5)) - ax1 = f.add_subplot(121) - ax1.imshow(vasMap, cmap='gray', interpolation='nearest') - currfig = ax1.imshow(altPosMap, cmap='jet', interpolation='nearest', vmin=-30, vmax=50, alpha=0.5) + f=plt.figure(figsize=(20,5)) + ax1=f.add_subplot(121) + ax1.imshow(vasMap,cmap='gray',interpolation='nearest') + currfig=ax1.imshow(altPosMap,cmap='jet',interpolation='nearest',vmin=-30,vmax=50,alpha=0.5) f.colorbar(currfig) ax1.axis('off') ax1.set_title('altitude position') - ax2 = f.add_subplot(122) - ax2.imshow(vasMap, cmap='gray', interpolation='nearest') - currfig = ax2.imshow(aziPosMap, cmap='jet', interpolation='nearest', vmin=0, vmax=100, alpha=0.5) + ax2=f.add_subplot(122) + ax2.imshow(vasMap,cmap='gray',interpolation='nearest') + currfig=ax2.imshow(aziPosMap,cmap='jet',interpolation='nearest',vmin=0,vmax=100,alpha=0.5) f.colorbar(currfig) ax2.axis('off') ax2.set_title('azimuth position') - def generateStandardOutput(self, traces=None, isSave=False, saveFolder=None): + + def generateStandardOutput(self,traces = None,isSave = False,saveFolder = None): if not hasattr(self, 'finalPatches'): self.processTrial() @@ -2198,13 +2152,13 @@ def generateStandardOutput(self, traces=None, isSave=False, saveFolder=None): self._getSignMap() try: - zoom = self.vasculatureMap.shape[0] / self.altPosMap.shape[0] + zoom = self.vasculatureMap.shape[0]/self.altPosMap.shape[0] except: zoom = 1 trialName = self.getName() - f = plt.figure(figsize=(15, 10)) + f = plt.figure(figsize=(15,10)) f.suptitle(trialName) f_331 = f.add_subplot(331) @@ -2215,35 +2169,33 @@ def generateStandardOutput(self, traces=None, isSave=False, saveFolder=None): f_331.set_axis_off() if traces: - f_3389 = f.add_axes([0.4, 0.1, 0.5, 0.23]) - ROIlegend = [] + f_3389 = f.add_axes([0.4,0.1,0.5,0.23]) + ROIlegend=[] for i in range(len(traces)): - pt.plot_mask(traces[i]['mask'], plotAxis=f_331, borderWidth=5, zoom=zoom, color=traces[i]['ROIcolor']) + pt.plot_mask(traces[i]['mask'], plotAxis = f_331, borderWidth=5, zoom=zoom, color=traces[i]['ROIcolor']) - currT = traces[i]['trace'][0] - currTrace = traces[i]['trace'][1] - f_3389.plot(currT, currTrace - np.mean(currTrace), '-', color=traces[i]['ROIcolor'], lw=2) + currT=traces[i]['trace'][0] + currTrace=traces[i]['trace'][1] + f_3389.plot(currT,currTrace-np.mean(currTrace), '-', color=traces[i]['ROIcolor'], lw=2) - ROIlegend.append( - ['center:' + str(traces[i]['position']) + ', baseline:' + '{0:.0f}'.format(np.mean(currTrace))]) + ROIlegend.append(['center:'+str(traces[i]['position'])+', baseline:' + '{0:.0f}'.format(np.mean(currTrace))]) - f_3389.legend(ROIlegend, prop={'size': 8}) + f_3389.legend(ROIlegend,prop={'size':8}) - ymin = np.floor(f_3389.yaxis.get_data_interval()[0] / 10) * 10 - ymax = np.ceil(f_3389.yaxis.get_data_interval()[1] / 10) * 10 + ymin = np.floor(f_3389.yaxis.get_data_interval()[0]/10)*10 + ymax = np.ceil(f_3389.yaxis.get_data_interval()[1]/10)*10 for i in range(len(traces)): - f_3389.plot([traces[i]['tracePhaseTime'], traces[i]['tracePhaseTime']], [ymin, ymax], '--', - color=traces[i]['ROIcolor'], lw=2) + f_3389.plot([traces[i]['tracePhaseTime'],traces[i]['tracePhaseTime']],[ymin,ymax],'--',color=traces[i]['ROIcolor'], lw=2) f_3389.set_ylim([ymin, ymax]) f_3389.set_xlabel('time (sec)') f_3389.set_ylabel('normalized count') + f_332 = f.add_subplot(332) - currfig = f_332.imshow(np.mean([self.altPowerMap, self.aziPowerMap], axis=0), cmap='hot', - interpolation='nearest') + currfig = f_332.imshow(np.mean([self.altPowerMap,self.aziPowerMap],axis=0), cmap='hot', interpolation='nearest') f.colorbar(currfig) f_332.set_axis_off() @@ -2252,11 +2204,11 @@ def generateStandardOutput(self, traces=None, isSave=False, saveFolder=None): vm = self.vasculatureMap.astype(np.float) vmin = np.amin(vm) vmax = np.amax(vm) - vmin = vmin - 0.5 * (vmax - vmin) + vmin = vmin - 0.5*(vmax-vmin) f_333.imshow(self.vasculatureMap, vmin=vmin, vmax=vmax, cmap='gray', interpolation='nearest') except: pass - plotPatches(self.finalPatches, plotaxis=f_333, zoom=zoom, markersize=3) + plotPatches(self.finalPatches,plotaxis=f_333,zoom=zoom, markersize=3) f_333.set_axis_off() f_334 = f.add_subplot(334) @@ -2280,25 +2232,24 @@ def generateStandardOutput(self, traces=None, isSave=False, saveFolder=None): plotPatchBorders3(self.finalPatches, self.altPosMapf, self.aziPosMapf, - plotAxis=f_337, - plotSize=500, - borderWidth=2, - zoom=1, - centerPatchKey='patch01', - markerSize=5, - closingIteration=1, - arrowLength=15) + plotAxis = f_337, + plotSize = 500, + borderWidth = 2, + zoom = 1, + centerPatchKey = 'patch01', + markerSize = 5, + closingIteration = 1, + arrowLength = 15) f_337.get_xaxis().set_visible(False) f_337.get_yaxis().set_visible(False) if isSave: - f.savefig(os.path.join(saveFolder, trialName + '.pdf'), format='pdf', dpi=300, orientation='landscape', - papertype='a4') - f.savefig(os.path.join(saveFolder, trialName + '.png'), format='png', dpi=300, orientation='landscape', - papertype='a4') + f.savefig(os.path.join(saveFolder,trialName+'.pdf'), format='pdf', dpi = 300, orientation='landscape', papertype='a4') + f.savefig(os.path.join(saveFolder,trialName+'.png'), format='png', dpi = 300, orientation='landscape', papertype='a4') del f - def generateNormalizedMaps(self, centerPatchKey='patch01', mapSize=512, isPlot=False, borderValue=0.): + + def generateNormalizedMaps(self,centerPatchKey='patch01',mapSize = 512,isPlot = False,borderValue=0.): if not hasattr(self, 'finalPatches'): self.processTrial() @@ -2317,16 +2268,15 @@ def generateNormalizedMaps(self, centerPatchKey='patch01', mapSize=512, isPlot=F aziPosMapC = ia.center_image(self.aziPosMap, centerPixel=centerPixel, newSize=mapSize, borderValue=borderValue) aziPosMapNor = ia.rotate_image(aziPosMapC, rotationAngle, borderValue=borderValue) + if hasattr(self, 'altPowerMap') and self.altPowerMap is not None: - altPowerMapC = ia.center_image(self.altPowerMap, centerPixel=centerPixel, newSize=mapSize, - borderValue=borderValue) + altPowerMapC = ia.center_image(self.altPowerMap, centerPixel=centerPixel, newSize=mapSize, borderValue=borderValue) altPowerMapNor = ia.rotate_image(altPowerMapC, rotationAngle, borderValue=borderValue) else: altPowerMapNor = None if hasattr(self, 'aziPowerMap') and self.aziPowerMap is not None: - aziPowerMapC = ia.center_image(self.aziPowerMap, centerPixel=centerPixel, newSize=mapSize, - borderValue=borderValue) + aziPowerMapC = ia.center_image(self.aziPowerMap, centerPixel=centerPixel, newSize=mapSize, borderValue=borderValue) aziPowerMapNor = ia.rotate_image(aziPowerMapC, rotationAngle, borderValue=borderValue) else: aziPowerMapNor = None @@ -2338,43 +2288,45 @@ def generateNormalizedMaps(self, centerPatchKey='patch01', mapSize=512, isPlot=F signMapfNor = ia.rotate_image(signMapfC, rotationAngle, borderValue=borderValue) if isPlot: + trialName = self.getName() - f = plt.figure(figsize=(15, 8)) - f.suptitle('normalized maps for' + trialName) + f = plt.figure(figsize=(15,8)) + f.suptitle('normalized maps for'+trialName) f_231 = f.add_subplot(231) - currfig = f_231.imshow(altPosMapNor, vmin=-30, vmax=50, cmap='hsv', interpolation='nearest') + currfig = f_231.imshow(altPosMapNor,vmin=-30,vmax=50, cmap = 'hsv', interpolation='nearest') f.colorbar(currfig) f_231.set_axis_off() f_231.set_title('normalized altitude position') f_232 = f.add_subplot(232) - currfig = f_232.imshow(aziPosMapNor, vmin=0, vmax=120, cmap='hsv', interpolation='nearest') + currfig = f_232.imshow(aziPosMapNor,vmin=0,vmax=120, cmap = 'hsv', interpolation='nearest') f.colorbar(currfig) f_232.set_axis_off() f_232.set_title('normalized altitude position') f_233 = f.add_subplot(233) - currfig = f_233.imshow(signMapfNor, vmin=-1, vmax=1, cmap='jet', interpolation='nearest') + currfig = f_233.imshow(signMapfNor,vmin=-1,vmax=1,cmap = 'jet', interpolation='nearest') f.colorbar(currfig) f_233.set_axis_off() f_233.set_title('normalized sign map') f_234 = f.add_subplot(234) - currfig = f_234.imshow(altPowerMapNor, cmap='hot', interpolation='nearest') + currfig = f_234.imshow(altPowerMapNor,cmap = 'hot', interpolation='nearest') f.colorbar(currfig) f_234.set_axis_off() f_234.set_title('normalized altitude power') f_235 = f.add_subplot(235) - currfig = f_235.imshow(aziPowerMapNor, cmap='hot', interpolation='nearest') + currfig = f_235.imshow(aziPowerMapNor,cmap = 'hot', interpolation='nearest') f.colorbar(currfig) f_235.set_axis_off() f_235.set_title('normalized azimuth power') return altPosMapNor, aziPosMapNor, altPowerMapNor, aziPowerMapNor, signMapNor, signMapfNor - def generateNormalizedTrial(self, centerPatchKey='patch01', mapSize=512, isPlot=False, borderValue=0.): + + def generateNormalizedTrial(self,centerPatchKey='patch01', mapSize=512, isPlot=False, borderValue=0.): if not hasattr(self, 'finalPatches'): self.processTrial() @@ -2393,15 +2345,13 @@ def generateNormalizedTrial(self, centerPatchKey='patch01', mapSize=512, isPlot= # aziPosMapNor = aziPosMapNor - aziPosOrigin if hasattr(self, 'altPowerMap') and self.altPowerMap is not None: - altPowerMapC = ia.center_image(self.altPowerMap, centerPixel=centerPixel, newSize=mapSize, - borderValue=borderValue) + altPowerMapC = ia.center_image(self.altPowerMap, centerPixel=centerPixel, newSize=mapSize, borderValue=borderValue) altPowerMapNor = ia.rotate_image(altPowerMapC, rotationAngle, borderValue=borderValue) else: altPowerMapNor = None if hasattr(self, 'aziPowerMap') and self.aziPowerMap is not None: - aziPowerMapC = ia.center_image(self.aziPowerMap, centerPixel=centerPixel, newSize=mapSize, - borderValue=borderValue) + aziPowerMapC = ia.center_image(self.aziPowerMap, centerPixel=centerPixel, newSize=mapSize, borderValue=borderValue) aziPowerMapNor = ia.rotate_image(aziPowerMapC, rotationAngle, borderValue=borderValue) else: aziPowerMapNor = None @@ -2425,8 +2375,8 @@ def generateNormalizedTrial(self, centerPatchKey='patch01', mapSize=512, isPlot= trialName = self.getName() - f = plt.figure(figsize=(15, 8)) - f.suptitle('normalized maps for' + trialName) + f = plt.figure(figsize=(15,8)) + f.suptitle('normalized maps for'+trialName) f_231 = f.add_subplot(231) currfig = f_231.imshow(self.altPosMap, vmin=-30, vmax=50, cmap='hsv', interpolation='nearest') @@ -2471,7 +2421,8 @@ def generateNormalizedTrial(self, centerPatchKey='patch01', mapSize=512, isPlot= altPosMapNor, aziPosMapNor, altPosMapNor, aziPowerMapNor, vasMapNor, params={}, isAnesthetized=self.isAnesthetized) - def getNormalizeTransform(self, centerPatchKey='patch01'): + + def getNormalizeTransform(self,centerPatchKey = 'patch01'): try: centerPatchObj = self.finalPatchesMarked[centerPatchKey] @@ -2485,11 +2436,12 @@ def getNormalizeTransform(self, centerPatchKey='patch01'): aziGradMap = np.gradient(self.aziPosMapf) aziGradMapX = np.sum(aziGradMap[0] * centerPatchObj.array) aziGradMapY = np.sum(aziGradMap[1] * centerPatchObj.array) - rotationAngle = -(np.arctan2(-aziGradMapX, aziGradMapY) % (2 * np.pi)) * 180 / np.pi + rotationAngle = -(np.arctan2(-aziGradMapX,aziGradMapY)%(2*np.pi))*180/np.pi return centerPixel, rotationAngle - def normalize(self, centerPatchKey='patch01', mapSize=800, isPlot=False, borderValue=0.): + + def normalize(self,centerPatchKey = 'patch01',mapSize = 800,isPlot = False,borderValue=0.): ''' Generate normalized vasculature map and normalized final patches @@ -2512,10 +2464,10 @@ def normalize(self, centerPatchKey='patch01', mapSize=800, isPlot=False, borderV try: vasMap = self.vasculatureMap.astype(np.float) - zoom = int(float(vasMap.shape[0]) / float(self.aziPosMapf.shape[0])) + zoom = int(float(vasMap.shape[0])/float(self.aziPosMapf.shape[0])) except AttributeError as e: - print 'Can not find vasculature map!!\n\n' - print e + print('Can not find vasculature map!!\n\n') + print(e) zoom = 1 mapSize = mapSize * zoom @@ -2528,43 +2480,43 @@ def normalize(self, centerPatchKey='patch01', mapSize=800, isPlot=False, borderV pass patchesNor = {} - for key, patch in patches.iteritems(): + for key, patch in patches.items(): patchArray = patch.array.astype(np.float) - patchArrayNor = ni.zoom(patchArray, zoom=zoom) - patchArrayNor = ia.center_image(patchArrayNor, centerPixel=centerPixel, newSize=mapSize, - borderValue=borderValue) + patchArrayNor = ni.zoom(patchArray,zoom=zoom) + patchArrayNor = ia.center_image(patchArrayNor, centerPixel=centerPixel, newSize=mapSize, borderValue=borderValue) patchArrayNor = ia.rotate_image(patchArrayNor, rotationAngle, borderValue=borderValue) patchArrayNor = np.round(patchArrayNor).astype(np.int8) - newPatch = Patch(patchArrayNor, patch.sign) - patchesNor.update({key: newPatch}) + newPatch = Patch(patchArrayNor,patch.sign) + patchesNor.update({key:newPatch}) if isPlot: - f = plt.figure(figsize=(12, 5)) + f = plt.figure(figsize=(12,5)) ax1 = f.add_subplot(121) ax1.set_title('original') try: - ax1.imshow(vasMap, cmap='gray', interpolation='nearest') + ax1.imshow(vasMap, cmap = 'gray', interpolation = 'nearest') except: pass - h = plotPatches(patches, plotaxis=ax1, zoom=zoom) + h = plotPatches(patches, plotaxis = ax1, zoom = zoom) ax1.set_axis_off() ax2 = f.add_subplot(122) ax2.set_title('normalized') try: - ax2.imshow(vasMapNor, cmap='gray', interpolation='nearest') + ax2.imshow(vasMapNor, cmap = 'gray', interpolation = 'nearest') except: pass - h = plotPatches(patchesNor, plotaxis=ax2, zoom=1) + h = plotPatches(patchesNor, plotaxis = ax2, zoom = 1) ax2.set_axis_off() + return vasMapNor, patchesNor - def plotNormalizedPatchCenter(self, centerPatchKey='patch01', mapSize=512, plotAxis=None, markerSize=5., - markerEdgeWidth=2.): + + def plotNormalizedPatchCenter(self,centerPatchKey = 'patch01',mapSize = 512,plotAxis = None,markerSize = 5.,markerEdgeWidth = 2.): if not plotAxis: - f = plt.figure() + f=plt.figure() plotAxis = f.add_subplot(111) if not hasattr(self, 'finalPatches'): @@ -2582,9 +2534,9 @@ def plotNormalizedPatchCenter(self, centerPatchKey='patch01', mapSize=512, plotA aziGradMap = np.gradient(aziPosMapf) aziGradMapX = np.sum(aziGradMap[0] * centerPatchObj.array) aziGradMapY = np.sum(aziGradMap[1] * centerPatchObj.array) - rotationAngle = -(np.arctan2(-aziGradMapX, aziGradMapY) % (2 * np.pi)) * 180 / np.pi + rotationAngle = -(np.arctan2(-aziGradMapX,aziGradMapY)%(2*np.pi))*180/np.pi - for key, patch in self.finalPatches.iteritems(): + for key, patch in self.finalPatches.items(): patchArray = patch.array.astype(np.float32) patchSign = patch.sign @@ -2597,27 +2549,28 @@ def plotNormalizedPatchCenter(self, centerPatchKey='patch01', mapSize=512, plotA if patchSign == 1: plotAxis.plot(center[1], - mapSize - center[0], + mapSize-center[0], 'o', mfc='none', - mec='r', - markersize=markerSize, - mew=markerEdgeWidth) + mec = 'r', + markersize = markerSize, + mew = markerEdgeWidth) elif patchSign == -1: plotAxis.plot(center[1], - mapSize - center[0], + mapSize-center[0], 'o', mfc='none', - mec='b', - markersize=markerSize, - mew=markerEdgeWidth) + mec = 'b', + markersize = markerSize, + mew = markerEdgeWidth) plotAxis.set_xlim([0, mapSize]) plotAxis.set_ylim([0, mapSize]) plotAxis.set_axis_off() - def plotTrial(self, isSave=False, saveFolder=None): + + def plotTrial(self,isSave = False,saveFolder = None): if not hasattr(self, 'finalPatches'): self.processTrial() @@ -2632,8 +2585,8 @@ def plotTrial(self, isSave=False, saveFolder=None): trialName = self.getName() - # plot figure 1 - f1 = plt.figure(figsize=(18, 9)) + #plot figure 1 + f1=plt.figure(figsize=(18,9)) f1.suptitle(trialName) f1_231 = f1.add_subplot(231) currfig = f1_231.imshow(self.altPosMapf, vmin=-40, vmax=60, cmap='hsv', interpolation='nearest') @@ -2657,116 +2610,113 @@ def plotTrial(self, isSave=False, saveFolder=None): f1_234.set_title('filtered visual sign map') f1_235 = f1.add_subplot(235) try: - currfig = f1_235.imshow(np.mean([self.altPowerMap, self.aziPowerMap], axis=0), cmap='hot', - interpolation='nearest') + currfig = f1_235.imshow(np.mean([self.altPowerMap,self.aziPowerMap],axis=0), cmap='hot', interpolation='nearest') f1.colorbar(currfig) plt.axis('off') f1_235.set_title('power map') - except TypeError: - pass + except TypeError: pass f1_236 = f1.add_subplot(236) currfig = f1_236.imshow(self.rawPatchMap, vmin=0, vmax=1, cmap='jet', interpolation='nearest') f1.colorbar(currfig) plt.axis('off') f1_236.set_title('raw patchmap') - # plot figure 2 - f2 = plt.figure(figsize=(10, 8)) + + #plot figure 2 + f2 = plt.figure(figsize=(10,8)) f2.suptitle(trialName) f2_221 = f2.add_subplot(221) - for key, value in self.rawPatches.iteritems(): - currfig = f2_221.imshow(self.altPosMapf * value.getMask(), cmap='jet', vmin=-40, vmax=60, - interpolation='nearest') + for key, value in self.rawPatches.items(): + currfig = f2_221.imshow(self.altPosMapf * value.getMask(), vmin=-40, vmax=60, interpolation='nearest') f2.colorbar(currfig) plt.tick_params( - axis='both', # changes apply to the x-axis - which='both', # both major and minor ticks are affected - bottom='off', # ticks along the bottom edge are off - top='off', # ticks along the top edge are off - left='off', - right='off', - labelbottom='off', - labelleft='off') + axis='both', # changes apply to the x-axis + which='both', # both major and minor ticks are affected + bottom='off', # ticks along the bottom edge are off + top='off', # ticks along the top edge are off + left='off', + right='off', + labelbottom='off', + labelleft='off') f2_221.set_title('patches with altitude postion') f2_222 = f2.add_subplot(222) - for key, value in self.rawPatches.iteritems(): - currfig = f2_222.imshow(self.aziPosMapf * value.getMask(), cmap='jet', vmin=-10, vmax=120, - interpolation='nearest') + for key, value in self.rawPatches.items(): + currfig = f2_222.imshow(self.aziPosMapf * value.getMask(), vmin=-10, vmax=120, interpolation='nearest') f2.colorbar(currfig) plt.tick_params( - axis='both', # changes apply to the x-axis - which='both', # both major and minor ticks are affected - bottom='off', # ticks along the bottom edge are off - top='off', # ticks along the top edge are off - left='off', - right='off', - labelbottom='off', - labelleft='off') + axis='both', # changes apply to the x-axis + which='both', # both major and minor ticks are affected + bottom='off', # ticks along the bottom edge are off + top='off', # ticks along the top edge are off + left='off', + right='off', + labelbottom='off', + labelleft='off') f2_222.set_title('patches with azimuth postion') f2_223 = f2.add_subplot(223) try: - f2_223.imshow(self.vasculatureMap, cmap='gray', interpolation='nearest') + f2_223.imshow(self.vasculatureMap, cmap = 'gray', interpolation = 'nearest') except: pass - h = plotPatches(self.rawPatches, plotaxis=f2_223, zoom=zoom) - f2.colorbar(h[h.keys()[0]]) + h = plotPatches(self.rawPatches, plotaxis = f2_223, zoom = zoom) + f2.colorbar(h[list(h.keys())[0]]) plt.axis('off') plt.title('patches with center and sign') + f2_224 = f2.add_subplot(224) - currfig = f2_224.imshow(self.eccentricityMapf, cmap='jet', interpolation='nearest') + currfig = f2_224.imshow(self.eccentricityMapf, interpolation='nearest') plt.tick_params( - axis='both', # changes apply to the x-axis - which='both', # both major and minor ticks are affected - bottom='off', # ticks along the bottom edge are off - top='off', # ticks along the top edge are off - left='off', - right='off', - labelbottom='off') + axis='both', # changes apply to the x-axis + which='both', # both major and minor ticks are affected + bottom='off', # ticks along the bottom edge are off + top='off', # ticks along the top edge are off + left='off', + right='off', + labelbottom='off') f2_224.set_title('patches with patch eccentricity') f2.colorbar(currfig) - # plot figure 3 - f3 = plt.figure(figsize=(18, 7)) + #plot figure 3 + f3=plt.figure(figsize=(18,7)) f3.suptitle(trialName) f3_131 = f3.add_subplot(131) try: - f3_131.imshow(self.vasculatureMap, cmap='gray', interpolation='nearest') + f3_131.imshow(self.vasculatureMap, cmap = 'gray', interpolation = 'nearest') except: pass - h = plotPatches(self.rawPatches, plotaxis=f3_131, zoom=zoom) + h = plotPatches(self.rawPatches, plotaxis = f3_131, zoom = zoom) f3_131.set_axis_off() f3_131.set_title('original patches') f3_132 = f3.add_subplot(132) try: - f3_132.imshow(self.vasculatureMap, cmap='gray', interpolation='nearest') + f3_132.imshow(self.vasculatureMap, cmap = 'gray', interpolation = 'nearest') except: pass - h = plotPatches(self.patchesAfterSplit, plotaxis=f3_132, zoom=zoom) + h = plotPatches(self.patchesAfterSplit, plotaxis = f3_132, zoom = zoom) f3_132.set_axis_off() f3_132.set_title('patches after split') f3_133 = f3.add_subplot(133) try: - f3_133.imshow(self.vasculatureMap, cmap='gray', interpolation='nearest') + f3_133.imshow(self.vasculatureMap, cmap = 'gray', interpolation = 'nearest') except: pass - h = plotPatches(self.patchesAfterMerge, plotaxis=f3_133, zoom=zoom) + h = plotPatches(self.patchesAfterMerge, plotaxis = f3_133, zoom = zoom) f3_133.set_axis_off() f3_133.set_title('patches after merge') if isSave: - f1.savefig(os.path.join(saveFolder, trialName + '_SignMap.pdf'), format='pdf', dpi=600, - orientation='landscape', papertype='a4') - f2.savefig(os.path.join(saveFolder, trialName + '_RawPatches.pdf'), format='pdf', dpi=600, - orientation='landscape', papertype='a4') - f3.savefig(os.path.join(saveFolder, trialName + '_SplitMerge.pdf'), format='pdf', dpi=600, - orientation='landscape', papertype='a4') + f1.savefig(os.path.join(saveFolder,trialName+'_SignMap.pdf'), format='pdf', dpi = 600, orientation='landscape', papertype='a4') + f2.savefig(os.path.join(saveFolder,trialName+'_RawPatches.pdf'), format='pdf', dpi = 600, orientation='landscape', papertype='a4') + f3.savefig(os.path.join(saveFolder,trialName+'_SplitMerge.pdf'), format='pdf', dpi = 600, orientation='landscape', papertype='a4') + + + def plotFinalPatches(self,plotAxis = None): - def plotFinalPatches(self, plotAxis=None): if not hasattr(self, 'finalPatches'): self.processTrial() @@ -2779,121 +2729,99 @@ def plotFinalPatches(self, plotAxis=None): name = self.getName() if not plotAxis: - f = plt.figure(figsize=(10, 10)) - plotAxis = f.add_subplot(111) + f=plt.figure(figsize=(10,10)) + plotAxis=f.add_subplot(111) try: - plotAxis.imshow(self.vasculatureMap, cmap='gray', interpolation='nearest') + plotAxis.imshow(self.vasculatureMap, cmap = 'gray', interpolation = 'nearest') except: pass - h = plotPatches(self.finalPatches, plotaxis=plotAxis, zoom=zoom) + h = plotPatches(self.finalPatches, plotaxis = plotAxis, zoom = zoom) plotAxis.set_axis_off() plotAxis.set_title(name) - def plotFinalPatchBorders(self, plotAxis=None, plotName=True, plotVasMap=True, isTitle=True, isColor=True, - borderWidth=2, fontSize=15, interpolation='bilinear'): - if hasattr(self, 'finalPatchesMarked'): - finalPatches = self.finalPatchesMarked - elif hasattr(self, 'finalPatches'): - finalPatches = self.finalPatches - else: - self.processTrial();finalPatches = self.finalPatches + def plotFinalPatchBorders(self,plotAxis = None,plotName=True,plotVasMap=True,isTitle=True,isColor=True, + borderWidth=2,fontSize=15,interpolation='bilinear'): - try: - zoom = self.vasculatureMap.shape[0] / self.altPosMap.shape[0] - except AttributeError: - zoom = 1 + if hasattr(self,'finalPatchesMarked'):finalPatches=self.finalPatchesMarked + elif hasattr(self, 'finalPatches'):finalPatches=self.finalPatches + else:self.processTrial();finalPatches=self.finalPatches + + try:zoom = self.vasculatureMap.shape[0] / self.altPosMap.shape[0] + except AttributeError:zoom = 1 name = self.getName() if not plotAxis: - f = plt.figure(figsize=(10, 10)) - plotAxis = f.add_subplot(111) + f=plt.figure(figsize=(10,10)) + plotAxis=f.add_subplot(111) if plotVasMap: - try: - plotAxis.imshow(self.vasculatureMap, cmap='gray', interpolation='nearest') - except AttributeError: - pass + try:plotAxis.imshow(self.vasculatureMap, cmap = 'gray', interpolation = 'nearest') + except AttributeError:pass - for key, patch in finalPatches.iteritems(): + for key, patch in finalPatches.items(): mask = patch.getMask() if isColor: - if patch.sign == 1: - plotColor = '#ff0000' - elif patch.sign == -1: - plotColor = '#0000ff' - else: - plotColor = '#000000' - else: - plotColor = '#000000' + if patch.sign == 1:plotColor='#ff0000' + elif patch.sign == -1:plotColor='#0000ff' + else:plotColor='#000000' + else:plotColor='#000000' im = pt.plot_mask(mask, plotAxis=plotAxis, color=plotColor, zoom=zoom, borderWidth=borderWidth) im.set_interpolation(interpolation) if plotName: - center = patch.getCenter() - plotAxis.text(center[1] * zoom, center[0] * zoom, key, verticalalignment='center', - horizontalalignment='center', color=plotColor, fontsize=fontSize) + center=patch.getCenter() + plotAxis.text(center[1]*zoom,center[0]*zoom,key,verticalalignment='center', horizontalalignment='center',color=plotColor,fontsize=fontSize) plotAxis.set_axis_off() - if isTitle: plotAxis.set_title(name) + if isTitle:plotAxis.set_title(name) return plotAxis.get_figure() + def plotFinalPatchBorders2(self, plotAxis=None, plotName=True, plotVasMap=True, isTitle=True, isColor=True, positiveColor='#ff0000', negativeColor='#0000ff', borderWidth=2, fontSize=15): - if hasattr(self, 'finalPatchesMarked'): - finalPatches = self.finalPatchesMarked - elif hasattr(self, 'finalPatches'): - finalPatches = self.finalPatches - else: - self.processTrial();finalPatches = self.finalPatches + if hasattr(self,'finalPatchesMarked'):finalPatches=self.finalPatchesMarked + elif hasattr(self, 'finalPatches'):finalPatches=self.finalPatches + else:self.processTrial();finalPatches=self.finalPatches - try: - zoom = self.vasculatureMap.shape[0] / self.altPosMap.shape[0] - except AttributeError: - zoom = 1 + try:zoom = self.vasculatureMap.shape[0] / self.altPosMap.shape[0] + except AttributeError:zoom = 1 name = self.getName() if not plotAxis: - f = plt.figure(figsize=(10, 10)) - plotAxis = f.add_subplot(111) + f=plt.figure(figsize=(10,10)) + plotAxis=f.add_subplot(111) if (plotVasMap) and (self.vasculatureMap is not None): - try: - plotAxis.imshow(self.vasculatureMap, cmap='gray', interpolation='nearest') - except AttributeError: - plotAxis.invert_yaxis();pass - else: - plotAxis.invert_yaxis() + try:plotAxis.imshow(self.vasculatureMap, cmap = 'gray', interpolation = 'nearest') + except AttributeError: plotAxis.invert_yaxis();pass + else: plotAxis.invert_yaxis() - for key, patch in finalPatches.iteritems(): + for key, patch in finalPatches.items(): if isColor: - if patch.sign == 1: - plotColor = positiveColor - elif patch.sign == -1: - plotColor = negativeColor - else: - plotColor = '#000000' - else: - plotColor = '#000000' + if patch.sign == 1:plotColor=positiveColor + elif patch.sign == -1:plotColor=negativeColor + else:plotColor='#000000' + else:plotColor='#000000' - currArray = ni.binary_erosion(patch.array, iterations=1) + currArray = ni.binary_erosion(patch.array,iterations=1) im = pt.plot_mask_borders(currArray, plotAxis=plotAxis, color=plotColor, zoom=zoom, borderWidth=borderWidth) if plotName: - center = patch.getCenter() - plotAxis.text(center[1] * zoom, center[0] * zoom, key, verticalalignment='center', - horizontalalignment='center', color=plotColor, fontsize=fontSize) + center=patch.getCenter() + plotAxis.text(center[1]*zoom,center[0]*zoom,key,verticalalignment='center', horizontalalignment='center',color=plotColor,fontsize=fontSize) plotAxis.set_axis_off() - if isTitle: plotAxis.set_title(name) + if isTitle:plotAxis.set_title(name) return plotAxis.get_figure() + def getBaselineFluorscence(self): ''' get mean baseline fluorescence of each visual area @@ -2906,42 +2834,44 @@ def getBaselineFluorscence(self): vasMap = ia.array_nor(self.vasculatureMap) - # get V1 mean fluorscence + #get V1 mean fluorscence try: - V1 = finalPatches['V1'] + V1 = finalPatches['V1'] except KeyError: - V1 = finalPatches['patch01'] + V1 = finalPatches['patch01'] V1array = V1.array - zoom = vasMap.shape[-1] / V1array.shape[-1] + zoom = vasMap.shape[-1]/V1array.shape[-1] - if zoom != 1: - V1array = ni.zoom(V1array, zoom) + if zoom!=1: + V1array = ni.zoom(V1array,zoom) V1array = ia.binarize(V1array, 0.5) V1area = np.sum(V1array).astype(np.float) - V1totalF = np.sum(V1array * vasMap).astype(np.float) - V1meanF = V1totalF / V1area + V1totalF = np.sum(V1array*vasMap).astype(np.float) + V1meanF = V1totalF/V1area - # get fluorscence for all visual areas normalized by V1 + #get fluorscence for all visual areas normalized by V1 baselineDict = {} - for key, patch in finalPatches.iteritems(): + for key, patch in finalPatches.items(): array = patch.array - if zoom != 1: - array = ni.zoom(array, zoom) + if zoom!=1: + array = ni.zoom(array,zoom) array = ia.binarize(array, 0.5) area = np.sum(array).astype(np.float) - totalF = np.sum(array * vasMap).astype(np.float) + totalF = np.sum(array*vasMap).astype(np.float) + + meanFnor = (totalF/area)/V1meanF - meanFnor = (totalF / area) / V1meanF + baselineDict.update({key:meanFnor}) - baselineDict.update({key: meanFnor}) return baselineDict + def getMeanPowerAmplitude(self): ''' get mean response power amplitude of each visual area @@ -2953,39 +2883,40 @@ def getMeanPowerAmplitude(self): finalPatches = self.finalPatches try: - powerMap = ia.array_nor(np.mean([self.altPowerMapf, self.aziPowerMapf], axis=0)) + powerMap=ia.array_nor(np.mean([self.altPowerMapf, self.aziPowerMapf], axis=0)) except AttributeError: - _ = self._getSignMap() - powerMap = ia.array_nor(np.mean([self.altPowerMapf, self.aziPowerMapf], axis=0)) + _=self._getSignMap() + powerMap=ia.array_nor(np.mean([self.altPowerMapf, self.aziPowerMapf], axis=0)) - # get V1 mean fluorscence + #get V1 mean fluorscence try: - V1 = finalPatches['V1'] + V1 = finalPatches['V1'] except KeyError: - V1 = finalPatches['patch01'] + V1 = finalPatches['patch01'] V1array = V1.array V1area = np.sum(V1array).astype(np.float) - V1totalPower = np.sum(V1array * powerMap).astype(np.float) - V1meanPower = V1totalPower / V1area + V1totalPower = np.sum(V1array*powerMap).astype(np.float) + V1meanPower = V1totalPower/V1area - # get mean power amplitude for all visual areas normalized by V1 + #get mean power amplitude for all visual areas normalized by V1 meanPowerDict = {} - for key, patch in finalPatches.iteritems(): + for key, patch in finalPatches.items(): array = patch.array area = np.sum(array).astype(np.float) - totalPower = np.sum(array * powerMap).astype(np.float) + totalPower = np.sum(array*powerMap).astype(np.float) - meanPowerNor = (totalPower / area) / V1meanPower + meanPowerNor = (totalPower/area)/V1meanPower - meanPowerDict.update({key: meanPowerNor}) + meanPowerDict.update({key:meanPowerNor}) return meanPowerDict - def getCorticalArea(self, pixelSize=0.0129): + + def getCorticalArea(self,pixelSize = 0.0129): ''' get area of each visual area (mm^2) unit of pixelSize is mm @@ -2996,25 +2927,27 @@ def getCorticalArea(self, pixelSize=0.0129): except AttributeError: finalPatches = self.finalPatches - # get mean power amplitude for all visual areas normalized by V1 + #get mean power amplitude for all visual areas normalized by V1 areaDict = {} - for key, patch in finalPatches.iteritems(): - area = patch.getArea().astype(np.float) * (pixelSize ** 2) + for key, patch in finalPatches.items(): + + area = patch.getArea().astype(np.float)*(pixelSize**2) - areaDict.update({key: area}) + areaDict.update({key:area}) return areaDict - def getMagnification(self, pixelSize=0.0129, isFilter=False, erodeIter=None, ): + + def getMagnification(self,pixelSize = 0.0129, isFilter = False,erodeIter = None,): ''' get magnification of each visual area (mm^2/deg^2) unit of pixelSize is mm ''' - if not hasattr(self, 'determinantMap'): + if not hasattr(self,'determinantMap'): _ = self._getDeterminantMap() - if hasattr(self, 'finalPathesMarked'): + if hasattr(self,'finalPathesMarked'): finalPatches = self.finalPatchesMarked elif hasattr(self, 'finalPatches'): finalPatches = self.finalPatches @@ -3022,27 +2955,28 @@ def getMagnification(self, pixelSize=0.0129, isFilter=False, erodeIter=None, ): self.processTrial() finalPatches = self.finalPatches - magMap = 1 / self.determinantMap + magMap = 1/self.determinantMap if isFilter: - magMap = ni.filters.gaussian_filter(magMap, self.params['signMapFilterSigma']) + magMap = ni.filters.gaussian_filter(magMap,self.params['signMapFilterSigma']) - # get mean power amplitude for all visual areas normalized by V1 + #get mean power amplitude for all visual areas normalized by V1 magDict = {} - for key, patch in finalPatches.iteritems(): + for key, patch in finalPatches.items(): array = patch.array.astype(np.float) if erodeIter: - array = ni.binary_erosion(array, iterations=erodeIter) + array = ni.binary_erosion(array,iterations=erodeIter) area = np.sum(array) - totalMag = np.sum(array * magMap) + totalMag = np.sum(array*magMap) - magDict.update({key: (pixelSize ** 2) * totalMag / area}) + magDict.update({key:(pixelSize**2)*totalMag/area}) return magDict + def getVisualFieldOrigin(self): ''' get the visual field origin as the retinotopic coordinates at the pixels @@ -3052,49 +2986,50 @@ def getVisualFieldOrigin(self): mean retinotopic locations of all overlap pixels ''' - if not hasattr(self, 'finalPatchesMarked'): - raise LookupError, 'Please mark the final patches first!!' + if not hasattr(self,'finalPatchesMarked'): + raise LookupError('Please mark the final patches first!!') - if not hasattr(self, 'altPosMapf'): - _ = self._getSignMap() + if not hasattr(self,'altPosMapf'): + _=self._getSignMap() try: V1 = self.finalPatchesMarked['V1'].array.astype(np.float) LM = self.finalPatchesMarked['LM'].array.astype(np.float) RL = self.finalPatchesMarked['RL'].array.astype(np.float) - overlap = 0 # number of overlaping pixels - iterNum = 1 # number of iteration - while overlap < 1: - # print 'Iteration number for finding overlapping pixel:', iterNum - V1 = ni.morphology.binary_dilation(V1, iterations=1).astype(np.float) - LM = ni.morphology.binary_dilation(LM, iterations=1).astype(np.float) - RL = ni.morphology.binary_dilation(RL, iterations=1).astype(np.float) - totalField = V1 + LM + RL - # plt.imshow(totalField) - overlap = len(np.argwhere(totalField == 3)) + overlap=0 #number of overlaping pixels + iterNum = 1 #number of iteration + while overlap<1: + # print 'Iteration number for finding overlapping pixel:', iterNum + V1=ni.morphology.binary_dilation(V1,iterations=1).astype(np.float) + LM=ni.morphology.binary_dilation(LM,iterations=1).astype(np.float) + RL=ni.morphology.binary_dilation(RL,iterations=1).astype(np.float) + totalField = V1+LM+RL + # plt.imshow(totalField) + overlap = len(np.argwhere(totalField==3)) iterNum += 1 - # print 'Number of overlapping pixels:', overlap - # plt.show() + # print 'Number of overlapping pixels:', overlap + # plt.show() - altPosOrigin = np.mean(self.altPosMapf[totalField == 3], axis=0) - aziPosOrigin = np.mean(self.aziPosMapf[totalField == 3], axis=0) + altPosOrigin = np.mean(self.altPosMapf[totalField==3],axis=0) + aziPosOrigin = np.mean(self.aziPosMapf[totalField==3],axis=0) except KeyError: - print 'Can not find necessary visual areas (V1, LM, RL) for normalization. \nSetting origins to 0 ...' + print('Can not find necessary visual areas (V1, LM, RL) for normalization. \nSetting origins to 0 ...') altPosOrigin = 0. aziPosOrigin = 0. return altPosOrigin, aziPosOrigin - def plotMagnificationMap(self, pixelSize=0.0129, plotAxis=None, isFilter=False): + + def plotMagnificationMap(self,pixelSize = 0.0129,plotAxis = None,isFilter = False): ''' param pixelSize: mm ''' - if not hasattr(self, 'determinantMap'): + if not hasattr(self,'determinantMap'): _ = self._getDeterminantMap() - if hasattr(self, 'finalPathesMarked'): + if hasattr(self,'finalPathesMarked'): finalPatches = self.finalPatchesMarked elif hasattr(self, 'finalPatches'): finalPatches = self.finalPatches @@ -3103,24 +3038,25 @@ def plotMagnificationMap(self, pixelSize=0.0129, plotAxis=None, isFilter=False): finalPatches = self.finalPatches name = self.getName() - magMap = (pixelSize ** 2) / self.determinantMap + magMap = (pixelSize**2)/self.determinantMap if isFilter: - magMap = ni.filters.gaussian_filter(magMap, self.params['signMapFilterSigma']) + magMap = ni.filters.gaussian_filter(magMap,self.params['signMapFilterSigma']) if not plotAxis: - f = plt.figure(figsize=(10, 10)) + f=plt.figure(figsize=(10,10)) ax = f.add_subplot(111) else: ax = plotAxis - for key, patch in finalPatches.iteritems(): - currMagMap = patch.getMask() * magMap - ax.imshow(currMagMap, cmap='hot_r', vmin=0, vmax=0.015, interpolation='nearest') + for key, patch in finalPatches.items(): + currMagMap = patch.getMask()*magMap + ax.imshow(currMagMap,cmap='hot_r',vmin = 0, vmax = 0.015,interpolation='nearest') ax.set_aspect(1) ax.set_title(name) + def _generateTotalMask(self): ''' generate a single mask (0s and 1s) of the entire visual areas @@ -3128,17 +3064,18 @@ def _generateTotalMask(self): mask = np.zeros(self.altPosMap.shape) - for patch in self.finalPatches.itervalues(): + for patch in self.finalPatches.values(): mask = mask + patch.array.astype(np.float) mask = ni.binary_closing(mask, - structure=np.array([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]), - iterations=self.params['borderWidth']) + structure = np.array([[1.,1.,1.],[1.,1.,1.],[1.,1.,1.]]), + iterations = self.params['borderWidth']) + return mask.astype(np.int8) - def plotRetinotopicLocation(self, plotAxis=None, location=(0., 50.), color='#ff0000', searchRange=3., borderWidth=1, - closeIter=3, openIter=3): + + def plotRetinotopicLocation(self,plotAxis=None,location=(0.,50.),color='#ff0000',searchRange=3.,borderWidth=1,closeIter=3,openIter=3): if not plotAxis: f = plt.figure() @@ -3147,45 +3084,47 @@ def plotRetinotopicLocation(self, plotAxis=None, location=(0., 50.), color='#ff0 altPosMap = self.altPosMapf aziPosMap = self.aziPosMapf - altMin = location[0] - np.abs(searchRange) - altMax = location[0] + np.abs(searchRange) + altMin = location[0]-np.abs(searchRange) + altMax = location[0]+np.abs(searchRange) - aziMin = location[1] - np.abs(searchRange) - aziMax = location[1] + np.abs(searchRange) + aziMin = location[1]-np.abs(searchRange) + aziMax = location[1]+np.abs(searchRange) - altMask = np.logical_and(altPosMap >= altMin, altPosMap <= altMax) - aziMask = np.logical_and(aziPosMap >= aziMin, aziPosMap <= aziMax) + altMask = np.logical_and(altPosMap>=altMin,altPosMap<=altMax) + aziMask = np.logical_and(aziPosMap>=aziMin,aziPosMap<=aziMax) - mask = np.logical_and(altMask, aziMask).astype(np.float) + mask = np.logical_and(altMask,aziMask).astype(np.float) totalMask = self._generateTotalMask() - mask = (mask * totalMask).astype(np.int) + mask = (mask*totalMask).astype(np.int) - mask = ni.binary_closing(mask, iterations=closeIter) + mask = ni.binary_closing(mask,iterations=closeIter) - mask = ni.binary_opening(mask, iterations=openIter) + mask = ni.binary_opening(mask,iterations=openIter) - mask = mask.astype(np.float) - mask[mask == 0] = np.nan + mask=mask.astype(np.float) + mask[mask==0]=np.nan pt.plot_mask(mask, plotAxis=plotAxis, color=color, borderWidth=borderWidth) - def plotPatchesWithName(self, patchDict, plotAxis=None): - if not hasattr(self, - patchDict): raise LookupError, 'This RetinotopicMappingTrial object does not have "' + patchDict + '" attribute!' + def plotPatchesWithName(self,patchDict,plotAxis=None): + + if not hasattr(self,patchDict): raise LookupError('This RetinotopicMappingTrial object does not have "' + patchDict + '" attribute!') patchesForPlotting = self.__dict__[patchDict] if plotAxis is None: f = plt.figure(); plotAxis = f.add_subplot(111) plotAxis.figure.suptitle(self.getName()) - plotPatches(patchesForPlotting, plotaxis=plotAxis, markersize=0) + plotPatches(patchesForPlotting,plotaxis=plotAxis,markersize=0) + + for key,patch in patchesForPlotting.items(): - for key, patch in patchesForPlotting.iteritems(): center = patch.getCenter() - plotAxis.text(center[1], center[0], key, verticalalignment='center', horizontalalignment='center') + plotAxis.text(center[1],center[0],key,verticalalignment='center', horizontalalignment='center') return plotAxis.figure + def plotVisualCoverage(self, is_normalize=False, altRange=(-40., 60.), aziRange=(-20., 100.)): ''' plot the visual coverage of each visual area in a compact way @@ -3193,9 +3132,9 @@ def plotVisualCoverage(self, is_normalize=False, altRange=(-40., 60.), aziRange= if is_normalize is True, the retinotopy will correct for visual origin ''' - if hasattr(self, 'finalPatchesMarked'): + if hasattr(self,'finalPatchesMarked'): finalPatches = self.finalPatchesMarked - elif hasattr(self, 'finalPatches'): + elif hasattr(self,'finalPatches'): finalPatches = self.finalPatches else: self.processTrial() @@ -3206,23 +3145,23 @@ def plotVisualCoverage(self, is_normalize=False, altRange=(-40., 60.), aziRange= else: visualFieldOrigin = None - figList, axList = pt.grid_axis(3, 4, len(finalPatches.keys()), figsize=(12, 10)) + figList, axList = pt.grid_axis(3, 4, len(list(finalPatches.keys())), figsize=(12, 10)) i = 0 pixelSize = self.params['visualSpacePixelSize'] closeIter = self.params['visualSpaceCloseIter'] - for key, patch in finalPatches.iteritems(): + for key, patch in finalPatches.items(): currAx = axList[i] - visualSpace, altAxis, aziAxis = patch.getVisualSpace(self.altPosMapf, - self.aziPosMapf, - altRange=altRange, - aziRange=aziRange, - visualFieldOrigin=visualFieldOrigin, - pixelSize=pixelSize, - closeIter=closeIter, - isplot=False) + visualSpace, altAxis, aziAxis=patch.getVisualSpace(self.altPosMapf, + self.aziPosMapf, + altRange=altRange, + aziRange=aziRange, + visualFieldOrigin=visualFieldOrigin, + pixelSize = pixelSize, + closeIter = closeIter, + isplot = False) if patch.sign == 1: plotColor = '#ff0000' @@ -3241,26 +3180,27 @@ def plotVisualCoverage(self, is_normalize=False, altRange=(-40., 60.), aziRange= currAx.set_title(key) - i = i + 1 + i=i+1 return figList, axList + def plotContours(self, - isNormalize=True, # is resetting the origin of visual field - altLevels=np.arange(-30., 50., 5.), - aziLevels=np.arange(0., 120., 5.), + isNormalize = True, #is resetting the origin of visual field + altLevels = np.arange(-30.,50.,5.), + aziLevels = np.arange(0.,120.,5.), isPlottingBorder=True, - inline=False, - lineWidth=3, - figSize=(12, 12), - fontSize=15, - altAxis=None, - aziAxis=None): + inline = False, + lineWidth = 3, + figSize = (12,12), + fontSize = 15, + altAxis = None, + aziAxis = None): ''' plot contours of altitute posititon and azimuth position ''' - if not hasattr(self, 'altPosMapf'): + if not hasattr(self,'altPosMapf'): self._getSignMap() altPosMap = self.altPosMapf @@ -3271,24 +3211,25 @@ def plotContours(self, altPosMap = altPosMap - altPosOrigin aziPosMap = aziPosMap - aziPosOrigin - if hasattr(self, 'vasculatureMap') and type(self.vasculatureMap) != type(None) and isPlottingBorder: - zoom = self.vasculatureMap.shape[0] / altPosMap.shape[0] - altPosMap = ni.zoom(altPosMap, zoom) - aziPosMap = ni.zoom(aziPosMap, zoom) - totalMask = ni.zoom(self._generateTotalMask().astype(np.float32), zoom) - altPosMap[totalMask < 0.5] = np.nan - aziPosMap[totalMask < 0.5] = np.nan + if hasattr(self,'vasculatureMap') and type(self.vasculatureMap)!=type(None) and isPlottingBorder: + zoom = self.vasculatureMap.shape[0]/altPosMap.shape[0] + altPosMap = ni.zoom(altPosMap,zoom) + aziPosMap = ni.zoom(aziPosMap,zoom) + totalMask = ni.zoom(self._generateTotalMask().astype(np.float32),zoom) + altPosMap[totalMask<0.5]=np.nan + aziPosMap[totalMask<0.5]=np.nan else: totalMask = self._generateTotalMask() - altPosMap[totalMask == 0] = np.nan - aziPosMap[totalMask == 0] = np.nan + altPosMap[totalMask==0]=np.nan + aziPosMap[totalMask==0]=np.nan + + X,Y = np.meshgrid(np.arange(altPosMap.shape[1]), + np.arange(altPosMap.shape[0])) - X, Y = np.meshgrid(np.arange(altPosMap.shape[1]), - np.arange(altPosMap.shape[0])) # plotting altitute contours if not altAxis: - altf = plt.figure(figsize=figSize, facecolor='#ffffff') + altf=plt.figure(figsize=figSize,facecolor='#ffffff') altAxis = altf.add_subplot(111) altContour = altAxis.contour(X, @@ -3299,7 +3240,7 @@ def plotContours(self, linewidths=lineWidth) if inline: - altContour.clabel(inline=inline, fontsize=fontSize, fmt='%1.1f') + altContour.clabel(inline=inline, fontsize=fontSize,fmt='%1.1f') else: altAxis.get_figure().colorbar(altContour) @@ -3313,9 +3254,10 @@ def plotContours(self, altAxis.set_title('Altitute Positions') + # plotting azimuth contours if not aziAxis: - azif = plt.figure(figsize=figSize, facecolor='#ffffff') + azif=plt.figure(figsize=figSize,facecolor='#ffffff') aziAxis = azif.add_subplot(111) aziContour = aziAxis.contour(X, @@ -3325,7 +3267,7 @@ def plotContours(self, levels=aziLevels, linewidths=lineWidth) if inline: - aziContour.clabel(inline=1, fontsize=fontSize, fmt='%1.1f') + aziContour.clabel(inline=1, fontsize=fontSize,fmt='%1.1f') else: aziAxis.get_figure().colorbar(aziContour) @@ -3337,6 +3279,7 @@ def plotContours(self, borderWidth=lineWidth, interpolation='bilinear') + aziAxis.set_title('Azimuth Positions') return altAxis, aziAxis @@ -3344,20 +3287,17 @@ def plotContours(self, class Patch(object): - def __init__(self, patchArray, sign): + def __init__(self,patchArray,sign): - if isinstance(patchArray, sparse.coo_matrix): - self.sparseArray = patchArray.astype(np.uint8) + if isinstance(patchArray,sparse.coo_matrix):self.sparseArray=patchArray.astype(np.uint8) else: arr = patchArray.astype(np.int8) arr[arr > 0] = 1 arr[arr == 0] = 0 self.sparseArray = sparse.coo_matrix(arr) - if sign == 1 or sign == 0 or sign == -1: - self.sign = int(sign) - else: - raise ValueError, 'Sign should be -1, 0 or 1!' + if sign==1 or sign==0 or sign==-1: self.sign = int(sign) + else: raise ValueError('Sign should be -1, 0 or 1!') @property def array(self): @@ -3369,7 +3309,7 @@ def getCenter(self): [rowIndex, columnIndex] ''' pixels = np.argwhere(self.array) - center = np.mean(pixels.astype(np.float32), axis=0) + center = np.mean(pixels.astype(np.float32), axis = 0) return np.round(center).astype(np.int) def getArea(self): @@ -3382,7 +3322,7 @@ def getMask(self): ''' generating ploting mask for the patch ''' - mask = np.array(self.array, dtype=np.float32) + mask = np.array(self.array, dtype = np.float32) mask[mask == 0] = np.nan return mask @@ -3390,29 +3330,29 @@ def getSignedMask(self): ''' generating ploting mask with visual sign for the patch ''' - signedMask = np.array(self.array * self.sign, dtype=np.float32) + signedMask = np.array(self.array * self.sign, dtype = np.float32) signedMask[signedMask == 0] = np.nan return signedMask def getDict(self): - return {'sparseArray': self.sparseArray, 'sign': self.sign} + return {'sparseArray':self.sparseArray,'sign':self.sign} - def getTrace(self, mov): + def getTrace(self,mov): ''' return trace of this patch in a certain movie ''' return ia.get_trace(mov, self.array) - def isTouching(self, patch2, distance=1): + def isTouching(self, patch2, distance = 1): ''' decide if this patch is adjacent to another patch within certain distance ''' if distance < 1: - raise LookupError, 'distance should be integer no less than 1.' + raise LookupError('distance should be integer no less than 1.') bigPatch = ni.binary_dilation(self.array, - iterations=distance).astype(np.int) + iterations = distance).astype(np.int) if np.amax(bigPatch + patch2.array) > 1: return True @@ -3452,8 +3392,7 @@ def getVisualSpace(self, altMap, aziMap, altRange=(-40., 60.), aziRange=(-20., 1 if patchArray[i, j]: corAlt = altMap[i, j] corAzi = aziMap[i, j] - if (corAlt >= altRange[0]) & (corAlt <= altRange[1]) & (corAzi >= aziRange[0]) & ( - corAzi <= aziRange[1]): + if (corAlt >= altRange[0]) & (corAlt <= altRange[1]) & (corAzi >= aziRange[0]) & (corAzi <= aziRange[1]): indAlt = (corAlt - altRange[0]) // pixelSize indAzi = (corAzi - aziRange[0]) // pixelSize visualSpace[int(indAlt), int(indAzi)] = 1 @@ -3508,25 +3447,25 @@ def eccentricityMap(self, altMap, aziMap, altCenter, aziCenter): aziCenter2 = aziCenter * np.pi / 180 eccMap = np.zeros(self.array.shape) - # eccMap[:] = np.nan - # for i in xrange(self.array.shape[0]): - # for j in xrange(self.array.shape[1]): - # if self.array[i,j]: - # alt = altMap2[i,j] - # azi = aziMap2[i,j] - # eccMap[i,j] = np.arctan(np.sqrt(np.tan(alt-altCenter2)**2 + ((np.tan(azi-aziCenter2)**2)/(np.cos(alt-altCenter2)**2)))) +# eccMap[:] = np.nan +# for i in xrange(self.array.shape[0]): +# for j in xrange(self.array.shape[1]): +# if self.array[i,j]: +# alt = altMap2[i,j] +# azi = aziMap2[i,j] +# eccMap[i,j] = np.arctan(np.sqrt(np.tan(alt-altCenter2)**2 + ((np.tan(azi-aziCenter2)**2)/(np.cos(alt-altCenter2)**2)))) eccMap = np.arctan( - np.sqrt( - np.square(np.tan(altMap2 - altCenter2)) - + - np.square(np.tan(aziMap2 - aziCenter2)) / np.square(np.cos(altMap2 - altCenter2)) - ) - ) + np.sqrt( + np.square(np.tan(altMap2-altCenter2)) + + + np.square(np.tan(aziMap2-aziCenter2))/np.square(np.cos(altMap2-altCenter2)) + ) + ) eccMap = eccMap * 180 / np.pi - eccMap[self.array == 0] = np.nan + eccMap[self.array==0]=np.nan return eccMap - def split2(self, eccMap, patchName='patch00', cutStep=1, borderWidth=2, isplot=False): + def split2(self, eccMap, patchName = 'patch00', cutStep = 1, borderWidth = 2, isplot = False): ''' split this patch into two or more patch, according to the eccentricity map (in degree). return a dictionary of patches after split @@ -3535,56 +3474,60 @@ def split2(self, eccMap, patchName='patch00', cutStep=1, borderWidth=2, isplot=F ''' minMarker = localMin(eccMap, cutStep) - connectivity = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]]) + connectivity=np.array([[1,1,1],[1,1,1],[1,1,1]]) - newLabel = sm.watershed(eccMap, minMarker, connectivity=connectivity, mask=self.array) + newLabel = sm.watershed(eccMap, minMarker, connectivity=connectivity, mask = self.array) border = ni.binary_dilation(self.array).astype(np.int8) - self.array - for i in xrange(1, np.amax(newLabel) + 1): - currArray = np.zeros(self.array.shape, dtype=np.int8) + for i in range(1,np.amax(newLabel)+1): + currArray = np.zeros(self.array.shape, dtype = np.int8) currArray[newLabel == i] = 1 currBorder = ni.binary_dilation(currArray).astype(np.int8) - currArray - border = border + currBorder + border = border+currBorder border[border > 1] = 1 border = sm.skeletonize(border) + if borderWidth > 1: - border = ni.binary_dilation(border, iterations=borderWidth - 1).astype(np.int8) + border = ni.binary_dilation(border, iterations = borderWidth - 1).astype(np.int8) newPatchMap = ni.binary_dilation(self.array).astype(np.int8) * (-1 * (border - 1)) + labeledNewPatchMap, patchNum = ni.label(newPatchMap) - # if patchNum != np.amax(newLabel): - # print 'number of patches: ', patchNum, '; number of local minimum:', np.amax(newLabel) - # raise ValueError, "Number of patches after splitting does not equal to number of local minimum!" + +# if patchNum != np.amax(newLabel): +# print 'number of patches: ', patchNum, '; number of local minimum:', np.amax(newLabel) +# raise ValueError, "Number of patches after splitting does not equal to number of local minimum!" newPatchDict = {} - for j in xrange(1, patchNum + 1): + for j in range(1, patchNum + 1): currPatchName = patchName + '.' + str(j) - currArray = np.zeros(self.array.shape, dtype=np.int8) + currArray = np.zeros(self.array.shape, dtype = np.int8) currArray[labeledNewPatchMap == j] = 1 currArray = currArray * self.array if np.sum(currArray[:]) > 0: - newPatchDict.update({currPatchName: Patch(currArray, self.sign)}) + newPatchDict.update({currPatchName : Patch(currArray, self.sign)}) if isplot: plt.figure() plt.subplot(121) - plt.imshow(self.array, cmap='jet', interpolation='nearest') + plt.imshow(self.array, interpolation = 'nearest') plt.title(patchName + ': before split') plt.subplot(122) - plt.imshow(labeledNewPatchMap, cmap='jet', interpolation='nearest') + plt.imshow(labeledNewPatchMap, interpolation = 'nearest') plt.title(patchName + ': after split') + return newPatchDict - def split(self, eccMap, patchName='patch00', cutStep=1, borderWidth=2, isplot=False): + def split(self, eccMap, patchName = 'patch00', cutStep = 1, borderWidth = 2, isplot = False): ''' split this patch into two or more patch, according to the eccentricity map (in degree). return a dictionary of patches after split @@ -3594,7 +3537,7 @@ def split(self, eccMap, patchName='patch00', cutStep=1, borderWidth=2, isplot=Fa minMarker = localMin(eccMap, cutStep) plt.figure() - plt.imshow(minMarker, cmap='jet', vmin=0, interpolation='nearest') + plt.imshow(minMarker, vmin = 0, interpolation='nearest') plt.colorbar() plt.title('markers 1') plt.show() @@ -3602,34 +3545,36 @@ def split(self, eccMap, patchName='patch00', cutStep=1, borderWidth=2, isplot=Fa minMarker = minMarker.astype(np.int32) selfArray = self.array.astype(np.int32) minMarker = minMarker + 1 - minMarker[minMarker == 1] = 0 - minMarker = minMarker + (-1 * (selfArray - 1)) - # minMarker: marker type for opencv watershed, - # sure background = 1 - # unknow = 0 - # sure forgrand = 2,3,4... etc + minMarker[minMarker==1] = 0 + minMarker = minMarker + (-1 * (selfArray-1)) + #minMarker: marker type for opencv watershed, + #sure background = 1 + #unknow = 0 + #sure forgrand = 2,3,4... etc plt.figure() - plt.imshow(minMarker, cmap='jet', vmin=0, interpolation='nearest') + plt.imshow(minMarker, vmin = 0, interpolation='nearest') plt.colorbar() plt.title('markers 2') plt.show() eccMapNor = (np.round(ia.array_nor(eccMap) * 255)).astype(np.uint8) - eccMapRGB = cv2.cvtColor(eccMapNor, cv2.COLOR_GRAY2RGB) - # eccMapRGB: image type for opencv watershed, RGB, [uint8, uint8, uint8] + eccMapRGB = cv2.cvtColor(eccMapNor,cv2.COLOR_GRAY2RGB) + #eccMapRGB: image type for opencv watershed, RGB, [uint8, uint8, uint8] newLabel = cv2.watershed(eccMapRGB, minMarker) plt.figure() - plt.imshow(newLabel, cmap='jet', vmin=0, interpolation='nearest') + plt.imshow(newLabel, vmin = 0, interpolation='nearest') plt.colorbar() plt.title('markers 3') plt.show() newBorder = np.zeros(newLabel.shape).astype(np.int) - newBorder[newLabel == -1] = 1 + newBorder[newLabel==-1]=1 + + border = ni.binary_dilation(self.array).astype(np.int) - self.array @@ -3639,63 +3584,67 @@ def split(self, eccMap, patchName='patch00', cutStep=1, borderWidth=2, isplot=Fa border = sm.skeletonize(border) + if borderWidth > 1: - border = ni.binary_dilation(border, iterations=borderWidth - 1).astype(np.int8) + border = ni.binary_dilation(border, iterations = borderWidth - 1).astype(np.int8) newPatchMap = ni.binary_dilation(self.array).astype(np.int8) * (-1 * (border - 1)) + labeledNewPatchMap, patchNum = ni.label(newPatchMap) - # if patchNum != np.amax(newLabel): - # print 'number of patches: ', patchNum, '; number of local minimum:', np.amax(newLabel) - # raise ValueError, "Number of patches after splitting does not equal to number of local minimum!" + +# if patchNum != np.amax(newLabel): +# print 'number of patches: ', patchNum, '; number of local minimum:', np.amax(newLabel) +# raise ValueError, "Number of patches after splitting does not equal to number of local minimum!" newPatchDict = {} - for j in xrange(1, patchNum + 1): + for j in range(1, patchNum + 1): currPatchName = patchName + '.' + str(j) - currArray = np.zeros(self.array.shape, dtype=np.int8) + currArray = np.zeros(self.array.shape, dtype = np.int8) currArray[labeledNewPatchMap == j] = 1 currArray = currArray * self.array if np.sum(currArray[:]) > 0: - newPatchDict.update({currPatchName: Patch(currArray, self.sign)}) + newPatchDict.update({currPatchName : Patch(currArray, self.sign)}) if isplot: plt.figure() plt.subplot(121) - plt.imshow(self.array, cmap='jet', interpolation='nearest') + plt.imshow(self.array, interpolation = 'nearest') plt.title(patchName + ': before split') plt.subplot(122) - plt.imshow(labeledNewPatchMap, cmap='jet', interpolation='nearest') + plt.imshow(labeledNewPatchMap, interpolation = 'nearest') plt.title(patchName + ': after split') + return newPatchDict - def getBorder(self, borderWidth=2): + def getBorder(self, borderWidth = 2): ''' return boder of this patch with boder width defined by "borderWidth" ''' - patchMap = np.array(self.array, dtype=np.float32) + patchMap = np.array(self.array, dtype = np.float32) - smallPatch = ni.binary_erosion(patchMap, iterations=borderWidth).astype(np.float32) + smallPatch = ni.binary_erosion(patchMap, iterations = borderWidth).astype(np.float32) border = patchMap - smallPatch - border[border == 0] = np.nan + border[border==0] = np.nan return border - def getCorticalPixelForVisualSpaceCenter(self, eccMap): + def getCorticalPixelForVisualSpaceCenter(self,eccMap): ''' return the coordinates of the pixel representing the center of the visual space of the patch ''' - eccMap2 = np.array(eccMap).astype(np.float) + eccMap2=np.array(eccMap).astype(np.float) - eccMap2[self.array == 0] = np.nan + eccMap2[self.array==0]=np.nan cor = np.array(np.where(eccMap2 == np.nanmin(eccMap2))).transpose() @@ -3703,185 +3652,192 @@ def getCorticalPixelForVisualSpaceCenter(self, eccMap): if __name__ == "__main__": + plt.ioff() - # testTrial = ft.loadFile(r'\\aibsdata2\nc-ophys\Jun\exampleData\testTrial.pkl') - - # params = {'borderWidth': 1, - # 'closeIter': 3, - # 'dilationIter': 10, - # 'eccMapFilterSigma': 5.0, - # 'mergeOverlapThr': 0.1, - # 'openIter': 3, - # 'phaseMapFilterSigma': 0.8, - # 'signMapFilterSigma': 9.0, - # 'signMapThr': 0.3, - # 'smallPatchThr': 200, - # 'splitLocalMinCutStep': 10., - # 'splitOverlapThr': 1.1, - # 'visualSpaceCloseIter': 15, - # 'visualSpacePixelSize': 0.5} - # - # trial = RetinotopicMappingTrial(mouseID = testTrial['mouseID'], # str, mouseID - # dateRecorded = testTrial['dateRecorded'], # int, date recorded, yearmonthday - # trialNum = testTrial['trialNum'], # str, number of the trail on that day - # mouseType = testTrial['mouseType'], # str, mouse Genotype - # visualStimType = testTrial['visualStimType'], # str, stimulation type - # visualStimBackground = testTrial['visualStimBackground'], # str, background of visual stimulation - # imageExposureTime = testTrial['imageExposureTime'], # exposure time of the recorded image - # altPosMap = testTrial['altPosMap'], # altitute position map - # aziPosMap = testTrial['aziPosMap'], # azimuth position map - # altPowerMap = testTrial['altPowerMap'], # altitude power map - # aziPowerMap = testTrial['aziPowerMap'], # azimuth power map - # vasculatureMap = testTrial['vasculatureMap'], # vasculature map - # params = params, # testTrial['params'], # parameters for imaging analysis - # isAnesthetized = testTrial['isAnesthetized']) - # - # trial.processTrial(isPlot=True) - # trial._getSignMap() - # - # traces = trial.getTraces(moviePath = r'\\aibsdata2\nc-ophys\Jun\exampleData\testMov.tif', isPlot = True) - # trial.generateStandardOutput(traces=traces) - # - # trialDict = trial.generateTrialDict() - # print trialDict.keys() - # - # trial.plotTrial() - # - # altPosMapfNor, aziPosMapfNor, altPowerMapfNor, aziPowerMapfNor, signMapfNor = trial.generateNormalizedMaps(isPlot = True) - # - # - # trial.plotNormalizedPatchCenter() - # - # trial.plotFinalPatches() - # - # - # vasMapNor, finalPatchesNor = trial.normalize(isPlot = True) - - # plt.show() - - # ------------------------------------------------------------------------------------------------ - # testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\20140623_M140174_Trial3.pkl") - # - # testTrial.plotFinalPatchBorders() - # plt.show() - # - # corArea = testTrial.getCorticalArea() - # print '\nCortical area for each visual area (mm^2):' - # for key, item in corArea.iteritems(): - # print key, ':', item - # - # baselineF = testTrial.getBaselineFluorscence() - # print '\nNormalized baseline fluroscence for each visual area:' - # for key, item in baselineF.iteritems(): - # print key, ':', item - # - # power = testTrial.getMeanPowerAmplitude() - # print '\nNormalized power amplitude for each visual area:' - # for key, item in power.iteritems(): - # print key, ':', item - # - # magnification = testTrial.getMagnification() - # print '\nMagnification for each visual area (mm^2/deg^2):' - # for key, item in magnification.iteritems(): - # print key, ':', item - - # altPosOrigin, aziPosOrigin = testTrial.getVisualFieldOrigin() - # print 'Altitude origin:', altPosOrigin - # print 'Azimuth origin:', aziPosOrigin - - # ------------------------------------------------------------------------------------------------- - - # testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\20150130_M160809_Trial1_2_3_4.pkl") - # visualArea = 'LM' - # patch = testTrial.finalPatchesMarked[visualArea] - # - # testTrial._getSignMap() - # - # f=plt.figure() - # ax=f.add_subplot(111) - # ax.imshow(testTrial.altPosMapf,vmin=-30,vmax=50,cmap='hsv',interpolation='nearest') - # pt.plot_mask(patch.getMask(),plotAxis=ax) - # - # VSlist = patch.getVisualSpace(testTrial.altPosMapf,testTrial.aziPosMapf) - # - # VSSpace, VSCoverage, VSAltCenter, VSAziCenter = VSlist - # print 'Altitute center for area '+visualArea+': '+str(VSAltCenter) - # print 'Azimuth center for area '+visualArea+': '+str(VSAziCenter) - # plt.figure() - # plt.imshow(VSSpace,interpolation='nearest') - # plt.title(visualArea) - # plt.show() - # ------------------------------------------------------------------------------------------------ - - # testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\20150116_M156569_Trial1_2_3_4.pkl") - # visualArea = 'V1' - # patch = testTrial.finalPatchesMarked[visualArea] - # - # testTrial._getSignMap() - # - # f=plt.figure() - # ax=f.add_subplot(111) - # ax.imshow(testTrial.altPosMapf,vmin=-30,vmax=50,cmap='hsv',interpolation='nearest') - # pt.plot_mask(patch.getMask(),plotAxis=ax) - # - # plt.show() - - # ---------------------------------------------------------------------------------------------- - - # testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\populationTrial_Ai9330min.pkl") - # testTrial.plotMagnificationMap(isFilter=False) - # plt.show() - - # ---------------------------------------------------------------------------------------------- - # testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\populationTrial_All.pkl") - # totalMask = testTrial._generateTotalMask() - # plt.imshow(totalMask) - # plt.title('total mask of all visual areas') - # plt.show() - - # ---------------------------------------------------------------------------------------------- - # testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\populationTrial_All.pkl") - # - # locationList = [[0.,40.], - # [0.,50.], - # [0.,60.]] - # - # colorList = pt.random_color(len(locationList)) - # - # f=plt.figure(figsize=(12,12)) - # ax=f.add_subplot(111) - # testTrial.plotFinalPatchBorders(plotAxis=ax,plotName=False,isTitle=True) - # - # for i, location in enumerate(locationList): - # testTrial.plotRetinotopicLocation(plotAxis=ax,location=location,color=colorList[i]) - # - # labels = ['alt:'+str(x[0])+'; azi:'+str(x[1]) for x in locationList] - # [ax.plot(None,None,ls='',c=c,label=l) for c,l in zip(colorList,labels)] - # leg = ax.legend(labels,frameon=False) - # for color,text in zip(colorList,leg.get_texts()): - # text.set_color(color) - # - # plt.show() - - # ---------------------------------------------------------------------------------------------- - - # testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\populationTial_Ai93&Ai9630min.pkl") - # testTrial.plotVisualCoverage() - # plt.show() - # - # ---------------------------------------------------------------------------------------------- +# testTrial = ft.loadFile(r'\\aibsdata2\nc-ophys\Jun\exampleData\testTrial.pkl') + +# params = {'borderWidth': 1, +# 'closeIter': 3, +# 'dilationIter': 10, +# 'eccMapFilterSigma': 5.0, +# 'mergeOverlapThr': 0.1, +# 'openIter': 3, +# 'phaseMapFilterSigma': 0.8, +# 'signMapFilterSigma': 9.0, +# 'signMapThr': 0.3, +# 'smallPatchThr': 200, +# 'splitLocalMinCutStep': 10., +# 'splitOverlapThr': 1.1, +# 'visualSpaceCloseIter': 15, +# 'visualSpacePixelSize': 0.5} +# +# trial = RetinotopicMappingTrial(mouseID = testTrial['mouseID'], # str, mouseID +# dateRecorded = testTrial['dateRecorded'], # int, date recorded, yearmonthday +# trialNum = testTrial['trialNum'], # str, number of the trail on that day +# mouseType = testTrial['mouseType'], # str, mouse Genotype +# visualStimType = testTrial['visualStimType'], # str, stimulation type +# visualStimBackground = testTrial['visualStimBackground'], # str, background of visual stimulation +# imageExposureTime = testTrial['imageExposureTime'], # exposure time of the recorded image +# altPosMap = testTrial['altPosMap'], # altitute position map +# aziPosMap = testTrial['aziPosMap'], # azimuth position map +# altPowerMap = testTrial['altPowerMap'], # altitude power map +# aziPowerMap = testTrial['aziPowerMap'], # azimuth power map +# vasculatureMap = testTrial['vasculatureMap'], # vasculature map +# params = params, # testTrial['params'], # parameters for imaging analysis +# isAnesthetized = testTrial['isAnesthetized']) +# +# trial.processTrial(isPlot=True) +# trial._getSignMap() +# +# traces = trial.getTraces(moviePath = r'\\aibsdata2\nc-ophys\Jun\exampleData\testMov.tif', isPlot = True) +# trial.generateStandardOutput(traces=traces) +# +# trialDict = trial.generateTrialDict() +# print trialDict.keys() +# +# trial.plotTrial() +# +# altPosMapfNor, aziPosMapfNor, altPowerMapfNor, aziPowerMapfNor, signMapfNor = trial.generateNormalizedMaps(isPlot = True) +# +# +# trial.plotNormalizedPatchCenter() +# +# trial.plotFinalPatches() +# +# +# vasMapNor, finalPatchesNor = trial.normalize(isPlot = True) + +# plt.show() + + +#------------------------------------------------------------------------------------------------ +# testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\20140623_M140174_Trial3.pkl") +# +# testTrial.plotFinalPatchBorders() +# plt.show() +# +# corArea = testTrial.getCorticalArea() +# print '\nCortical area for each visual area (mm^2):' +# for key, item in corArea.iteritems(): +# print key, ':', item +# +# baselineF = testTrial.getBaselineFluorscence() +# print '\nNormalized baseline fluroscence for each visual area:' +# for key, item in baselineF.iteritems(): +# print key, ':', item +# +# power = testTrial.getMeanPowerAmplitude() +# print '\nNormalized power amplitude for each visual area:' +# for key, item in power.iteritems(): +# print key, ':', item +# +# magnification = testTrial.getMagnification() +# print '\nMagnification for each visual area (mm^2/deg^2):' +# for key, item in magnification.iteritems(): +# print key, ':', item + +# altPosOrigin, aziPosOrigin = testTrial.getVisualFieldOrigin() +# print 'Altitude origin:', altPosOrigin +# print 'Azimuth origin:', aziPosOrigin + + +#------------------------------------------------------------------------------------------------- + +# testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\20150130_M160809_Trial1_2_3_4.pkl") +# visualArea = 'LM' +# patch = testTrial.finalPatchesMarked[visualArea] +# +# testTrial._getSignMap() +# +# f=plt.figure() +# ax=f.add_subplot(111) +# ax.imshow(testTrial.altPosMapf,vmin=-30,vmax=50,cmap='hsv',interpolation='nearest') +# pt.plot_mask(patch.getMask(),plotAxis=ax) +# +# VSlist = patch.getVisualSpace(testTrial.altPosMapf,testTrial.aziPosMapf) +# +# VSSpace, VSCoverage, VSAltCenter, VSAziCenter = VSlist +# print 'Altitute center for area '+visualArea+': '+str(VSAltCenter) +# print 'Azimuth center for area '+visualArea+': '+str(VSAziCenter) +# plt.figure() +# plt.imshow(VSSpace,interpolation='nearest') +# plt.title(visualArea) +# plt.show() +#------------------------------------------------------------------------------------------------ + +# testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\20150116_M156569_Trial1_2_3_4.pkl") +# visualArea = 'V1' +# patch = testTrial.finalPatchesMarked[visualArea] +# +# testTrial._getSignMap() +# +# f=plt.figure() +# ax=f.add_subplot(111) +# ax.imshow(testTrial.altPosMapf,vmin=-30,vmax=50,cmap='hsv',interpolation='nearest') +# pt.plot_mask(patch.getMask(),plotAxis=ax) +# +# plt.show() + +#---------------------------------------------------------------------------------------------- + +# testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\populationTrial_Ai9330min.pkl") +# testTrial.plotMagnificationMap(isFilter=False) +# plt.show() + + +#---------------------------------------------------------------------------------------------- +# testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\populationTrial_All.pkl") +# totalMask = testTrial._generateTotalMask() +# plt.imshow(totalMask) +# plt.title('total mask of all visual areas') +# plt.show() + + +#---------------------------------------------------------------------------------------------- +# testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\populationTrial_All.pkl") +# +# locationList = [[0.,40.], +# [0.,50.], +# [0.,60.]] +# +# colorList = pt.random_color(len(locationList)) +# +# f=plt.figure(figsize=(12,12)) +# ax=f.add_subplot(111) +# testTrial.plotFinalPatchBorders(plotAxis=ax,plotName=False,isTitle=True) +# +# for i, location in enumerate(locationList): +# testTrial.plotRetinotopicLocation(plotAxis=ax,location=location,color=colorList[i]) +# +# labels = ['alt:'+str(x[0])+'; azi:'+str(x[1]) for x in locationList] +# [ax.plot(None,None,ls='',c=c,label=l) for c,l in zip(colorList,labels)] +# leg = ax.legend(labels,frameon=False) +# for color,text in zip(colorList,leg.get_texts()): +# text.set_color(color) +# +# plt.show() + + +#---------------------------------------------------------------------------------------------- + + +# testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\populationTial_Ai93&Ai9630min.pkl") +# testTrial.plotVisualCoverage() +# plt.show() +# +#---------------------------------------------------------------------------------------------- # testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\populationTial_Ai93&Ai9630min.pkl") - testTrial, traces = loadTrial( - r"E:\data\2015-11-13-150821-M177931-RetinotopicMapping\20150821_MM177931_Trial1_3_4.pkl") + testTrial, traces = loadTrial(r"E:\data\2015-11-13-150821-M177931-RetinotopicMapping\20150821_MM177931_Trial1_3_4.pkl") # testTrial.processTrial(isPlot=True) testTrial.plotFinalPatchBorders2(borderWidth=1) plt.show() -# ---------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------- # testTrial, traces = loadTrial(r"E:\data2\2015-02-03-population-maps\populationTial_Ai93&Ai9630min.pkl") # eccMap = testTrial.eccentricityMapf # V1 = testTrial.finalPatchesMarked['V1'] # V1center = V1.getCorticalPixelForVisualSpaceCenter(eccMap) # print V1center + diff --git a/corticalmapping/SingleCellAnalysis.py b/corticalmapping/SingleCellAnalysis.py index 9c4575b..e0c5c36 100644 --- a/corticalmapping/SingleCellAnalysis.py +++ b/corticalmapping/SingleCellAnalysis.py @@ -1,17 +1,17 @@ -import warnings +from corticalmapping.core.ImageAnalysis import ROI, WeightedROI + +__author__ = 'junz' + import numpy as np import matplotlib.pyplot as plt -import core.PlottingTools as pt -import core.ImageAnalysis as ia +from . import core.PlottingTools as pt +from . import core.ImageAnalysis as ia +from . import core.FileTools as ft import scipy.ndimage as ni import scipy.interpolate as ip -import scipy.stats as stats import math import h5py -from pandas import DataFrame -from corticalmapping.core.ImageAnalysis import ROI, WeightedROI -warnings.simplefilter('always', RuntimeWarning) def get_sparse_noise_onset_index(sparseNoiseDisplayLog): """ @@ -22,30 +22,30 @@ def get_sparse_noise_onset_index(sparseNoiseDisplayLog): onsetIndWithLocationSign: indices of frames for each white square, list with element structure [np.array([alt, azi]),sign,[list of indices]] """ + frames = sparseNoiseDisplayLog['presentation']['displayFrames'] - frames = [tuple([np.array([x[1][1], x[1][0]]), x[2], x[3], i]) for i, x in enumerate(frames)] - dtype = [('location', np.ndarray), ('sign', int), ('isOnset', int), ('index', int)] - frames = np.array(frames, dtype=dtype) + frames = [tuple([np.array([x[1][1],x[1][0]]),x[2],x[3],i]) for i, x in enumerate(frames)] + dtype = [('location',np.ndarray),('sign',int),('isOnset',int),('index',int)] + frames = np.array(frames, dtype = dtype) allOnsetInd = [] for i in range(len(frames)): - if frames[i]['isOnset'] == 1 and (i == 0 or frames[i - 1]['isOnset'] == -1): + if frames[i]['isOnset'] == 1 and (i == 0 or frames[i-1]['isOnset'] == -1): allOnsetInd.append(i) onsetFrames = frames[allOnsetInd] - allSquares = list(set([tuple([x[0][0], x[0][1], x[1]]) for x in onsetFrames])) + allSquares = list(set([tuple([x[0][0],x[0][1],x[1]]) for x in onsetFrames])) onsetIndWithLocationSign = [] for square in allSquares: indices = [] for onsetFrame in onsetFrames: - if onsetFrame['location'][0] == square[0] and onsetFrame['location'][1] == square[1] and onsetFrame[ - 'sign'] == square[2]: + if onsetFrame['location'][0]==square[0] and onsetFrame['location'][1]==square[1] and onsetFrame['sign']==square[2]: indices.append(onsetFrame['index']) - onsetIndWithLocationSign.append([np.array([square[0], square[1]]), square[2], indices]) + onsetIndWithLocationSign.append([np.array([square[0],square[1]]),square[2],indices]) return allOnsetInd, onsetIndWithLocationSign @@ -55,16 +55,12 @@ def get_peak_weighted_roi(arr, thr): return: a WeightROI object representing the mask which contains the peak of arr and cut by the thr (thr) """ nanLabel = np.isnan(arr) - arr2 = arr.copy() - arr2[nanLabel] = np.nanmin(arr) - labeled, _ = ni.label(arr2 >= thr) - peakCoor = np.array(np.where(arr2 == np.amax(arr2))).transpose()[0] + arr2=arr.copy();arr2[nanLabel]=np.nanmin(arr) + labeled,_=ni.label(arr2>=thr) + peakCoor = np.array(np.where(arr2==np.amax(arr2))).transpose()[0] peakMask = ia.get_marked_masks(labeled, peakCoor) - if peakMask is None: - # print('Threshold too high! No ROI found. Returning None'.) - return None - else: - return WeightedROI(arr2 * peakMask) + if peakMask is None: 'Threshold too high! No ROI found. Returning None'; return None + else: return WeightedROI(arr2 * peakMask) def plot_2d_receptive_field(mapArray, altPos, aziPos, plot_axis=None, **kwargs): @@ -79,11 +75,8 @@ def plot_2d_receptive_field(mapArray, altPos, aziPos, plot_axis=None, **kwargs): :return: plot_axis """ - if plot_axis == None: - f = plt.figure(figsize=(10, 10)) - plot_axis = f.add_subplot(111) - - fig = plot_axis.imshow(mapArray, **kwargs) + if plot_axis == None: f=plt.figure(figsize=(10,10)); plot_axis=f.add_subplot(111) + fig = plot_axis.imshow(mapArray,**kwargs) plot_axis.set_yticks(np.arange(len(altPos))) plot_axis.set_xticks(np.arange(len(aziPos))) plot_axis.set_yticklabels(altPos.astype(np.int)) @@ -96,13 +89,12 @@ def merge_weighted_rois(roi1, roi2): merge two WeightedROI objects, most useful for merge ON and OFF subfields """ if (roi1.pixelSizeX != roi2.pixelSizeX) or (roi1.pixelSizeY != roi2.pixelSizeY): - raise ValueError, 'The pixel sizes of the two WeightedROI objects should match!' + raise ValueError('The pixel sizes of the two WeightedROI objects should match!') if roi1.pixelSizeUnit != roi2.pixelSizeUnit: - raise ValueError, 'The pixel size units of the two WeightedROI objects should match!' + raise ValueError('The pixel size units of the two WeightedROI objects should match!') - mask1 = roi1.get_weighted_mask() - mask2 = roi2.get_weighted_mask() + mask1 = roi1.get_weighted_mask(); mask2 = roi2.get_weighted_mask() return WeightedROI(mask1 + mask2, pixelSize=[roi1.pixelSizeY, roi1.pixelSizeX], pixelSizeUnit=roi1.pixelSizeUnit) @@ -112,221 +104,16 @@ def merge_binary_rois(roi1, roi2): merge two ROI objects, most useful for merge ON and OFF subfields """ if (roi1.pixelSizeX != roi2.pixelSizeX) or (roi1.pixelSizeY != roi2.pixelSizeY): - raise ValueError, 'The pixel sizes of the two WeightedROI objects should match!' + raise ValueError('The pixel sizes of the two WeightedROI objects should match!') if roi1.pixelSizeUnit != roi2.pixelSizeUnit: - raise ValueError, 'The pixel size units of the two WeightedROI objects should match!' + raise ValueError('The pixel size units of the two WeightedROI objects should match!') - mask1 = roi1.get_binary_mask() - mask2 = roi2.get_binary_mask() - mask3 = np.logical_or(mask1, mask2).astype(np.int8) + mask1 = roi1.get_binary_mask(); mask2 = roi2.get_binary_mask(); mask3 = np.logical_or(mask1, mask2).astype(np.int8) return ROI(mask3, pixelSize=[roi1.pixelSizeY, roi1.pixelSizeX], pixelSizeUnit=roi1.pixelSizeUnit) -def get_dff(traces, t_axis, response_window, baseline_window): - """ - - :param traces: 3d array, roi x trial x time points - :param t_axis: local timestamps of sta responses - :param response_window: - :param baseline_window: - :return dffs_trial: 3d array, roi x trial x 1, list of dffs for each roi, each_trial - :return dffs_mean: 1d array, mean dff of each roi, collapsed across trials before dff calculation - """ - - baseline_ind = np.logical_and(t_axis > baseline_window[0], t_axis <= baseline_window[1]) - response_ind = np.logical_and(t_axis > response_window[0], t_axis <= response_window[1]) - - baselines = np.mean(traces[:, :, baseline_ind], axis=2, keepdims=True) - responses = np.mean(traces[:, :, response_ind], axis=2, keepdims=True) - - dffs_trial = (responses - baselines) / baselines - - traces_mean = np.mean(traces, axis=1) # roi x time points - baselines_mean = np.mean(traces_mean[:, baseline_ind], axis=1) - responses_mean = np.mean(traces_mean[:, response_ind], axis=1) - dffs_mean = (responses_mean - baselines_mean) / baselines_mean - - return dffs_trial, dffs_mean.squeeze() - - -def get_df(traces, t_axis, response_window, baseline_window): - """ - - :param traces: 3d array, roi x trial x time points - :param t_axis: local timestamps of sta responses - :param response_window: - :param baseline_window: - :return dfs_trial: 3d array, roi x trial x 1, list of dffs for each roi, each_trial - :return dfs_mean: 1d array, mean df of each roi - """ - - baseline_ind = np.logical_and(t_axis > baseline_window[0], t_axis <= baseline_window[1]) - response_ind = np.logical_and(t_axis > response_window[0], t_axis <= response_window[1]) - - baselines = np.mean(traces[:, :, baseline_ind], axis=2, keepdims=True) - responses = np.mean(traces[:, :, response_ind], axis=2, keepdims=True) - - dfs_trial = responses - baselines - - dfs_mean = np.mean(dfs_trial, axis=1).squeeze() - - return dfs_trial, dfs_mean - - -def get_df_dff_trace(trace, t_axis, baseline_window): - - baseline_ind = np.logical_and(t_axis > baseline_window[0], t_axis <= baseline_window[1]) - - baseline = np.mean(trace[baseline_ind]) - - trace_df = trace - baseline - trace_dff = trace_df / baseline - - return trace_df, trace_dff - - -def get_skewness(trace, ts, filter_length=5.): - """ - calculate skewness of a calcium trace, returns the skewness of input trace and the skewness of the trace after - removing slow trend. Because slow drifting trend creates artificial and confounding skewness other than calcium - signal. - - :param trace: 1d array - :param ts: 1d array, timestamps of the input trace in seconds - :param filter_length: float, second, the length to filter input trace to get slow trend - :return skew_o: skewness of original input trace - :return skew_d: skewness of detrended trace - """ - - fs = 1. / np.mean(np.diff(ts)) - sigma = float(filter_length) * fs - skew_o = stats.skew(trace) - - trend = ni.gaussian_filter1d(trace, sigma=sigma) - trace_d = trace - trend - skew_d = stats.skew(trace_d) - - return skew_o, skew_d - - -def get_dgc_condition_name(alt, azi, sf, tf, dire, con, rad): - return 'alt{:06.1f}_azi{:06.1f}_sf{:04.2f}_tf{:04.1f}_dire{:03d}_con{:04.2f}_rad{:03d}'.format(alt, - azi, - sf, - tf, - dire, - con, - rad) - - -def get_dgc_condition_params(condi_name): - alt = float(condi_name[3:9]) - azi = float(condi_name[13:19]) - sf = float(condi_name[22:26]) - tf = float(condi_name[29:33]) - dire = int(condi_name[38:41]) - con = float(condi_name[45:49]) - rad = int(condi_name[53:56]) - return alt, azi, sf, tf, dire, con, rad - - -def get_strf_from_nwb(h5_grp, roi_ind, trace_type='sta_f_center_subtracted', location_unit='degree'): - - sta_ts = h5_grp.attrs['sta_timestamps'] - - probe_ns = h5_grp.keys() - probe_ns.sort() - - locations = [] - signs = [] - traces = [] - trigger_ts = [] - - for probe_i, probe_n in enumerate(probe_ns): - - locations.append([float(probe_n[3:9]), float(probe_n[13:19])]) - signs.append(int(probe_n[24:26])) - - traces.append(h5_grp['{}/{}'.format(probe_n, trace_type)][roi_ind, :, :]) - trigger_ts.append(h5_grp['{}/global_trigger_timestamps'.format(probe_n)].value) - - return SpatialTemporalReceptiveField(locations=locations, signs=signs, traces=traces, time=sta_ts, - trigger_ts=trigger_ts, name='roi_{:04d}'.format(roi_ind), - locationUnit=location_unit, trace_data_type=trace_type) - - -def get_dgc_response_matrix_from_nwb(h5_grp, roi_ind, trace_type='sta_f_center_subtracted'): - sta_ts = h5_grp.attrs['sta_timestamps'] - - dgcrm = DataFrame([], columns=['alt', 'azi', 'sf', 'tf', 'dire', 'con', 'rad', 'onset_ts', 'matrix']) - - condi_ns = h5_grp.keys() - condi_ns.sort() - - for condi_i, condi_n in enumerate(condi_ns): - - condi_grp = h5_grp[condi_n] - - alt, azi, sf, tf, dire, con, rad = get_dgc_condition_params(condi_name=condi_n) - - if 'global_trigger_timestamps' in condi_grp.attrs: - onset_ts = condi_grp.attrs['global_trigger_timestamps'] - else: - onset_ts = [] - - matrix = condi_grp[trace_type][roi_ind, :, :] - - dgcrm.loc[condi_i, 'alt'] = alt - dgcrm.loc[condi_i, 'azi'] = azi - dgcrm.loc[condi_i, 'sf'] = sf - dgcrm.loc[condi_i, 'tf'] = tf - dgcrm.loc[condi_i, 'dire'] = dire - dgcrm.loc[condi_i, 'con'] = con - dgcrm.loc[condi_i, 'rad'] = rad - dgcrm.loc[condi_i, 'onset_ts'] = onset_ts - dgcrm.loc[condi_i, 'matrix'] = matrix - - return DriftingGratingResponseMatrix(sta_ts=sta_ts, trace_type=trace_type, data=dgcrm) - - -def get_local_similarity_index(mask1, mask2): - """ - calculate local similarity index between two receptive field maps - - LSI = sum(mask1 x mask2) / sqrt( sum(mask1 x mask1) * sum(mask2 x mask2)) - - DOI: https://doi.org/10.1523/JNEUROSCI.0863-13.2013 - - :param mask1: 2d array - :param mask2: 2d array - :return: - """ - - if not len(mask1.shape) == len(mask2.shape) == 2: - raise ValueError('mask1 and mask2 should both be 2d array with same shape.') - - square1 = np.sum((mask1 * mask1).flat) - square2 = np.sum((mask2 * mask2).flat) - - if square1 == 0 or square2 == 0.: - return np.nan - else: - value1 = np.sum((mask1 * mask2).flat) - value2 = np.sqrt(square1 * square2) - return value1 / value2 - - -def dire2ori(dire): - """ - convert grating drifting direction to grating orientation, unit: degrees - direction: right is 0 degree, increase counterclockwise - orientation: horizontal 0 degree, increase counterclockwise - """ - return (dire + 90) % 180 - - class SpatialReceptiveField(WeightedROI): """ Object for spatial receptive field, a subclass of WeightedROI object @@ -347,17 +134,17 @@ def __init__(self, mask, altPos, aziPos, sign=None, temporalWindow=None, pixelSi the correct way to process RF: gaussian filter first, interpolation second, and thr third """ - super(SpatialReceptiveField, self).__init__(mask, pixelSize=None, pixelSizeUnit=pixelSizeUnit) + super(SpatialReceptiveField,self).__init__(mask, pixelSize = None, pixelSizeUnit = pixelSizeUnit) self.altPos = altPos self.aziPos = aziPos self.dataType = dataType - if (sign is None or sign == 'ON' or sign == 'OFF' or sign == 'ON_OFF'): - self.sign = sign - elif sign == 1: - self.sign = 'ON' - elif sign == -1: - self.sign = 'OFF' + if (sign is None or sign=='ON' or sign=='OFF' or sign=='ON_OFF'): + self.sign=sign + elif sign==1: + self.sign='ON' + elif sign==-1: + self.sign='OFF' else: raise ValueError('sign should be 1, -1, "ON", "OFF", "ON_OFF" or None!') self.temporalWindow = temporalWindow @@ -372,9 +159,6 @@ def __init__(self, mask, altPos, aziPos, sign=None, temporalWindow=None, pixelSi else: raise ValueError('interpolate_rate should be larger than 1!') - def __str__(self): - return 'corticalmapping.SingleCellAnalysis.SpatialReceptiveField object' - def get_name(self): name = [] @@ -392,12 +176,12 @@ def get_name(self): name.append('thr:None') if self.filter_sigma is not None: - name.append('sigma:' + str(self.filter_sigma)) + name.append('sigma:'+str(self.filter_sigma)) else: name.append('sigma:None') if self.interpolate_rate is not None: - name.append('interp:' + str(self.interpolate_rate)) + name.append('interp:'+str(self.interpolate_rate)) else: name.append('interp:None') @@ -421,10 +205,10 @@ def plot_rf(self, plot_axis=None, is_colorbar=False, cmap='Reds', interpolation= else: interpolate_rate = self.interpolate_rate - plot_axis.set_yticks(range(len(self.altPos))[::interpolate_rate]) - plot_axis.set_xticks(range(len(self.aziPos))[::interpolate_rate]) - plot_axis.set_yticklabels(['{:.1f}'.format(p) for p in self.altPos[::interpolate_rate]]) - plot_axis.set_xticklabels(['{:.1f}'.format(p) for p in self.aziPos[::interpolate_rate]]) + plot_axis.set_yticks(list(range(len(self.altPos)))[::interpolate_rate]) + plot_axis.set_xticks(list(range(len(self.aziPos)))[::interpolate_rate]) + plot_axis.set_yticklabels(self.altPos[::interpolate_rate]) + plot_axis.set_xticklabels(self.aziPos[::interpolate_rate]) if is_colorbar: plot_axis.get_figure().colorbar(curr_plot) @@ -449,9 +233,9 @@ def plot_contour(self, plot_axis=None, peak_amplitude=None, level_num=10, **kwar elif self.sign == 'OFF': colors = 'b' else: - colors = 'k' + colors ='k' - contour_levels = list(np.arange(level_num) * (float(peak_amplitude) / (level_num))) + contour_levels = list(np.arange(level_num) * (float(peak_amplitude) / (level_num))) if self.thr is not None: contour_levels = [l for l in contour_levels if l >= self.thr] @@ -474,13 +258,13 @@ def plot_contour(self, plot_axis=None, peak_amplitude=None, level_num=10, **kwar plot_axis.set_aspect('equal') if self.interpolate_rate is not None: - plot_axis.set_yticks(range(len(self.altPos))[::self.interpolate_rate]) - plot_axis.set_xticks(range(len(self.aziPos))[::self.interpolate_rate]) + plot_axis.set_yticks(list(range(len(self.altPos)))[::self.interpolate_rate]) + plot_axis.set_xticks(list(range(len(self.aziPos)))[::self.interpolate_rate]) plot_axis.set_yticklabels(self.altPos[::self.interpolate_rate]) plot_axis.set_xticklabels(self.aziPos[::self.interpolate_rate]) else: - plot_axis.set_yticks(range(len(self.altPos))) - plot_axis.set_xticks(range(len(self.aziPos))) + plot_axis.set_yticks(list(range(len(self.altPos)))) + plot_axis.set_xticks(list(range(len(self.aziPos)))) plot_axis.set_yticklabels(self.altPos) plot_axis.set_xticklabels(self.aziPos) @@ -489,14 +273,14 @@ def plot_contour(self, plot_axis=None, peak_amplitude=None, level_num=10, **kwar def threshold(self, thr): """ - threshold the current receptive field, return a new SpatialReceptiveField object after thresholding + thr the current receptive field, return a new SpatialReceptiveField object after thresholding """ - if (self.thr is not None) and (thr < self.thr): - raise ValueError, 'Can not cut a thresholded receptive field with a lower thresold!' + if (self.thr is not None) and (thr 1: - stdTrace = np.std(np.array(traces, dtype=np.float32), axis=0) - semTrace = stdTrace / np.sqrt(float(len(traces))) - axis.fill_between(self.time, meanTrace - semTrace, meanTrace + semTrace, facecolor=color, - linewidth=0, alpha=0.5) - axis.plot(self.time, meanTrace, '-', color=color, lw=1) + meanTrace = np.mean(np.array(traces, dtype=np.float32),axis=0) + stdTrace = np.std(np.array(traces, dtype=np.float32),axis=0) + semTrace = stdTrace/np.sqrt(float(len(traces))) + if self.data[index]['sign'] == 1: color = '#ff0000' + if self.data[index]['sign'] == -1: color = '#0000ff' + # print self.time.shape + # print (meanTrace-semTrace).shape + # print (meanTrace+semTrace).shape + axis.fill_between(self.time,meanTrace-semTrace,meanTrace+semTrace,facecolor=color,linewidth=0,alpha=0.5) + axis.plot(self.time,meanTrace,'-',color=color,lw=1) return axisLists[0][0].figure def _get_axis_layout(self, f=None, figSize=(10, 10), yRange=(0, 20), altRange=None, aziRange=None, **kwargs): - locations = np.array(self.get_probes()) + locations = np.array(self.get_locations()) - altPositions = np.sort(np.unique(locations[:, 0]))[::-1] - if altRange is not None: - altPositions = np.array([x for x in altPositions if (x >= altRange[0] and x <= altRange[1])]) + altPositions = np.sort(np.unique(locations[:,0]))[::-1] + if altRange is not None: altPositions = np.array([x for x in altPositions if (x>=altRange[0] and x<=altRange[1])]) - aziPositions = np.sort(np.unique(locations[:, 1])) - if aziRange is not None: - aziPositions = np.array([x for x in aziPositions if (x >= aziRange[0] and x <= aziRange[1])]) + aziPositions = np.sort(np.unique(locations[:,1])) + if aziRange is not None: aziPositions = np.array([x for x in aziPositions if (x>=aziRange[0] and x<=aziRange[1])]) - indexLists = [[[] for aziPosition in aziPositions] for altPosition in altPositions] + indexLists = [ [[] for aziPosition in aziPositions] for altPosition in altPositions] - if f is None: - f = plt.figure(figsize=figSize) - - f.suptitle('cell:{}; xrange:[{:6.3f}, {:6.3f}]; yrange: [{:.3f}, {:.3f}]'. - format(self.name, self.time[0], self.time[-1], yRange[0], yRange[1])) + if f is None: f=plt.figure(figsize=figSize) + f.suptitle('cell:'+str(self.name)+'; xrange:['+str(self.time[0])[0:6]+','+str(self.time[-1])[0:6]+']; yrange:'+str(yRange)) axisLists = pt.tile_axis(f, len(altPositions), len(aziPositions), **kwargs) for i, altPosition in enumerate(altPositions): for j, aziPosition in enumerate(aziPositions): - axisLists[i][j].text(0, yRange[1], str(int(altPosition)) + ';' + str(int(aziPosition)), - ha='left', va='top', fontsize=10) - axisLists[i][j].set_xlim([self.time[0], self.time[-1]]) + axisLists[i][j].text(0,yRange[1],str(int(altPosition))+';'+str(int(aziPosition)),ha='left',va='top',fontsize=10) + axisLists[i][j].set_xlim([self.time[0],self.time[-1]]) axisLists[i][j].set_ylim(yRange) for k, location in enumerate(locations): if location[0] == altPosition and location[1] == aziPosition: indexLists[i][j].append(k) + return indexLists, axisLists def get_amplitude_map(self, timeWindow=(0, 0.5)): @@ -924,21 +530,16 @@ def get_amplitude_map(self, timeWindow=(0, 0.5)): coordinate of each pixel is defined by np.meshgrid(allAziPos, allAltPos) """ - windowIndex = np.logical_and(self.time >= timeWindow[0], self.time <= timeWindow[1]) + windowIndex = np.logical_and(self.time>=timeWindow[0], self.time<=timeWindow[1]) - indON, indOFF, allAltPos, allAziPos = self._sort_index() + indON,indOFF,allAltPos,allAziPos = self._sort_index() - ampON = np.zeros(indON.shape) - ampON[:] = np.nan - ampOFF = ampON.copy() + ampON = np.zeros(indON.shape); ampON[:]=np.nan; ampOFF = ampON.copy() for i in np.ndindex(indON.shape): - traceIndON = indON[i] - traceIndOFF = indOFF[i] - if traceIndON is not None: - ampON[i] = np.mean(np.mean(self.data.iloc[traceIndON]['traces'], axis=0)[windowIndex]) - if traceIndOFF is not None: - ampOFF[i] = np.mean(np.mean(self.data.iloc[traceIndOFF]['traces'], axis=0)[windowIndex]) + traceIndON = indON[i]; traceIndOFF = indOFF[i] + if traceIndON is not None: ampON[i] = np.mean(np.mean(self.data[traceIndON]['traces'],axis=0)[windowIndex]) + if traceIndOFF is not None: ampOFF[i] = np.mean(np.mean(self.data[traceIndOFF]['traces'],axis=0)[windowIndex]) return ampON, ampOFF, allAltPos, allAziPos @@ -952,12 +553,8 @@ def get_amplitude_receptive_field(self, timeWindow=(0, 0.5)): ampON, ampOFF, allAltPos, allAziPos = self.get_amplitude_map(timeWindow) - ampRFON = SpatialReceptiveField(mask=ampON, altPos=allAltPos, aziPos=allAziPos, sign=1, - temporalWindow=timeWindow, pixelSizeUnit=self.locationUnit, - dataType='amplitude') - ampRFOFF = SpatialReceptiveField(mask=ampOFF, altPos=allAltPos, aziPos=allAziPos, sign=-1, - temporalWindow=timeWindow, pixelSizeUnit=self.locationUnit, - dataType='amplitude') + ampRFON = SpatialReceptiveField(ampON,allAltPos,allAziPos,sign=1,temporalWindow=timeWindow,pixelSizeUnit=self.locationUnit,dataType='amplitude') + ampRFOFF = SpatialReceptiveField(ampOFF,allAltPos,allAziPos,sign=-1,temporalWindow=timeWindow,pixelSizeUnit=self.locationUnit,dataType='amplitude') return ampRFON, ampRFOFF @@ -975,20 +572,20 @@ def get_delta_amplitude_map(self, timeWindow=(0, 0.5)): indON, indOFF, allAltPos, allAziPos = self._sort_index() - ampON = np.zeros(indON.shape) - ampON[:] = np.nan + ampON = np.zeros(indON.shape); + ampON[:] = np.nan; ampOFF = ampON.copy() for i in np.ndindex(indON.shape): - traceIndON = indON[i] + traceIndON = indON[i]; traceIndOFF = indOFF[i] if traceIndON is not None: - curr_trace_ON = np.mean(self.data.iloc[traceIndON]['traces'], axis=0) + curr_trace_ON = np.mean(self.data[traceIndON]['traces'], axis=0) curr_baseline_ON = np.mean(curr_trace_ON[baseline_index]) curr_delta_trace_ON = curr_trace_ON - curr_baseline_ON ampON[i] = np.mean(curr_delta_trace_ON[windowIndex]) if traceIndOFF is not None: - curr_trace_OFF = np.mean(self.data.iloc[traceIndOFF]['traces'], axis=0) + curr_trace_OFF = np.mean(self.data[traceIndOFF]['traces'], axis=0) curr_baseline_OFF = np.mean(curr_trace_OFF[baseline_index]) curr_delta_trace_OFF = curr_trace_OFF - curr_baseline_OFF ampOFF[i] = np.mean(curr_delta_trace_OFF[windowIndex]) @@ -1005,12 +602,11 @@ def get_delta_amplitude_receptive_field(self, timeWindow=(0, 0.5)): ampON, ampOFF, allAltPos, allAziPos = self.get_delta_amplitude_map(timeWindow) - ampRFON = SpatialReceptiveField(mask=ampON, altPos=allAltPos, aziPos=allAziPos, sign=1, - temporalWindow=timeWindow, pixelSizeUnit=self.locationUnit, - dataType='delta_amplitude') - ampRFOFF = SpatialReceptiveField(mask=ampOFF, altPos=allAltPos, aziPos=allAziPos, sign=-1, - temporalWindow=timeWindow, pixelSizeUnit=self.locationUnit, - dataType='delta_amplitude') + ampRFON = SpatialReceptiveField(ampON, allAltPos, allAziPos, sign=1, temporalWindow=timeWindow, + pixelSizeUnit=self.locationUnit, dataType='delta_amplitude') + ampRFOFF = SpatialReceptiveField(ampOFF, allAltPos, allAziPos, sign=-1,temporalWindow=timeWindow, + pixelSizeUnit=self.locationUnit, dataType='delta_amplitude') + return ampRFON, ampRFOFF def get_zscore_map(self, timeWindow=(0, 0.5)): @@ -1036,12 +632,10 @@ def get_zscore_receptive_field(self, timeWindow=(0, 0.5)): ampON, ampOFF, allAltPos, allAziPos = self.get_amplitude_map(timeWindow) - zscoreRFON = SpatialReceptiveField(mask=ia.zscore(ampON), altPos=allAltPos, aziPos=allAziPos, sign='ON', - temporalWindow=timeWindow, pixelSizeUnit=self.locationUnit, - dataType='zscore') - zscoreRFOFF = SpatialReceptiveField(mask=ia.zscore(ampOFF), altPos=allAltPos, aziPos=allAziPos, sign='OFF', - temporalWindow=timeWindow, pixelSizeUnit=self.locationUnit, - dataType='zscore') + zscoreRFON = SpatialReceptiveField(ia.zscore(ampON),allAltPos,allAziPos,sign='ON',temporalWindow=timeWindow, + pixelSizeUnit=self.locationUnit,dataType='zscore') + zscoreRFOFF = SpatialReceptiveField(ia.zscore(ampOFF),allAltPos,allAziPos,sign='OFF',temporalWindow=timeWindow, + pixelSizeUnit=self.locationUnit,dataType='zscore') return zscoreRFON, zscoreRFOFF @@ -1065,15 +659,15 @@ def get_zscore_rois(self, timeWindow=(0, 0.5), zscoreThr=2): if zscoreROION is not None and zscoreROIOFF is not None: zscoreROIALL = WeightedROI(zscoreROION.get_weighted_mask() + zscoreROIOFF.get_weighted_mask()) elif zscoreROION is None and zscoreROIOFF is not None: - print 'No zscore receptive field found for ON channel. Threshold too high.' + print('No zscore receptive field found for ON channel. Threshold too high.') zscoreROIALL = zscoreROIOFF elif zscoreROION is not None and zscoreROIOFF is None: - print 'No zscore receptive field found for OFF channel. Threshold too high.' + print('No zscore receptive field found for OFF channel. Threshold too high.') zscoreROIALL = zscoreROION else: zscoreROIALL = None - return zscoreROION, zscoreROIOFF, zscoreROIALL, allAltPos, allAziPos + return zscoreROION,zscoreROIOFF,zscoreROIALL,allAltPos,allAziPos def get_zscore_thresholded_receptive_fields(self, timeWindow=(0, 0.3), thr_ratio=0.3, filter_sigma=None, interpolate_rate=None, absolute_thr=None): @@ -1093,11 +687,11 @@ def get_zscore_thresholded_receptive_fields(self, timeWindow=(0, 0.3), thr_ratio zscoreON, zscoreOFF, allAltPos, allAziPos = self.get_zscore_map(timeWindow) - zscoreRFON = SpatialReceptiveField(zscoreON, allAltPos, allAziPos, sign='ON', temporalWindow=timeWindow, + zscoreRFON = SpatialReceptiveField(zscoreON, allAltPos, allAziPos, sign='ON',temporalWindow=timeWindow, pixelSizeUnit=self.locationUnit, dataType='zscore') zscoreRFOFF = SpatialReceptiveField(zscoreOFF, allAltPos, allAziPos, sign='OFF', temporalWindow=timeWindow, - pixelSizeUnit=self.locationUnit, dataType='zscore') + pixelSizeUnit=self.locationUnit, dataType='zscore') if filter_sigma is not None: zscoreRFON = zscoreRFON.gaussian_filter(filter_sigma) @@ -1109,7 +703,7 @@ def get_zscore_thresholded_receptive_fields(self, timeWindow=(0, 0.3), thr_ratio max_value = max([np.amax(zscoreRFON.get_weighted_mask()), np.amax(zscoreRFOFF.get_weighted_mask())]) - thr = max_value * thr_ratio + thr = max_value * thr_ratio if absolute_thr is not None: thr = max([thr, absolute_thr]) @@ -1117,7 +711,7 @@ def get_zscore_thresholded_receptive_fields(self, timeWindow=(0, 0.3), thr_ratio zscoreRFON = zscoreRFON.threshold(thr) zscoreRFOFF = zscoreRFOFF.threshold(thr) - zscoreRFALL = SpatialReceptiveField(zscoreRFON.get_weighted_mask() + zscoreRFOFF.get_weighted_mask(), + zscoreRFALL = SpatialReceptiveField(zscoreRFON.get_weighted_mask()+zscoreRFOFF.get_weighted_mask(), zscoreRFON.altPos, zscoreRFON.aziPos, sign='ON_OFF', temporalWindow=timeWindow, pixelSizeUnit=self.locationUnit, dataType='zscore', thr=thr, filter_sigma=filter_sigma, @@ -1134,7 +728,7 @@ def get_zscore_roi_centers(self, timeWindow=(0, 0.5), zscoreThr=2): zscore ROIs was generated by the method get_zscore_rois() """ - zscoreROION, zscoreROIOFF, zscoreROIALL, allAltPos, allAziPos = self.get_zscore_rois(timeWindow, zscoreThr) + zscoreROION,zscoreROIOFF,zscoreROIALL,allAltPos,allAziPos = self.get_zscore_rois(timeWindow, zscoreThr) if zscoreROION is not None: centerON = zscoreROION.get_weighted_center_in_coordinate(allAltPos, allAziPos) else: @@ -1160,1469 +754,133 @@ def _sort_index(self): allAltPos = np.array(sorted(list(set(list(self.data['altitude'])))))[::-1] allAziPos = np.array(sorted(list(set(list(self.data['azimuth']))))) - indON = [[None for azi in allAziPos] for alt in allAltPos] - indOFF = [[None for azi in allAziPos] for alt in allAltPos] + indON = [[None for azi in allAziPos] for alt in allAltPos]; indOFF = [[None for azi in allAziPos] for alt in allAltPos] - for i, traceItem in self.data.iterrows(): - alt = traceItem['altitude'] - azi = traceItem['azimuth'] - sign = traceItem['sign'] + for i, traceItem in enumerate(self.data): + alt = traceItem['altitude'];azi = traceItem['azimuth'];sign = traceItem['sign'] for j, altPos in enumerate(allAltPos): for k, aziPos in enumerate(allAziPos): - if alt == altPos and azi == aziPos: - - if sign == 1: - if indON[j][k] is not None: - raise LookupError, 'Duplication of trace items found at location: ' + str( - [alt, azi]) + '; sign: 1!' - else: - indON[j][k] = i + if alt==altPos and azi==aziPos: + if sign==1: + if indON[j][k] is not None: raise LookupError('Duplication of trace items found at location:'+str([alt, azi])+'; sign: 1!') + else: indON[j][k]=i - if sign == -1: - if indOFF[j][k] is not None: - raise LookupError, 'Duplication of trace items found at location: ' + str( - [alt, azi]) + '; sign:-1!' - else: - indOFF[j][k] = i + if sign==-1: + if indOFF[j][k] is not None: raise LookupError('Duplication of trace items found at location:'+str([alt, azi])+'; sign:-1!') + else: indOFF[j][k]=i - indON = np.array([np.array(x) for x in indON]); - indOFF = np.array([np.array(x) for x in indOFF]) + indON = np.array([np.array(x) for x in indON]); indOFF = np.array([np.array(x) for x in indOFF]) - return indON, indOFF, allAltPos, allAziPos + return indON,indOFF,allAltPos,allAziPos - def shrink(self, altRange=None, aziRange=None, is_reset_index=True): + def shrink(self,altRange=None,aziRange=None): """ shrink the current spatial temporal receptive field into the defined altitude and/or azimuth range """ - if altRange is None and aziRange is None: - raise LookupError, 'At least one of altRange and aziRange should be defined!' + if altRange is None and aziRange is None: raise LookupError('At least one of altRange and aziRange should be defined!') - if altRange is not None: - indAlt = np.logical_and(self.data['altitude'] >= altRange[0], - self.data['altitude'] <= altRange[1]) - else: - indAlt = np.ones(len(self.data), dtype=np.bool) - - if aziRange is not None: - indAzi = np.logical_and(self.data['azimuth'] >= aziRange[0], - self.data['azimuth'] <= aziRange[1]) - else: - indAzi = np.ones(len(self.data), dtype=np.bool) - - ind = np.logical_and(indAlt, indAzi) + if altRange is not None: indAlt = np.logical_and(self.data['altitude']>=altRange[0],self.data['altitude']<=altRange[1]) + else: indAlt = np.ones(len(self.data),dtype=np.bool) + if aziRange is not None: indAzi = np.logical_and(self.data['azimuth']>=aziRange[0],self.data['azimuth']<=aziRange[1]) + else: indAzi = np.ones(len(self.data),dtype=np.bool) + ind = np.logical_and(indAlt,indAzi) + self.data = self.data[ind] - if np.sum(ind) == 0: - raise ValueError('No probes were sampled within the given altitude and azimuth range.') - - if is_reset_index: - self.data = self.data[ind].reset_index(drop=True) - else: - self.data = self.data[ind] - - def get_local_dff_strf(self, is_collaps_before_normalize=True, add_to_trace=0.): + @staticmethod + def from_h5_group(h5Group): """ - - :param is_collaps_before_normalize: if True, for each location, the traces across multiple trials will be - averaged before calculating df/f - :return: + load SpatialTemporalReceptiveField object from a hdf5 data group """ - bl_inds = self.time <= 0 - # print(bl_inds) - - dff_traces = [] - for probe_i, probe in self.data.iterrows(): - curr_traces = np.array(probe['traces']) + add_to_trace - - if is_collaps_before_normalize: - curr_traces = np.mean(curr_traces, axis=0, keepdims=True) - - curr_bl = np.mean(curr_traces[:, bl_inds], axis=1, keepdims=True) - curr_dff = (curr_traces - curr_bl) / curr_bl - - dff_traces.append(list(curr_dff)) - - locations = zip(list(self.data['altitude']), list(self.data['azimuth'])) - - if is_collaps_before_normalize: - - strf_dff = SpatialTemporalReceptiveField(locations=locations, - signs=list(self.data['sign']), - traces=dff_traces, - trigger_ts=None, - time=self.time, - name=self.name, - locationUnit=self.locationUnit, - trace_data_type=self.trace_data_type + '_local_dff') - - else: - - strf_dff = SpatialTemporalReceptiveField(locations=locations, - signs=list(self.data['sign']), - traces=dff_traces, - trigger_ts=list(self.data['trigger_ts']), - time=self.time, - name=self.name, - locationUnit=self.locationUnit, - trace_data_type=self.trace_data_type + '_local_dff') - return strf_dff - - def get_data_range(self): - - v_min = None - v_max = None - - for probe_i, probe in self.data.iterrows(): - - curr_trace = np.array(probe['traces']) - - if curr_trace.shape[0] > 1: - curr_std_trace = np.std(np.array(curr_trace, dtype=np.float32), axis=0, keepdims=True) - curr_sem_trace = curr_std_trace / np.sqrt(float(len(curr_trace))) - curr_trace_high = curr_trace + curr_sem_trace - curr_trace_low = curr_trace - curr_sem_trace - else: - curr_trace_low = curr_trace - curr_trace_high = curr_trace - - if v_min is None: - v_min = np.amin(curr_trace_low) - else: - v_min = min([v_min, np.amin(curr_trace_low)]) - - if v_max is None: - v_max = np.max(curr_trace_high) - else: - v_max = max([v_max, np.amax(curr_trace_high)]) - - return v_min, v_max - - def temporal_subset(self): - # todo: finish this. But need to do TimeIntervals containing TimeIntervals first in TimingAnalysis - pass - - -class DriftingGratingResponseMatrix(DataFrame): - """ - class for response matrix to drifting grating circle - contains event triggered traces for all traces of one roi - - subclassed from pandas.DataFrame with more attribute: - sta_ts: 1d array, local time stamps for event triggered traces - trace_type: str, type of traces - - columns: - alt - altitute of circle center - azi - azimuth of circle center - sf - spatial frequency, cpd - tf - temporal frequency, Hz - dire - drifting direction, deg, 0 is to right, increase counter-clockwise - con - contrast, [0, 1] - rad - radius, deg - onset_ts - 1d array, global onset time stamps for each trial - matrix - 2d array, trial x time point - """ - - def __init__(self, sta_ts, trace_type='', *args, **kwargs): - - super(DriftingGratingResponseMatrix, self).__init__(*args, **kwargs) - - self.sta_ts = sta_ts - self.trace_type = trace_type + time = h5Group.attrs['time'] + # try: + # name = h5Group.parent.name[1:] + '.' + h5Group.parent.attrs['name'] + # except KeyError: + # name = None + try: + name = h5Group.attrs['name'] + except KeyError: + name = None + locationUnit = h5Group.attrs['retinotopic_location_unit'] + trace_data_type = h5Group.attrs['trace_data_type'] + locations = [] + signs = [] + traces = [] + for key, traceItem in h5Group.items(): + locations.append(np.array([traceItem.attrs['altitude'], traceItem.attrs['azimuth']])) + signs.append((traceItem.attrs['sign'])) + traces.append(traceItem.value) - self.check_integrity() + return SpatialTemporalReceptiveField(locations, signs, traces, time, name, locationUnit, trace_data_type) - def get_condition_name(self, row_index): - condition_name = get_dgc_condition_name(alt=self.loc[row_index, 'alt'], - azi=self.loc[row_index, 'azi'], - sf=self.loc[row_index, 'sf'], - tf=self.loc[row_index, 'tf'], - dire=self.loc[row_index, 'dire'], - con=self.loc[row_index, 'con'], - rad=self.loc[row_index, 'rad']) - return condition_name +if __name__=='__main__': - def check_integrity(self): + plt.ioff() - if len(self.sta_ts.shape) != 1: - raise ValueError('self.sta_ts should be 1d array.') + #===================================================================== + # f = h5py.File(r"E:\data2\2015-07-02-150610-M160809-2P_analysis\cells_test.hdf5") + # STRF = load_STRF_FromH5(f['cell0003']['spatial_temporal_receptive_field']) + # ampRFON, ampRFOFF = STRF.get_amplitude_receptive_field() + # + # print ampRFON.sign + # print ampRFOFF.get_weighted_mask()[7,9] + # + # plt.imshow(ampRFON.get_weighted_mask(),interpolation='nearest') + # plt.show() + #===================================================================== - if self.duplicated(subset=['alt', 'azi', 'sf', 'tf', 'dire', 'con', 'rad']).any(): - raise ValueError('there is duplicated conditions.') + #===================================================================== + # f = h5py.File(r"E:\data2\2015-07-02-150610-M160809-2P_analysis\cells_test.hdf5") + # STRF = load_STRF_FromH5(f['cell0003']['spatial_temporal_receptive_field']) + # zscoreRFON, zscoreRFOFF = STRF.get_zscore_receptive_field() + # + # print zscoreRFON.sign + # print zscoreRFOFF.get_weighted_mask()[7,9] + # + # plt.imshow(zscoreRFON.get_weighted_mask(),interpolation='nearest') + # plt.show() + #===================================================================== - sta_ts_len = self.sta_ts.shape[0] + #===================================================================== + # f = h5py.File(r"E:\data2\2015-07-02-150610-M160809-2P_analysis\cells_test.hdf5") + # STRF = load_STRF_FromH5(f['cell0003']['spatial_temporal_receptive_field']) + # zscoreRFON, zscoreRFOFF = STRF.get_amplitude_receptive_field() + # + # zscoreRFON.interpolate(10) + # + # plt.imshow(zscoreRFON.get_weighted_mask(),interpolation='nearest') + # plt.show() + #===================================================================== - for row_i, row in self.iterrows(): + #===================================================================== + # f = h5py.File(r"E:\data2\2015-07-02-150610-M160809-2P_analysis\cells_test.hdf5") + # STRF = load_STRF_FromH5(f['cell0003']['spatial_temporal_receptive_field']) + # STRF.shrink([-10,10],None) + # print np.unique(np.array(STRF.get_locations())[:,0]) + # STRF.shrink(None,[0,20]) + # print np.unique(np.array(STRF.get_locations())[:,1]) + #===================================================================== - if len(row['onset_ts']) == 0: - pass - # print('condition: {}. No onset timestamps available.'.format(self.get_condition_name(row_i))) - else: - if (len(row['onset_ts'].shape) != 1): - raise ValueError( - 'condition: {}, onset_ts should be 1-d array.'.format(self.get_condition_name(row_i))) + # ===================================================================== + dfile = h5py.File(r"G:\2016-08-15-160815-M238599-wf2p-Retinotopy\sparse_noise_2p\cells_refined.hdf5", 'r') + strf = SpatialTemporalReceptiveField.from_h5_group(dfile['cell0519']['spatial_temporal_receptive_field']) - if row['matrix'].shape[0] != row['onset_ts'].shape[0]: - print('condition: {}, mismatched trial number ({}) and onset number ({}).' - .format(self.get_condition_name(row_i), row['matrix'].shape[0], - row['onset_ts'].shape[0])) + rf_on, rf_off, rf_all = strf.get_zscore_thresholded_receptive_fields(timeWindow=(0., 0.3), thr_ratio=0.4, + filter_sigma=1., interpolate_rate=10, + absolute_thr=0.8) - if len(row['matrix'].shape) != 2: - raise ValueError('condition: {}, onset_ts should be 2-d array.'.format(self.get_condition_name(row_i))) + peak_amplitude = max([np.amax(rf_on.get_weighted_mask()), np.amax(rf_off.get_weighted_mask())]) - if row['matrix'].shape[1] != sta_ts_len: - raise ValueError('condition: {}, mismatched trace length ({}) and sta ts length ({}).' - .format(self.get_condition_name(row_i), row['matrix'].shape[1], sta_ts_len)) + f = plt.figure(figsize=(6, 8)) + ax = f.add_subplot(111) + rf_on.plot_contour(ax, peak_amplitude=peak_amplitude, level_num=10, linewidths=1.5) + rf_off.plot_contour(ax, peak_amplitude=peak_amplitude, level_num=10, linewidths=1.5) + plt.show() - def get_df_response_matrix(self, baseline_win=(-0.5, 0.)): - """ - return df response matrix - :param baseline_win: - :return: - """ + # ===================================================================== - baseline_ind = np.logical_and(self.sta_ts > baseline_win[0], self.sta_ts <= baseline_win[1]) - - dgcrm_df = self.copy() - - for row_i, row in self.iterrows(): - curr_matrix = row['matrix'].astype(np.float64) - curr_baseline = np.mean(curr_matrix[:, baseline_ind], axis=1, keepdims=True) - dgcrm_df.loc[row_i, 'matrix'] = curr_matrix - curr_baseline - - return DriftingGratingResponseMatrix(sta_ts=self.sta_ts, trace_type='{}_df'.format(self.trace_type), - data=dgcrm_df) - - def get_zscore_response_matrix(self, baseline_win=(-0.5, 0.)): - """ - - return zscore response matrix, zscore is calculated as (trace - baseline_mean) / baseline_std - - :param baseline_win: - :return: - """ - - baseline_ind = np.logical_and(self.sta_ts > baseline_win[0], self.sta_ts <= baseline_win[1]) - - dgcrm_zscore = self.copy() - - for row_i, row in self.iterrows(): - curr_matrix = row['matrix'].astype(np.float64) - curr_baseline_mean = np.mean(curr_matrix[:, baseline_ind], axis=1, keepdims=True) - curr_baseline_std = np.std(curr_matrix[:, baseline_ind].flat) - dgcrm_zscore.loc[row_i, 'matrix'] = (curr_matrix - curr_baseline_mean) / curr_baseline_std - - return DriftingGratingResponseMatrix(sta_ts=self.sta_ts, trace_type='{}_zscore'.format(self.trace_type), - data=dgcrm_zscore) - - def get_dff_response_matrix(self, baseline_win=(-0.5, 0.), bias=0., warning_level=0.9): - """ - - return df over f response matrix - - :param baseline_win: - :param bias: float, a number added to all matrices before calculating df over f - :param warning_level: float, if the absolute value of the baseline of a given condition and a given trial is - smaller than this value, print a waring - :return: - """ - - baseline_ind = np.logical_and(self.sta_ts > baseline_win[0], self.sta_ts <= baseline_win[1]) - - dgcrm_dff = self.copy() - - for row_i, row in self.iterrows(): - curr_matrix = row['matrix'].astype(np.float64) - curr_matrix = curr_matrix + bias - dff_matrix = np.empty(curr_matrix.shape, dtype=np.float32) - for trial_i in range(curr_matrix.shape[0]): - curr_trial = curr_matrix[trial_i, :] - curr_baseline = np.mean(curr_trial[baseline_ind]) - - # print(curr_baseline) - if curr_baseline <= warning_level: - msg = '\ncondition:{}, trial:{}, baseline too low: {}'.format(self.get_condition_name(row_i), - trial_i, - curr_baseline) - warnings.warn(msg, RuntimeWarning) - - curr_trial_dff = (curr_trial - curr_baseline) / curr_baseline - dff_matrix[trial_i, :] = curr_trial_dff - - dgcrm_dff.loc[row_i, 'matrix'] = dff_matrix - - return DriftingGratingResponseMatrix(sta_ts=self.sta_ts, trace_type='{}_dff'.format(self.trace_type), - data=dgcrm_dff) - - def get_condition_trial_responses(self, condi_i, response_win=(0., 1.)): - """ - for a given condition specified by df index: condi_i, return responses for each trial - :param condi_i: int - :param response_win: list of two floats, time window to calculate responses - :return: 1d array, response of each trial for the specified condition - """ - - response_ind = np.logical_and(self.sta_ts > response_win[0], self.sta_ts <= response_win[1]) - - traces = self.loc[condi_i, 'matrix'] - - responses = np.mean(traces[:, response_ind], axis=1) - - return responses.squeeze() - - def collapse_trials(self): - - """ - calculate mean response for each condition across all trials - - :return: DriftingGratingResponseMatrix object - """ - - dgcrm_collapsed = self.copy() - - for row_i, row in self.iterrows(): - curr_matrix = row['matrix'] - dgcrm_collapsed.loc[row_i, 'matrix'] = np.mean(curr_matrix, axis=0, keepdims=True) - dgcrm_collapsed.loc[row_i, 'onset_ts'] = [] - - return DriftingGratingResponseMatrix(sta_ts=self.sta_ts, trace_type='{}_collapsed'.format(self.trace_type), - data=dgcrm_collapsed) - - def get_response_table(self, response_win=(0., 1.)): - - response_ind = np.logical_and(self.sta_ts > response_win[0], self.sta_ts <= response_win[1]) - - dgcrt = self.loc[:, ['alt', 'azi', 'sf', 'tf', 'dire', 'con', 'rad']] - dgcrt['resp_mean'] = np.nan - dgcrt['resp_max'] = np.nan - dgcrt['resp_min'] = np.nan - dgcrt['resp_std'] = np.nan - dgcrt['resp_stdev'] = np.nan - - for row_i, row in self.iterrows(): - - responses = np.mean(row['matrix'][:, response_ind], axis=1) - - dgcrt.loc[row_i, 'resp_mean'] = np.mean(responses) - dgcrt.loc[row_i, 'resp_max'] = np.max(responses) - dgcrt.loc[row_i, 'resp_min'] = np.min(responses) - - if len(responses) > 1: - dgcrt.loc[row_i, 'resp_std'] = np.std(responses) - dgcrt.loc[row_i, 'resp_stdev'] = np.std(responses) / np.sqrt(len(responses)) - - return DriftingGratingResponseTable(trace_type=self.trace_type, data=dgcrt) - - def get_df_response_table(self, baseline_win=(-0.5, 0.), response_win=(0., 1.)): - """ - this is suppose to give the most robust measurement of df response table. - - for each condition: - 1. mean_baseline is calculated by averaging across all trials and all data points in the baseline_win - 2. mean_response is calculated by averaging across all trials and all data points in the response_win - 3. df for every condition is defined by (mean_response - mean_baseline) and response table is generated - - # separate operation - 4. for each trial of each condition, df is calculated by (mean_response - mean_baseline) - 5. one-way anova is performed from these trial responses - 6. peak positive condition and peak negative condition is selected from previously generated response table - 7. ttest is performed for these two conditions against blank trial responses - - - :param baseline_win: - :param response_win: - :return df_response_table: - :return p_anova: - :return p_ttest_pos: - :return p_ttest_neg: - """ - - baseline_ind = np.logical_and(self.sta_ts > baseline_win[0], self.sta_ts <= baseline_win[1]) - response_ind = np.logical_and(self.sta_ts > response_win[0], self.sta_ts <= response_win[1]) - - dgcrt = self.loc[:, ['alt', 'azi', 'sf', 'tf', 'dire', 'con', 'rad']] - dgcrt['resp_mean'] = np.nan - dgcrt['resp_max'] = np.nan - dgcrt['resp_min'] = np.nan - dgcrt['resp_std'] = np.nan - dgcrt['resp_stdev'] = np.nan - - trial_responses = [] - - for row_i, row in self.iterrows(): - curr_matrix = row['matrix'] - baseline_mean = np.mean(curr_matrix[:, baseline_ind].astype(np.float64).flat) - response_mean = np.mean(curr_matrix[:, response_ind].astype(np.float64).flat) - dgcrt.loc[row_i, 'resp_mean'] = response_mean - baseline_mean - - baseline_trial = np.mean(curr_matrix[:, baseline_ind].astype(np.float64), axis=1) - response_trial = np.mean(curr_matrix[:, response_ind].astype(np.float64), axis=1) - curr_trial_responses = response_trial - baseline_trial - trial_responses.append(curr_trial_responses) - - dgcrt.loc[row_i, 'resp_max'] = np.max(curr_trial_responses) - dgcrt.loc[row_i, 'resp_min'] = np.min(curr_trial_responses) - dgcrt.loc[row_i, 'resp_std'] = np.std(curr_trial_responses) - dgcrt.loc[row_i, 'resp_stdev'] = np.std(curr_trial_responses) / np.sqrt(len(curr_trial_responses)) - - _, p_anova = stats.f_oneway(*trial_responses) - - df_response_table = DriftingGratingResponseTable(data=dgcrt, trace_type='{}_df'.format(self.trace_type)) - responses_blank = trial_responses[df_response_table.blank_condi_ind] - responses_peak_pos = trial_responses[df_response_table.peak_condi_ind_pos] - responses_peak_neg = trial_responses[df_response_table.peak_condi_ind_neg] - - n_min_pos = np.min([len(responses_blank), len(responses_peak_pos)]) - _, p_ttest_pos = stats.ttest_rel(responses_blank[0:n_min_pos], responses_peak_pos[0:n_min_pos]) - n_min_neg = np.min([len(responses_blank), len(responses_peak_neg)]) - _, p_ttest_neg = stats.ttest_rel(responses_blank[0:n_min_pos], responses_peak_neg[0:n_min_neg]) - - return df_response_table, p_anova, p_ttest_pos, p_ttest_neg - - def get_dff_response_table(self, baseline_win=(-0.5, 0.), response_win=(0., 1.), bias=0, warning_level=0.1): - """ - this is suppose to give the most robust measurement of df/f response table. - - for each condition: - 1. mean_baseline is calculated by averaging across all trials and all data points in the baseline_win - 2. mean_response is calculated by averaging across all trials and all data points in the response_win - 3. df/f for each condition is defined by - (mean_response - mean_baseline) / mean_baseline and response table is generated - - # separate operation - 4. for each trial of each condition, df is calculated by (mean_response - mean_baseline) / mean_baseline - 5. one-way anova is performed from these trial responses - 6. peak positive condition and peak negative condition is selected from previously generated response table - 7. ttest is performed for these two conditions against blank trial responses - - - :param baseline_win: - :param response_win: - :param bias: float, a constant added to all matrices - :param warning_level: float, warning level of low baseline - :return dff_response_table: - :return p_anova: - :return p_ttest_pos: - :return p_ttest_neg: - """ - - baseline_ind = np.logical_and(self.sta_ts > baseline_win[0], self.sta_ts <= baseline_win[1]) - response_ind = np.logical_and(self.sta_ts > response_win[0], self.sta_ts <= response_win[1]) - - dgcrt = self.loc[:, ['alt', 'azi', 'sf', 'tf', 'dire', 'con', 'rad']] - dgcrt['resp_mean'] = np.nan - dgcrt['resp_max'] = np.nan - dgcrt['resp_min'] = np.nan - dgcrt['resp_std'] = np.nan - dgcrt['resp_stdev'] = np.nan - - trial_responses = [] - - for row_i, row in self.iterrows(): - curr_matrix = row['matrix'] + bias - baseline_mean = np.mean(curr_matrix[:, baseline_ind].astype(np.float64).flat) - response_mean = np.mean(curr_matrix[:, response_ind].astype(np.float64).flat) - dgcrt.loc[row_i, 'resp_mean'] = (response_mean - baseline_mean) / baseline_mean - - if baseline_mean <= warning_level: - msg = '\ncondition:{}, mean baseline too low: {}'.format(self.get_condition_name(row_i), baseline_mean) - warnings.warn(msg, RuntimeWarning) - - - baseline_trial = np.mean(curr_matrix[:, baseline_ind].astype(np.float64), axis=1) - response_trial = np.mean(curr_matrix[:, response_ind].astype(np.float64), axis=1) - curr_trial_responses = (response_trial - baseline_trial) / baseline_trial - - if np.min(baseline_trial) <= warning_level: - msg = '\ncondition:{}, trial baseline too low: {}'.format(self.get_condition_name(row_i), - np.min(baseline_trial)) - warnings.warn(msg, RuntimeWarning) - - - trial_responses.append(curr_trial_responses) - dgcrt.loc[row_i, 'resp_max'] = np.max(curr_trial_responses) - dgcrt.loc[row_i, 'resp_min'] = np.min(curr_trial_responses) - dgcrt.loc[row_i, 'resp_std'] = np.std(curr_trial_responses) - dgcrt.loc[row_i, 'resp_stdev'] = np.std(curr_trial_responses) / np.sqrt(len(curr_trial_responses)) - - _, p_anova = stats.f_oneway(*trial_responses) - - dff_response_table = DriftingGratingResponseTable(data=dgcrt, trace_type='{}_df'.format(self.trace_type)) - responses_blank = trial_responses[dff_response_table.blank_condi_ind] - responses_peak_pos = trial_responses[dff_response_table.peak_condi_ind_pos] - responses_peak_neg = trial_responses[dff_response_table.peak_condi_ind_neg] - - n_min_pos = np.min([len(responses_blank), len(responses_peak_pos)]) - _, p_ttest_pos = stats.ttest_rel(responses_blank[0:n_min_pos], responses_peak_pos[0:n_min_pos]) - n_min_neg = np.min([len(responses_blank), len(responses_peak_neg)]) - _, p_ttest_neg = stats.ttest_rel(responses_blank[0:n_min_pos], responses_peak_neg[0:n_min_neg]) - - return dff_response_table, p_anova, p_ttest_pos, p_ttest_neg - - def get_zscore_response_table(self, baseline_win=(-0.5, 0.), response_win=(0., 1.)): - """ - this is suppose to give the most robust measurement of zscore response table. - - for each condition: - 1. mean_baseline is calculated by averaging across all trials and all data points in the baseline_win - 2. mean_response is calculated by averaging across all trials and all data points in the response_win - 3. mean_standard_deviation is calculated as following: - i. the baseline of each trial is normalized with zero mean - ii. normalized baselines are concatenated to a 1d array - iii. mean_standard_deviation is calculated from the concatenated baseline - 4. zscore for each condition is defined by - (mean_response - mean_baseline) / mean_standard_deviation and response table is generated - - # separate operation - 4. for each trial of each condition, zscore is calculated by (mean_response - mean_baseline) / mean_standard_deviation - 5. one-way anova is performed from these trial responses - 6. peak positive condition and peak negative condition is selected from previously generated response table - 7. ttest is performed for these two conditions against blank trial responses - - - :param baseline_win: - :param response_win: - :return zscore_response_table: - :return p_anova: - :return p_ttest_pos: - :return p_ttest_neg: - """ - - baseline_ind = np.logical_and(self.sta_ts > baseline_win[0], self.sta_ts <= baseline_win[1]) - response_ind = np.logical_and(self.sta_ts > response_win[0], self.sta_ts <= response_win[1]) - - dgcrt = self.loc[:, ['alt', 'azi', 'sf', 'tf', 'dire', 'con', 'rad']] - dgcrt['resp_mean'] = np.nan - dgcrt['resp_max'] = np.nan - dgcrt['resp_min'] = np.nan - dgcrt['resp_std'] = np.nan - dgcrt['resp_stdev'] = np.nan - - trial_responses = [] - - for row_i, row in self.iterrows(): - curr_matrix = row['matrix'] - - baseline = curr_matrix[:, baseline_ind].astype(np.float64) - baseline_trial = np.mean(baseline, axis=1, keepdims=True) - baseline_norm = baseline - baseline_trial - std_mean = np.std(baseline_norm.flat) - baseline_mean = np.mean(baseline_trial.flat) - response_mean = np.mean(curr_matrix[:, response_ind].astype(np.float64).flat) - dgcrt.loc[row_i, 'resp_mean'] = (response_mean - baseline_mean) / std_mean - - baseline_trial = np.mean(curr_matrix[:, baseline_ind].astype(np.float64), axis=1) - # std_trial = np.std(curr_matrix[:, baseline_ind].astype(np.float64), axis=1) - response_trial = np.mean(curr_matrix[:, response_ind].astype(np.float64), axis=1) - curr_trial_responses = (response_trial - baseline_trial) / std_mean - - trial_responses.append(curr_trial_responses) - dgcrt.loc[row_i, 'resp_max'] = np.max(curr_trial_responses) - dgcrt.loc[row_i, 'resp_min'] = np.min(curr_trial_responses) - dgcrt.loc[row_i, 'resp_std'] = np.std(curr_trial_responses) - dgcrt.loc[row_i, 'resp_stdev'] = np.std(curr_trial_responses) / np.sqrt(len(curr_trial_responses)) - - _, p_anova = stats.f_oneway(*trial_responses) - - zscore_response_table = DriftingGratingResponseTable(data=dgcrt, trace_type='{}_df'.format(self.trace_type)) - responses_blank = trial_responses[zscore_response_table.blank_condi_ind] - responses_peak_pos = trial_responses[zscore_response_table.peak_condi_ind_pos] - responses_peak_neg = trial_responses[zscore_response_table.peak_condi_ind_neg] - - n_min_pos = np.min([len(responses_blank), len(responses_peak_pos)]) - _, p_ttest_pos = stats.ttest_rel(responses_blank[0:n_min_pos], responses_peak_pos[0:n_min_pos]) - n_min_neg = np.min([len(responses_blank), len(responses_peak_neg)]) - _, p_ttest_neg = stats.ttest_rel(responses_blank[0:n_min_pos], responses_peak_neg[0:n_min_neg]) - - return zscore_response_table, p_anova, p_ttest_pos, p_ttest_neg - - def plot_traces(self, condi_ind, axis=None, blank_ind=None, block_dur=None, response_window=None, - baseline_window=None, trace_color='#ff0000', block_face_color='#aaaaaa', - response_window_color='#ff00ff', baseline_window_color='#888888', blank_trace_color='#888888', - lw_single=0.5, lw_mean=2.): - - if axis is None: - f = plt.figure() - axis = f.add_subplot(111) - - if block_dur is not None: - axis.axvspan(0., block_dur, color=block_face_color) - - if baseline_window is not None: - axis.axvline(x=baseline_window[0], linestyle='--', color=baseline_window_color, lw=1.5) - axis.axvline(x=baseline_window[1], linestyle='--', color=baseline_window_color, lw=1.5) - - if response_window is not None: - axis.axvline(x=response_window[0], linestyle='--', color=response_window_color, lw=1.5) - axis.axvline(x=response_window[1], linestyle='--', color=response_window_color, lw=1.5) - - ymin = None - ymax = None - - if blank_ind is not None: - traces_blank = self.loc[blank_ind, 'matrix'] - for t in traces_blank: - axis.plot(self.sta_ts, t, color=blank_trace_color, lw=lw_single) - - axis.plot(self.sta_ts, np.mean(traces_blank, axis=0), color=blank_trace_color, lw=lw_mean) - - ymin = np.amin(traces_blank) - ymax = np.amax(traces_blank) - - - traces = self.loc[condi_ind, 'matrix'] - for t in traces: - axis.plot(self.sta_ts, t, color=trace_color, lw=lw_single) - - if ymin is None: - ymin = np.amin(traces) - else: - ymin = min([ymin, np.amin(traces)]) - - if ymax is None: - ymax = np.amax(traces) - else: - ymax = max([ymax, np.amax(traces)]) - - axis.plot(self.sta_ts, np.mean(traces, axis=0), color=trace_color, lw=lw_mean) - axis.set_xlim([self.sta_ts[0], self.sta_ts[-1]]) - - return ymin, ymax - - -class DriftingGratingResponseTable(DataFrame): - """ - class for response table to drifting grating circle - contains responses to all conditions of one roi - - subclassed from pandas.DataFrame with more attribute: - trace_type: str, type of traces - - columns: - alt - float, altitute of circle center - azi - float, azimuth of circle center - sf - float, spatial frequency, cpd - tf - float, temporal frequency, Hz - dire - int, drifting direction, deg, 0 is to right, increase counter-clockwise - con - float, contrast, [0, 1] - rad - float, radius, deg - resp_mean - float, mean response to the condition - resp_max - float, max response to the condition - resp_min - float, min response to the condition - resp_std - float, standard deviation across trials - resp_stdev - float, standard error of mean across trials - """ - - def __init__(self, trace_type='', *args, **kwargs): - - super(DriftingGratingResponseTable, self).__init__(*args, **kwargs) - - self.trace_type = trace_type - - def get_peak_condi_params_pos(self): - ind = self.peak_condi_ind_pos - return self.loc[ind, ['sf', 'tf', 'dire', 'con', 'rad']] - - @property - def sfs(self): - return self['sf'].unique() - - @property - def tfs(self): - return self['tf'].unique() - - @property - def dires(self): - return self['dire'].unique() - - @property - def cons(self): - return self['con'].unique() - - @property - def rads(self): - return self['rad'].unique() - - @property - def blank_condi_ind(self): - """ - if more than one blank conditions found, raise error - :return: int, blank condition index. None if no blank condition found - """ - inds = [] - - for row_i, row in self.iterrows(): - if row['sf'] == 0.: - inds.append(row_i) - if row['tf'] == 0.: - inds.append(row_i) - if row['con'] == 0.: - inds.append(row_i) - if row['rad'] == 0.: - inds.append(row_i) - - inds = list(set(inds)) - - if len(inds) == 0: # no blank condition - return None - elif len(inds) == 1: # 1 blank condition - return inds[0] - else: - raise LookupError('more than one blank conditions found ({}).'.format(len(inds))) - - @property - def peak_condi_ind_pos(self): - """return the index of the condition with biggest postitive response (exclude the blank condition)""" - if self.blank_condi_ind is None: - return self['resp_mean'].argmax() - else: - return self.drop(self.blank_condi_ind)['resp_mean'].idxmax() - - @property - def peak_condi_ind_neg(self): - """return the index of the condition with biggest negative response (exclude the blank condition)""" - if self.blank_condi_ind is None: - return self['resp_mean'].argmin() - else: - return self.drop(self.blank_condi_ind)['resp_mean'].idxmin() - - @property - def peak_response_pos(self): - return self.loc[self.peak_condi_ind_pos, 'resp_mean'] - - @property - def peak_response_neg(self): - return self.loc[self.peak_condi_ind_neg, 'resp_mean'] - - @property - def peak_response_abs(self): - return np.max([abs(self.peak_response_pos), - abs(self.peak_response_neg)]) - - def get_sf_tf_matrix(self, response_dir='pos'): - """ - rerurn 2d array of sf/tf responses, rows: sf; cols: tf, other conditions are at peak in positive or negative - direction - :param response_dir: 'pos' or 'neg', response type to select peak condition - :return responses: 2d array of 'resp_mean' - :return sf_lst: 1d array, sf conditions - :return tf_lst: 1d array, tf conditions - """ - - if response_dir == 'pos': - ind_p = self.peak_condi_ind_pos - elif response_dir == 'neg': - ind_p = self.peak_condi_ind_neg - else: - raise LookupError('Do not understand response_dir ({}). Should be "pos" or "neg"'.format(response_dir)) - - alt_p = self.loc[ind_p, 'alt'] - azi_p = self.loc[ind_p, 'azi'] - dire_p = self.loc[ind_p, 'dire'] - con_p = self.loc[ind_p, 'con'] - rad_p = self.loc[ind_p, 'rad'] - - df_sub = self.loc[(self['alt'] == alt_p) & (self['azi'] == azi_p) & (self['dire'] == dire_p) & - (self['con'] == con_p) & (self['rad'] == rad_p)] - - sfs = list(df_sub['sf'].unique()) - sfs.sort() - tfs = list(df_sub['tf'].unique()) - tfs.sort() - - resps = np.zeros((len(sfs), len(tfs))) - resps[:] = np.nan - - for sf_i, sf in enumerate(sfs): - for tf_i, tf in enumerate(tfs): - - curr_condi = df_sub[(df_sub['sf'] == sf) & (df_sub['tf'] == tf)] - # print(curr_condi['resp_mean']) - - if not curr_condi.empty: - resps[sf_i, tf_i] = curr_condi['resp_mean'] - - return resps, sfs, tfs - - def get_dire_tuning(self, response_dir='pos', is_collapse_sf=True, is_collapse_tf=False): - """ - dataframe of direction responses, other conditions are at peak in positive or negative direction, if not - specified by is_collapse - :param is_collapse_sf: bool, - :param is_collapse_tf: bool, - :param response_dir: 'pos' or 'neg', response type to select peak condition - :return dire_tuning: dataframe with two columns: 'dire','resp_mean', 'resp_max', 'resp_min', 'resp_std', - 'resp_stdev' - """ - - if response_dir == 'pos': - ind_p = self.peak_condi_ind_pos - elif response_dir == 'neg': - ind_p = self.peak_condi_ind_neg - else: - raise LookupError('Do not understand response_dir ({}). Should be "pos" or "neg"'.format(response_dir)) - - alt_p = self.loc[ind_p, 'alt'] - azi_p = self.loc[ind_p, 'azi'] - sf_p = self.loc[ind_p, 'sf'] - tf_p = self.loc[ind_p, 'tf'] - con_p = self.loc[ind_p, 'con'] - rad_p = self.loc[ind_p, 'rad'] - - # print('sf_p: {}'.format(sf_p)) - # print('tf_p: {}'.format(tf_p)) - - df_sub = self.loc[(self['alt'] == alt_p) & (self['azi'] == azi_p) & (self['con'] == con_p) & - (self['rad'] == rad_p)] - - df_sub = df_sub[['sf', 'tf', 'dire', 'resp_mean', 'resp_max', 'resp_min', 'resp_std', 'resp_stdev']] - # print(df_sub) - - if is_collapse_sf: - df_sub = df_sub.groupby(['tf', 'dire']).mean().reset_index() - else: - df_sub = df_sub.loc[df_sub['sf'] == sf_p].drop('sf', axis=1) - - if is_collapse_tf: - df_sub = df_sub.groupby(['dire']).mean().reset_index() - else: - df_sub = df_sub.loc[df_sub['tf'] == tf_p].drop('tf', axis=1) - - # print(df_sub) - - return df_sub[['dire', 'resp_mean', 'resp_max', 'resp_min', 'resp_std', 'resp_stdev']].copy() - - def get_sf_tuning(self, response_dir='pos', is_collapse_tf=False, is_collapse_dire=False): - """ - dataframe of sf responses, other conditions are at peak in positive or negative direction, if not - specified by is_collapse - :param is_collapse_tf: bool, - :param is_collapse_dire: bool, - :param response_dir: 'pos' or 'neg', response type to select peak condition - :return sf_tuning: dataframe with two columns: 'sf','resp_mean', 'resp_max', 'resp_min', 'resp_std', - 'resp_stdev' - """ - - if response_dir == 'pos': - ind_p = self.peak_condi_ind_pos - elif response_dir == 'neg': - ind_p = self.peak_condi_ind_neg - else: - raise LookupError('Do not understand response_dir ({}). Should be "pos" or "neg"'.format(response_dir)) - - alt_p = self.loc[ind_p, 'alt'] - azi_p = self.loc[ind_p, 'azi'] - dire_p = self.loc[ind_p, 'dire'] - tf_p = self.loc[ind_p, 'tf'] - con_p = self.loc[ind_p, 'con'] - rad_p = self.loc[ind_p, 'rad'] - - df_sub = self.loc[(self['alt'] == alt_p) & (self['azi'] == azi_p) & (self['con'] == con_p) & - (self['rad'] == rad_p)] - - df_sub = df_sub[['sf', 'tf', 'dire', 'resp_mean', 'resp_max', 'resp_min', 'resp_std', 'resp_stdev']] - # print(df_sub) - - if is_collapse_tf: - df_sub = df_sub.groupby(['sf', 'dire']).mean().reset_index() - else: - df_sub = df_sub.loc[df_sub['tf'] == tf_p].drop('tf', axis=1) - - if is_collapse_dire: - df_sub = df_sub.groupby(['sf']).mean().reset_index() - else: - df_sub = df_sub.loc[df_sub['dire'] == dire_p].drop('dire', axis=1) - - # print(df_sub) - return df_sub[['sf', 'resp_mean', 'resp_max', 'resp_min', 'resp_std', 'resp_stdev']].copy() - - def get_tf_tuning(self, response_dir='pos', is_collapse_sf=False, is_collapse_dire=False): - """ - dataframe of tf responses, other conditions are at peak in positive or negative direction, if not - specified by is_collapse - :param is_collapse_sf: bool, - :param is_collapse_dire: bool, - :param response_dir: 'pos' or 'neg', response type to select peak condition - :return tf_tuning: dataframe with two columns: 'tf','resp_mean', 'resp_max', 'resp_min', 'resp_std', - 'resp_stdev' - """ - - if response_dir == 'pos': - ind_p = self.peak_condi_ind_pos - elif response_dir == 'neg': - ind_p = self.peak_condi_ind_neg - else: - raise LookupError('Do not understand response_dir ({}). Should be "pos" or "neg"'.format(response_dir)) - - alt_p = self.loc[ind_p, 'alt'] - azi_p = self.loc[ind_p, 'azi'] - dire_p = self.loc[ind_p, 'dire'] - sf_p = self.loc[ind_p, 'sf'] - con_p = self.loc[ind_p, 'con'] - rad_p = self.loc[ind_p, 'rad'] - - df_sub = self.loc[(self['alt'] == alt_p) & (self['azi'] == azi_p) & (self['con'] == con_p) & - (self['rad'] == rad_p)] - - df_sub = df_sub[['sf', 'tf', 'dire', 'resp_mean', 'resp_max', 'resp_min', 'resp_std', 'resp_stdev']] - # print(df_sub) - - if is_collapse_sf: - df_sub = df_sub.groupby(['tf', 'dire']).mean().reset_index() - else: - df_sub = df_sub.loc[df_sub['sf'] == sf_p].drop('sf', axis=1) - - if is_collapse_dire: - df_sub = df_sub.groupby(['tf']).mean().reset_index() - else: - df_sub = df_sub.loc[df_sub['dire'] == dire_p].drop('dire', axis=1) - - # print(df_sub) - return df_sub[['tf', 'resp_mean', 'resp_max', 'resp_min', 'resp_std', 'resp_stdev']].copy() - - @staticmethod - def get_dire_tuning_properties(dire_tuning, response_dir='pos', elevation_bias=0.): - """ - - input dire_tuning has two columns: directions in degrees and mean responses to each direction. - the responses are treated in 3 different ways - - 1. raw, no treatment - 2. elevated, if minimum is below elevation_bias, the whole curve will be elevated to have minimum - response equal to elevation_bias. - 3. rectified, the response smaller than 0 will be set as zero. - - because gOSI and gDSI calculation is independent of curve base line. so gOSI_raw and gDSI_raw represent - gOSI and gDSI in both raw and elevated conditions. - - :param dire_tuning: - :param response_dir: str, 'pos' or 'neg - :param elevation_bias: float, minimum response after elevation. - :return OSI_raw: - :return DSI_raw: - :return gOSI_raw: - :return gDSI_raw: - :return OSI_ele: - :return DSI_ele: - :return gOSI_ele: - :return gDSI_ele: - :return OSI_rec: - :return DSI_rec: - :return gOSI_rec: - :return gDSI_rec: - :return peak_dire_raw: optimal direction in tested conditions - :return vs_dire_raw: vector sum of raw direction responses - :return vs_dire_ele: vector sum of elevated direction response - :return vs_dire_rec: vactor sum of rectified direction responses - """ - - dire_tuning_2 = dire_tuning.copy() - - if response_dir == 'pos': - pass - elif response_dir == 'neg': - dire_tuning_2['resp_mean'] = -dire_tuning_2['resp_mean'] - else: - raise LookupError('Do not understand response_dir ({}). Should be "pos" or "neg"'.format(response_dir)) - - if np.max(dire_tuning_2['resp_mean']) <= 0.: # no positive response - return tuple([np.nan] * 16) - - else: - - dire_tuning_2['dire'] = dire_tuning_2['dire'] % 360 - arcs = np.array(list(dire_tuning_2['dire'] * np.pi / 180)) - - if np.min(dire_tuning_2['resp_mean']) < elevation_bias: - dire_tuning_2['resp_mean_ele'] = dire_tuning_2['resp_mean'] - np.min(dire_tuning_2['resp_mean']) + \ - elevation_bias - else: - dire_tuning_2['resp_mean_ele'] = dire_tuning_2['resp_mean'] - - dire_tuning_2['resp_mean_rec'] = dire_tuning_2['resp_mean'] - dire_tuning_2.loc[dire_tuning_2['resp_mean'] < 0, 'resp_mean_rec'] = 0 - - - # get orientation indices - peak_dire_raw_ind = dire_tuning_2['resp_mean'].argmax() - peak_dire_raw = dire_tuning_2.loc[peak_dire_raw_ind, 'dire'] - oppo_dire_ind = (dire_tuning_2['dire'] == ((peak_dire_raw + 180) % 360)).argmax() - othr_dire_ind_1 = (dire_tuning_2['dire'] == ((peak_dire_raw + 90) % 360)).argmax() - othr_dire_ind_2 = (dire_tuning_2['dire'] == ((peak_dire_raw - 90) % 360)).argmax() - - # get raw os tuning properties - peak_resp_raw = dire_tuning_2.loc[peak_dire_raw_ind, 'resp_mean'] - oppo_resp_raw = dire_tuning_2.loc[oppo_dire_ind, 'resp_mean'] - othr_resp_raw_1 = dire_tuning_2.loc[othr_dire_ind_1, 'resp_mean'] - othr_resp_raw_2 = dire_tuning_2.loc[othr_dire_ind_2, 'resp_mean'] - - # print('aaa, {}, {}, {}, {}'.format(peak_resp_raw, oppo_resp_raw, othr_resp_raw_1, othr_resp_raw_2)) - - othr_resp_raw = (othr_resp_raw_1 + othr_resp_raw_2) / 2. - - # print('othr_resp_raw, {}'.format(othr_resp_raw)) - # print('bbb, {}'.format(peak_resp_raw - othr_resp_raw)) - # print('ccc, {}'.format(peak_resp_raw + othr_resp_raw)) - - OSI_raw = (peak_resp_raw - othr_resp_raw) / (peak_resp_raw + othr_resp_raw) - DSI_raw = (peak_resp_raw - oppo_resp_raw) / (peak_resp_raw + oppo_resp_raw) - - resp_raw = np.array(list(dire_tuning_2['resp_mean'])) - vs_raw = np.sum(resp_raw * np.exp(1j * arcs)) / np.sum(resp_raw) - vs_dire_raw = (np.angle(vs_raw) * 180 / np.pi) % 360 - gDSI_raw = np.abs(vs_raw) - - vs2_raw = np.sum(resp_raw * np.exp(1j * 2 * arcs)) / np.sum(resp_raw) - gOSI_raw = np.abs(vs2_raw) - - # get elevated os properties - peak_resp_ele = dire_tuning_2.loc[peak_dire_raw_ind, 'resp_mean_ele'] - oppo_resp_ele = dire_tuning_2.loc[oppo_dire_ind, 'resp_mean_ele'] - othr_resp_ele_1 = dire_tuning_2.loc[othr_dire_ind_1, 'resp_mean_ele'] - othr_resp_ele_2 = dire_tuning_2.loc[othr_dire_ind_2, 'resp_mean_ele'] - - othr_resp_ele = (othr_resp_ele_1 + othr_resp_ele_2) / 2. - OSI_ele = (peak_resp_ele - othr_resp_ele) / (peak_resp_ele + othr_resp_ele) - DSI_ele = (peak_resp_ele - oppo_resp_ele) / (peak_resp_ele + oppo_resp_ele) - - resp_ele = np.array(list(dire_tuning_2['resp_mean_ele'])) - vs_ele = np.sum(resp_ele * np.exp(1j * arcs)) / np.sum(resp_ele) - vs_dire_ele = (np.angle(vs_ele) * 180 / np.pi) % 360 - gDSI_ele = np.abs(vs_ele) - - vs2_ele = np.sum(resp_ele * np.exp(1j * 2 * arcs)) / np.sum(resp_ele) - gOSI_ele = np.abs(vs2_ele) - - # get rectified os tuning properties - peak_resp_rec = dire_tuning_2.loc[peak_dire_raw_ind, 'resp_mean_rec'] - oppo_resp_rec = dire_tuning_2.loc[oppo_dire_ind, 'resp_mean_rec'] - othr_resp_rec_1 = dire_tuning_2.loc[othr_dire_ind_1, 'resp_mean_rec'] - othr_resp_rec_2 = dire_tuning_2.loc[othr_dire_ind_2, 'resp_mean_rec'] - - othr_resp_rec = (othr_resp_rec_1 + othr_resp_rec_2) / 2. - OSI_rec = (peak_resp_rec - othr_resp_rec) / (peak_resp_rec + othr_resp_rec) - DSI_rec = (peak_resp_rec - oppo_resp_rec) / (peak_resp_rec + oppo_resp_rec) - - resp_rec = np.array(list(dire_tuning_2['resp_mean_rec'])) - vs_rec = np.sum(resp_rec * np.exp(1j * arcs)) / np.sum(resp_rec) - vs_dire_rec = (np.angle(vs_rec) * 180 / np.pi) % 360 - gDSI_rec = np.abs(vs_rec) - - vs2_rec = np.sum(resp_rec * np.exp(1j * 2 * arcs)) / np.sum(resp_rec) - gOSI_rec = np.abs(vs2_rec) - - return OSI_raw, DSI_raw, gOSI_raw, gDSI_raw, OSI_ele, DSI_ele, gOSI_ele, gDSI_ele, OSI_rec, DSI_rec, \ - gOSI_rec, gDSI_rec, peak_dire_raw, vs_dire_raw, vs_dire_ele, vs_dire_rec - - @staticmethod - def get_tf_tuning_properties(tf_tuning, response_dir='pos', elevation_bias=0.): - """ - - :param tf_tuning: - :param response_dir: str, 'pos' or 'neg - :param elevation_bias: float, minimum response after elevation. - :return peak_tf_raw: tf condition (presented) with maxmium response - :return weighted_tf_raw: average tf conditions weighted by response - :return weighted_tf_log_raw: average tf conditions weighted by response (on log scale) - :return weighted_tf_ele: - :return weighted_tf_log_ele: - :return weighted_tf_rec: - :return weighted_tf_log_rec: - """ - - tf_tuning_2 = tf_tuning.copy() - - if response_dir == 'pos': - pass - elif response_dir == 'neg': - tf_tuning_2['resp_mean'] = -tf_tuning_2['resp_mean'] - else: - raise LookupError('Do not understand response_dir ({}). Should be "pos" or "neg"'.format(response_dir)) - - if np.max(tf_tuning_2['resp_mean']) <= 0.: - return tuple([np.nan] * 7) - else: - - if np.min(tf_tuning_2['resp_mean']) < elevation_bias: - tf_tuning_2['resp_mean_ele'] = tf_tuning_2['resp_mean'] -np.min(tf_tuning_2['resp_mean']) \ - + elevation_bias - else: - tf_tuning_2['resp_mean_ele'] = tf_tuning_2['resp_mean'] - - tf_tuning_2['resp_mean_rec'] = tf_tuning_2['resp_mean'] - tf_tuning_2.loc[tf_tuning_2['resp_mean'] < 0, 'resp_mean_rec'] = 0. - - peak_tf_raw_ind = tf_tuning_2['resp_mean'].argmax() - peak_tf_raw = tf_tuning_2.loc[peak_tf_raw_ind, 'tf'] - - tfs = tf_tuning_2['tf'].astype(np.float) - tfs_log = np.log(tfs) / np.log(2) - # print('aaa, {}'.format(tfs_log)) - - # get raw weight tuning - resp_raw = tf_tuning_2['resp_mean'].astype(np.float) - weighted_tf_raw = np.sum(tfs * resp_raw) / np.sum(resp_raw) - weighted_tf_log_raw = np.sum(tfs_log * resp_raw) / np.sum(resp_raw) - # print('bbb, {}'.format(weighted_tf_log_raw)) - # print('ccc, {}'.format(resp_raw)) - weighted_tf_log_raw = 2 ** weighted_tf_log_raw - - # get elevated weight tuning - resp_ele = tf_tuning_2['resp_mean_ele'].astype(np.float) - weighted_tf_ele = np.sum(tfs * resp_ele) / np.sum(resp_ele) - weighted_tf_log_ele = np.sum(tfs_log * resp_ele) / np.sum(resp_ele) - weighted_tf_log_ele = 2 ** weighted_tf_log_ele - - # get rectified weight tuning - resp_rec = tf_tuning_2['resp_mean_rec'].astype(np.float) - weighted_tf_rec = np.sum(tfs * resp_rec) / np.sum(resp_rec) - weighted_tf_log_rec = np.sum(tfs_log * resp_rec) / np.sum(resp_rec) - weighted_tf_log_rec = 2 ** weighted_tf_log_rec - - return peak_tf_raw, weighted_tf_raw, weighted_tf_log_raw, weighted_tf_ele, weighted_tf_log_ele, \ - weighted_tf_rec, weighted_tf_log_rec - - @staticmethod - def get_sf_tuning_properties(sf_tuning, response_dir='pos', elevation_bias=0.): - """ - - :param sf_tuning: - :param response_dir: str, 'pos' or 'neg - :param elevation_bias: float, minimum response after elevation. - :return peak_sf_raw: sf condition (presented) with maxmium response - :return weighted_sf_raw: average sf conditions weighted by response - :return weighted_sf_log_raw: average sf conditions weighted by response (on log scale) - :return weighted_sf_ele: - :return weighted_sf_log_ele: - :return weighted_sf_rec: - :return weighted_sf_log_rec: - """ - - sf_tuning_2 = sf_tuning.copy() - - if response_dir == 'pos': - pass - elif response_dir == 'neg': - sf_tuning_2['resp_mean'] = -sf_tuning_2['resp_mean'] - else: - raise LookupError('Do not understand response_dir ({}). Should be "pos" or "neg"'.format(response_dir)) - - if np.max(sf_tuning_2['resp_mean']) <= 0.: - return tuple([np.nan] * 7) - else: - - if np.min(sf_tuning_2['resp_mean']) < elevation_bias: - sf_tuning_2['resp_mean_ele'] = sf_tuning_2['resp_mean'] - np.min(sf_tuning_2['resp_mean']) \ - + elevation_bias - else: - sf_tuning_2['resp_mean_ele'] = sf_tuning_2['resp_mean'] - - sf_tuning_2['resp_mean_rec'] = sf_tuning_2['resp_mean'] - sf_tuning_2.loc[sf_tuning_2['resp_mean'] < 0, 'resp_mean_rec'] = 0. - - peak_sf_raw_ind = sf_tuning_2['resp_mean'].argmax() - peak_sf_raw = sf_tuning_2.loc[peak_sf_raw_ind, 'sf'] - - sfs = sf_tuning_2['sf'].astype(np.float) - sfs_log = np.log(sfs) / np.log(2) - - # get raw weight tuning - resp_raw = sf_tuning_2['resp_mean'].astype(np.float) - weighted_sf_raw = np.sum(sfs * resp_raw) / np.sum(resp_raw) - weighted_sf_log_raw = np.sum(sfs_log * resp_raw) / np.sum(resp_raw) - weighted_sf_log_raw = 2 ** weighted_sf_log_raw - - # get elevated weight tuning - resp_ele = sf_tuning_2['resp_mean_ele'].astype(np.float) - weighted_sf_ele = np.sum(sfs * resp_ele) / np.sum(resp_ele) - weighted_sf_log_ele = np.sum(sfs_log * resp_ele) / np.sum(resp_ele) - weighted_sf_log_ele = 2 ** weighted_sf_log_ele - - # get rectified weight tuning - resp_rec = sf_tuning_2['resp_mean_rec'].astype(np.float) - weighted_sf_rec = np.sum(sfs * resp_rec) / np.sum(resp_rec) - weighted_sf_log_rec = np.sum(sfs_log * resp_rec) / np.sum(resp_rec) - weighted_sf_log_rec = 2 ** weighted_sf_log_rec - - return peak_sf_raw, weighted_sf_raw, weighted_sf_log_raw, weighted_sf_ele, weighted_sf_log_ele, \ - weighted_sf_rec, weighted_sf_log_rec - - # @staticmethod - # def get_sf_tuning_properties_old(sf_tuning, response_dir='pos', is_rectify=True): - # """ - # :param sf_tuning: - # :param response_dir: str, 'pos' or 'neg - # :param is_rectify: bool, if True, responses below zero will be set as zero - # :return peak_sf_raw: sf condition (presented) with maxmium response - # :return peak_sf_linear: average sf conditions weighted by response amplitude - # :return peak_sf_log: average sf conditions weighted by response amplitude (on log scale) - # """ - # - # if response_dir == 'pos': - # pass - # elif response_dir == 'neg': - # sf_tuning['resp_mean'] = -sf_tuning['resp_mean'] - # else: - # raise LookupError('Do not understand response_dir ({}). Should be "pos" or "neg"'.format(response_dir)) - # - # if is_rectify: - # sf_tuning.loc[sf_tuning['resp_mean'] < 0., 'resp_mean'] = 0. - # - # if np.max(sf_tuning['resp_mean']) <= 0.: - # return tuple([np.nan] * 3) - # else: - # peak_sf_raw_ind = sf_tuning['resp_mean'].argmax() - # peak_sf_raw = sf_tuning.loc[peak_sf_raw_ind, 'sf'] - # - # - # sfs = sf_tuning['sf'].astype(np.float) - # sfs_log = np.log(sfs / 0.01) / np.log(2) - # resp = sf_tuning['resp_mean'].astype(np.float) - # - # peak_sf_linear = np.sum(sfs * resp) / np.sum(resp) - # - # peak_sf_log = np.sum(sfs_log * resp) / np.sum(resp) - # peak_sf_log = 2 ** peak_sf_log * 0.01 - # - # return peak_sf_raw, peak_sf_linear, peak_sf_log - - def plot_sf_tf_matrix(self, response_dir='pos', axis=None, cmap='RdBu_r', vmax=4, vmin=-4): - - if axis is None: - f = plt.figure() - axis = f.add_subplot(111) - - if response_dir == 'pos': - sftf, sfs, tfs = self.get_sf_tf_matrix(response_dir='pos') - elif response_dir == 'neg': - sftf, sfs, tfs = self.get_sf_tf_matrix(response_dir='neg') - else: - raise ValueError('Do not understand "response_dir" ({}). Should be "pos" or "neg".'.format(response_dir)) - - axis.imshow(sftf, cmap=cmap, vmax=vmax, vmin=vmin, interpolation='nearest') - axis.set_yticks(range(len(sfs))) - axis.set_yticklabels(sfs) - axis.set_xticks(range(len(tfs))) - axis.set_xticklabels(tfs) - axis.tick_params(length=0) - - def plot_dire_tuning(self, axis=None, response_dir='pos', is_collapse_sf=True, is_collapse_tf=False, - trace_color='#ff0000', postprocess='raw', is_plot_errbar=False, - is_normalize=False, **kwargs): - """ - - :param axis: - :param response_dir: - :param is_collapse_sf: - :param is_collapse_tf: - :param trace_color: - :param is_normalize: - :param postprocess: str, 'raw', 'elevate' or 'rectify' - 'raw': plot raw response - 'elevate': if there is response below zero, how curve will be elevated so the minimum - is zero - 'rectify': if there is response below zero, those responses will be set as zero. - :return: - """ - - if axis is None: - f = plt.figure() - axis = f.add_axes([0, 0, 1, 1], projection='polar') - - dire_tuning = self.get_dire_tuning(response_dir=response_dir, is_collapse_sf=is_collapse_sf, - is_collapse_tf=is_collapse_tf) - - dire_tuning = dire_tuning.sort_values(by='dire') - dire_tuning = dire_tuning.append(dire_tuning.iloc[0, :]) - dire_tuning['dire'] = dire_tuning['dire'] * np.pi / 180. - - if response_dir == 'neg': - dire_tuning['resp_mean'] = -dire_tuning['resp_mean'] - - # bias = -np.min(dire_tuning['resp_mean']) - # - # # print('bias: {}'.format(bias)) - # - # resp = dire_tuning['resp_mean'] + bias - - resp = dire_tuning['resp_mean'] - bias = 0. - - if postprocess == 'raw': - pass - elif postprocess == 'elevate': - if np.min(resp) < 0.: - resp = resp - np.min(resp) - bias = -np.min(resp) - elif postprocess == 'rectify': - resp[resp < 0] = 0 - else: - raise LookupError('do not understand "postprocess": ({}). should be "raw", ' - '"elevate" or "rectify".'.format(postprocess)) - - if is_normalize: - if is_plot_errbar: - raise ValueError('Cannot plot normalized tuning curve with error bar.') - else: - resp = resp / np.max(resp) - - if is_plot_errbar: - y1 = np.array(resp - dire_tuning['resp_stdev']) - y1[y1 < 0.] = 0. - y2 = np.array(resp + dire_tuning['resp_stdev']) - y2[y2 < 0.] = 0. - axis.fill_between(x=np.array(dire_tuning['dire']), y1=y1, y2=y2, - edgecolor='none', facecolor='#cccccc') - - axis.plot(dire_tuning['dire'], resp, '-', color=trace_color, **kwargs) - - axis.set_xticklabels([]) - ylim = axis.get_ylim() - ymax = np.ceil(ylim[1] * 100) / 100 - axis.set_ylim([0, ymax]) - axis.set_yticks([ymax]) - - return ymax - - -if __name__ == '__main__': - plt.ioff() - # ===================================================================== - # f = h5py.File(r"F:\data2\chandelier_cell_project\M441626\2019-04-03-deepscope\190403_M441626_110.nwb", 'r') - # f = h5py.File(r"G:\190410_M439943_110.nwb", 'r') - # f = h5py.File(r"G:\repacked\190410_M439943_110_repacked.nwb", 'r') - f = h5py.File(r"G:\repacked\180323_M360495_110_repacked.nwb", 'r') - dgcrm = get_dgc_response_matrix_from_nwb(f['analysis/response_table_001_DriftingGratingCircleRetinotopicMapping/plane0'], - roi_ind=0, - trace_type='sta_f_center_raw') - - dgcrt_zscore, _, _, _ = dgcrm.get_df_response_table(response_win=[0., 1.], baseline_win=[-0.5, 0.]) - - dgcrt_zscore.plot_dire_tuning(axis=None, response_dir='pos', is_collapse_sf=True, is_collapse_tf=False, - postprocess='elevate') - plt.show() - - dire_tuning = dgcrt_zscore.get_dire_tuning(response_dir='pos', is_collapse_sf=True, is_collapse_tf=False) - print(dire_tuning) - _ = DriftingGratingResponseTable.get_dire_tuning_properties(dire_tuning=dire_tuning, - response_dir='pos', - elevation_bias=0.) - OSI_raw, DSI_raw, gOSI_raw, gDSI_raw, OSI_ele, DSI_ele ,gOSI_ele, gDSI_ele, OSI_rec, DSI_rec, \ - gOSI_rec, gDSI_rec, peak_dire_raw, vs_dire_raw, vs_dire_ele, vs_dire_rec = _ - - print('\nOSI_raw: {}'.format(OSI_raw)) - print('DSI_raw: {}'.format(DSI_raw)) - print('gOSI_raw: {}'.format(gOSI_raw)) - print('gDSI_raw: {}'.format(gDSI_raw)) - print('\nOSI_ele: {}'.format(OSI_ele)) - print('DSI_ele: {}'.format(DSI_ele)) - print('gOSI_ele: {}'.format(gOSI_ele)) - print('gDSI_ele: {}'.format(gDSI_ele)) - print('\nOSI_rec: {}'.format(OSI_rec)) - print('DSI_rec: {}'.format(DSI_rec)) - print('gOSI_rec: {}'.format(gOSI_rec)) - print('gDSI_rec: {}'.format(gDSI_rec)) - print('\npeak_dire_raw: {}'.format(peak_dire_raw)) - print('\nvs_dire_raw: {}'.format(vs_dire_raw)) - print('\nvs_dire_ele: {}'.format(vs_dire_ele)) - print('\nvs_dire_rec: {}'.format(vs_dire_rec)) - - - sf_tuning = dgcrt_zscore.get_sf_tuning(response_dir='pos', is_collapse_tf=False, is_collapse_dire=False) - print - print(sf_tuning) - _ = DriftingGratingResponseTable.get_sf_tuning_properties(sf_tuning=sf_tuning, response_dir='pos', - elevation_bias=0.) - peak_sf_raw, weighted_sf_raw, weighted_sf_log_raw, weighted_sf_ele, weighted_sf_log_ele, \ - weighted_sf_rec, weighted_sf_log_rec = _ - print('\npeak_sf_raw: {}'.format(peak_sf_raw)) - print('weighted_sf_raw: {}'.format(weighted_sf_raw)) - print('weighted_sf_log_raw: {}'.format(weighted_sf_log_raw)) - print('weighted_sf_ele: {}'.format(weighted_sf_ele)) - print('weighted_sf_log_ele: {}'.format(weighted_sf_log_ele)) - print('weighted_sf_rec: {}'.format(weighted_sf_rec)) - print('weighted_sf_log_rec: {}'.format(weighted_sf_log_rec)) - - tf_tuning = dgcrt_zscore.get_tf_tuning(response_dir='pos', is_collapse_sf=False, is_collapse_dire=False) - print - print(tf_tuning) - _ = DriftingGratingResponseTable.get_tf_tuning_properties(tf_tuning=tf_tuning, response_dir='pos', - elevation_bias=0.) - peak_tf_raw, weighted_tf_raw, weighted_tf_log_raw, weighted_tf_ele, weighted_tf_log_ele, \ - weighted_tf_rec, weighted_tf_log_rec = _ - print('\npeak_tf_raw: {}'.format(peak_tf_raw)) - print('weighted_tf_raw: {}'.format(weighted_tf_raw)) - print('weighted_tf_log_raw: {}'.format(weighted_tf_log_raw)) - print('weighted_tf_ele: {}'.format(weighted_tf_ele)) - print('weighted_tf_log_ele: {}'.format(weighted_tf_log_ele)) - print('weighted_tf_rec: {}'.format(weighted_tf_rec)) - print('weighted_tf_log_rec: {}'.format(weighted_tf_log_rec)) - - # ===================================================================== - - # ===================================================================== - # f = h5py.File(r"E:\data2\2015-07-02-150610-M160809-2P_analysis\cells_test.hdf5") - # STRF = load_STRF_FromH5(f['cell0003']['spatial_temporal_receptive_field']) - # ampRFON, ampRFOFF = STRF.get_amplitude_receptive_field() - # - # print ampRFON.sign - # print ampRFOFF.get_weighted_mask()[7,9] - # - # plt.imshow(ampRFON.get_weighted_mask(),interpolation='nearest') - # plt.show() - # ===================================================================== - - # ===================================================================== - # f = h5py.File(r"E:\data2\2015-07-02-150610-M160809-2P_analysis\cells_test.hdf5") - # STRF = load_STRF_FromH5(f['cell0003']['spatial_temporal_receptive_field']) - # zscoreRFON, zscoreRFOFF = STRF.get_zscore_receptive_field() - # - # print zscoreRFON.sign - # print zscoreRFOFF.get_weighted_mask()[7,9] - # - # plt.imshow(zscoreRFON.get_weighted_mask(),interpolation='nearest') - # plt.show() - # ===================================================================== - - # ===================================================================== - # f = h5py.File(r"E:\data2\2015-07-02-150610-M160809-2P_analysis\cells_test.hdf5") - # STRF = load_STRF_FromH5(f['cell0003']['spatial_temporal_receptive_field']) - # zscoreRFON, zscoreRFOFF = STRF.get_amplitude_receptive_field() - # - # zscoreRFON.interpolate(10) - # - # plt.imshow(zscoreRFON.get_weighted_mask(),interpolation='nearest') - # plt.show() - # ===================================================================== - - # ===================================================================== - # f = h5py.File(r"E:\data2\2015-07-02-150610-M160809-2P_analysis\cells_test.hdf5") - # STRF = load_STRF_FromH5(f['cell0003']['spatial_temporal_receptive_field']) - # STRF.shrink([-10,10],None) - # print np.unique(np.array(STRF.get_locations())[:,0]) - # STRF.shrink(None,[0,20]) - # print np.unique(np.array(STRF.get_locations())[:,1]) - # ===================================================================== - - # ===================================================================== - # dfile = h5py.File(r"G:\2016-08-15-160815-M238599-wf2p-Retinotopy\sparse_noise_2p\cells_refined.hdf5", 'r') - # strf = SpatialTemporalReceptiveField.from_h5_group(dfile['cell0519']['spatial_temporal_receptive_field']) - # - # rf_on, rf_off, rf_all = strf.get_zscore_thresholded_receptive_fields(timeWindow=(0., 0.3), thr_ratio=0.4, - # filter_sigma=1., interpolate_rate=10, - # absolute_thr=0.8) - # - # peak_amplitude = max([np.amax(rf_on.get_weighted_mask()), np.amax(rf_off.get_weighted_mask())]) - # - # f = plt.figure(figsize=(6, 8)) - # ax = f.add_subplot(111) - # rf_on.plot_contour(ax, peak_amplitude=peak_amplitude, level_num=10, linewidths=1.5) - # rf_off.plot_contour(ax, peak_amplitude=peak_amplitude, level_num=10, linewidths=1.5) - # plt.show() - - # ===================================================================== - print '\nfor debug...' + print('for debug...') \ No newline at end of file diff --git a/corticalmapping/VasculatureMapMatching.py b/corticalmapping/VasculatureMapMatching.py index 2bb2cb0..e911650 100644 --- a/corticalmapping/VasculatureMapMatching.py +++ b/corticalmapping/VasculatureMapMatching.py @@ -28,8 +28,8 @@ import tifffile as tf import corticalmapping.core.PlottingTools as pt -try: import cv2; from core.ImageAnalysis import rigid_transform_cv2 as rigid_transform -except ImportError as e: print e; from core.ImageAnalysis import rigid_transform as rigid_transform +try: import cv2; from .core.ImageAnalysis import rigid_transform_cv2 as rigid_transform +except ImportError as e: print(e); from .core.ImageAnalysis import rigid_transform as rigid_transform class AppForm(QMainWindow): @@ -81,7 +81,7 @@ def __init__(self, parent=None): def save_alignment_json(self): - path = unicode(QFileDialog.getSaveFileName(self, + path = str(QFileDialog.getSaveFileName(self, 'Save file', self.currSaveFolder, '*.json')) @@ -177,7 +177,7 @@ def get_RPath(self): try: if len(fnames) == 0: # no file is chosen - print "no file is chosen! Setting reference map as None..." + print("no file is chosen! Setting reference map as None...") self.textbrowser_RPath.clear() self.ReferenceVasMap = None @@ -204,7 +204,7 @@ def get_RPath(self): self.ReferenceVasMap = pt.merge_normalized_images([currMap[0]]) self.textbrowser_RPath.setText(filePath) else: - print 'Can not read reference map '+filePath + print('Can not read reference map '+filePath) self.textbrowser_RPath.clear() self.ReferenceVasMap = None @@ -226,21 +226,21 @@ def get_RPath(self): elif 'JCam' in fileName: currMap, _ = ft.importRawJCam(filePath) else: - print 'Can not read '+filePath + print('Can not read '+filePath) mapList.append(currMap[0].astype(np.float32)) if len(mapList) == 0: - print "no file can be read! Setting reference map as None..." + print("no file can be read! Setting reference map as None...") self.textbrowser_RPath.clear() self.ReferenceVasMap = None else: self.ReferenceVasMap = pt.merge_normalized_images(mapList).astype(np.float32) self.textbrowser_RPath.setText(displayText) - except Exception, e: - print e, '\n\n' - print 'Can not load reference Map! Setting it as None...' + except Exception as e: + print(e, '\n\n') + print('Can not load reference Map! Setting it as None...') self.textbrowser_RPath.clear() self.ReferenceVasMap = None @@ -268,7 +268,7 @@ def get_MPath(self): try: if len(fnames) == 0: # no file is chosen - print "no file is chosen! Setting matching map as None..." + print("no file is chosen! Setting matching map as None...") self.textbrowser_MPath.clear() self.MatchingVasMap = None self.MatchingVasMapRaw = None @@ -303,7 +303,7 @@ def get_MPath(self): self.MatchingVasMapRaw = currMap[0] self.textbrowser_MPath.setText(filePath) else: - print 'Can not read matching map '+filePath + print('Can not read matching map '+filePath) self.textbrowser_MPath.clear() self.MatchingVasMap = None self.MatchingVasMapAfterChange = None @@ -325,12 +325,12 @@ def get_MPath(self): elif 'JCam' in fileName: currMap, _ = ft.importRawJCam(filePath) else: - print 'Can not read '+filePath + print('Can not read '+filePath) mapList.append(currMap[0].astype(np.float32)) if len(mapList) == 0: - print "no file can be read! Setting matching map as None..." + print("no file can be read! Setting matching map as None...") self.textbrowser_MPath.clear() self.MatchingVasMap = None self.MatchingVasMapRaw = None @@ -341,9 +341,9 @@ def get_MPath(self): self.textbrowser_MPath.setText(displayText) self.MatchingVasMapAfterChange = None - except Exception, e: - print e, '\n\n' - print 'Can not load matching Map! Setting it as None...' + except Exception as e: + print(e, '\n\n') + print('Can not load matching Map! Setting it as None...') self.textbrowser_MPath.clear() self.MatchingVasMap = None self.MatchingVasMapRaw = None diff --git a/corticalmapping/VisualStim.py b/corticalmapping/VisualStim.py index 2b96398..c4f0584 100644 --- a/corticalmapping/VisualStim.py +++ b/corticalmapping/VisualStim.py @@ -16,19 +16,19 @@ from random import shuffle import socket -import tifffile as tf -import core.FileTools as ft -import core.ImageAnalysis as ia +#import tifffile as tf +from . import core.FileTools as ft +from . import core.ImageAnalysis as ia -from zro import RemoteObject, Proxy +#from zro import RemoteObject, Proxy try: import toolbox.IO.nidaq as iodaq except ImportError as e: - print e - print 'import iodaq from aibs package...' + print(e) + print('import iodaq from aibs package...') try: import aibs.iodaq as iodaq - except ImportError as er: print er + except ImportError as er: print(er) @@ -62,7 +62,7 @@ def analyze_frames(ts, refreshRate, checkPoint=(0.02, 0.033, 0.05, 0.1)): frameNumber = len(frameDuration[frameDuration>checkNumber]) frame_stats += 'Number of frames longer than %d ms: %d; %.2f%% \n' % (round(checkNumber*1000), frameNumber, round(frameNumber*10000/(len(ts)-1))/100) - print frame_stats + print(frame_stats) return frameDuration, frame_stats @@ -84,7 +84,7 @@ def noise_movie(frameFilter, widthFilter, heightFilter, isplot = False): filterXY = filterX * filterY - for i in xrange(rawMovFFT.shape[0]): + for i in range(rawMovFFT.shape[0]): rawMovFFT[i] = frameFilter[i]* (rawMovFFT[i] * filterXY) @@ -99,7 +99,7 @@ def noise_movie(frameFilter, widthFilter, heightFilter, isplot = False): noise_movie = ((filteredMov - np.amin(filteredMov)) / rangeFilteredMov) * 2 - 1 if isplot: - tf.imshow(noise_movie, vmin=-1, vmax=1, cmap='gray') + print('no tiffs 4 u')# tf.imshow(noise_movie, vmin=-1, vmax=1, cmap='gray') return noise_movie @@ -118,7 +118,7 @@ def generate_filter(length, # length of filter filterArray = np.ones(length) - for i in xrange(len(freqs)): + for i in range(len(freqs)): if ((freqs[i] > 0) and (freqs[i] < Flow) or (freqs[i] > Fhigh)) or \ ((freqs[i] < 0) and (freqs[i] > -Flow) or (freqs[i] < -Fhigh)): filterArray[i] = 0 @@ -129,7 +129,7 @@ def generate_filter(length, # length of filter filterArray = (filterArray - np.amin(filterArray)) / (np.amax(filterArray) - np.amin(filterArray)) elif mode == 'box': filterArray[0] = 0 - else: raise NameError, 'Variable "mode" should be either "1/f" or "box"!' + else: raise NameError('Variable "mode" should be either "1/f" or "box"!') if Flow == 0: filterArray[0] = 1 @@ -143,10 +143,10 @@ def lookup_image(img, lookupI, lookupJ): """ if not img.shape == lookupI.shape: - raise LookupError, 'The image and lookupI should have same size!!' + raise LookupError('The image and lookupI should have same size!!') if not lookupI.shape == lookupJ.shape: - raise LookupError, 'The lookupI and lookupJ should have same size!!' + raise LookupError('The lookupI and lookupJ should have same size!!') img2 = np.zeros(img.shape) @@ -184,7 +184,7 @@ def get_warped_square(degCorX,degCorY,center,width,height,ori,foregroundColor=1, frame = np.ones(degCorX.shape,dtype=np.float32)*backgroundColor - if ori < 0. or ori > 180.: raise ValueError, 'ori should be between 0 and 180.' + if ori < 0. or ori > 180.: raise ValueError('ori should be between 0 and 180.') k1 = np.tan(ori*np.pi/180.) k2 = np.tan((ori+90.)*np.pi/180.) @@ -209,9 +209,9 @@ def circle_mask(map_x, map_y, center, radius): :return: binary mask for the circle, value range [0., 1.] """ - if map_x.shape != map_y.shape: raise ValueError, 'map_x and map_y should have same shape!' + if map_x.shape != map_y.shape: raise ValueError('map_x and map_y should have same shape!') - if len(map_x.shape) != 2: raise ValueError, 'map_x and map_y should be 2-d!!' + if len(map_x.shape) != 2: raise ValueError('map_x and map_y should be 2-d!!') circle_mask = np.zeros(map_x.shape, dtype = np.uint8) for (i, j), value in np.ndenumerate(circle_mask): @@ -235,9 +235,9 @@ def get_grating(map_x, map_y, ori=0., spatial_freq=0.1, center=(0.,60.), phase=0 :return: a frame as floating point 2-d array with grating, value range [0., 1.] """ - if map_x.shape != map_y.shape: raise ValueError, 'map_x and map_y should have same shape!' + if map_x.shape != map_y.shape: raise ValueError('map_x and map_y should have same shape!') - if len(map_x.shape) != 2: raise ValueError, 'map_x and map_y should be 2-d!!' + if len(map_x.shape) != 2: raise ValueError('map_x and map_y should be 2-d!!') map_x_h = np.array(map_x, dtype = np.float32) map_y_h = np.array(map_y, dtype = np.float32) @@ -276,7 +276,7 @@ def __init__(self, refreshRate = 60.): if resolution[0] % downSampleRate != 0 or resolution[1] % downSampleRate != 0: - raise ArithmeticError, 'Resolution pixel numbers are not divisible by down sampling rate' + raise ArithmeticError('Resolution pixel numbers are not divisible by down sampling rate') self.resolution = resolution self.dis = dis @@ -304,7 +304,7 @@ def __init__(self, resolution[0]=self.resolution[0]/downSampleRate resolution[1]=self.resolution[1]/downSampleRate - mapcorX, mapcorY = np.meshgrid(range(resolution[1]), range(resolution[0])) + mapcorX, mapcorY = np.meshgrid(list(range(resolution[1])), list(range(resolution[0]))) if self.visualField == "left": mapX = np.linspace(self.C2Acm, -1.0 * self.C2Pcm, resolution[1]) @@ -331,7 +331,7 @@ def set_downsample_rate(self, downSampleRate): if self.resolution[0] % downSampleRate != 0 or self.resolution[1] % downSampleRate != 0: - raise ArithmeticError, 'resolutionolution pixel numbers are not divisible by down sampling rate' + raise ArithmeticError('resolutionolution pixel numbers are not divisible by down sampling rate') self.downSampleRate=downSampleRate @@ -339,7 +339,7 @@ def set_downsample_rate(self, downSampleRate): resolution[0]=self.resolution[0]/downSampleRate resolution[1]=self.resolution[1]/downSampleRate - mapcorX, mapcorY = np.meshgrid(range(resolution[1]), range(resolution[0])) + mapcorX, mapcorY = np.meshgrid(list(range(resolution[1])), list(range(resolution[0]))) if self.visualField == "left": mapX = np.linspace(self.C2Acm, -1.0 * self.C2Pcm, resolution[1]) @@ -362,7 +362,7 @@ def remap(self): resolution[0]=self.resolution[0]/self.downSampleRate resolution[1]=self.resolution[1]/self.downSampleRate - mapcorX, mapcorY = np.meshgrid(range(resolution[1]), range(resolution[0])) + mapcorX, mapcorY = np.meshgrid(list(range(resolution[1])), list(range(resolution[0]))) newmapX = np.zeros(resolution,dtype=np.float16) newmapY = np.zeros(resolution,dtype=np.float16) @@ -384,7 +384,7 @@ def plot_map(self): resolution[0]=self.resolution[0]/self.downSampleRate resolution[1]=self.resolution[1]/self.downSampleRate - mapcorX, mapcorY = np.meshgrid(range(resolution[1]), range(resolution[0])) + mapcorX, mapcorY = np.meshgrid(list(range(resolution[1])), list(range(resolution[0]))) f1 = plt.figure(figsize=(12,5)) f1.suptitle('Remap monitor', fontsize=14, fontweight='bold') @@ -392,7 +392,7 @@ def plot_map(self): OMX = plt.subplot(221) OMX.set_title('Linear Map X (cm)') currfig = plt.imshow(self.linCorX) - levels1 = range(int(np.floor(self.linCorX.min() / 10) * 10), int((np.ceil(self.linCorX.max() / 10)+1) * 10), 10) + levels1 = list(range(int(np.floor(self.linCorX.min() / 10) * 10), int((np.ceil(self.linCorX.max() / 10)+1) * 10), 10)) im1 =plt.contour(mapcorX, mapcorY, self.linCorX, levels1, colors = 'k', linewidth = 2) # plt.clabel(im1, levels1, fontsize = 10, inline = 1, fmt='%2.1f') f1.colorbar(currfig,ticks=levels1) @@ -401,7 +401,7 @@ def plot_map(self): OMY = plt.subplot(222) OMY.set_title('Linear Map Y (cm)') currfig = plt.imshow(self.linCorY) - levels2 = range(int(np.floor(self.linCorY.min() / 10) * 10), int((np.ceil(self.linCorY.max() / 10)+1) * 10), 10) + levels2 = list(range(int(np.floor(self.linCorY.min() / 10) * 10), int((np.ceil(self.linCorY.max() / 10)+1) * 10), 10)) im2 =plt.contour(mapcorX, mapcorY, self.linCorY, levels2, colors = 'k', linewidth = 2) # plt.clabel(im2, levels2, fontsize = 10, inline = 1, fmt='%2.2f') f1.colorbar(currfig,ticks=levels2) @@ -410,7 +410,7 @@ def plot_map(self): NMX = plt.subplot(223) NMX.set_title('Spherical Map X (deg)') currfig = plt.imshow(self.degCorX) - levels3 = range(int(np.floor(self.degCorX.min() / 10) * 10), int((np.ceil(self.degCorX.max() / 10)+1) * 10), 10) + levels3 = list(range(int(np.floor(self.degCorX.min() / 10) * 10), int((np.ceil(self.degCorX.max() / 10)+1) * 10), 10)) im3 =plt.contour(mapcorX, mapcorY, self.degCorX, levels3, colors = 'k', linewidth = 2) # plt.clabel(im3, levels3, fontsize = 10, inline = 1, fmt='%2.1f') f1.colorbar(currfig,ticks=levels3) @@ -419,7 +419,7 @@ def plot_map(self): NMY = plt.subplot(224) NMY.set_title('Spherical Map Y (deg)') currfig = plt.imshow(self.degCorY) - levels4 = range(int(np.floor(self.degCorY.min() / 10) * 10), int((np.ceil(self.degCorY.max() / 10)+1) * 10), 10) + levels4 = list(range(int(np.floor(self.degCorY.min() / 10) * 10), int((np.ceil(self.degCorY.max() / 10)+1) * 10), 10)) im4 =plt.contour(mapcorX, mapcorY, self.degCorY, levels4, colors = 'k', linewidth = 2) # plt.clabel(im4, levels4, fontsize = 10, inline = 1, fmt='%2.1f') f1.colorbar(currfig,ticks=levels4) @@ -450,13 +450,13 @@ def generate_Lookup_table(self): lookupI = np.zeros(degCorX.shape).astype(np.int32) lookupJ = np.zeros(degCorX.shape).astype(np.int32) - for j in xrange(lookupI.shape[1]): + for j in range(lookupI.shape[1]): currDegX = degCorX[0,j] diffDegX = degNoWarpCorX[0,:] - currDegX IndJ = np.argmin(np.abs(diffDegX)) lookupJ[:,j] = IndJ - for i in xrange(lookupI.shape[0]): + for i in range(lookupI.shape[0]): currDegY = degCorY[i,j] diffDegY = degNoWarpCorY[:,IndJ] - currDegY indI = np.argmin(np.abs(diffDegY)) @@ -524,7 +524,7 @@ def get_center(self): centerH = screen_height - self.height_pixel / 2 else: - raise LookupError, '"position" attributor should be "northeast", "southeast", "northwest" and "southwest"' + raise LookupError('"position" attributor should be "northeast", "southeast", "northwest" and "southwest"') return int(centerW), int(centerH) @@ -538,7 +538,7 @@ def get_frames(self): refreshRate = self.monitor.refreshRate if refreshRate % self.freq != 0: - raise ArithmeticError, "self update frequency of should be divisible by monitor's refresh rate." + raise ArithmeticError("self update frequency of should be divisible by monitor's refresh rate.") return refreshRate/self.freq @@ -575,17 +575,17 @@ def generate_frames(self): """ place holder of function "generate_frames" for each specific stimulus """ - print 'Nothing executed! This is place holder of function "generate_frames" for each specific stimulus.' - print 'This function should return a list of tuples, each tuple represents a single frame of the stimulus and contains all the information to recreate the frame.' + print('Nothing executed! This is place holder of function "generate_frames" for each specific stimulus.') + print('This function should return a list of tuples, each tuple represents a single frame of the stimulus and contains all the information to recreate the frame.') def generate_movie(self): """ place holder of function "generate_movie" for each specific stimulus """ - print 'Nothing executed! This is place holder of function "generate_movie" for each specific stimulus.' - print 'This function should return two things:' - print 'First: a 3-d array (with format of uint8) of the stimulus to be displayed.' - print 'Second: a dictionary contain the information of this particular stimulus' + print('Nothing executed! This is place holder of function "generate_movie" for each specific stimulus.') + print('This function should return two things:') + print('First: a 3-d array (with format of uint8) of the stimulus to be displayed.') + print('Second: a dictionary contain the information of this particular stimulus') def clear(self): self.frames = None @@ -653,7 +653,7 @@ def generate_movie(self): dtype=np.float16) if not (self.coordinate == 'degree' or self.coordinate == 'linear'): - raise LookupError, 'the "coordinate" attributate show be either "degree" or "linear"' + raise LookupError('the "coordinate" attributate show be either "degree" or "linear"') for i in range(len(self.frames)): currFrame = self.frames[i] @@ -668,7 +668,7 @@ def generate_movie(self): fullSequence[i] = currFCsequence if i in range(0, len(self.frames), len(self.frames) / 10): - print ['Generating numpy sequence: ' + str(int(100 * (i + 1) / len(self.frames))) + '%'] + print(['Generating numpy sequence: ' + str(int(100 * (i + 1) / len(self.frames))) + '%']) mondict = dict(self.monitor.__dict__) indicatordict = dict(self.indicator.__dict__) @@ -740,7 +740,7 @@ def generate_squares(self): mapY = self.monitor.linCorY else: - raise LookupError, 'the "coordinate" attributate show be either "degree" or "linear"' + raise LookupError('the "coordinate" attributate show be either "degree" or "linear"') minX = mapX.min() maxX = mapX.max() @@ -799,7 +799,7 @@ def generate_sweeps(self): mapX = self.monitor.linCorX mapY = self.monitor.linCorY else: - raise LookupError, 'the "coordinate" attributate show be either "degree" or "linear"' + raise LookupError('the "coordinate" attributate show be either "degree" or "linear"') minX = mapX.min() maxX = mapX.max() @@ -818,7 +818,7 @@ def generate_sweeps(self): stepX = np.arange(minX - sweepWidth, maxX + stepWidth, stepWidth)[::-1] # stepX = np.arange(maxX, minX - sweepWidth - stepWidth, -1 * stepWidth) else: - raise LookupError, 'attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".' + raise LookupError('attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".') sweepTable = [] @@ -954,7 +954,7 @@ def generate_movie(self): fullSequence[i] = currNMsequence if i in range(0, len(self.frames),len(self.frames)/10): - print ['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%'] + print(['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%']) mondict=dict(self.monitor.__dict__) @@ -980,7 +980,7 @@ def set_direction(self,direction): self.direction = direction self.clear() else: - raise LookupError, 'attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".' + raise LookupError('attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".') def set_sweep_sigma(self,sweepSigma): self.sweepSigma = sweepSigma @@ -1096,7 +1096,7 @@ def generate_sweeps(self): mapY = self.monitor.linCorY else: - raise LookupError, 'the "coordinate" attributate show be either "degree" or "linear"' + raise LookupError('the "coordinate" attributate show be either "degree" or "linear"') minX = mapX.min() maxX = mapX.max() @@ -1115,7 +1115,7 @@ def generate_sweeps(self): stepX = np.arange(minX - edgeWidth - sweepWidth / 2, maxX + edgeWidth + stepWidth + sweepWidth / 2, stepWidth)[::-1] # stepX = np.arange(maxX + edgeWidth + sweepWidth / 2, minX - edgeWidth - stepWidth - sweepWidth / 2, -1 * stepWidth) else: - raise LookupError, 'attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".' + raise LookupError('attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".') sweepTable = [] @@ -1264,7 +1264,7 @@ def generate_movie(self): fullSequence[i] = currNMsequence if i in range(0, len(self.frames),len(self.frames)/10): - print ['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%'] + print(['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%']) mondict=dict(self.monitor.__dict__) @@ -1288,7 +1288,7 @@ def set_direction(self,direction): self.direction = direction self.clear() else: - raise LookupError, 'attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".' + raise LookupError('attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".') def set_sweep_sigma(self,sweepSigma): self.sweepSigma = sweepSigma @@ -1358,7 +1358,7 @@ def generate_squares(self): mapY = self.monitor.linCorY else: - raise LookupError, 'the "coordinate" attributate show be either "degree" or "linear"' + raise LookupError('the "coordinate" attributate show be either "degree" or "linear"') minX = mapX.min() maxX = mapX.max() @@ -1418,7 +1418,7 @@ def generate_sweeps(self): mapY = self.monitor.linCorY else: - raise LookupError, 'the "coordinate" attributate show be either "degree" or "linear"' + raise LookupError('the "coordinate" attributate show be either "degree" or "linear"') all_x = mapX.flatten(); all_y = mapY.flatten() rotation_matrix = np.array([[np.cos(self.rotation_angle), np.sin(self.rotation_angle)], @@ -1442,7 +1442,7 @@ def generate_sweeps(self): stepX = np.arange(min_x_r - sweepWidth, max_x_r + stepWidth, stepWidth)[::-1] # stepX = np.arange(maxX, minX - sweepWidth - stepWidth, -1 * stepWidth) else: - raise LookupError, 'attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".' + raise LookupError('attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".') sweepTable = [] @@ -1578,7 +1578,7 @@ def generate_movie(self): fullSequence[i] = currNMsequence if i in range(0, len(self.frames),len(self.frames)/10): - print ['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%'] + print(['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%']) mondict=dict(self.monitor.__dict__) @@ -1604,7 +1604,7 @@ def set_direction(self,direction): self.direction = direction self.clear() else: - raise LookupError, 'attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".' + raise LookupError('attribute "direction" should be "B2U", "U2B", "L2R" or "R2L".') def set_sweep_sigma(self,sweepSigma): self.sweepSigma = sweepSigma @@ -1695,7 +1695,7 @@ def generate_frames(self): #initilize indicator color frames[:,3] = -1 - for i in xrange(frames.shape[0]): + for i in range(frames.shape[0]): # current iteration number frames[i,2] = i // iterationFrameNum @@ -1759,7 +1759,7 @@ def generate_movie(self): fullSequence[i] = currFNsequence if i in range(0, len(self.frames),len(self.frames)/10): - print ['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%'] + print(['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%']) mondict=dict(self.monitor.__dict__) indicatordict=dict(self.indicator.__dict__) @@ -1893,7 +1893,7 @@ def generate_frames(self): #initilize indicator color frames[:,3] = -1 - for i in xrange(frames.shape[0]): + for i in range(frames.shape[0]): # current iteration number frames[i,2] = i // iterationFrameNum @@ -1973,7 +1973,7 @@ def generate_movie(self): fullSequence[i] = currGNsequence if i in range(0, len(self.frames),len(self.frames)/10): - print ['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%'] + print(['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%']) mondict=dict(self.monitor.__dict__) indicatordict=dict(self.indicator.__dict__) @@ -2073,7 +2073,7 @@ def generate_frames(self): #initilize indicator color frames[:,3] = -1 - for i in xrange(frames.shape[0]): + for i in range(frames.shape[0]): # current iteration number frames[i, 2] = i // iterationFrameNum @@ -2126,7 +2126,7 @@ def generate_movie(self): mapX = self.monitor.linCorX mapY = self.monitor.linCorY else: - raise LookupError, 'the "coordinate" attributate show be either "degree" or "linear"' + raise LookupError('the "coordinate" attributate show be either "degree" or "linear"') circleMask = circle_mask(mapX,mapY,self.center,self.radius).astype(np.float16) @@ -2143,7 +2143,7 @@ def generate_movie(self): fullSequence[i] = currFCsequence if i in range(0, len(self.frames),len(self.frames)/10): - print ['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%'] + print(['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%']) mondict=dict(self.monitor.__dict__) indicatordict=dict(self.indicator.__dict__) @@ -2281,7 +2281,7 @@ def _generate_grid_points_sequence(self): allGridPoints = [[x,1] for x in gridPoints] + [[x,-1] for x in gridPoints] shuffle(allGridPoints) # remove coincident hit of same location by continuous frames - print 'removing coincident hit of same location with continuous frames:' + print('removing coincident hit of same location with continuous frames:') while True: iteration = 0 coincidentHitNum = 0 @@ -2290,7 +2290,7 @@ def _generate_grid_points_sequence(self): allGridPoints[i+1], allGridPoints[i+2] = allGridPoints[i+2], allGridPoints[i+1] coincidentHitNum += 1 iteration += 1 - print 'iteration:',iteration,' continous hits number:',coincidentHitNum + print('iteration:',iteration,' continous hits number:',coincidentHitNum) if coincidentHitNum == 0: break @@ -2390,7 +2390,7 @@ def generate_movie(self): fullSequence[i, indicatorHmin:indicatorHmax, indicatorWmin:indicatorWmax]=currFrame[3] if i in range(0, len(self.frames),len(self.frames)/10): - print ['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%'] + print(['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%']) #generate log dictionary mondict=dict(self.monitor.__dict__) @@ -2446,11 +2446,11 @@ def __init__(self, for tf in tf_list: period = 1. / tf if (0.05 * period) < (blockDur % period) < (0.95 * period): - print period - print blockDur % period - print 0.95 * period + print(period) + print(blockDur % period) + print(0.95 * period) error_msg = 'Duration of each block times tf '+ str(tf) + ' should be close to a whole number!' - raise ValueError, error_msg + raise ValueError(error_msg) def _generate_all_conditions(self): """ @@ -2590,7 +2590,7 @@ def generate_movie(self): if self.coordinate=='degree':corX=self.monitor.degCorX;corY=self.monitor.degCorY elif self.coordinate=='linear':corX=self.monitor.linCorX;corY=self.monitor.linCorY else: - raise LookupError, "self.coordinate should be either 'linear' or 'degree'." + raise LookupError("self.coordinate should be either 'linear' or 'degree'.") indicatorWmin=self.indicator.centerWpixel - (self.indicator.width_pixel / 2) indicatorWmax=self.indicator.centerWpixel + (self.indicator.width_pixel / 2) @@ -2624,7 +2624,7 @@ def generate_movie(self): if i in range(0, len(self.frames),len(self.frames)/10): - print ['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%'] + print(['Generating numpy sequence: '+str(int(100 * (i+1) / len(self.frames)))+'%']) @@ -2918,7 +2918,7 @@ def __init__(self, self._remote_obj = RemoteObject(rep_port=self.displayControlPort) self._remote_obj.close = self.flag_to_close() except Exception as e: - print e + print(e) # set up remote zro object for sync program if self.isRemoteSync: @@ -2927,7 +2927,7 @@ def __init__(self, if displayIteration % 1 == 0: self.displayIteration = displayIteration else: - raise ArithmeticError, "displayIteration should be a whole number." + raise ArithmeticError("displayIteration should be a whole number.") self.displayOrder = displayOrder self.logdir = logdir @@ -2954,7 +2954,7 @@ def set_any_array(self, anyArray, logDict = None): to display any numpy 3-d array. """ if len(anyArray.shape) != 3: - raise LookupError, "Input numpy array should have dimension of 3!" + raise LookupError("Input numpy array should have dimension of 3!") Vmax = np.amax(anyArray).astype(np.float32) Vmin = np.amin(anyArray).astype(np.float32) @@ -2966,7 +2966,7 @@ def set_any_array(self, anyArray, logDict = None): if type(logDict) is dict: self.sequenceLog = logDict else: - raise ValueError, '"logDict" should be a dictionary!' + raise ValueError('"logDict" should be a dictionary!') else: self.sequenceLog = {} self.clear() @@ -2991,12 +2991,12 @@ def trigger_display(self): try: refreshRate = self.sequenceLog['monitor']['refreshRate'] except KeyError: - print "No monitor refresh rate information, assuming 60Hz.\n" + print("No monitor refresh rate information, assuming 60Hz.\n") refreshRate = 60. #prepare display frames log if self.sequence is None: - raise LookupError, "Please set the sequence to be displayed!!\n" + raise LookupError("Please set the sequence to be displayed!!\n") try: sequenceFrames = self.sequenceLog['stimulation']['frames'] if self.displayOrder == -1: sequenceFrames = sequenceFrames[::-1] @@ -3005,17 +3005,17 @@ def trigger_display(self): for i in range(self.displayIteration): self.displayFrames += sequenceFrames except Exception as e: - print e - print "No frame information in sequenceLog dictionary. \nSetting displayFrames to 'None'.\n" + print(e) + print("No frame information in sequenceLog dictionary. \nSetting displayFrames to 'None'.\n") self.displayFrames = None # calculate expected display time displayTime = float(self.sequence.shape[0]) * self.displayIteration / refreshRate - print '\n Expected display time: ', displayTime, ' seconds\n' + print('\n Expected display time: ', displayTime, ' seconds\n') # generate file name self._get_file_name() - print 'File name:', self.fileName + '\n' + print('File name:', self.fileName + '\n') # ---------------------------- early preparation for display---------------------------------------------------- @@ -3056,12 +3056,12 @@ def trigger_display(self): try: self._get_file_name() - print 'File name:', self.fileName + '\n' + print('File name:', self.fileName + '\n') self.remoteSync.set_output_path(os.path.join("c:/sync/output", self.fileName + '-sync.h5'), timestamp=False) self.remoteSync.start() except Exception as err: - print "remote sync object is not started correctly. \n" + str(err) + "\n\n" + print("remote sync object is not started correctly. \n" + str(err) + "\n\n") # handle display trigger if self.isTriggered: @@ -3095,22 +3095,22 @@ def trigger_display(self): try: self.remoteSync.stop() except Exception as err: - print "remote sync object is not stopped correctly. \n" + str(err) + print("remote sync object is not stopped correctly. \n" + str(err)) # backup remote sync file try: backupFileFolder = self._get_backup_folder() - print '\nRemote sync backup file folder: ' + backupFileFolder + '\n' + print('\nRemote sync backup file folder: ' + backupFileFolder + '\n') if backupFileFolder is not None: if not (os.path.isdir(backupFileFolder)): os.makedirs(backupFileFolder) backupFilePath = os.path.join(backupFileFolder,self.fileName+'-sync.h5') time.sleep(self.remoteSyncSaveWaitTime ) # wait remote sync to finish saving self.remoteSync.copy_last_dataset(backupFilePath) - print "remote sync dataset saved successfully." + print("remote sync dataset saved successfully.") else: - print "did not find backup path, no remote sync dataset has been saved." + print("did not find backup path, no remote sync dataset has been saved.") except Exception as e: - print "remote sync dataset is not saved successfully!\n", e + print("remote sync dataset is not saved successfully!\n", e) # save display log self.save_log() @@ -3121,38 +3121,38 @@ def trigger_display(self): refreshRate=self.sequenceLog['monitor'][ 'refreshRate']) except KeyError: - print "No monitor refresh rate information, assuming 60Hz." + print("No monitor refresh rate information, assuming 60Hz.") self.frameDuration, self.frame_stats = analyze_frames(ts=self.timeStamp, refreshRate=60.) self.clear() return None try: self.remoteSync.stop() except Exception as err: - print "remote sync object is not stopped correctly. \n" + str(err) + print("remote sync object is not stopped correctly. \n" + str(err)) self.save_log() #analyze frames try: self.frameDuration, self.frame_stats = analyze_frames(ts = self.timeStamp, refreshRate = self.sequenceLog['monitor']['refreshRate']) except KeyError: - print "No monitor refresh rate information, assuming 60Hz." + print("No monitor refresh rate information, assuming 60Hz.") self.frameDuration, self.frame_stats = analyze_frames(ts = self.timeStamp, refreshRate = 60.) # backup remote dataset if self.isRemoteSync: try: backupFileFolder = self._get_backup_folder() - print '\nRemote sync backup file folder: ' + backupFileFolder + '\n' + print('\nRemote sync backup file folder: ' + backupFileFolder + '\n') if backupFileFolder is not None: if not (os.path.isdir(backupFileFolder)): os.makedirs(backupFileFolder) backupFilePath = os.path.join(backupFileFolder,self.fileName+'-sync.h5') time.sleep(self.remoteSyncSaveWaitTime ) # wait remote sync to finish saving self.remoteSync.copy_last_dataset(backupFilePath) - print "remote sync dataset saved successfully." + print("remote sync dataset saved successfully.") else: - print "did not find backup path, no remote sync dataset has been saved." + print("did not find backup path, no remote sync dataset has been saved.") except Exception as e: - print "remote sync dataset is not saved successfully!\n", e + print("remote sync dataset is not saved successfully!\n", e) #clear display data self.clear() @@ -3172,7 +3172,7 @@ def _wait_for_trigger(self, event): triggerTask = iodaq.DigitalInput(self.triggerNIDev, self.triggerNIPort, self.triggerNILine) triggerTask.StartTask() - print "Waiting for trigger: " + event + ' on ' + triggerTask.devstr + print("Waiting for trigger: " + event + ' on ' + triggerTask.devstr) if event == 'LowLevel': lastTTL = triggerTask.read() @@ -3180,33 +3180,33 @@ def _wait_for_trigger(self, event): lastTTL = triggerTask.read()[0] self._update_display_status() else: - if self.keepDisplay: triggerTask.StopTask(); print 'Trigger detected. Start displaying...\n\n'; return True - else: triggerTask.StopTask(); print 'Manual stop signal detected during waiting period. Stop the program.'; return False + if self.keepDisplay: triggerTask.StopTask(); print('Trigger detected. Start displaying...\n\n'); return True + else: triggerTask.StopTask(); print('Manual stop signal detected during waiting period. Stop the program.'); return False elif event == 'HighLevel': lastTTL = triggerTask.read()[0] while lastTTL != 1 and self.keepDisplay: lastTTL = triggerTask.read()[0] self._update_display_status() else: - if self.keepDisplay: triggerTask.StopTask(); print 'Trigger detected. Start displaying...\n\n'; return True - else: triggerTask.StopTask(); print 'Manual stop signal detected during waiting period. Stop the program.'; return False + if self.keepDisplay: triggerTask.StopTask(); print('Trigger detected. Start displaying...\n\n'); return True + else: triggerTask.StopTask(); print('Manual stop signal detected during waiting period. Stop the program.'); return False elif event == 'NegativeEdge': lastTTL = triggerTask.read()[0] while self.keepDisplay: currentTTL = triggerTask.read()[0] if (lastTTL == 1) and (currentTTL == 0):break else:lastTTL = int(currentTTL);self._update_display_status() - else: triggerTask.StopTask(); print 'Manual stop signal detected during waiting period. Stop the program.';return False - triggerTask.StopTask(); print 'Trigger detected. Start displaying...\n\n'; return True + else: triggerTask.StopTask(); print('Manual stop signal detected during waiting period. Stop the program.');return False + triggerTask.StopTask(); print('Trigger detected. Start displaying...\n\n'); return True elif event == 'PositiveEdge': lastTTL = triggerTask.read()[0] while self.keepDisplay: currentTTL = triggerTask.read()[0] if (lastTTL == 0) and (currentTTL == 1):break else:lastTTL = int(currentTTL);self._update_display_status() - else: triggerTask.StopTask(); print 'Manual stop signal detected during waiting period. Stop the program.'; return False - triggerTask.StopTask(); print 'Trigger detected. Start displaying...\n\n'; return True - else:raise NameError, 'trigger should be one of "NegativeEdge", "PositiveEdge", "HighLevel", or "LowLevel"!' + else: triggerTask.StopTask(); print('Manual stop signal detected during waiting period. Stop the program.'); return False + triggerTask.StopTask(); print('Trigger detected. Start displaying...\n\n'); return True + else:raise NameError('trigger should be one of "NegativeEdge", "PositiveEdge", "HighLevel", or "LowLevel"!') def _get_file_name(self): @@ -3246,7 +3246,7 @@ def _get_file_number(self): fileNumber = int(numStr, 2) # print array, fileNumber except Exception as e: - print e + print(e) fileNumber = None return fileNumber @@ -3302,7 +3302,7 @@ def _display(self, window, stim): if self.displayFrames is not None: self.displayFrames = self.displayFrames[:i] - if self.keepDisplay == True: print '\nDisplay successfully completed.' + if self.keepDisplay == True: print('\nDisplay successfully completed.') def flag_to_close(self): @@ -3311,19 +3311,19 @@ def flag_to_close(self): def _update_display_status(self): - if self.keepDisplay is None: raise LookupError, 'self.keepDisplay should start as True for updating display status' + if self.keepDisplay is None: raise LookupError('self.keepDisplay should start as True for updating display status') #check keyboard input 'q' or 'escape' keyList = event.getKeys(['q','escape']) if len(keyList) > 0: self.keepDisplay = False - print "Keyboard stop signal detected. Stop displaying. \n" + print("Keyboard stop signal detected. Stop displaying. \n") try: msg, addr = self.displayControlSock.recvfrom(128) if msg[0:4].upper() == 'STOP': self.keepDisplay = False - print "Remote stop signal detected. Stop displaying. \n" + print("Remote stop signal detected. Stop displaying. \n") except: pass if self.isRemoteSync: @@ -3339,7 +3339,7 @@ def set_display_order(self, displayOrder): def set_display_iteration(self, displayIteration): if displayIteration % 1 == 0:self.displayIteration = displayIteration - else:raise ArithmeticError, "displayIteration should be a whole number." + else:raise ArithmeticError("displayIteration should be a whole number.") self.clear() @@ -3347,7 +3347,7 @@ def save_log(self): if self.displayLength is None: self.clear() - raise LookupError, "Please display sequence first!" + raise LookupError("Please display sequence first!") if self.fileName is None: self._get_file_name() @@ -3377,7 +3377,7 @@ def save_log(self): #generate full log dictionary path = os.path.join(directory, filename) ft.saveFile(path,logFile) - print ".pkl file generated successfully." + print(".pkl file generated successfully.") backupFileFolder = self._get_backup_folder() @@ -3385,15 +3385,15 @@ def save_log(self): if not (os.path.isdir(backupFileFolder)): os.makedirs(backupFileFolder) backupFilePath = os.path.join(backupFileFolder,filename) ft.saveFile(backupFilePath,logFile) - print ".pkl backup file generate successfully" + print(".pkl backup file generate successfully") else: - print "did not find backup path, no backup has been saved." + print("did not find backup path, no backup has been saved.") def _get_backup_folder(self): if self.fileName is None: - raise LookupError, 'self.fileName not found.' + raise LookupError('self.fileName not found.') else: if self.backupdir is not None: @@ -3628,4 +3628,4 @@ def clear(self): # print phases # ============================================================================================================================== - print 'for debug...' \ No newline at end of file + print('for debug...') \ No newline at end of file diff --git a/corticalmapping/core/FileTools.py b/corticalmapping/core/FileTools.py index d431025..5315e40 100644 --- a/corticalmapping/core/FileTools.py +++ b/corticalmapping/core/FileTools.py @@ -5,40 +5,24 @@ import os import shutil import struct +from . import ImageAnalysis as ia +from . import tifffile as tf import h5py -import warnings -import numbers -try: - import ImageAnalysis as ia -except (AttributeError, ImportError): - from . import ImageAnalysis as ia - -try: - import tifffile as tf -except ImportError: - import skimage.external.tifffile as tf try: import cv2 -except ImportError as e: print('cannot import OpenCV. {}'.format(e)) - -try: import sync.dataset as sync_dset -except ImportError as e: print('cannot import sync.dataset. {}'.format(e)) - - -def is_integer(var): - return isinstance(var, numbers.Integral) +except ImportError as e: print('can not import OpenCV. ' + str(e)) def saveFile(path,data): f = open(path,'wb') - pickle.dump(data, f) + pickle.dump(data, f, 2) f.close() def loadFile(path): f = open(path,'rb') - data = pickle.load(f) + data = pickle.load(f, encoding='bytes') f.close() return data @@ -271,13 +255,13 @@ def importRawNewJPhys(path, channelNum = len(channels) channelLength = len(JPhysFile) / channelNum -# print('length of JPhys:', len(JPhysFile)) -# print('length of JPhys channel number:', channelNum) +# print 'length of JPhys:', len(JPhysFile) +# print 'length of JPhys channel number:', channelNum if len(JPhysFile) % channelNum != 0: raise ArithmeticError('Length of the file should be divisible by channel number!') - JPhysFile = JPhysFile.reshape([channelLength, channelNum]) + JPhysFile = JPhysFile.reshape([int(channelLength), int(channelNum)]) headerMatrix = JPhysFile[0:headerLength,:] bodyMatrix = JPhysFile[headerLength:,:] @@ -346,7 +330,7 @@ def importRawJPhys2(path, # first time of visual stimulation visualStart = None - for i in xrange(80,len(photodiode)): + for i in range(80,len(photodiode)): if ((photodiode[i] - photodiodeThr) * (photodiode[i-1] - photodiodeThr)) < 0 and \ ((photodiode[i] - photodiodeThr) * (photodiode[i-75] - photodiodeThr)) < 0: #first frame of big change visualStart = i*(1./sf) @@ -413,7 +397,7 @@ def importRawNewJPhys2(path, # first time of visual stimulation visualStart = None - for i in xrange(80,len(photodiode)): + for i in range(80,len(photodiode)): if ((photodiode[i] - photodiodeThr) * (photodiode[i-1] - photodiodeThr)) < 0 and \ ((photodiode[i] - photodiodeThr) * (photodiode[i-75] - photodiodeThr)) < 0: #first frame of big change visualStart = i*(1./sf) @@ -492,8 +476,8 @@ def generateAVI(saveFolder, out.release() cv2.destroyAllWindows() - - + + def importRawJCamF(path, saveFolder = None, dtype = np.dtype(' 0: - warnings.warn('You choose to extract digital channels by index. But there are ' - 'digital channels with string labels: {}. All the string labels ' - 'will be lost.'.format(str(digital_cns))) - digital_cns = range(ds.meta_data['ni_daq']['event_bits']) - digital_cns = [str(cn) for cn in digital_cns] - - # print(digital_cns) - - for digital_i, digital_cn in enumerate(digital_cns): - if digital_cn: - digital_channels[digital_cn] = {'rise': ds.get_rising_edges(line=digital_i, units='seconds'), - 'fall': ds.get_falling_edges(line=digital_i, units='seconds')} - - # read analog channels - data_f = h5py.File(f_path, 'r') - if 'analog_meta' not in data_f.keys(): - data_f.close() - print ('no analog data found in file: {}.'.format(f_path)) - return {'digital_channels': digital_channels} - else: - - analog_channels = {} - - if analog_downsample_rate is None: - analog_downsample_rate = 1 - analog_fs = ds.analog_meta_data['analog_sample_rate'] / analog_downsample_rate - if analog_labels is not None: - analog_cns = analog_labels - for analog_cn in analog_cns: - analog_channels[str(analog_cn)] = ds.get_analog_channel(channel=analog_cn, - downsample=analog_downsample_rate) - elif by_label: - analog_cns = [al for al in ds.analog_meta_data['analog_labels'] if al] - - for analog_cn in analog_cns: - analog_channels[str(analog_cn)] = ds.get_analog_channel(channel=analog_cn, - downsample=analog_downsample_rate) - else: - analog_cns = [al for al in ds.analog_meta_data['analog_labels'] if al] - if len(analog_cns) > 0: - warnings.warn('You choose to extract analog channels by index. But there are ' - 'analog channels with string labels: {}. All the string labels ' - 'will be lost.'.format(str(digital_cns))) - analog_cns = ds.analog_meta_data['analog_channels'] - - for analog_ind, analog_cn in enumerate(analog_cns): - analog_channels[str(analog_cn)] = ds.get_analog_channel(channel=analog_ind, - downsample=analog_downsample_rate) - - return {'digital_channels': digital_channels, - 'analog_channels': analog_channels, - 'analog_sample_rate': analog_fs} - if __name__=='__main__': - # ---------------------------------------------------------------------------- - sync_path = r"D:\data2\rabies_tracing_project\method_development" \ - r"\2017-10-05-read-sync\171003_M345521_FlashingCircle_106_171003165755.h5" - # sync_path = r"\\allen\programs\braintv\workgroups\nc-ophys\ImageData\Soumya\trees\m255" \ - # r"\log_m255\sync_pkl\m255_presynaptic_pop_vol1_stimDG_bessel170918013215.h5" - sync_dict = read_sync(f_path=sync_path, by_label=False, digital_labels=['vsync_2p'], - analog_labels=['photodiode'], analog_downsample_rate=None) - # sync_dict = read_sync(f_path=sync_path, by_label=False, digital_labels=None, - # analog_labels=None, analog_downsample_rate=None) - # print(sync_dict) - # ---------------------------------------------------------------------------- - #---------------------------------------------------------------------------- + # mov = np.random.rand(250,512,512,4) # generateAVI(r'C:\JunZhuang\labwork\data\python_temp_folder','tempMov',mov) + #---------------------------------------------------------------------------- - # print(int2str(5)) - # print(int2str(5,2)) - # print(int2str(155,6)) + # print int2str(5) + # print int2str(5,2) + # print int2str(155,6) #---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- @@ -808,15 +691,15 @@ def read_sync(f_path, analog_downsample_rate=None, by_label=True, digital_labels # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- - # ff = h5py.File(r"E:\data\python_temp_folder\test4.hdf5") - # test_dict = {'a':1, 'b':2, 'c': {'A': 4, 'B': 5}} - # write_dictionary_to_h5group_recursively(target=ff, source=test_dict, is_overwrite=True) - # ff.close() - # - # ff = h5py.File(r"E:\data\python_temp_folder\test4.hdf5") - # test_dict2 = {'a': {'C': 6, 'D': 7}, 'c': {'A': 4, 'B': 6}, 'd':10, 'e':{'E':11, 'F':'xx'}} - # write_dictionary_to_h5group_recursively(target=ff, source=test_dict2, is_overwrite=False) - # ff.close() + ff = h5py.File(r"E:\data\python_temp_folder\test4.hdf5") + test_dict = {'a':1, 'b':2, 'c': {'A': 4, 'B': 5}} + write_dictionary_to_h5group_recursively(target=ff, source=test_dict, is_overwrite=True) + ff.close() + + ff = h5py.File(r"E:\data\python_temp_folder\test4.hdf5") + test_dict2 = {'a': {'C': 6, 'D': 7}, 'c': {'A': 4, 'B': 6}, 'd':10, 'e':{'E':11, 'F':'xx'}} + write_dictionary_to_h5group_recursively(target=ff, source=test_dict2, is_overwrite=False) + ff.close() # ---------------------------------------------------------------------------- diff --git a/corticalmapping/core/ImageAnalysis.py b/corticalmapping/core/ImageAnalysis.py index 4423cbb..a6e13ce 100644 --- a/corticalmapping/core/ImageAnalysis.py +++ b/corticalmapping/core/ImageAnalysis.py @@ -8,28 +8,13 @@ import scipy.ndimage as ni import scipy.stats as stats import skimage.morphology as sm -import skimage.measure as measure +from . import FileTools as ft +from . import PlottingTools as pt import time - -try: - import FileTools as ft -except (AttributeError, ImportError): - from . import FileTools as ft - -try: - import PlottingTools as pt -except (AttributeError, ImportError): - from . import PlottingTools as pt - -try: - import cv2 -except ImportError as e: - print(e) - -try: - from toolbox.misc import BinarySlicer -except ImportError as e: - print(e) +try: import cv2 +except ImportError as e: print(e) +try: from toolbox.misc import BinarySlicer +except ImportError as e: print(e) def resample(t1,y1,interval,kind='linear', isPlot = False): @@ -85,16 +70,8 @@ def array_nor(A): normalize a np.array to the scale [0, 1] ''' - if np.isnan(A).any(): - B = A.astype(np.float) - maxv = np.nanmax(B.flat) - minv = np.nanmin(B.flat) - else: - B=A.astype(np.float) - maxv = np.max(B.flat) - minv = np.min(B.flat) - - return (B - minv) / (maxv - minv) + B=A.astype(np.float) + return (B-np.amin(B))/(np.amax(B)-np.amin(B)) def array_nor_median(A): @@ -141,10 +118,10 @@ def distance(p0, p1): #old code====================================================================== # if (len(p0.shape) > 1) or (len(p1.shape) > 1): - # raise(LookupError('Both input arrays should be 1d array!!')) + # raise LookupError, 'Both input arrays should be 1d array!!' # # if p0.shape != p1.shape: - # raise LookupError('The two input arrays should have same dimensions.') + # raise LookupError, 'The two input arrays should have same dimensions.' # # distance = math.sqrt(np.sum(((p0.astype(np.float)-p1.astype(np.float))**2))) #=============================================================================== @@ -384,21 +361,21 @@ def rigid_transform(img, zoom=None, rotation=None, offset=None, outputShape=None newImg = img.astype(np.float32) - if zoom is not None: + if zoom: if len(img.shape) == 2: newZoom = (zoom,zoom) elif len(img.shape) == 3: newZoom = (1,zoom,zoom) newImg = ni.zoom(newImg,zoom=newZoom,mode=mode,cval=cval) - if rotation is not None: + if rotation: newImg = expand_image(newImg) if len(img.shape) == 2: newImg = ni.rotate(newImg,angle=rotation,reshape=False,mode=mode,cval=cval) elif len(img.shape) == 3: newImg = ni.rotate(newImg,angle=rotation,axes=(1,2),reshape=False,mode=mode,cval=cval) - if offset is not None: + if offset: if len(img.shape) == 2: newImg = ni.shift(newImg,(offset[1],offset[0]),mode=mode,cval=cval) if len(img.shape) == 3: @@ -452,26 +429,20 @@ def rigid_transform_cv2_3d(img, zoom=None, rotation=None, offset=None, outputSha if len(img.shape) != 3: raise LookupError('Input image is not a 3d array!') - # if outputShape is None: - # - # if zoom is not None: - # try: - # newHeight = int(img.shape[1] * zoom[0]) - # newWidth = int(img.shape[2] * zoom[1]) - # except TypeError: - # newHeight = int(img.shape[1] * zoom) - # newWidth = int(img.shape[2] * zoom) - # else: - # newHeight = img.shape[1] - # newWidth = img.shape[2] - # else: - # newHeight = outputShape[0] - # newWidth = outputShape[1] - - frame_1 = rigid_transform_cv2_2d(img[0, :, :], zoom=zoom, rotation=rotation, offset=offset, outputShape=outputShape) - newHeight = frame_1.shape[0] - newWidth = frame_1.shape[1] - + if not outputShape: + if zoom is not None: + try: + newHeight = int(img.shape[1]*zoom[0]) + newWidth = int(img.shape[2]*zoom[1]) + except TypeError: + newHeight = int(img.shape[1] * zoom) + newWidth = int(img.shape[2] * zoom) + else: + newHeight = img.shape[1] + newWidth = img.shape[2] + else: + newHeight = outputShape[0] + newWidth = outputShape[1] newImg = np.empty((img.shape[0],newHeight,newWidth),dtype=img.dtype) for i in range(img.shape[0]): @@ -587,7 +558,7 @@ def temporal_filter_movie(mov, # array of movie filterArray = np.ones(frameNum) - for i in xrange(frameNum): + for i in range(frameNum): if ((freqs[i] > 0) and (freqs[i] < Flow) or (freqs[i] > Fhigh)) or \ ((freqs[i] < 0) and (freqs[i] > -Flow) or (freqs[i] < -Fhigh)): filterArray[i] = 0 @@ -605,8 +576,8 @@ def temporal_filter_movie(mov, # array of movie movFFT = np.fft.fft(mov, axis = 0) - for i in xrange(mov.shape[1]): - for j in xrange(mov.shape[2]): + for i in range(mov.shape[1]): + for j in range(mov.shape[2]): movFFT[:,i,j] = movFFT[:,i,j] * filterArray movF = np.real(np.fft.ifft(movFFT, axis = 0)) @@ -719,7 +690,7 @@ def get_trace_binaryslicer(bl_obj, mask, mask_mode = 'binary'): raise ValueError('Binary mask should only contain zeros and ones!!') else: mask_ind = np.where(mask != 0) - # print(mask_ind) + # print mask_ind min_row = min(mask_ind[0]); max_row = max(mask_ind[0]) + 1 min_col = min(mask_ind[1]); max_col = max(mask_ind[1]) + 1 finalMask = np.array(mask.astype(np.float))[min_row:max_row, min_col:max_col] @@ -749,7 +720,7 @@ def get_trace_binaryslicer(bl_obj, mask, mask_mode = 'binary'): finalMask = finalMask[min_row:max_row, min_col:max_col] mov = bl_obj[:,min_row:max_row, min_col:max_col] - # print(mov) + # print mov return get_trace(mov, finalMask, maskMode='weighted') @@ -834,7 +805,7 @@ def get_trace_binaryslicer3(bl_obj, masks, mask_mode = 'binary', loading_frame_n print('Translating in chunks: '+str(chunkNum-1)+' x '+str(loading_frame_num)+' frame(s)'+' + '+str(frameNum % loading_frame_num)+' frame(s)') traces = {} - for key in masks.iterkeys(): traces.update({'trace_'+key:[]}) + for key in masks.keys(): traces.update({'trace_'+key:[]}) for i in range(chunkNum): indStart = i*loading_frame_num @@ -842,13 +813,13 @@ def get_trace_binaryslicer3(bl_obj, masks, mask_mode = 'binary', loading_frame_n if indEnd > frameNum: indEnd = frameNum print('Extracting signal from frame '+str(indStart)+' to frame '+str(indEnd)+'.\t'+str(i*100./chunkNum)+'%') currMov = bl_obj[indStart:indEnd,:,:] - for key, mask in masks.iteritems(): + for key, mask in masks.items(): if len(mask.shape) != 2: raise ValueError('Mask "' + key + '" should be 2d!') if bl_obj.shape[1] != mask.shape[0] or bl_obj.shape[2] != mask.shape[1]: raise ValueError('the size of each frame of the BinarySlicer object should be the same as the size of mask "' + key + '"!') traces['trace_'+key].append(get_trace(currMov, mask, maskMode=mask_mode)) - for key in traces.iterkeys(): + for key in traces.keys(): traces[key] = np.concatenate(traces[key]) return traces @@ -916,7 +887,7 @@ def discretize(array, binSize): newArray = np.zeros(flatArray.shape) newArray[:] = np.nan - for i in xrange(len(indArray)): + for i in range(len(indArray)): if np.isnan(flatArray[i]) == False: newArray[i] = bins[indArray[i]] @@ -1054,7 +1025,7 @@ def get_area_edges(img, else: return edgesThick.astype(np.bool) -def z_downsample(img, downSampleRate, is_verbose=True): +def z_downsample(img, downSampleRate): ''' downsample input image in z direction ''' @@ -1066,15 +1037,13 @@ def z_downsample(img, downSampleRate, is_verbose=True): newFrameNum = img.shape[0] //downSampleRate newImg = np.empty((newFrameNum,img.shape[1],img.shape[2]),dtype=img.dtype) - if is_verbose: - print('Start downsampling...') + print('Start downsampling...') for i in range(newFrameNum): # print (float(i)*100/newFrameNum),'%' currChunk = img[i*downSampleRate:(i+1)*downSampleRate,:,:].astype(np.float) currFrame = np.mean(currChunk,axis=0) newImg[i,:,:]=currFrame.astype(img.dtype) - if is_verbose: - print('End of downsampling.') + print('End of downsampling.') return newImg @@ -1128,7 +1097,7 @@ def get_marked_masks(labeled, markCoor): ''' masks = get_masks(labeled) - for key, value in masks.iteritems(): + for key, value in masks.items(): if hit_or_miss(markCoor, value): return value return None @@ -1138,9 +1107,9 @@ def sort_masks(masks, keyPrefix=None, labelLength=3): sort a dictionary of binary masks, big to small ''' - maskNum = len(masks.keys()) + maskNum = len(list(masks.keys())) order = [] - for key, mask in masks.iteritems(): + for key, mask in masks.items(): order.append([key,np.sum(mask.flatten())]) order = sorted(order, key=lambda a:a[1], reverse=True) @@ -1158,7 +1127,7 @@ def sort_masks(masks, keyPrefix=None, labelLength=3): # down sample a 3-d array in 0 direction # ''' # -# if len(A.shape) != 3: raise ValueError('input array should be 3-d.') +# if len(A.shape) != 3: raise ValueError, 'input array should be 3-d.' # rate = int(rate) # dataType = A.dtype # newZDepth = (A.shape[0] - (A.shape[0]%rate))/rate @@ -1186,9 +1155,9 @@ def get_average_movie(mov, frameTS, onsetTimes, chunkDur, isReturnN=False): chunkFrameDur = int(np.ceil(chunkDur / meanFrameDur)) - # print('chunkDur:', chunkDur) - # print('meanFrameDur:', meanFrameDur) - # print('chunkFrameDur:', chunkFrameDur) + # print 'chunkDur:', chunkDur + # print 'meanFrameDur:', meanFrameDur + # print 'chunkFrameDur:', chunkFrameDur sumMov = None n = 0. @@ -1201,19 +1170,19 @@ def get_average_movie(mov, frameTS, onsetTimes, chunkDur, isReturnN=False): if onset >= frameTS[0] and onset + chunkDur <= frameTS[-1]: if i // (onset_num // 10) > curr_onset: - # print(t0 - time.time(), ' second :', (i // (onset_num // 10)) * 10, '%') - print('{:09.2f} second: {:2d} %'.format(time.time() - t0, (i // (onset_num // 10)) * 10)) + # print t0 - time.time(), ' second :', (i // (onset_num // 10)) * 10, '%' + print(('{:09.2f} second: {:2d} %'.format(time.time() - t0, (i // (onset_num // 10)) * 10))) curr_onset = i // (onset_num // 10) - onsetFrameInd = np.argmin(np.abs(frameTS-onset)) - # print('Chunk:',int(n),'; Starting frame index:',onsetFrameInd,'; Ending frame index', onsetFrameInd+chunkFrameDur) + onsetFrameInd = int(np.argmin(np.abs(frameTS-onset))) + # print 'Chunk:',int(n),'; Starting frame index:',onsetFrameInd,'; Ending frame index', onsetFrameInd+chunkFrameDur if onsetFrameInd+chunkFrameDur <= mov.shape[0]: if sumMov is None: sumMov = np.zeros((chunkFrameDur,mov.shape[1],mov.shape[2]), dtype=np.float64) sumMov += mov[onsetFrameInd:onsetFrameInd+chunkFrameDur,:,:].astype(np.float64) n += 1. else: - print('Ending frame index ('+str(int(onsetFrameInd+chunkFrameDur))+') is larger than frames in movie (' + + print('Ending frame index ('+str(int(onsetFrameInd+chunkFrameDur))+') is larger than frames in movie ('+\ str(int(mov.shape[0]))+'.\nExclude this trigger.') continue @@ -1236,9 +1205,9 @@ def get_average_movie2(mov, frameTS, onsetTimes, chunkDur, verbose=True): chunkFrameDur = int(np.ceil(chunkDur / meanFrameDur)) - # print('chunkDur:', chunkDur) - # print('meanFrameDur:', meanFrameDur) - # print('chunkFrameDur:', chunkFrameDur) + # print 'chunkDur:', chunkDur + # print 'meanFrameDur:', meanFrameDur + # print 'chunkFrameDur:', chunkFrameDur sumMov = None real_count = 0 @@ -1250,17 +1219,17 @@ def get_average_movie2(mov, frameTS, onsetTimes, chunkDur, verbose=True): for i, onset in enumerate(onsetTimes): if verbose and (i // (onset_num // 10) > curr_onset): - print('{:09.2f} second: {:2d} %'.format(time.time() - t0, (i // (onset_num // 10)) * 10)) + print(('{:09.2f} second: {:2d} %'.format(time.time() - t0, (i // (onset_num // 10)) * 10))) curr_onset = i // (onset_num // 10) if onset < frameTS[0]: # the onset is before imaging start time. Exclude this onset. continue - # print('onset number:', count, 'is before imaging start time. Exclude this onset.') + # print 'onset number:', count, 'is before imaging start time. Exclude this onset.' else: onsetFrameInd = np.argmin(np.abs(frameTS-onset)) # if verbose: - # print('Chunk:',int(count),'; Starting frame index:',onsetFrameInd,'; Ending frame index', onsetFrameInd+chunkFrameDur) + # print 'Chunk:',int(count),'; Starting frame index:',onsetFrameInd,'; Ending frame index', onsetFrameInd+chunkFrameDur if onsetFrameInd+chunkFrameDur <= mov.shape[0]: if sumMov is None: sumMov = np.zeros((chunkFrameDur,mov.shape[1],mov.shape[2])) @@ -1268,7 +1237,7 @@ def get_average_movie2(mov, frameTS, onsetTimes, chunkDur, verbose=True): real_count += 1. else: # the chunk exceeds the end of imaging. continue - # print('the chunk of onset number', count, 'exceeds the end of imaging. Exclude this onset.') + # print 'the chunk of onset number', count, 'exceeds the end of imaging. Exclude this onset.' if sumMov is None: @@ -1317,211 +1286,15 @@ def regression_detrend_1d(sig, trend): return sig_detrend, slope, r_value -def merge_weighted_rois(roi1, roi2): - """ - merge two WeightedROI objects, most useful for merge ON and OFF subfields - """ - if (roi1.pixelSizeX != roi2.pixelSizeX) or (roi1.pixelSizeY != roi2.pixelSizeY): - raise ValueError('The pixel sizes of the two WeightedROI objects should match!') - - if roi1.pixelSizeUnit != roi2.pixelSizeUnit: - raise ValueError('The pixel size units of the two WeightedROI objects should match!') - - mask1 = roi1.get_weighted_mask() - mask2 = roi2.get_weighted_mask() - - return WeightedROI(mask1 + mask2, pixelSize=[roi1.pixelSizeY, roi1.pixelSizeX], pixelSizeUnit=roi1.pixelSizeUnit) - - -def merge_binary_rois(roi1, roi2): - """ - merge two ROI objects, most useful for merge ON and OFF subfields - """ - if (roi1.pixelSizeX != roi2.pixelSizeX) or (roi1.pixelSizeY != roi2.pixelSizeY): - raise ValueError('The pixel sizes of the two WeightedROI objects should match!') - - if roi1.pixelSizeUnit != roi2.pixelSizeUnit: - raise ValueError('The pixel size units of the two WeightedROI objects should match!') - - mask1 = roi1.get_binary_mask() - mask2 = roi2.get_binary_mask() - mask3 = np.logical_or(mask1, mask2).astype(np.int8) - - return ROI(mask3, pixelSize=[roi1.pixelSizeY, roi1.pixelSizeX], pixelSizeUnit=roi1.pixelSizeUnit) - - -def get_peak_weighted_roi(arr, thr): - """ - return: a WeightROI object representing the mask which contains the peak of arr and cut by the thr (thr) - """ - nanLabel = np.isnan(arr) - arr2 = arr.copy() - arr2[nanLabel] = np.nanmin(arr) - labeled, _ = ni.label(arr2 >= thr) - peakCoor = np.array(np.where(arr2 == np.amax(arr2))).transpose()[0] - peakMask = get_marked_masks(labeled, peakCoor) - if peakMask is None: - 'Threshold too high! No ROI found. Returning None'; return None - else: - return WeightedROI(arr2 * peakMask) - - -def pairwise_distance(coords): - """ - giving coordinates of a set of points, return the pairwise distances of all pairs - :param coords: 2d array, shape is (n, 2). first column: x coordinates, second column: y coordinates. - :return: - """ - - if len(coords.shape) != 2: - raise ValueError("input coordinates should be 2d array.") - - if coords.shape[1] != 2: - raise ValueError("input coordinates should have 2 columns.") - - if coords.shape[0] < 2: - return np.array([]) - else: - point_num = coords.shape[0] - pairs = np.zeros((point_num * (point_num - 1), 4)) - - pair_ind = 0 - for i in range(0, point_num - 1): - for j in range(i + 1, point_num): - pairs[pair_ind, :] = [coords[i, 0], coords[i, 1], coords[j, 0], coords[j, 1]] - pair_ind = pair_ind + 1 - - dis = np.sqrt(np.square(pairs[:, 0] - pairs[:, 2]) + np.square(pairs[:, 1], pairs[:, 3])) - return dis - - -def pairwise_magnification(coords1, coords2): - """ - giving two sets of coordinates of a set of points, say receptive field center location and cortical - location of a set of rois in field of view - - return the pairwise magnification distance of coords1 over distance of coords2 of all pairs - - :param coords1: 2d array, shape is (n, 2). first column: x coordinates, second column: y coordinates. - :param coords2: 2d array, shape is (n, 2). first column: x coordinates, second column: y coordinates. - coords1 one and coords2 should have same shape. - :return: - """ - - if len(coords1.shape) != 2: - raise ValueError("input coordinates should be 2d array.") - - if coords1.shape[1] != 2: - raise ValueError("input coordinates should have 2 columns.") - - if coords1.shape != coords2.shape: - raise ValueError("two input coordinates should have same shape.") - - if coords1.shape[0] < 2: - return np.array([]) - else: - point_num = coords1.shape[0] - pairs = np.zeros((point_num * (point_num - 1) / 2, 8)) - - pair_ind = 0 - for i in range(0, point_num - 1): - for j in range(i + 1, point_num): - - pairs[pair_ind, :] = [coords1[i, 0], coords1[i, 1], coords1[j, 0], coords1[j, 1], - coords2[i, 0], coords2[i, 1], coords2[j, 0], coords2[j, 1]] - pair_ind = pair_ind + 1 - - mag = np.sqrt(np.square(pairs[:, 0] - pairs[:, 2]) + np.square(pairs[:, 1], pairs[:, 3])) / \ - np.sqrt(np.square(pairs[:, 4] - pairs[:, 6]) + np.square(pairs[:, 5], pairs[:, 7])) - - return mag - - -def get_circularity(mask, is_skimage=True): - """ - return circularity of the shape marked by the input mask. If the mask label more than one - continuous regions, only analyze the first one retuned by scipy.ndimage.label. - - This does not consider holes. - - there are two ways to estimate perimeter: - 1. is_skimage=True uses the shape through the center of border pixels. if the labeled region is - large and wide in all orientation, the measurement is very precise. But if the labeled region - is small or narrow, it underestmates the perimeter thus overestimates the circularity. Sometimes - it can be larger than 1. - - 2. is_skimage=False uses the outer boundary line of border pixels. This will treat all shapes as - rectangle, thus systematically underestimates the circularity. upper bound will be the - circularity of square: 0.7853981633974483. - - :param mask: 2d binary array, if not binary, all pixel <= zero will be considered as 0. - all pixels > 0 will be considered as 1. - :param is_skimage: bool. if Ture, use skimage.measure.perimeter to estimate perimeter - :return: circularity, defined by 4 * pi * area / (perimeter) ^ 2 - """ - - if len(mask.shape) != 2: - raise ValueError('input mask should be a 2d array.') - - msk = np.zeros(mask.shape, dtype=np.uint8) - msk[mask>0] = 1 - - labeled, roi_num = ni.label(msk) - - if roi_num > 1: # found more than one labeled regions - # raise(ValueError('input mask should have only one continuous region labeled. {} found.'.format(roi_num))) - print('input mask has {} (> 1) continuous regions labeled. only analyze the first one'.format(roi_num)) - - msk[:] = 0 - msk[labeled == 1] = 1 - elif roi_num == 0: # found no labeled region - print('Did not find labeled region. Returning None') - return None - else: # found one labeled region - pass - - if is_skimage: - perimeter = measure.perimeter(msk) - else: - rows, cols = np.where(msk == 1) - perimeter = 2. * (max(rows) - min(rows) + 1.) + 2. * (max(cols) - min(cols) + 1.) - area = np.sum(msk.flat) - - return 4 * np.pi * area / (perimeter ** 2) - - -def fit_ellipse(mask): - """ - using opencv to fit a mask into ellipse - - :param mask: 2d array, dtype np.uint8 - :return: - """ - - mask2 = np.array(mask) - - if len(mask.shape) != 2: - raise ValueError('input "mask" should be a 2d array.') - - if not mask.dtype == np.uint8: - raise ValueError("input mask should have dtype as np.uint8") - - _, cons, _ = cv2.findContours(image=mask2, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE) - - if len(cons) == 0: - print('ImageAnalysis.fit_ellipse: No contour detected. Returning None.') - return None - elif cons[0].shape[0] < 5: - print('ImageAnalysis.fit_ellipse: No contour detected. Returning None.') - return None - else: - if len(cons) > 1: - print('ImageAnalysis.fit_ellipse: More than one contours detected. Taking the first one.') - con = cons[0] - - box = cv2.fitEllipse(con) - ell = Ellipse.from_cv2_box(box) - return ell +# def get_surround_pixels(shape, (i, j), connectivity=8): +# """ +# given a 2-d shape and a pixel location [i, j], return the locations of its surround pixels. +# +# :param shape: tuple or list of integers, should have length of 2 +# :param i: +# :param j: +# :return: +# """ class ROI(object): @@ -1608,18 +1381,16 @@ def get_nan_mask(self): mask[self.pixels] = 1 return mask - def get_pixel_area(self, verbose=False): + def get_pixel_area(self): ''' return the area coverage of the ROI ''' if (self.pixelSizeX is not None) and (self.pixelSizeX is not None): - if verbose: - print('returning area with unit:' + self.pixelSizeUnit + '^2') + print('returning area with unit:' + self.pixelSizeUnit + '^2') return float(len(self.pixels[0]))*self.pixelSizeX*self.pixelSizeY else: - print('Did not find information about pixel size. ' - 'Returning area as pixel counts without unit.') + print('returning area as pixel counts without unit.') return len(self.pixels[0]) def get_binary_area(self): @@ -1640,7 +1411,7 @@ def get_binary_trace(self, mov): ''' binaryMask = self.get_binary_mask().astype(np.float32) trace = np.multiply(mov, np.array([binaryMask])).sum(axis=1).sum(axis=1) - # print(trace) + # print trace return trace / self.get_binary_area() def get_binary_trace_pixelwise(self, mov): @@ -1653,7 +1424,7 @@ def get_binary_trace_pixelwise(self, mov): for pixel in pixels: # trace += mov[:, pixel[0], pixel[1]] # somehow this is less precise !! do not use trace = trace + mov[:, int(pixel[0]), int(pixel[1])].flatten().astype(np.float32) - # print(trace) + # print trace return trace / self.get_binary_area() def plot_binary_mask(self, plotAxis=None, color='#ff0000', alpha=1): @@ -1681,7 +1452,7 @@ def to_h5_group(self, h5Group): dataDict = dict(self.__dict__) _ = dataDict.pop('dimension');_ = dataDict.pop('pixelSizeX');_ = dataDict.pop('pixelSizeY');_ = dataDict.pop('pixelSizeUnit') - for key, value in dataDict.iteritems(): + for key, value in dataDict.items(): if value is None: h5Group.create_dataset(key,data='None') else: h5Group.create_dataset(key,data=value) @@ -1711,24 +1482,15 @@ def from_h5_group(h5Group): if pixelSizeUnit is 'None': pixelSizeUnit = None pixels = h5Group['pixels'].value - if 'weights' in h5Group.keys(): + if 'weights' in list(h5Group.keys()): weights = h5Group['weights'].value mask = np.zeros(dimension, dtype=np.float32); mask[tuple(pixels)] = weights - roi = WeightedROI(mask, pixelSize=pixelSize, pixelSizeUnit=pixelSizeUnit) + return WeightedROI(mask, pixelSize=pixelSize, pixelSizeUnit=pixelSizeUnit) else: mask = np.zeros(dimension, dtype=np.uint8); mask[tuple(pixels)] = 1 - roi = ROI(mask, pixelSize=pixelSize, pixelSizeUnit=pixelSizeUnit) - - for key in h5Group.keys(): - if key not in ['pixels', 'weights']: - if h5Group[key].value == 'None': - setattr(roi, key, None) - else: - setattr(roi, key, h5Group[key].value) - - return roi + return ROI(mask, pixelSize=pixelSize, pixelSizeUnit=pixelSizeUnit) class WeightedROI(ROI): @@ -1738,7 +1500,8 @@ def __init__(self, mask, pixelSize = None, pixelSizeUnit = None): self.weights = mask[self.pixels] def __str__(self): - return 'corticalmapping.core.ImageAnalysis.WeightedROI object' + return 'corticalmapping.core.ImageAnalysis.WeightedROI object, subclass of ' \ + 'corticalmapping.core.ImageAnalysis.ROI' def get_peak(self): return np.max(self.weights) @@ -1746,9 +1509,6 @@ def get_peak(self): def get_weight_sum(self): return sum(self.weights) - def get_mean_weight(self): - return np.mean(self.weights) - def get_weighted_mask(self): mask = np.zeros(self.dimension,dtype=np.float32) mask[self.pixels] = self.weights @@ -1772,7 +1532,7 @@ def get_weighted_center_in_coordinate(self, yCor, xCor): weightMask = self.get_weighted_mask() if np.sum(weightMask.flatten()) == 0: - return [np.nan, np.nan] + return None else: xMap, yMap = np.meshgrid(xCor, yCor) xCenter = np.sum((xMap*weightMask).flatten())/np.sum(weightMask.flatten()) @@ -1803,7 +1563,7 @@ def get_weighted_trace(self, mov, is_area_weighted=False): ''' weightedMask = self.get_weighted_mask() trace = np.multiply(mov, np.array([weightedMask])).sum(axis=-1).sum(axis=-1) - # print(trace) + # print trace if is_area_weighted: return trace / self.get_binary_area() elif not is_area_weighted: @@ -1823,45 +1583,14 @@ def get_weighted_trace_pixelwise(self, mov, is_area_weighted=False): for i, pixel in enumerate(pixels): # trace += mov[:, pixel[0], pixel[1]] # somehow this is less precise !! do not use trace = trace + self.weights[i] * (mov[:, pixel[0], pixel[1]]).astype(np.float32) - # print(trace) - if not is_area_weighted: + # print trace + if is_area_weighted: return trace / self.get_binary_area() - elif is_area_weighted: + elif not is_area_weighted: return trace / self.get_weight_sum() else: raise ValueError('is_area_weighted should be a boolean variable.') - def ellipse_fitting(self, thr=None, is_plot=False): - """ - using opencv to fit a ellipse - - :param thr: float, threshold to threshold the mask - :param is_plot: bool - :return ell: corticalmapping.ImageAnalysis.Ellipse object - """ - - if thr is None: - mask_thr = self.get_binary_mask() * 255 - else: - mask = self.get_weighted_mask() - mask_thr = np.zeros(mask.shape, dtype=np.uint8) - mask_thr[mask >= thr] = 255 - - ell = fit_ellipse(mask_thr) - - if is_plot: - f = plt.figure() - ax = f.add_subplot(111) - img = np.array([mask_thr, mask_thr, mask_thr]).transpose((1, 2, 0)).copy() - if ell is not None: - img = ell.draw(img=img, thickness=2) - ax.set_title('angle={} deg'.format(ell.angle)) - img = cv2.cvtColor(img, code=cv2.COLOR_BGR2RGB) - ax.imshow(img, interpolation='nearest') - plt.show() - - return ell - @staticmethod def from_h5_group(h5Group): ''' @@ -1877,128 +1606,7 @@ def from_h5_group(h5Group): weights = h5Group['weights'].value mask = np.zeros(dimension, dtype=np.float32) mask[tuple(pixels)] = weights - - roi = WeightedROI(mask, pixelSize=pixelSize, pixelSizeUnit=pixelSizeUnit) - - for key in h5Group.keys(): - if key not in ['pixels', 'weights']: - if h5Group[key].value == 'None': - setattr(roi, key, None) - else: - setattr(roi, key, h5Group[key].value) - - return roi - - -class Ellipse(object): - """ - ellipse object - - :attribute center: tuple of two positive floats, (center height, center width) - :attribute axes: tuple of two positive floats, (radius of the long axis, radius of short axis) - :attribute angle: float, degree, counterclockwise rotation of long axis, from right direction - """ - - def __init__(self, center, axes, angle): - """ - ellipse object - - :param center: tuple of two positive floats, (center height, center width) - :param axes: tuple of two positive floats, (radius of the long axis, radius of short axis) - :param angle: float, degree, counterclockwise rotation of long axis, from right direction - """ - self.center = center - - if axes[0] <= 0. or axes[1] <= 0.: - raise ValueError('length of axes should be larger than 0.') - - if axes[0] >= axes[1]: - self.axes = axes - self.angle = angle % 180. - else: - self.axes = (axes[1], axes[0]) - self.angle = (angle + 90.) % 180. - - def get_aspect_ratio(self): - return(self.axes[0] / self.axes[1]) - - def get_cv2_ellips(self): - """ - :return: the ellipse in opencv3 format for drawing - """ - # return ((int(round(self.center[1])), int(round(self.center[0]))), - # (int(round(self.axes[0])), int(round(self.axes[1]))), - # -self.angle, 0, 360) - return ((int(self.center[1]), int(self.center[0])), - (int(self.axes[0]), int(self.axes[1])), - -self.angle, 0, 360) - - def get_area(self): - return np.pi * self.axes[0] * self.axes[1] - - def get_binary_mask(self, shape): - """ - :param shape: tuple of 2 positive integers (height, width) - :return: binary mask of the ellipse with given shape - """ - mask = np.zeros(shape=shape, dtype=np.uint8) - ell_cv2 = self.get_cv2_ellips() - mask = cv2.ellipse(mask, center=ell_cv2[0], axes=ell_cv2[1], angle=ell_cv2[2], startAngle=0, endAngle=360, - color=1, thickness=-1) - return mask.astype(np.uint8) - - def get_intensity(self, img): - """ - :param img: 2d gray scale image - :return: mean intensity of ellipse - """ - - if len(img.shape) != 2: - raise ValueError('input image should be 2d array.') - - mask = self.get_binary_mask(img.shape) - return np.mean(img[mask]) - - def draw(self, img, color=(0, 255, 0), thickness=3): - """ - :param img: 3d array, (height x width x channel), opencv frame - :param color: - :param thickness: - :return: - """ - - ell_cv2 = self.get_cv2_ellips() - img_marked = cv2.ellipse(img=img, center=ell_cv2[0], axes=ell_cv2[1], angle=ell_cv2[2], startAngle=ell_cv2[3], - endAngle=ell_cv2[4], color=color, thickness=thickness) - - # img_marked = cv2.ellipse(img, box=self.to_cv2_box(), color=color, thickness=thickness) - - return img_marked - - def copy(self): - return Ellipse(center=self.center, - axes=self.axes, - angle=self.angle) - - def info(self): - s = 'center: ({:6.2f}, {:6.2f})\n'.format(self.center[0], self.center[1]) - s += 'axes: ({:6.2f}, {:6.2f})\n'.format(self.axes[0], self.axes[1]) - s += 'angle: {:8.2f} deg\n'.format(self.angle) - s += 'area: {:9.2f}\n'.format(self.get_area()) - return s - - @staticmethod - def from_cv2_box(box): - """ - get Ellipse object from cv2 rotated rectangle object (from cv2.fitEllipse() function) - """ - center = (box[0][1], box[0][0]) - axes = (box[1][0] / 2., box[1][1] / 2.) - angle = -box[2] - return Ellipse(center=center, axes=axes, angle=angle) - - def to_cv2_box(self): - return ((self.center[1], self.center[0]), (self.axes[0] * 2., self.axes[1] * 2), -self.angle) + return WeightedROI(mask, pixelSize=pixelSize, pixelSizeUnit=pixelSizeUnit) if __name__ == '__main__': @@ -2048,7 +1656,7 @@ def to_cv2_box(self): #============================================================ #============================================================ - a=np.array(range(15)+range(10)[::-1]).reshape((5,5)) + a=np.array(list(range(15))+list(range(10))[::-1]).reshape((5,5)) print(a) labeled,_ = ni.label(a>7) peakCoor = np.array(np.where(a==np.amax(a))).transpose()[0] @@ -2060,7 +1668,7 @@ def to_cv2_box(self): #============================================================ # mov = np.arange(64).reshape((4,4,4)) - # print(mov) + # print mov # # mask1 = np.zeros((4,4)); mask1[2,2]=1; mask1[1,1]=1 # trace1 = get_trace(mov,mask1,maskMode='binary') @@ -2089,7 +1697,7 @@ def to_cv2_box(self): # # masks = {'mask1':mask1, 'mask2':mask2} # traces = get_trace_binaryslicer3(bl_obj,masks,mask_mode='binary',loading_frame_num=2) - # print(traces) + # print traces # assert(traces['trace_mask1'][2] == 39.5) # assert(traces['trace_mask2'][3] == 60.5) #============================================================ @@ -2099,13 +1707,13 @@ def to_cv2_box(self): # roi1 = np.zeros((10, 10)) # roi1[4:8, 3:7] = 1 # roi1 = ROI(roi1) - # print(roi1.get_pixel_array()) - # print(roi1.get_pixel_list()) - # print(roi1.get_pixel_tuple()) + # print roi1.get_pixel_array() + # print roi1.get_pixel_list() + # print roi1.get_pixel_tuple() # roi2 = np.zeros((10, 10)) # roi2[5:9, 5:8] = 1 # roi2 = ROI(roi2) - # print(roi1.binary_overlap(roi2)) + # print roi1.binary_overlap(roi2) # ============================================================ # ============================================================ @@ -2113,9 +1721,9 @@ def to_cv2_box(self): # trend = np.zeros((100,)) # trend[5] = 1 # detrended, slope, r = regression_detrend_1d(sig, trend) - # print(detrended) - # print(slope) - # print(r) + # print detrended + # print slope + # print r # ============================================================ # ============================================================ diff --git a/corticalmapping/core/PlottingTools.py b/corticalmapping/core/PlottingTools.py index f5d918d..d6c2fb5 100644 --- a/corticalmapping/core/PlottingTools.py +++ b/corticalmapping/core/PlottingTools.py @@ -8,21 +8,14 @@ import numpy as np import matplotlib.pyplot as plt from matplotlib import cm -import matplotlib import matplotlib.gridspec as gridspec import colorsys import matplotlib.colors as col import scipy.ndimage as ni -try: - import tifffile as tf -except ImportError: - import skimage.external.tifffile as tf - -try: - import ImageAnalysis as ia -except (AttributeError, ImportError): - from . import ImageAnalysis as ia +from . import tifffile as tf +from . import ImageAnalysis as ia +from . import TimingAnalysis as ta try: import cv2 @@ -41,7 +34,7 @@ def get_color_str(R, G, B): """ get hex color string from R,G,B value (integer with uint8 format) """ - if not (isinstance(R, (int, long)) and isinstance(G, (int, long)) and isinstance(G, (int, long))): + if not (isinstance(R, int) and isinstance(G, int) and isinstance(G, int)): raise TypeError('Input R, G and B should be integer!') if not ((0 <= R <= 255) and (0 <= G <= 255) and ( @@ -143,9 +136,7 @@ def bar_graph(left, elif errorDir == 'negative': yerr = [[error], [0]] else: - raise (ValueError, '"errorDir" should be one of the following: "both", "positive" of "negative".') - - + raise ValueError plotAxis.errorbar(left + width / 2, height, @@ -161,8 +152,7 @@ def bar_graph(left, color=faceColor, edgecolor=edgeColor, lw=lw, - label=label, - align='edge') + label=label) return plotAxis @@ -522,15 +512,13 @@ def save_figure_without_borders(f, """ remove borders of a figure """ - f.gca().get_xaxis().set_visible(False) - f.gca().get_yaxis().set_visible(False) + # f.gca().get_xaxis().set_visible(False) + # f.gca().get_yaxis().set_visible(False) f.gca().set_axis_off() f.gca().set_title('') if removeSuperTitle: f.suptitle('') - f.tight_layout(pad=0., h_pad=0., w_pad=0., rect=(0, 0, 1, 1)) - # f.savefig(savePath, frameon=False, **kwargs) - f.savefig(savePath, pad_inches=0, bbox_inches='tight', frameon=False, **kwargs) + f.savefig(savePath, pad_inches=0, bbox_inches='tight', **kwargs) def merge_normalized_images(imgList, isFilter=True, sigma=50, mergeMethod='mean', dtype=np.float32): @@ -662,7 +650,7 @@ def plot_spike_waveforms(unit_ts, channels, channel_ts, fig=None, t_range=(-0.00 :return: fig """ - # print('in plotting tools.') + # print 'in plotting tools.' if fig is None: fig = plt.figure(figsize=(8, 6)) @@ -671,14 +659,14 @@ def plot_spike_waveforms(unit_ts, channels, channel_ts, fig=None, t_range=(-0.00 t_step = np.mean(np.diff(channel_ts)) ind_range = [int(t_range[0] / t_step), int(t_range[1] / t_step)] - # print('ind_range:', ind_range) + # print 'ind_range:', ind_range if t_range[0] < 0: base_point_num = -int(t_range[0] / t_step) else: base_point_num = ind_range[1] - ind_range[0] - # print('getting spike indices ...') + # print 'getting spike indices ...' unit_inds = np.round((unit_ts - channel_ts[0]) / t_step).astype(np.int64) unit_inds = np.array([ind for ind in unit_inds if (ind + ind_range[0]) >= 0 and (ind + ind_range[1]) < len(channel_ts)]) @@ -686,11 +674,11 @@ def plot_spike_waveforms(unit_ts, channels, channel_ts, fig=None, t_range=(-0.00 # axis direction: (channel, spike, time) traces = np.zeros((ch_num, len(unit_inds), ind_range[1] - ind_range[0]), dtype=np.float32) - # print('traces shape:', traces.shape) + # print 'traces shape:', traces.shape - # print('filling traces ...') + # print 'filling traces ...' for i, ch in enumerate(channels): - # print('current channel:', i) + # print 'current channel:', i for j, unit_ind in enumerate(unit_inds): curr_trace = ch[unit_ind + ind_range[0]: unit_ind + ind_range[1]] traces[i, j, :] = curr_trace - np.mean(curr_trace[0:base_point_num]) @@ -699,8 +687,8 @@ def plot_spike_waveforms(unit_ts, channels, channel_ts, fig=None, t_range=(-0.00 traces_max = np.amax(traces) mean_traces = np.mean(traces, axis=1) - # print(traces_min) - # print(traces_max) + # print traces_min + # print traces_max t_axis = t_range[0] + np.arange(traces.shape[2], dtype=np.float32) * t_step for k in range(traces.shape[0]): @@ -827,71 +815,10 @@ def plot_multiple_traces(traces, x=None, plot_axis=None, mean_kw=None, is_plot_s return plot_axis -def plot_dire_distribution(dires, weights=None, is_arc=False, bins=12, plot_ax=None, plot_type='bar', **kwargs): - """ - plot the distribution of a list of directions in a nice way. - - :param dires: array of float. directions to be plotted. - :param weights: array with same size as dires, weights of data - :param is_arc: bool. If True, dires are in [0, 2*pi] scale, if False, dires are in [0, 360] scale - :param bins: int, how many bins are there - :param plot_ax: matplotlib.axes._subplots.PolarAxesSubplot object - :param plot_type: str, 'bar' or 'line' - :param kwargs: if plot_type == 'bar', key word argument to the plot_ax.bar() function; - if plot_type == 'line', kew word argument to the plot_ax.plot() function; - :return: - """ - - if plot_ax is None: - f = plt.figure(figsize=(5,5)) - plot_ax = f.add_subplot(111, projection='polar') - - if not isinstance(plot_ax, matplotlib.projections.polar.PolarAxes): - raise TypeError('input "plot_ax" should be a "matplotlib.projections.polar.PolarAxes" or ' - 'a "matplotlib.axes._subplots.PolarAxesSubplot" object') - - plot_dires = np.array(dires, dtype=np.float64) - - if is_arc is False: - plot_dires = plot_dires * np.pi / 180. - - plot_dires = plot_dires % (2 * np.pi) - - bin_width = np.pi * 2 / bins - - for dire_i, dire in enumerate(plot_dires): - if dire > ((np.pi * 2) - (bin_width / 2)): - plot_dires[dire_i] = dire - (np.pi * 2) - - # print(plot_dires) - counts, bin_lst = np.histogram(plot_dires, weights=weights, bins=bins, range=[-bin_width / 2., (np.pi * 2) - (bin_width / 2)]) - bin_lst = bin_lst[0:-1] + (bin_width / 2) - - if plot_type == 'bar': - plot_ax.bar(bin_lst, counts, width=bin_width, align='center', **kwargs) - elif plot_type == 'line': - counts = list(counts) - counts.append(counts[0]) - bin_lst = list(bin_lst) - bin_lst.append(bin_lst[0]) - plot_ax.plot(bin_lst, counts, **kwargs) - else: - raise LookupError('Do not understand parameter "plot_type", should be "bar" or "line".') - - plot_ax.set_xticklabels([]) - - return plot_ax, counts[:-1], bin_lst[:-1] - if __name__ == '__main__': plt.ioff() - # ---------------------------------------------------- - dires = [0,0,0,90,90,90,90,90,90,180,180] - plot_dire_distribution(dires=dires, is_arc=False) - plt.show() - # ---------------------------------------------------- - # ---------------------------------------------------- # bg = np.random.rand(100,100) # maskBin=np.zeros((100,100),dtype=np.uint8) @@ -1015,8 +942,8 @@ def plot_dire_distribution(dires, weights=None, is_arc=False, bins=12, plot_ax= # ---------------------------------------------------- # ---------------------------------------------------- - # grid_axis2(nrows=4, ncols=3, share_level=1) - # plt.show() + grid_axis2(nrows=4, ncols=3, share_level=1) + plt.show() # ---------------------------------------------------- print('for debug') diff --git a/corticalmapping/core/TimingAnalysis.py b/corticalmapping/core/TimingAnalysis.py index 2530691..51dfa41 100644 --- a/corticalmapping/core/TimingAnalysis.py +++ b/corticalmapping/core/TimingAnalysis.py @@ -89,8 +89,8 @@ def discrete_cross_correlation(ts1, ts2, t_range=(-1., 1.), bins=100, isPlot=Fal """ bin_width = (float(t_range[1]) - float(t_range[0])) / bins - t = np.arange(bins).astype(np.float64) * bin_width + t_range[0] - intervals = zip(t, t + bin_width) + t = np.arange(bins) * bin_width + t_range[0] + intervals = list(zip(t, t + bin_width)) values = np.zeros(bins, dtype=np.int64) ts1s = np.sort(ts1) # sort first timestamps array ts2s = np.sort(ts2) # sort second timestamps array @@ -101,7 +101,7 @@ def discrete_cross_correlation(ts1, ts2, t_range=(-1., 1.), bins=100, isPlot=Fal n = len(ts1s) if n == 0: - print 'no overlapping time range (defined as ' + str(t_range) + ' between two input timestamp arrays' + print('no overlapping time range (defined as ' + str(t_range) + ' between two input timestamp arrays') # return None else: ts2_start_ind = 0 @@ -129,7 +129,7 @@ def discrete_cross_correlation(ts1, ts2, t_range=(-1., 1.), bins=100, isPlot=Fal ax = f.add_subplot(111) ax.bar([a[0] for a in intervals], values, bin_width * 0.9) - return t, values.astype(np.float64) + return t, values.astype(np.float32) def find_nearest(trace, value, direction=0): @@ -258,7 +258,7 @@ def sliding_power_spectrum(trace, fs, sliding_window_length=5., sliding_step_len freq_axis: frequency for each row (from low to high) ''' - if len(trace.shape) != 1: raise ValueError, 'Input trace should be 1d array!' + if len(trace.shape) != 1: raise ValueError('Input trace should be 1d array!') total_length = len(trace) / float(fs) @@ -268,14 +268,14 @@ def sliding_power_spectrum(trace, fs, sliding_window_length=5., sliding_step_len freq_axis = np.arange(freq_bins, dtype=np.float32) * freq_bin_width + freq_range[0] if sliding_step_length is None: sliding_step_length = sliding_window_length - if sliding_step_length > sliding_window_length: print "Step length larger than window length, not using all data points!" + if sliding_step_length > sliding_window_length: print("Step length larger than window length, not using all data points!") times = np.arange(0., total_length, sliding_step_length) times = times[(times + sliding_window_length) < total_length] - if len(times) == 0: raise ValueError, 'No time point found.' + if len(times) == 0: raise ValueError('No time point found.') else: points_in_window = int(sliding_window_length * fs) - if points_in_window <= 0: raise ValueError, 'Sliding window length too short!' + if points_in_window <= 0: raise ValueError('Sliding window length too short!') else: spectrum = np.zeros((len(freq_axis), len(times))) for idx, start_time in enumerate(times): @@ -291,8 +291,8 @@ def sliding_power_spectrum(trace, fs, sliding_window_length=5., sliding_step_len fig = ax.imshow(spectrum, interpolation='nearest', **kwargs) ax.set_xlabel('times (sec)') ax.set_ylabel('frequency (Hz)') - ax.set_xticks(range(len(times))[::(len(times)//10)]) - ax.set_yticks(range(len(freq_axis))[::(len(freq_axis)//10)]) + ax.set_xticks(list(range(len(times)))[::(len(times)//10)]) + ax.set_yticks(list(range(len(freq_axis)))[::(len(freq_axis)//10)]) ax.set_xticklabels(times[::(len(times)//10)]) ax.set_yticklabels(freq_axis[::(len(freq_axis)//10)]) ax.invert_yaxis() @@ -640,7 +640,7 @@ def event_triggered_average_irregular(ts_event, continuous, ts_continuous, t_ran eta = np.zeros(t.shape, dtype=np.float32) eta[:] = np.nan - print '\nStart calculating event triggered average ...' + print('\nStart calculating event triggered average ...') percentage = None for ind_eve, eve in enumerate(ts_event): @@ -648,7 +648,7 @@ def event_triggered_average_irregular(ts_event, continuous, ts_continuous, t_ran # for display curr_percentage = int((float(ind_eve) * 100. / float(len(ts_event))) // 10) * 10 if curr_percentage != percentage: - print 'progress: ' + str(curr_percentage) + '%' + print('progress: ' + str(curr_percentage) + '%') # print eve, ':', ts_continuous[-1] percentage = curr_percentage @@ -784,73 +784,6 @@ def event_triggered_event_trains(event_ts, triggers, t_range=(-1., 2.)): return etts, t_range -def threshold_to_intervals(trace, thr, comparison='>='): - """ - threshold a 1d trace, return intervals of indices that are above the threshold. - - :param trace: 1d array - :param thr: float - :param comparison: str, '>', '>=', '<' or '<=' - :return: list of tuples, each tuple contains two non-negative integers representing - the start index and the end index of thresholded intervals. the first int should - be smaller than the second int. - """ - - if len(trace.shape) != 1: - raise ValueError("the input 'trace' should be a 1d array.") - - flag = False - - start = [] - end = [] - - for pi, pv in enumerate(trace): - - if comparison == '>=': - if pv >= thr and (not flag): - start.append(pi) - flag = True - - if pv < thr and flag: - end.append(pi) - flag = False - - elif comparison == '>': - if pv > thr and (not flag): - start.append(pi) - flag = True - - if pv <= thr and flag: - end.append(pi) - flag = False - - elif comparison == '<=': - if pv <= thr and (not flag): - start.append(pi) - flag = True - - if pv > thr and flag: - end.append(pi) - flag = False - - elif comparison == '<': - if pv < thr and (not flag): - start.append(pi) - flag = True - - if pv >= thr and flag: - end.append(pi) - flag = False - - else: - raise LookupError('Do not understand input "comparison", should be ">=", ">", "<=", "<".') - - if len(start) - len(end) == 1: - end.append(len(trace)) - - return zip(start, end) - - def haramp(trace, periods, ceil_f=4): """ get amplitudes of first couple harmonic components from a time series corresponding to a sinusoidal stimulus. @@ -886,133 +819,6 @@ def haramp(trace, periods, ceil_f=4): return harmonic -class TimeIntervals(object): - """ - class to describe time intervals, designed to represent epochs - - self.data save the (start, end) timestamps of each epochs. Shape (n, 2). - Each row: a single interval - column 0: start timestamps - column 1: end timestamps - - the intervals are incremental in time and should not have overlap within them. - """ - - def __init__(self, intervals): - self._intervals = self.check_integraty(intervals) - - def get_intervals(self): - return self._intervals - - @staticmethod - def check_integraty(intervals): - - intervals_cp = np.array([np.array(d, dtype=np.float64) for d in intervals]) - intervals_cp = intervals_cp.astype(np.float64) - - if len(intervals_cp.shape) != 2: - raise ValueError('intervals should be 2d.') - - if intervals_cp.shape[1] != 2: - raise ValueError('intervals.shape[1] should be 2. (start, end) of the interval') - - # for interval_i, interval in enumerate(intervals_cp): - # if interval[1] <= interval[0]: - # raise ValueError('the {}th interval: end time ({}) earlier than start time ({})'. - # format(interval_i, interval[1], interval[0])) - - intervals_cp = intervals_cp[intervals_cp[:, 0].argsort()] - - ts_list = np.concatenate(intervals_cp, axis=0) - if not check_monotonicity(arr=ts_list, direction='increasing'): - raise ValueError('The intervals should be incremental in time and should not have overlap within them.') - - return intervals_cp - - def overlap(self, time_intervals): - """ - return a new TimeIntervals object that represents the overlap between self and the input Timeintervals - - :param time_intervals: corticalmapping.core.TimingAnalysis.TimeIntervals object - """ - - starts0 = [[s, 1] for s in self._intervals[:, 0]] - ends0 = [[e, -1] for e in self._intervals[:, 1]] - - starts1 = [[s, 1] for s in time_intervals.get_intervals()[:, 0]] - ends1 = [[e, -1] for e in time_intervals.get_intervals()[:, 1]] - - events_lst = starts0 + ends0 + starts1 + ends1 - # print(events_lst) - - ts_arr = np.array([e[0] for e in events_lst]) - events_lst = [events_lst[i] for i in np.argsort(ts_arr)] - # print(events_lst) - - mask = np.cumsum([e[1] for e in events_lst]) - - new_starts = [] - new_ends = [] - - flag = 0 # 1: within overlap, 0: outside overlap - for ts_i, msk in enumerate(mask): - - if flag == 0 and msk == 2: - new_starts.append(events_lst[ts_i][0]) - flag = 1 - elif flag == 1 and msk < 2: - new_ends.append(events_lst[ts_i][0]) - flag = 0 - elif flag == 1 and msk == 2: - raise ValueError('do not understand the timestamps: flag={}, msk={}.'.format(flag, msk)) - else: - pass - - if len(new_starts) != len(new_ends): - raise ValueError('the length of new_starts ({}) does not equal the length of new_ends ({}).'. - format(len(new_starts), len(new_ends))) - - if new_starts: - new_intervals = np.array([np.array(new_starts), np.array(new_ends)]).transpose() - return TimeIntervals(intervals=new_intervals) - else: - return None - - def is_contain(self, time_interval): - """ - :param time_interval: list or tuple of two floats, representing one single time interval - :return: bool, if the input interval is completely contained by self - """ - - if len(time_interval) != 2: - raise ValueError('input "time_interval" should have two and only two elements.') - - if time_interval[0] >= time_interval[1]: - raise ValueError('the start of input "time_interval" should be earlier than the end.') - - for interval in self._intervals: - - if interval[0] > time_interval[0]: # current interval starts after input time_interval - return False - else: # current interval starts before input time_interval - if interval[1] < time_interval[0]: # current interval ends before input time_interval - pass - elif interval[1] < time_interval[1]: # current interval ends within input time_interval - return False - else: - return True # current interval contains input time_interval - - # all intervals in self end before input time_interval - # or self._intervals is empty - return False - - def to_h5_group(self, grp): - pass - - @staticmethod - def from_h5_group(grp): - pass - if __name__=='__main__': @@ -1117,4 +923,4 @@ def from_h5_group(grp): # butter_highpass_filter(is_plot=True) # ============================================================================================================ - print 'for debugging...' \ No newline at end of file + print('for debugging...') \ No newline at end of file diff --git a/corticalmapping/core/tifffile.py b/corticalmapping/core/tifffile.py new file mode 100644 index 0000000..8391991 --- /dev/null +++ b/corticalmapping/core/tifffile.py @@ -0,0 +1,3668 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# tifffile.py + +# Copyright (c) 2008-2014, Christoph Gohlke +# Copyright (c) 2008-2014, The Regents of the University of California +# Produced at the Laboratory for Fluorescence Dynamics +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holders nor the names of any +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +"""Read and write image data from and to TIFF files. + +Image and meta-data can be read from TIFF, BigTIFF, OME-TIFF, STK, LSM, NIH, +ImageJ, MicroManager, FluoView, SEQ and GEL files. +Only a subset of the TIFF specification is supported, mainly uncompressed +and losslessly compressed 2**(0 to 6) bit integer, 16, 32 and 64-bit float, +grayscale and RGB(A) images, which are commonly used in bio-scientific imaging. +Specifically, reading JPEG/CCITT compressed image data or EXIF/IPTC/GPS/XMP +meta-data is not implemented. Only primary info records are read for STK, +FluoView, MicroManager, and NIH image formats. + +TIFF, the Tagged Image File Format, is under the control of Adobe Systems. +BigTIFF allows for files greater than 4 GB. STK, LSM, FluoView, SEQ, GEL, +and OME-TIFF, are custom extensions defined by MetaMorph, Carl Zeiss +MicroImaging, Olympus, Media Cybernetics, Molecular Dynamics, and the Open +Microscopy Environment consortium respectively. + +For command line usage run ``python tifffile.py --help`` + +:Author: + `Christoph Gohlke `_ + +:Organization: + Laboratory for Fluorescence Dynamics, University of California, Irvine + +:Version: 2014.02.05 + +Requirements +------------ +* `CPython 2.7 or 3.3 `_ +* `Numpy 1.7 `_ +* `Matplotlib 1.3 `_ (optional for plotting) +* `Tifffile.c 2013.01.18 `_ + (recommended for faster decoding of PackBits and LZW encoded strings) + +Notes +----- +The API is not stable yet and might change between revisions. + +Tested on little-endian platforms only. + +Other Python packages and modules for reading bio-scientific TIFF files: +* `Imread `_ +* `PyLibTiff `_ +* `SimpleITK `_ +* `PyLSM `_ +* `PyMca.TiffIO.py `_ +* `BioImageXD.Readers `_ +* `Cellcognition.io `_ +* `CellProfiler.bioformats `_ + +Acknowledgements +---------------- +* Egor Zindy, University of Manchester, for cz_lsm_scan_info specifics. +* Wim Lewis for a bug fix and some read_cz_lsm functions. +* Hadrien Mary for help on reading MicroManager files. + +References +---------- +(1) TIFF 6.0 Specification and Supplements. Adobe Systems Incorporated. + http://partners.adobe.com/public/developer/tiff/ +(2) TIFF File Format FAQ. http://www.awaresystems.be/imaging/tiff/faq.html +(3) MetaMorph Stack (STK) Image File Format. + http://support.meta.moleculardevices.com/docs/t10243.pdf +(4) File Format Description - LSM 5xx Release 2.0. + http://ibb.gsf.de/homepage/karsten.rodenacker/IDL/Lsmfile.doc +(5) BioFormats. http://www.loci.wisc.edu/ome/formats.html +(6) The OME-TIFF format. + http://www.openmicroscopy.org/site/support/file-formats/ome-tiff +(7) TiffDecoder.java + http://rsbweb.nih.gov/ij/developer/source/ij/io/TiffDecoder.java.html +(8) UltraQuant(r) Version 6.0 for Windows Start-Up Guide. + http://www.ultralum.com/images%20ultralum/pdf/UQStart%20Up%20Guide.pdf +(9) Micro-Manager File Formats. + http://www.micro-manager.org/wiki/Micro-Manager_File_Formats + +Examples +-------- +>>> data = numpy.random.rand(301, 219) +>>> imsave('temp.tif', data) +>>> image = imread('temp.tif') +>>> assert numpy.all(image == data) + +>>> tif = TiffFile('test.tif') +>>> images = tif.asarray() +>>> image0 = tif[0].asarray() +>>> for page in tif: +... for tag in page.tags.values(): +... t = tag.name, tag.value +... image = page.asarray() +... if page.is_rgb: pass +... if page.is_palette: +... t = page.color_map +... if page.is_stk: +... t = page.mm_uic_tags.number_planes +... if page.is_lsm: +... t = page.cz_lsm_info +>>> tif.close() + +""" + + + +import sys +import os +import re +import glob +import math +import zlib +import time +import json +import struct +import warnings +import datetime +import collections +from fractions import Fraction +from xml.etree import cElementTree as ElementTree + +import numpy + +__version__ = '2014.02.05' +__docformat__ = 'restructuredtext en' +__all__ = ['imsave', 'imread', 'imshow', 'TiffFile', 'TiffSequence'] + + +def imsave(filename, data, photometric=None, planarconfig=None, + resolution=None, description=None, software='tifffile.py', + byteorder=None, bigtiff=False, compress=0, extratags=()): + """Write image data to TIFF file. + + Image data are written in one stripe per plane. + Dimensions larger than 2 or 3 (depending on photometric mode and + planar configuration) are flattened and saved as separate pages. + The 'sample_format' and 'bits_per_sample' TIFF tags are derived from + the data type. + + Parameters + ---------- + filename : str + Name of file to write. + data : array_like + Input image. The last dimensions are assumed to be image height, + width, and samples. + photometric : {'minisblack', 'miniswhite', 'rgb'} + The color space of the image data. + By default this setting is inferred from the data shape. + planarconfig : {'contig', 'planar'} + Specifies if samples are stored contiguous or in separate planes. + By default this setting is inferred from the data shape. + 'contig': last dimension contains samples. + 'planar': third last dimension contains samples. + resolution : (float, float) or ((int, int), (int, int)) + X and Y resolution in dots per inch as float or rational numbers. + description : str + The subject of the image. Saved with the first page only. + software : str + Name of the software used to create the image. + Saved with the first page only. + byteorder : {'<', '>'} + The endianness of the data in the file. + By default this is the system's native byte order. + bigtiff : bool + If True, the BigTIFF format is used. + By default the standard TIFF format is used for data less than 2000 MB. + compress : int + Values from 0 to 9 controlling the level of zlib compression. + If 0, data are written uncompressed (default). + extratags: sequence of tuples + Additional tags as [(code, dtype, count, value, writeonce)]. + code : int + The TIFF tag Id. + dtype : str + Data type of items in `value` in Python struct format. + One of B, s, H, I, 2I, b, h, i, f, d, Q, or q. + count : int + Number of data values. Not used for string values. + value : sequence + `Count` values compatible with `dtype`. + writeonce : bool + If True, the tag is written to the first page only. + + Examples + -------- + >>> data = numpy.ones((2, 5, 3, 301, 219), 'float32') * 0.5 + >>> imsave('temp.tif', data, compress=6) + + >>> data = numpy.ones((5, 301, 219, 3), 'uint8') + 127 + >>> value = u'{"shape": %s}' % str(list(data.shape)) + >>> imsave('temp.tif', data, extratags=[(270, 's', 0, value, True)]) + + """ + assert(photometric in (None, 'minisblack', 'miniswhite', 'rgb')) + assert(planarconfig in (None, 'contig', 'planar')) + assert(byteorder in (None, '<', '>')) + assert(0 <= compress <= 9) + + if byteorder is None: + byteorder = '<' if sys.byteorder == 'little' else '>' + + data = numpy.asarray(data, dtype=byteorder+data.dtype.char, order='C') + data_shape = shape = data.shape + data = numpy.atleast_2d(data) + + if not bigtiff and data.size * data.dtype.itemsize < 2000*2**20: + bigtiff = False + offset_size = 4 + tag_size = 12 + numtag_format = 'H' + offset_format = 'I' + val_format = '4s' + else: + bigtiff = True + offset_size = 8 + tag_size = 20 + numtag_format = 'Q' + offset_format = 'Q' + val_format = '8s' + + # unify shape of data + samplesperpixel = 1 + extrasamples = 0 + if photometric is None: + if data.ndim > 2 and (shape[-3] in (3, 4) or shape[-1] in (3, 4)): + photometric = 'rgb' + else: + photometric = 'minisblack' + if photometric == 'rgb': + if len(shape) < 3: + raise ValueError("not a RGB(A) image") + if planarconfig is None: + planarconfig = 'planar' if shape[-3] in (3, 4) else 'contig' + if planarconfig == 'contig': + if shape[-1] not in (3, 4): + raise ValueError("not a contiguous RGB(A) image") + data = data.reshape((-1, 1) + shape[-3:]) + samplesperpixel = shape[-1] + else: + if shape[-3] not in (3, 4): + raise ValueError("not a planar RGB(A) image") + data = data.reshape((-1, ) + shape[-3:] + (1, )) + samplesperpixel = shape[-3] + if samplesperpixel == 4: + extrasamples = 1 + elif planarconfig and len(shape) > 2: + if planarconfig == 'contig': + data = data.reshape((-1, 1) + shape[-3:]) + samplesperpixel = shape[-1] + else: + data = data.reshape((-1, ) + shape[-3:] + (1, )) + samplesperpixel = shape[-3] + extrasamples = samplesperpixel - 1 + else: + planarconfig = None + # remove trailing 1s + while len(shape) > 2 and shape[-1] == 1: + shape = shape[:-1] + data = data.reshape((-1, 1) + shape[-2:] + (1, )) + + shape = data.shape # (pages, planes, height, width, contig samples) + + bytestr = bytes if sys.version[0] == '2' else ( + lambda x: bytes(x, 'utf-8') if isinstance(x, str) else x) + tifftypes = {'B': 1, 's': 2, 'H': 3, 'I': 4, '2I': 5, 'b': 6, + 'h': 8, 'i': 9, 'f': 11, 'd': 12, 'Q': 16, 'q': 17} + tifftags = { + 'new_subfile_type': 254, 'subfile_type': 255, + 'image_width': 256, 'image_length': 257, 'bits_per_sample': 258, + 'compression': 259, 'photometric': 262, 'fill_order': 266, + 'document_name': 269, 'image_description': 270, 'strip_offsets': 273, + 'orientation': 274, 'samples_per_pixel': 277, 'rows_per_strip': 278, + 'strip_byte_counts': 279, 'x_resolution': 282, 'y_resolution': 283, + 'planar_configuration': 284, 'page_name': 285, 'resolution_unit': 296, + 'software': 305, 'datetime': 306, 'predictor': 317, 'color_map': 320, + 'extra_samples': 338, 'sample_format': 339} + tags = [] # list of (code, ifdentry, ifdvalue, writeonce) + + def pack(fmt, *val): + return struct.pack(byteorder+fmt, *val) + + def addtag(code, dtype, count, value, writeonce=False): + # compute ifdentry and ifdvalue bytes from code, dtype, count, value + # append (code, ifdentry, ifdvalue, writeonce) to tags list + code = tifftags[code] if code in tifftags else int(code) + if dtype not in tifftypes: + raise ValueError("unknown dtype %s" % dtype) + tifftype = tifftypes[dtype] + rawcount = count + if dtype == 's': + value = bytestr(value) + b'\0' + count = rawcount = len(value) + value = (value, ) + if len(dtype) > 1: + count *= int(dtype[:-1]) + dtype = dtype[-1] + ifdentry = [pack('HH', code, tifftype), + pack(offset_format, rawcount)] + ifdvalue = None + if count == 1: + if isinstance(value, (tuple, list)): + value = value[0] + ifdentry.append(pack(val_format, pack(dtype, value))) + elif struct.calcsize(dtype) * count <= offset_size: + ifdentry.append(pack(val_format, pack(str(count)+dtype, *value))) + else: + ifdentry.append(pack(offset_format, 0)) + ifdvalue = pack(str(count)+dtype, *value) + tags.append((code, b''.join(ifdentry), ifdvalue, writeonce)) + + def rational(arg, max_denominator=1000000): + # return nominator and denominator from float or two integers + try: + f = Fraction.from_float(arg) + except TypeError: + f = Fraction(arg[0], arg[1]) + f = f.limit_denominator(max_denominator) + return f.numerator, f.denominator + + if software: + addtag('software', 's', 0, software, writeonce=True) + if description: + addtag('image_description', 's', 0, description, writeonce=True) + elif shape != data_shape: + addtag('image_description', 's', 0, + "shape=(%s)" % (",".join('%i' % i for i in data_shape)), + writeonce=True) + addtag('datetime', 's', 0, + datetime.datetime.now().strftime("%Y:%m:%d %H:%M:%S"), + writeonce=True) + addtag('compression', 'H', 1, 32946 if compress else 1) + addtag('orientation', 'H', 1, 1) + addtag('image_width', 'I', 1, shape[-2]) + addtag('image_length', 'I', 1, shape[-3]) + addtag('new_subfile_type', 'I', 1, 0 if shape[0] == 1 else 2) + addtag('sample_format', 'H', 1, + {'u': 1, 'i': 2, 'f': 3, 'c': 6}[data.dtype.kind]) + addtag('photometric', 'H', 1, + {'miniswhite': 0, 'minisblack': 1, 'rgb': 2}[photometric]) + addtag('samples_per_pixel', 'H', 1, samplesperpixel) + if planarconfig: + addtag('planar_configuration', 'H', 1, 1 if planarconfig=='contig' + else 2) + addtag('bits_per_sample', 'H', samplesperpixel, + (data.dtype.itemsize * 8, ) * samplesperpixel) + else: + addtag('bits_per_sample', 'H', 1, data.dtype.itemsize * 8) + if extrasamples: + if photometric == 'rgb': + addtag('extra_samples', 'H', 1, 1) # alpha channel + else: + addtag('extra_samples', 'H', extrasamples, (0, ) * extrasamples) + if resolution: + addtag('x_resolution', '2I', 1, rational(resolution[0])) + addtag('y_resolution', '2I', 1, rational(resolution[1])) + addtag('resolution_unit', 'H', 1, 2) + addtag('rows_per_strip', 'I', 1, shape[-3]) + + # use one strip per plane + strip_byte_counts = (data[0, 0].size * data.dtype.itemsize, ) * shape[1] + addtag('strip_byte_counts', offset_format, shape[1], strip_byte_counts) + addtag('strip_offsets', offset_format, shape[1], (0, ) * shape[1]) + + # add extra tags from users + for t in extratags: + addtag(*t) + + # the entries in an IFD must be sorted in ascending order by tag code + tags = sorted(tags, key=lambda x: x[0]) + + with open(filename, 'wb') as fh: + seek = fh.seek + tell = fh.tell + + def write(arg, *args): + fh.write(pack(arg, *args) if args else arg) + + write({'<': b'II', '>': b'MM'}[byteorder]) + if bigtiff: + write('HHH', 43, 8, 0) + else: + write('H', 42) + ifd_offset = tell() + write(offset_format, 0) # first IFD + + for pageindex in range(shape[0]): + # update pointer at ifd_offset + pos = tell() + seek(ifd_offset) + write(offset_format, pos) + seek(pos) + + # write ifdentries + write(numtag_format, len(tags)) + tag_offset = tell() + write(b''.join(t[1] for t in tags)) + ifd_offset = tell() + write(offset_format, 0) # offset to next IFD + + # write tag values and patch offsets in ifdentries, if necessary + for tagindex, tag in enumerate(tags): + if tag[2]: + pos = tell() + seek(tag_offset + tagindex*tag_size + offset_size + 4) + write(offset_format, pos) + seek(pos) + if tag[0] == 273: + strip_offsets_offset = pos + elif tag[0] == 279: + strip_byte_counts_offset = pos + write(tag[2]) + + # write image data + data_offset = tell() + if compress: + strip_byte_counts = [] + for plane in data[pageindex]: + plane = zlib.compress(plane, compress) + strip_byte_counts.append(len(plane)) + fh.write(plane) + else: + # if this fails try update Python/numpy + data[pageindex].tofile(fh) + fh.flush() + + # update strip_offsets and strip_byte_counts if necessary + pos = tell() + for tagindex, tag in enumerate(tags): + if tag[0] == 273: # strip_offsets + if tag[2]: + seek(strip_offsets_offset) + strip_offset = data_offset + for size in strip_byte_counts: + write(offset_format, strip_offset) + strip_offset += size + else: + seek(tag_offset + tagindex*tag_size + offset_size + 4) + write(offset_format, data_offset) + elif tag[0] == 279: # strip_byte_counts + if compress: + if tag[2]: + seek(strip_byte_counts_offset) + for size in strip_byte_counts: + write(offset_format, size) + else: + seek(tag_offset + tagindex*tag_size + + offset_size + 4) + write(offset_format, strip_byte_counts[0]) + break + seek(pos) + fh.flush() + # remove tags that should be written only once + if pageindex == 0: + tags = [t for t in tags if not t[-1]] + + +def imread(files, *args, **kwargs): + """Return image data from TIFF file(s) as numpy array. + + The first image series is returned if no arguments are provided. + + Parameters + ---------- + files : str or list + File name, glob pattern, or list of file names. + key : int, slice, or sequence of page indices + Defines which pages to return as array. + series : int + Defines which series of pages in file to return as array. + multifile : bool + If True (default), OME-TIFF data may include pages from multiple files. + pattern : str + Regular expression pattern that matches axes names and indices in + file names. + + Examples + -------- + >>> im = imread('test.tif', 0) + >>> im.shape + (256, 256, 4) + >>> ims = imread(['test.tif', 'test.tif']) + >>> ims.shape + (2, 256, 256, 4) + + """ + kwargs_file = {} + if 'multifile' in kwargs: + kwargs_file['multifile'] = kwargs['multifile'] + del kwargs['multifile'] + else: + kwargs_file['multifile'] = True + kwargs_seq = {} + if 'pattern' in kwargs: + kwargs_seq['pattern'] = kwargs['pattern'] + del kwargs['pattern'] + + if isinstance(files, str) and any(i in files for i in '?*'): + files = glob.glob(files) + if not files: + raise ValueError('no files found') + if len(files) == 1: + files = files[0] + + if isinstance(files, str): + with TiffFile(files, **kwargs_file) as tif: + return tif.asarray(*args, **kwargs) + else: + with TiffSequence(files, **kwargs_seq) as imseq: + return imseq.asarray(*args, **kwargs) + + +class lazyattr(object): + """Lazy object attribute whose value is computed on first access.""" + __slots__ = ('func', ) + + def __init__(self, func): + self.func = func + + def __get__(self, instance, owner): + if instance is None: + return self + value = self.func(instance) + if value is NotImplemented: + return getattr(super(owner, instance), self.func.__name__) + setattr(instance, self.func.__name__, value) + return value + + +class TiffFile(object): + """Read image and meta-data from TIFF, STK, LSM, and FluoView files. + + TiffFile instances must be closed using the close method, which is + automatically called when using the 'with' statement. + + Attributes + ---------- + pages : list + All TIFF pages in file. + series : list of Records(shape, dtype, axes, TiffPages) + TIFF pages with compatible shapes and types. + micromanager_metadata: dict + Extra MicroManager non-TIFF metadata in the file, if exists. + + All attributes are read-only. + + Examples + -------- + >>> tif = TiffFile('test.tif') + ... try: + ... images = tif.asarray() + ... except Exception as e: + ... print(e) + ... finally: + ... tif.close() + + """ + def __init__(self, arg, name=None, multifile=False): + """Initialize instance from file. + + Parameters + ---------- + arg : str or open file + Name of file or open file object. + The file objects are closed in TiffFile.close(). + name : str + Human readable label of open file. + multifile : bool + If True, series may include pages from multiple files. + + """ + if isinstance(arg, str): + filename = os.path.abspath(arg) + self._fh = open(filename, 'rb') + else: + filename = str(name) + self._fh = arg + + self._fh.seek(0, 2) + self._fsize = self._fh.tell() + self._fh.seek(0) + self.fname = os.path.basename(filename) + self.fpath = os.path.dirname(filename) + self._tiffs = {self.fname: self} # cache of TiffFiles + self.offset_size = None + self.pages = [] + self._multifile = bool(multifile) + try: + self._fromfile() + except Exception: + self._fh.close() + raise + + def close(self): + """Close open file handle(s).""" + for tif in list(self._tiffs.values()): + if tif._fh: + tif._fh.close() + tif._fh = None + self._tiffs = {} + + def _fromfile(self): + """Read TIFF header and all page records from file.""" + self._fh.seek(0) + try: + self.byteorder = {b'II': '<', b'MM': '>'}[self._fh.read(2)] + except KeyError: + raise ValueError("not a valid TIFF file") + version = struct.unpack(self.byteorder+'H', self._fh.read(2))[0] + if version == 43: # BigTiff + self.offset_size, zero = struct.unpack(self.byteorder+'HH', + self._fh.read(4)) + if zero or self.offset_size != 8: + raise ValueError("not a valid BigTIFF file") + elif version == 42: + self.offset_size = 4 + else: + raise ValueError("not a TIFF file") + self.pages = [] + while True: + try: + page = TiffPage(self) + self.pages.append(page) + except StopIteration: + break + if not self.pages: + raise ValueError("empty TIFF file") + + if self.is_micromanager: + # MicroManager files contain metadata not stored in TIFF tags. + self.micromanager_metadata = read_micromanager_metadata(self._fh) + + @lazyattr + def series(self): + """Return series of TiffPage with compatible shape and properties.""" + series = [] + if self.is_ome: + series = self._omeseries() + elif self.is_fluoview: + dims = {b'X': 'X', b'Y': 'Y', b'Z': 'Z', b'T': 'T', + b'WAVELENGTH': 'C', b'TIME': 'T', b'XY': 'R', + b'EVENT': 'V', b'EXPOSURE': 'L'} + mmhd = list(reversed(self.pages[0].mm_header.dimensions)) + series = [Record( + axes=''.join(dims.get(i[0].strip().upper(), 'Q') + for i in mmhd if i[1] > 1), + shape=tuple(int(i[1]) for i in mmhd if i[1] > 1), + pages=self.pages, dtype=numpy.dtype(self.pages[0].dtype))] + elif self.is_lsm: + lsmi = self.pages[0].cz_lsm_info + axes = CZ_SCAN_TYPES[lsmi.scan_type] + if self.pages[0].is_rgb: + axes = axes.replace('C', '').replace('XY', 'XYC') + axes = axes[::-1] + shape = [getattr(lsmi, CZ_DIMENSIONS[i]) for i in axes] + pages = [p for p in self.pages if not p.is_reduced] + series = [Record(axes=axes, shape=shape, pages=pages, + dtype=numpy.dtype(pages[0].dtype))] + if len(pages) != len(self.pages): # reduced RGB pages + pages = [p for p in self.pages if p.is_reduced] + cp = 1 + i = 0 + while cp < len(pages) and i < len(shape)-2: + cp *= shape[i] + i += 1 + shape = shape[:i] + list(pages[0].shape) + axes = axes[:i] + 'CYX' + series.append(Record(axes=axes, shape=shape, pages=pages, + dtype=numpy.dtype(pages[0].dtype))) + elif self.is_imagej: + shape = [] + axes = [] + ij = self.pages[0].imagej_tags + if 'frames' in ij: + shape.append(ij['frames']) + axes.append('T') + if 'slices' in ij: + shape.append(ij['slices']) + axes.append('Z') + if 'channels' in ij and not self.is_rgb: + shape.append(ij['channels']) + axes.append('C') + remain = len(self.pages) // (numpy.prod(shape) if shape else 1) + if remain > 1: + shape.append(remain) + axes.append('I') + shape.extend(self.pages[0].shape) + axes.extend(self.pages[0].axes) + axes = ''.join(axes) + series = [Record(pages=self.pages, shape=shape, axes=axes, + dtype=numpy.dtype(self.pages[0].dtype))] + elif self.is_nih: + series = [Record(pages=self.pages, + shape=(len(self.pages),) + self.pages[0].shape, + axes='I' + self.pages[0].axes, + dtype=numpy.dtype(self.pages[0].dtype))] + elif self.pages[0].is_shaped: + shape = self.pages[0].tags['image_description'].value[7:-1] + shape = tuple(int(i) for i in shape.split(b',')) + series = [Record(pages=self.pages, shape=shape, + axes='Q' * len(shape), + dtype=numpy.dtype(self.pages[0].dtype))] + + if not series: + shapes = [] + pages = {} + for page in self.pages: + if not page.shape: + continue + shape = page.shape + (page.axes, + page.compression in TIFF_DECOMPESSORS) + if not shape in pages: + shapes.append(shape) + pages[shape] = [page] + else: + pages[shape].append(page) + series = [Record(pages=pages[s], + axes=(('I' + s[-2]) + if len(pages[s]) > 1 else s[-2]), + dtype=numpy.dtype(pages[s][0].dtype), + shape=((len(pages[s]), ) + s[:-2] + if len(pages[s]) > 1 else s[:-2])) + for s in shapes] + return series + + def asarray(self, key=None, series=None, memmap=False): + """Return image data of multiple TIFF pages as numpy array. + + By default the first image series is returned. + + Parameters + ---------- + key : int, slice, or sequence of page indices + Defines which pages to return as array. + series : int + Defines which series of pages to return as array. + memmap : bool + If True, use numpy.memmap to read arrays from file if possible. + + """ + if key is None and series is None: + series = 0 + if series is not None: + pages = self.series[series].pages + else: + pages = self.pages + + if key is None: + pass + elif isinstance(key, int): + pages = [pages[key]] + elif isinstance(key, slice): + pages = pages[key] + elif isinstance(key, collections.Iterable): + pages = [pages[k] for k in key] + else: + raise TypeError("key must be an int, slice, or sequence") + + if len(pages) == 1: + return pages[0].asarray(memmap=memmap) + elif self.is_nih: + result = numpy.vstack( + p.asarray(colormapped=False, squeeze=False, memmap=memmap) + for p in pages) + if pages[0].is_palette: + result = numpy.take(pages[0].color_map, result, axis=1) + result = numpy.swapaxes(result, 0, 1) + else: + if self.is_ome and any(p is None for p in pages): + firstpage = next(p for p in pages if p) + nopage = numpy.zeros_like(firstpage.asarray(memmap=memmap)) + result = numpy.vstack((p.asarray(memmap=memmap) if p else nopage) + for p in pages) + if key is None: + try: + result.shape = self.series[series].shape + except ValueError: + warnings.warn("failed to reshape %s to %s" % ( + result.shape, self.series[series].shape)) + result.shape = (-1,) + pages[0].shape + else: + result.shape = (-1,) + pages[0].shape + return result + + def _omeseries(self): + """Return image series in OME-TIFF file(s).""" + root = ElementTree.XML(self.pages[0].tags['image_description'].value) + uuid = root.attrib.get('UUID', None) + self._tiffs = {uuid: self} + modulo = {} + result = [] + for element in root: + if element.tag.endswith('BinaryOnly'): + warnings.warn("not an OME-TIFF master file") + break + if element.tag.endswith('StructuredAnnotations'): + for annot in element: + if not annot.attrib.get('Namespace', + '').endswith('modulo'): + continue + for value in annot: + for modul in value: + for along in modul: + if not along.tag[:-1].endswith('Along'): + continue + axis = along.tag[-1] + newaxis = along.attrib.get('Type', 'other') + newaxis = AXES_LABELS[newaxis] + if 'Start' in along.attrib: + labels = list(range( + int(along.attrib['Start']), + int(along.attrib['End']) + 1, + int(along.attrib.get('Step', 1)))) + else: + labels = [label.text for label in along + if label.tag.endswith('Label')] + modulo[axis] = (newaxis, labels) + if not element.tag.endswith('Image'): + continue + for pixels in element: + if not pixels.tag.endswith('Pixels'): + continue + atr = pixels.attrib + axes = "".join(reversed(atr['DimensionOrder'])) + shape = list(int(atr['Size'+ax]) for ax in axes) + size = numpy.prod(shape[:-2]) + ifds = [None] * size + for data in pixels: + if not data.tag.endswith('TiffData'): + continue + atr = data.attrib + ifd = int(atr.get('IFD', 0)) + num = int(atr.get('NumPlanes', 1 if 'IFD' in atr else 0)) + num = int(atr.get('PlaneCount', num)) + idx = [int(atr.get('First'+ax, 0)) for ax in axes[:-2]] + idx = numpy.ravel_multi_index(idx, shape[:-2]) + for uuid in data: + if uuid.tag.endswith('UUID'): + if uuid.text not in self._tiffs: + if not self._multifile: + # abort reading multi file OME series + return [] + fn = uuid.attrib['FileName'] + try: + tf = TiffFile(os.path.join(self.fpath, fn)) + except (IOError, ValueError): + warnings.warn("failed to read %s" % fn) + break + self._tiffs[uuid.text] = tf + pages = self._tiffs[uuid.text].pages + try: + for i in range(num if num else len(pages)): + ifds[idx + i] = pages[ifd + i] + except IndexError: + warnings.warn("ome-xml: index out of range") + break + else: + pages = self.pages + try: + for i in range(num if num else len(pages)): + ifds[idx + i] = pages[ifd + i] + except IndexError: + warnings.warn("ome-xml: index out of range") + result.append(Record(axes=axes, shape=shape, pages=ifds, + dtype=numpy.dtype(ifds[0].dtype))) + + for record in result: + for axis, (newaxis, labels) in list(modulo.items()): + i = record.axes.index(axis) + size = len(labels) + if record.shape[i] == size: + record.axes = record.axes.replace(axis, newaxis, 1) + else: + record.shape[i] //= size + record.shape.insert(i+1, size) + record.axes = record.axes.replace(axis, axis+newaxis, 1) + + return result + + def __len__(self): + """Return number of image pages in file.""" + return len(self.pages) + + def __getitem__(self, key): + """Return specified page.""" + return self.pages[key] + + def __iter__(self): + """Return iterator over pages.""" + return iter(self.pages) + + def __str__(self): + """Return string containing information about file.""" + result = [ + self.fname.capitalize(), + format_size(self._fsize), + {'<': 'little endian', '>': 'big endian'}[self.byteorder]] + if self.is_bigtiff: + result.append("bigtiff") + if len(self.pages) > 1: + result.append("%i pages" % len(self.pages)) + if len(self.series) > 1: + result.append("%i series" % len(self.series)) + if len(self._tiffs) > 1: + result.append("%i files" % (len(self._tiffs))) + return ", ".join(result) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + @lazyattr + def fstat(self): + try: + return os.fstat(self._fh.fileno()) + except Exception: # io.UnsupportedOperation + return None + + @lazyattr + def is_bigtiff(self): + return self.offset_size != 4 + + @lazyattr + def is_rgb(self): + return all(p.is_rgb for p in self.pages) + + @lazyattr + def is_palette(self): + return all(p.is_palette for p in self.pages) + + @lazyattr + def is_mdgel(self): + return any(p.is_mdgel for p in self.pages) + + @lazyattr + def is_mediacy(self): + return any(p.is_mediacy for p in self.pages) + + @lazyattr + def is_stk(self): + return all(p.is_stk for p in self.pages) + + @lazyattr + def is_lsm(self): + return self.pages[0].is_lsm + + @lazyattr + def is_imagej(self): + return self.pages[0].is_imagej + + @lazyattr + def is_micromanager(self): + return self.pages[0].is_micromanager + + @lazyattr + def is_nih(self): + return self.pages[0].is_nih + + @lazyattr + def is_fluoview(self): + return self.pages[0].is_fluoview + + @lazyattr + def is_ome(self): + return self.pages[0].is_ome + + +class TiffPage(object): + """A TIFF image file directory (IFD). + + Attributes + ---------- + index : int + Index of page in file. + dtype : str {TIFF_SAMPLE_DTYPES} + Data type of image, colormapped if applicable. + shape : tuple + Dimensions of the image array in TIFF page, + colormapped and with one alpha channel if applicable. + axes : str + Axes label codes: + 'X' width, 'Y' height, 'S' sample, 'P' plane, 'I' image series, + 'Z' depth, 'C' color|em-wavelength|channel, 'E' ex-wavelength|lambda, + 'T' time, 'R' region|tile, 'A' angle, 'F' phase, 'H' lifetime, + 'L' exposure, 'V' event, 'Q' unknown, '_' missing + tags : TiffTags + Dictionary of tags in page. + Tag values are also directly accessible as attributes. + color_map : numpy array + Color look up table, if exists. + mm_uic_tags: Record(dict) + Consolidated MetaMorph mm_uic# tags, if exists. + cz_lsm_scan_info: Record(dict) + LSM scan info attributes, if exists. + imagej_tags: Record(dict) + Consolidated ImageJ description and metadata tags, if exists. + + All attributes are read-only. + + """ + def __init__(self, parent): + """Initialize instance from file.""" + self.parent = parent + self.index = len(parent.pages) + self.shape = self._shape = () + self.dtype = self._dtype = None + self.axes = "" + self.tags = TiffTags() + + self._fromfile() + self._process_tags() + + def _fromfile(self): + """Read TIFF IFD structure and its tags from file. + + File cursor must be at storage position of IFD offset and is left at + offset to next IFD. + + Raises StopIteration if offset (first bytes read) is 0. + + """ + fh = self.parent._fh + byteorder = self.parent.byteorder + offset_size = self.parent.offset_size + + fmt = {4: 'I', 8: 'Q'}[offset_size] + offset = struct.unpack(byteorder + fmt, fh.read(offset_size))[0] + if not offset: + raise StopIteration() + + # read standard tags + tags = self.tags + fh.seek(offset) + fmt, size = {4: ('H', 2), 8: ('Q', 8)}[offset_size] + try: + numtags = struct.unpack(byteorder + fmt, fh.read(size))[0] + except Exception: + warnings.warn("corrupted page list") + raise StopIteration() + + tagcode = 0 + for _ in range(numtags): + try: + tag = TiffTag(self.parent) + except TiffTag.Error as e: + warnings.warn(str(e)) + finally: + if tagcode > tag.code: + warnings.warn("tags are not ordered by code") + tagcode = tag.code + if not tag.name in tags: + tags[tag.name] = tag + else: + # some files contain multiple IFD with same code + # e.g. MicroManager files contain two image_description + for ext in ('_1', '_2', '_3'): + name = tag.name + ext + if not name in tags: + tags[name] = tag + break + + # read LSM info subrecords + if self.is_lsm: + pos = fh.tell() + for name, reader in list(CZ_LSM_INFO_READERS.items()): + try: + offset = self.cz_lsm_info['offset_'+name] + except KeyError: + continue + if not offset: + continue + fh.seek(offset) + try: + setattr(self, 'cz_lsm_'+name, reader(fh, byteorder)) + except ValueError: + pass + fh.seek(pos) + + def _process_tags(self): + """Validate standard tags and initialize attributes. + + Raise ValueError if tag values are not supported. + + """ + tags = self.tags + for code, (name, default, dtype, count, validate) in list(TIFF_TAGS.items()): + if not (name in tags or default is None): + tags[name] = TiffTag(code, dtype=dtype, count=count, + value=default, name=name) + if name in tags and validate: + try: + if tags[name].count == 1: + setattr(self, name, validate[tags[name].value]) + else: + setattr(self, name, tuple( + validate[value] for value in tags[name].value)) + except KeyError: + raise ValueError("%s.value (%s) not supported" % + (name, tags[name].value)) + + tag = tags['bits_per_sample'] + if tag.count == 1: + self.bits_per_sample = tag.value + else: + value = tag.value[:self.samples_per_pixel] + if any((v-value[0] for v in value)): + self.bits_per_sample = value + else: + self.bits_per_sample = value[0] + + tag = tags['sample_format'] + if tag.count == 1: + self.sample_format = TIFF_SAMPLE_FORMATS[tag.value] + else: + value = tag.value[:self.samples_per_pixel] + if any((v-value[0] for v in value)): + self.sample_format = [TIFF_SAMPLE_FORMATS[v] for v in value] + else: + self.sample_format = TIFF_SAMPLE_FORMATS[value[0]] + + if not 'photometric' in tags: + self.photometric = None + + if 'image_length' in tags: + self.strips_per_image = int(math.floor( + float(self.image_length + self.rows_per_strip - 1) / + self.rows_per_strip)) + else: + self.strips_per_image = 0 + + key = (self.sample_format, self.bits_per_sample) + self.dtype = self._dtype = TIFF_SAMPLE_DTYPES.get(key, None) + + if self.is_imagej: + # consolidate imagej meta data + if 'image_description_1' in self.tags: # MicroManager + adict = imagej_description(tags['image_description_1'].value) + else: + adict = imagej_description(tags['image_description'].value) + if 'imagej_metadata' in tags: + try: + adict.update(imagej_metadata( + tags['imagej_metadata'].value, + tags['imagej_byte_counts'].value, + self.parent.byteorder)) + except Exception as e: + warnings.warn(str(e)) + self.imagej_tags = Record(adict) + + if not 'image_length' in self.tags or not 'image_width' in self.tags: + # some GEL file pages are missing image data + self.image_length = 0 + self.image_width = 0 + self.strip_offsets = 0 + self._shape = () + self.shape = () + self.axes = '' + + if self.is_palette: + self.dtype = self.tags['color_map'].dtype[1] + self.color_map = numpy.array(self.color_map, self.dtype) + dmax = self.color_map.max() + if dmax < 256: + self.dtype = numpy.uint8 + self.color_map = self.color_map.astype(self.dtype) + #else: + # self.dtype = numpy.uint8 + # self.color_map >>= 8 + # self.color_map = self.color_map.astype(self.dtype) + self.color_map.shape = (3, -1) + + if self.is_stk: + # consolidate mm_uci tags + planes = tags['mm_uic2'].count + self.mm_uic_tags = Record(tags['mm_uic2'].value) + for key in ('mm_uic3', 'mm_uic4', 'mm_uic1'): + if key in tags: + self.mm_uic_tags.update(tags[key].value) + if self.planar_configuration == 'contig': + self._shape = (planes, 1, self.image_length, self.image_width, + self.samples_per_pixel) + self.shape = tuple(self._shape[i] for i in (0, 2, 3, 4)) + self.axes = 'PYXS' + else: + self._shape = (planes, self.samples_per_pixel, + self.image_length, self.image_width, 1) + self.shape = self._shape[:4] + self.axes = 'PSYX' + if self.is_palette and (self.color_map.shape[1] + >= 2**self.bits_per_sample): + self.shape = (3, planes, self.image_length, self.image_width) + self.axes = 'CPYX' + else: + warnings.warn("palette cannot be applied") + self.is_palette = False + elif self.is_palette: + samples = 1 + if 'extra_samples' in self.tags: + samples += len(self.extra_samples) + if self.planar_configuration == 'contig': + self._shape = ( + 1, 1, self.image_length, self.image_width, samples) + else: + self._shape = ( + 1, samples, self.image_length, self.image_width, 1) + if self.color_map.shape[1] >= 2**self.bits_per_sample: + self.shape = (3, self.image_length, self.image_width) + self.axes = 'CYX' + else: + warnings.warn("palette cannot be applied") + self.is_palette = False + self.shape = (self.image_length, self.image_width) + self.axes = 'YX' + elif self.is_rgb or self.samples_per_pixel > 1: + if self.planar_configuration == 'contig': + self._shape = (1, 1, self.image_length, self.image_width, + self.samples_per_pixel) + self.shape = (self.image_length, self.image_width, + self.samples_per_pixel) + self.axes = 'YXS' + else: + self._shape = (1, self.samples_per_pixel, self.image_length, + self.image_width, 1) + self.shape = self._shape[1:-1] + self.axes = 'SYX' + if self.is_rgb and 'extra_samples' in self.tags: + extra_samples = self.extra_samples + if self.tags['extra_samples'].count == 1: + extra_samples = (extra_samples, ) + for exs in extra_samples: + if exs in ('unassalpha', 'assocalpha', 'unspecified'): + if self.planar_configuration == 'contig': + self.shape = self.shape[:2] + (4,) + else: + self.shape = (4,) + self.shape[1:] + break + else: + self._shape = (1, 1, self.image_length, self.image_width, 1) + self.shape = self._shape[2:4] + self.axes = 'YX' + + if not self.compression and not 'strip_byte_counts' in tags: + self.strip_byte_counts = numpy.prod(self.shape) * ( + self.bits_per_sample // 8) + + def asarray(self, squeeze=True, colormapped=True, rgbonly=True, + memmap=False): + """Read image data from file and return as numpy array. + + Raise ValueError if format is unsupported. + If any argument is False, the shape of the returned array might be + different from the page shape. + + Parameters + ---------- + squeeze : bool + If True, all length-1 dimensions (except X and Y) are + squeezed out from result. + colormapped : bool + If True, color mapping is applied for palette-indexed images. + rgbonly : bool + If True, return RGB(A) image without additional extra samples. + memmap : bool + If True, use numpy.memmap to read array if possible. + + """ + fh = self.parent._fh + if not fh: + raise IOError("TIFF file is not open") + if self.dtype is None: + raise ValueError("data type not supported: %s%i" % ( + self.sample_format, self.bits_per_sample)) + if self.compression not in TIFF_DECOMPESSORS: + raise ValueError("cannot decompress %s" % self.compression) + if ('ycbcr_subsampling' in self.tags + and self.tags['ycbcr_subsampling'].value not in (1, (1, 1))): + raise ValueError("YCbCr subsampling not supported") + tag = self.tags['sample_format'] + if tag.count != 1 and any((i-tag.value[0] for i in tag.value)): + raise ValueError("sample formats don't match %s" % str(tag.value)) + + dtype = self._dtype + shape = self._shape + + if not shape: + return None + + image_width = self.image_width + image_length = self.image_length + typecode = self.parent.byteorder + dtype + bits_per_sample = self.bits_per_sample + byteorder_is_native = ({'big': '>', 'little': '<'}[sys.byteorder] == + self.parent.byteorder) + + if self.is_tiled: + if 'tile_offsets' in self.tags: + byte_counts = self.tile_byte_counts + offsets = self.tile_offsets + else: + byte_counts = self.strip_byte_counts + offsets = self.strip_offsets + tile_width = self.tile_width + tile_length = self.tile_length + tw = (image_width + tile_width - 1) // tile_width + tl = (image_length + tile_length - 1) // tile_length + shape = shape[:-3] + (tl*tile_length, tw*tile_width, shape[-1]) + tile_shape = (tile_length, tile_width, shape[-1]) + runlen = tile_width + else: + byte_counts = self.strip_byte_counts + offsets = self.strip_offsets + runlen = image_width + + try: + offsets[0] + except TypeError: + offsets = (offsets, ) + byte_counts = (byte_counts, ) + if any(o < 2 for o in offsets): + raise ValueError("corrupted page") + + if (not self.is_tiled and (self.is_stk or (not self.compression + and bits_per_sample in (8, 16, 32, 64) + and all(offsets[i] == offsets[i+1] - byte_counts[i] + for i in range(len(offsets)-1))))): + # contiguous data + if (memmap and not (self.is_tiled or self.predictor or + ('extra_samples' in self.tags) or + (colormapped and self.is_palette) or + (not byteorder_is_native))): + result = numpy.memmap(fh, typecode, 'r', offsets[0], shape) + else: + fh.seek(offsets[0]) + result = numpy_fromfile(fh, typecode, numpy.prod(shape)) + result = result.astype('=' + dtype) + else: + if self.planar_configuration == 'contig': + runlen *= self.samples_per_pixel + if bits_per_sample in (8, 16, 32, 64, 128): + if (bits_per_sample * runlen) % 8: + raise ValueError("data and sample size mismatch") + + def unpack(x): + return numpy.fromstring(x, typecode) + elif isinstance(bits_per_sample, tuple): + def unpack(x): + return unpackrgb(x, typecode, bits_per_sample) + else: + def unpack(x): + return unpackints(x, typecode, bits_per_sample, runlen) + decompress = TIFF_DECOMPESSORS[self.compression] + if self.is_tiled: + result = numpy.empty(shape, dtype) + tw, tl, pl = 0, 0, 0 + for offset, bytecount in zip(offsets, byte_counts): + fh.seek(offset) + tile = unpack(decompress(fh.read(bytecount))) + tile.shape = tile_shape + if self.predictor == 'horizontal': + numpy.cumsum(tile, axis=-2, dtype=dtype, out=tile) + result[0, pl, tl:tl+tile_length, + tw:tw+tile_width, :] = tile + del tile + tw += tile_width + if tw >= shape[-2]: + tw, tl = 0, tl + tile_length + if tl >= shape[-3]: + tl, pl = 0, pl + 1 + result = result[..., :image_length, :image_width, :] + else: + strip_size = (self.rows_per_strip * self.image_width * + self.samples_per_pixel) + result = numpy.empty(shape, dtype).reshape(-1) + index = 0 + for offset, bytecount in zip(offsets, byte_counts): + fh.seek(offset) + strip = fh.read(bytecount) + strip = unpack(decompress(strip)) + size = min(result.size, strip.size, strip_size, + result.size - index) + result[index:index+size] = strip[:size] + del strip + index += size + + result.shape = self._shape + + if self.predictor == 'horizontal' and not self.is_tiled: + # work around bug in LSM510 software + if not (self.parent.is_lsm and not self.compression): + numpy.cumsum(result, axis=-2, dtype=dtype, out=result) + + if colormapped and self.is_palette: + if self.color_map.shape[1] >= 2**bits_per_sample: + # FluoView and LSM might fail here + result = numpy.take(self.color_map, + result[:, 0, :, :, 0], axis=1) + elif rgbonly and self.is_rgb and 'extra_samples' in self.tags: + # return only RGB and first alpha channel if exists + extra_samples = self.extra_samples + if self.tags['extra_samples'].count == 1: + extra_samples = (extra_samples, ) + for i, exs in enumerate(extra_samples): + if exs in ('unassalpha', 'assocalpha', 'unspecified'): + if self.planar_configuration == 'contig': + result = result[..., [0, 1, 2, 3+i]] + else: + result = result[:, [0, 1, 2, 3+i]] + break + else: + if self.planar_configuration == 'contig': + result = result[..., :3] + else: + result = result[:, :3] + + if squeeze: + try: + result.shape = self.shape + except ValueError: + warnings.warn("failed to reshape from %s to %s" % ( + str(result.shape), str(self.shape))) + + return result + + def __str__(self): + """Return string containing information about page.""" + s = ', '.join(s for s in ( + ' x '.join(str(i) for i in self.shape), + str(numpy.dtype(self.dtype)), + '%s bit' % str(self.bits_per_sample), + self.photometric if 'photometric' in self.tags else '', + self.compression if self.compression else 'raw', + '|'.join(t[3:] for t in ( + 'is_stk', 'is_lsm', 'is_nih', 'is_ome', 'is_imagej', + 'is_micromanager', 'is_fluoview', 'is_mdgel', 'is_mediacy', + 'is_reduced', 'is_tiled') if getattr(self, t))) if s) + return "Page %i: %s" % (self.index, s) + + def __getattr__(self, name): + """Return tag value.""" + if name in self.tags: + value = self.tags[name].value + setattr(self, name, value) + return value + raise AttributeError(name) + + @lazyattr + def is_rgb(self): + """True if page contains a RGB image.""" + return ('photometric' in self.tags and + self.tags['photometric'].value == 2) + + @lazyattr + def is_palette(self): + """True if page contains a palette-colored image.""" + return ('photometric' in self.tags and + self.tags['photometric'].value == 3) + + @lazyattr + def is_tiled(self): + """True if page contains tiled image.""" + return 'tile_width' in self.tags + + @lazyattr + def is_reduced(self): + """True if page is a reduced image of another image.""" + return bool(self.tags['new_subfile_type'].value & 1) + + @lazyattr + def is_mdgel(self): + """True if page contains md_file_tag tag.""" + return 'md_file_tag' in self.tags + + @lazyattr + def is_mediacy(self): + """True if page contains Media Cybernetics Id tag.""" + return ('mc_id' in self.tags and + self.tags['mc_id'].value.startswith(b'MC TIFF')) + + @lazyattr + def is_stk(self): + """True if page contains MM_UIC2 tag.""" + return 'mm_uic2' in self.tags + + @lazyattr + def is_lsm(self): + """True if page contains LSM CZ_LSM_INFO tag.""" + return 'cz_lsm_info' in self.tags + + @lazyattr + def is_fluoview(self): + """True if page contains FluoView MM_STAMP tag.""" + return 'mm_stamp' in self.tags + + @lazyattr + def is_nih(self): + """True if page contains NIH image header.""" + return 'nih_image_header' in self.tags + + @lazyattr + def is_ome(self): + """True if page contains OME-XML in image_description tag.""" + return ('image_description' in self.tags and self.tags[ + 'image_description'].value.startswith(b' parent.offset_size or code in CUSTOM_TAGS: + pos = fh.tell() + tof = {4: 'I', 8: 'Q'}[parent.offset_size] + self.value_offset = offset = struct.unpack(byteorder+tof, value)[0] + if offset < 0 or offset > parent._fsize: + raise TiffTag.Error("corrupt file - invalid tag value offset") + elif offset < 4: + raise TiffTag.Error("corrupt value offset for tag %i" % code) + fh.seek(offset) + if code in CUSTOM_TAGS: + readfunc = CUSTOM_TAGS[code][1] + value = readfunc(fh, byteorder, dtype, count) + fh.seek(0, 2) # bug in numpy/Python 3.x ? + if isinstance(value, dict): # numpy.core.records.record + value = Record(value) + elif code in TIFF_TAGS or dtype[-1] == 's': + value = struct.unpack(fmt, fh.read(size)) + else: + value = read_numpy(fh, byteorder, dtype, count) + fh.seek(0, 2) # bug in numpy/Python 3.x ? + fh.seek(pos) + else: + value = struct.unpack(fmt, value[:size]) + + if not code in CUSTOM_TAGS: + if len(value) == 1: + value = value[0] + + if dtype.endswith('s') and isinstance(value, bytes): + value = stripnull(value) + + self.code = code + self.name = name + self.dtype = dtype + self.count = count + self.value = value + + def __str__(self): + """Return string containing information about tag.""" + return ' '.join(str(getattr(self, s)) for s in self.__slots__) + + +class TiffSequence(object): + """Sequence of image files. + + Properties + ---------- + files : list + List of file names. + shape : tuple + Shape of image sequence. + axes : str + Labels of axes in shape. + + Examples + -------- + >>> ims = TiffSequence("test.oif.files/*.tif") + >>> ims = ims.asarray() + >>> ims.shape + (2, 100, 256, 256) + + """ + _axes_pattern = """ + # matches Olympus OIF and Leica TIFF series + _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4})) + _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))? + _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))? + _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))? + _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))? + _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))? + _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))? + """ + + class _ParseError(Exception): + pass + + def __init__(self, files, imread=TiffFile, pattern='axes'): + """Initialize instance from multiple files. + + Parameters + ---------- + files : str, or sequence of str + Glob pattern or sequence of file names. + imread : function or class + Image read function or class with asarray function returning numpy + array from single file. + pattern : str + Regular expression pattern that matches axes names and sequence + indices in file names. + + """ + if isinstance(files, str): + files = natural_sorted(glob.glob(files)) + files = list(files) + if not files: + raise ValueError("no files found") + #if not os.path.isfile(files[0]): + # raise ValueError("file not found") + self.files = files + + if hasattr(imread, 'asarray'): + _imread = imread + + def imread(fname, *args, **kwargs): + with _imread(fname) as im: + return im.asarray(*args, **kwargs) + + self.imread = imread + + self.pattern = self._axes_pattern if pattern == 'axes' else pattern + try: + self._parse() + if not self.axes: + self.axes = 'I' + except self._ParseError: + self.axes = 'I' + self.shape = (len(files),) + self._start_index = (0,) + self._indices = ((i,) for i in range(len(files))) + + def __str__(self): + """Return string with information about image sequence.""" + return "\n".join([ + self.files[0], + '* files: %i' % len(self.files), + '* axes: %s' % self.axes, + '* shape: %s' % str(self.shape)]) + + def __len__(self): + return len(self.files) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def close(self): + pass + + def asarray(self, *args, **kwargs): + """Read image data from all files and return as single numpy array. + + Raise IndexError if image shapes don't match. + + """ + im = self.imread(self.files[0]) + result_shape = self.shape + im.shape + result = numpy.zeros(result_shape, dtype=im.dtype) + result = result.reshape(-1, *im.shape) + for index, fname in zip(self._indices, self.files): + index = [i-j for i, j in zip(index, self._start_index)] + index = numpy.ravel_multi_index(index, self.shape) + im = self.imread(fname, *args, **kwargs) + result[index] = im + result.shape = result_shape + return result + + def _parse(self): + """Get axes and shape from file names.""" + if not self.pattern: + raise self._ParseError("invalid pattern") + pattern = re.compile(self.pattern, re.IGNORECASE | re.VERBOSE) + matches = pattern.findall(self.files[0]) + if not matches: + raise self._ParseError("pattern doesn't match file names") + matches = matches[-1] + if len(matches) % 2: + raise self._ParseError("pattern doesn't match axis name and index") + axes = ''.join(m for m in matches[::2] if m) + if not axes: + raise self._ParseError("pattern doesn't match file names") + + indices = [] + for fname in self.files: + matches = pattern.findall(fname)[-1] + if axes != ''.join(m for m in matches[::2] if m): + raise ValueError("axes don't match within the image sequence") + indices.append([int(m) for m in matches[1::2] if m]) + shape = tuple(numpy.max(indices, axis=0)) + start_index = tuple(numpy.min(indices, axis=0)) + shape = tuple(i-j+1 for i, j in zip(shape, start_index)) + if numpy.prod(shape) != len(self.files): + warnings.warn("files are missing. Missing data are zeroed") + + self.axes = axes.upper() + self.shape = shape + self._indices = indices + self._start_index = start_index + + +class Record(dict): + """Dictionary with attribute access. + + Can also be initialized with numpy.core.records.record. + + """ + __slots__ = () + + def __init__(self, arg=None, **kwargs): + if kwargs: + arg = kwargs + elif arg is None: + arg = {} + try: + dict.__init__(self, arg) + except (TypeError, ValueError): + for i, name in enumerate(arg.dtype.names): + v = arg[i] + self[name] = v if v.dtype.char != 'S' else stripnull(v) + + def __getattr__(self, name): + return self[name] + + def __setattr__(self, name, value): + self.__setitem__(name, value) + + def __str__(self): + """Pretty print Record.""" + s = [] + lists = [] + for k in sorted(self): + if k.startswith('_'): # does not work with byte + continue + v = self[k] + if isinstance(v, (list, tuple)) and len(v): + if isinstance(v[0], Record): + lists.append((k, v)) + continue + elif isinstance(v[0], TiffPage): + v = [i.index for i in v if i] + s.append( + ("* %s: %s" % (k, str(v))).split("\n", 1)[0] + [:PRINT_LINE_LEN].rstrip()) + for k, v in lists: + l = [] + for i, w in enumerate(v): + l.append("* %s[%i]\n %s" % (k, i, + str(w).replace("\n", "\n "))) + s.append('\n'.join(l)) + return '\n'.join(s) + + +class TiffTags(Record): + """Dictionary of TiffTags with attribute access.""" + def __str__(self): + """Return string with information about all tags.""" + s = [] + for tag in sorted(list(self.values()), key=lambda x: x.code): + typecode = "%i%s" % (tag.count * int(tag.dtype[0]), tag.dtype[1]) + line = "* %i %s (%s) %s" % (tag.code, tag.name, typecode, + str(tag.value).split('\n', 1)[0]) + s.append(line[:PRINT_LINE_LEN].lstrip()) + return '\n'.join(s) + + +def read_bytes(fh, byteorder, dtype, count): + """Read tag data from file and return as byte string.""" + return numpy_fromfile(fh, byteorder+dtype[-1], count).tostring() + + +def read_numpy(fh, byteorder, dtype, count): + """Read tag data from file and return as numpy array.""" + return numpy_fromfile(fh, byteorder+dtype[-1], count) + + +def read_json(fh, byteorder, dtype, count): + """Read tag data from file and return as object.""" + return json.loads(str(stripnull(fh.read(count)), 'utf-8')) + + +def read_mm_header(fh, byteorder, dtype, count): + """Read MM_HEADER tag from file and return as numpy.rec.array.""" + return numpy.rec.fromfile(fh, MM_HEADER, 1, byteorder=byteorder)[0] + + +def read_mm_stamp(fh, byteorder, dtype, count): + """Read MM_STAMP tag from file and return as numpy.array.""" + return numpy_fromfile(fh, byteorder+'8f8', 1)[0] + + +def read_mm_uic1(fh, byteorder, dtype, count): + """Read MM_UIC1 tag from file and return as dictionary.""" + t = fh.read(8*count) + t = struct.unpack('%s%iI' % (byteorder, 2*count), t) + return dict((MM_TAG_IDS[k], v) for k, v in zip(t[::2], t[1::2]) + if k in MM_TAG_IDS) + + +def read_mm_uic2(fh, byteorder, dtype, count): + """Read MM_UIC2 tag from file and return as dictionary.""" + result = {'number_planes': count} + values = numpy_fromfile(fh, byteorder+'I', 6*count) + result['z_distance'] = values[0::6] // values[1::6] + #result['date_created'] = tuple(values[2::6]) + #result['time_created'] = tuple(values[3::6]) + #result['date_modified'] = tuple(values[4::6]) + #result['time_modified'] = tuple(values[5::6]) + return result + + +def read_mm_uic3(fh, byteorder, dtype, count): + """Read MM_UIC3 tag from file and return as dictionary.""" + t = numpy_fromfile(fh, byteorder+'I', 2*count) + return {'wavelengths': t[0::2] // t[1::2]} + + +def read_mm_uic4(fh, byteorder, dtype, count): + """Read MM_UIC4 tag from file and return as dictionary.""" + t = struct.unpack(byteorder + 'hI'*count, fh.read(6*count)) + return dict((MM_TAG_IDS[k], v) for k, v in zip(t[::2], t[1::2]) + if k in MM_TAG_IDS) + + +def read_cz_lsm_info(fh, byteorder, dtype, count): + """Read CS_LSM_INFO tag from file and return as numpy.rec.array.""" + result = numpy.rec.fromfile(fh, CZ_LSM_INFO, 1, + byteorder=byteorder)[0] + {50350412: '1.3', 67127628: '2.0'}[result.magic_number] # validation + return result + + +def read_cz_lsm_time_stamps(fh, byteorder): + """Read LSM time stamps from file and return as list.""" + size, count = struct.unpack(byteorder+'II', fh.read(8)) + if size != (8 + 8 * count): + raise ValueError("lsm_time_stamps block is too short") + return struct.unpack(('%s%dd' % (byteorder, count)), + fh.read(8*count)) + + +def read_cz_lsm_event_list(fh, byteorder): + """Read LSM events from file and return as list of (time, type, text).""" + count = struct.unpack(byteorder+'II', fh.read(8))[1] + events = [] + while count > 0: + esize, etime, etype = struct.unpack(byteorder+'IdI', fh.read(16)) + etext = stripnull(fh.read(esize - 16)) + events.append((etime, etype, etext)) + count -= 1 + return events + + +def read_cz_lsm_scan_info(fh, byteorder): + """Read LSM scan information from file and return as Record.""" + block = Record() + blocks = [block] + unpack = struct.unpack + if 0x10000000 != struct.unpack(byteorder+"I", fh.read(4))[0]: + raise ValueError("not a lsm_scan_info structure") + fh.read(8) + while True: + entry, dtype, size = unpack(byteorder+"III", fh.read(12)) + if dtype == 2: + value = stripnull(fh.read(size)) + elif dtype == 4: + value = unpack(byteorder+"i", fh.read(4))[0] + elif dtype == 5: + value = unpack(byteorder+"d", fh.read(8))[0] + else: + value = 0 + if entry in CZ_LSM_SCAN_INFO_ARRAYS: + blocks.append(block) + name = CZ_LSM_SCAN_INFO_ARRAYS[entry] + newobj = [] + setattr(block, name, newobj) + block = newobj + elif entry in CZ_LSM_SCAN_INFO_STRUCTS: + blocks.append(block) + newobj = Record() + block.append(newobj) + block = newobj + elif entry in CZ_LSM_SCAN_INFO_ATTRIBUTES: + name = CZ_LSM_SCAN_INFO_ATTRIBUTES[entry] + setattr(block, name, value) + elif entry == 0xffffffff: + block = blocks.pop() + else: + setattr(block, "unknown_%x" % entry, value) + if not blocks: + break + return block + + +def read_nih_image_header(fh, byteorder, dtype, count): + """Read NIH_IMAGE_HEADER tag from file and return as numpy.rec.array.""" + a = numpy.rec.fromfile(fh, NIH_IMAGE_HEADER, 1, byteorder=byteorder)[0] + a = a.newbyteorder(byteorder) + a.xunit = a.xunit[:a._xunit_len] + a.um = a.um[:a._um_len] + return a + + +def imagej_metadata(data, bytecounts, byteorder): + """Return dict from ImageJ meta data tag value.""" + + _str = str if sys.version_info[0] < 3 else lambda x: str(x, 'cp1252') + + def read_string(data, byteorder): + return _str(stripnull(data[0 if byteorder == '<' else 1::2])) + + def read_double(data, byteorder): + return struct.unpack(byteorder+('d' * (len(data) // 8)), data) + + def read_bytes(data, byteorder): + #return struct.unpack('b' * len(data), data) + return numpy.fromstring(data, 'uint8') + + metadata_types = { # big endian + b'info': ('info', read_string), + b'labl': ('labels', read_string), + b'rang': ('ranges', read_double), + b'luts': ('luts', read_bytes), + b'roi ': ('roi', read_bytes), + b'over': ('overlays', read_bytes)} + metadata_types.update( # little endian + dict((k[::-1], v) for k, v in list(metadata_types.items()))) + + if not bytecounts: + raise ValueError("no ImageJ meta data") + + if not data[:4] in (b'IJIJ', b'JIJI'): + raise ValueError("invalid ImageJ meta data") + + header_size = bytecounts[0] + if header_size < 12 or header_size > 804: + raise ValueError("invalid ImageJ meta data header size") + + ntypes = (header_size - 4) // 8 + header = struct.unpack(byteorder+'4sI'*ntypes, data[4:4+ntypes*8]) + pos = 4 + ntypes * 8 + counter = 0 + result = {} + for mtype, count in zip(header[::2], header[1::2]): + values = [] + name, func = metadata_types.get(mtype, (_str(mtype), read_bytes)) + for _ in range(count): + counter += 1 + pos1 = pos + bytecounts[counter] + values.append(func(data[pos:pos1], byteorder)) + pos = pos1 + result[name.strip()] = values[0] if count == 1 else values + return result + + +def imagej_description(description): + """Return dict from ImageJ image_description tag.""" + def _bool(val): + return {b'true': True, b'false': False}[val.lower()] + + _str = str if sys.version_info[0] < 3 else lambda x: str(x, 'cp1252') + result = {} + for line in description.splitlines(): + try: + key, val = line.split(b'=') + except Exception: + continue + key = key.strip() + val = val.strip() + for dtype in (int, float, _bool, _str): + try: + val = dtype(val) + break + except Exception: + pass + result[_str(key)] = val + return result + + +def read_micromanager_metadata(fh): + """Read MicroManager non-TIFF settings from open file and return as dict. + + The settings can be used to read image data without parsing the TIFF file. + + Raise ValueError if file does not contain valid MicroManager metadata. + + """ + fh.seek(0) + try: + byteorder = {b'II': '<', b'MM': '>'}[fh.read(2)] + except IndexError: + raise ValueError("not a MicroManager TIFF file") + + results = {} + fh.seek(8) + (index_header, index_offset, display_header, display_offset, + comments_header, comments_offset, summary_header, summary_length + ) = struct.unpack(byteorder + "IIIIIIII", fh.read(32)) + + if summary_header != 2355492: + raise ValueError("invalid MicroManager summary_header") + results['summary'] = read_json(fh, byteorder, None, summary_length) + + if index_header != 54773648: + raise ValueError("invalid MicroManager index_header") + fh.seek(index_offset) + header, count = struct.unpack(byteorder + "II", fh.read(8)) + if header != 3453623: + raise ValueError("invalid MicroManager index_header") + data = struct.unpack(byteorder + "IIIII"*count, fh.read(20*count)) + results['index_map'] = { + 'channel': data[::5], 'slice': data[1::5], 'frame': data[2::5], + 'position': data[3::5], 'offset': data[4::5]} + + if display_header != 483765892: + raise ValueError("invalid MicroManager display_header") + fh.seek(display_offset) + header, count = struct.unpack(byteorder + "II", fh.read(8)) + if header != 347834724: + raise ValueError("invalid MicroManager display_header") + results['display_settings'] = read_json(fh, byteorder, None, count) + + if comments_header != 99384722: + raise ValueError("invalid MicroManager comments_header") + fh.seek(comments_offset) + header, count = struct.unpack(byteorder + "II", fh.read(8)) + if header != 84720485: + raise ValueError("invalid MicroManager comments_header") + results['comments'] = read_json(fh, byteorder, None, count) + + return results + + +def _replace_by(module_function, package=None, warn=True): + """Try replace decorated function by module.function.""" + try: + from importlib import import_module + except ImportError: + warnings.warn('Could not import module importlib') + return lambda func: func + + def decorate(func, module_function=module_function, warn=warn): + try: + module, function = module_function.split('.') + if not package: + module = import_module(module) + else: + module = import_module('.' + module, package=package) + func, oldfunc = getattr(module, function), func + globals()['__old_' + func.__name__] = oldfunc + except Exception: + if warn: + warnings.warn("failed to import %s" % module_function) + return func + + return decorate + + +@_replace_by('_tifffile.decodepackbits') +def decodepackbits(encoded): + """Decompress PackBits encoded byte string. + + PackBits is a simple byte-oriented run-length compression scheme. + + """ + func = ord if sys.version[0] == '2' else lambda x: x + result = [] + result_extend = result.extend + i = 0 + try: + while True: + n = func(encoded[i]) + 1 + i += 1 + if n < 129: + result_extend(encoded[i:i+n]) + i += n + elif n > 129: + result_extend(encoded[i:i+1] * (258-n)) + i += 1 + except IndexError: + pass + return b''.join(result) if sys.version[0] == '2' else bytes(result) + + +@_replace_by('_tifffile.decodelzw') +def decodelzw(encoded): + """Decompress LZW (Lempel-Ziv-Welch) encoded TIFF strip (byte string). + + The strip must begin with a CLEAR code and end with an EOI code. + + This is an implementation of the LZW decoding algorithm described in (1). + It is not compatible with old style LZW compressed files like quad-lzw.tif. + + """ + len_encoded = len(encoded) + bitcount_max = len_encoded * 8 + unpack = struct.unpack + + if sys.version[0] == '2': + newtable = [chr(i) for i in range(256)] + else: + newtable = [bytes([i]) for i in range(256)] + newtable.extend((0, 0)) + + def next_code(): + """Return integer of `bitw` bits at `bitcount` position in encoded.""" + start = bitcount // 8 + s = encoded[start:start+4] + try: + code = unpack('>I', s)[0] + except Exception: + code = unpack('>I', s + b'\x00'*(4-len(s)))[0] + code <<= bitcount % 8 + code &= mask + return code >> shr + + switchbitch = { # code: bit-width, shr-bits, bit-mask + 255: (9, 23, int(9*'1'+'0'*23, 2)), + 511: (10, 22, int(10*'1'+'0'*22, 2)), + 1023: (11, 21, int(11*'1'+'0'*21, 2)), + 2047: (12, 20, int(12*'1'+'0'*20, 2)), } + bitw, shr, mask = switchbitch[255] + bitcount = 0 + + if len_encoded < 4: + raise ValueError("strip must be at least 4 characters long") + + if next_code() != 256: + raise ValueError("strip must begin with CLEAR code") + + code = 0 + oldcode = 0 + result = [] + result_append = result.append + while True: + code = next_code() # ~5% faster when inlining this function + bitcount += bitw + if code == 257 or bitcount >= bitcount_max: # EOI + break + if code == 256: # CLEAR + table = newtable[:] + table_append = table.append + lentable = 258 + bitw, shr, mask = switchbitch[255] + code = next_code() + bitcount += bitw + if code == 257: # EOI + break + result_append(table[code]) + else: + if code < lentable: + decoded = table[code] + newcode = table[oldcode] + decoded[:1] + else: + newcode = table[oldcode] + newcode += newcode[:1] + decoded = newcode + result_append(decoded) + table_append(newcode) + lentable += 1 + oldcode = code + if lentable in switchbitch: + bitw, shr, mask = switchbitch[lentable] + + if code != 257: + warnings.warn( + "decodelzw encountered unexpected end of stream (code %i)" % code) + + return b''.join(result) + + +@_replace_by('_tifffile.unpackints') +def unpackints(data, dtype, itemsize, runlen=0): + """Decompress byte string to array of integers of any bit size <= 32. + + Parameters + ---------- + data : byte str + Data to decompress. + dtype : numpy.dtype or str + A numpy boolean or integer type. + itemsize : int + Number of bits per integer. + runlen : int + Number of consecutive integers, after which to start at next byte. + + """ + if itemsize == 1: # bitarray + data = numpy.fromstring(data, '|B') + data = numpy.unpackbits(data) + if runlen % 8: + data = data.reshape(-1, runlen + (8 - runlen % 8)) + data = data[:, :runlen].reshape(-1) + return data.astype(dtype) + + dtype = numpy.dtype(dtype) + if itemsize in (8, 16, 32, 64): + return numpy.fromstring(data, dtype) + if itemsize < 1 or itemsize > 32: + raise ValueError("itemsize out of range: %i" % itemsize) + if dtype.kind not in "biu": + raise ValueError("invalid dtype") + + itembytes = next(i for i in (1, 2, 4, 8) if 8 * i >= itemsize) + if itembytes != dtype.itemsize: + raise ValueError("dtype.itemsize too small") + if runlen == 0: + runlen = len(data) // itembytes + skipbits = runlen*itemsize % 8 + if skipbits: + skipbits = 8 - skipbits + shrbits = itembytes*8 - itemsize + bitmask = int(itemsize*'1'+'0'*shrbits, 2) + dtypestr = '>' + dtype.char # dtype always big endian? + + unpack = struct.unpack + l = runlen * (len(data)*8 // (runlen*itemsize + skipbits)) + result = numpy.empty((l, ), dtype) + bitcount = 0 + for i in range(len(result)): + start = bitcount // 8 + s = data[start:start+itembytes] + try: + code = unpack(dtypestr, s)[0] + except Exception: + code = unpack(dtypestr, s + b'\x00'*(itembytes-len(s)))[0] + code <<= bitcount % 8 + code &= bitmask + result[i] = code >> shrbits + bitcount += itemsize + if (i+1) % runlen == 0: + bitcount += skipbits + return result + + +def unpackrgb(data, dtype='>> data = struct.pack('BBBB', 0x21, 0x08, 0xff, 0xff) + >>> print(unpackrgb(data, '>> print(unpackrgb(data, '>> print(unpackrgb(data, '= bits) + data = numpy.fromstring(data, dtype.byteorder+dt) + result = numpy.empty((data.size, len(bitspersample)), dtype.char) + for i, bps in enumerate(bitspersample): + t = data >> int(numpy.sum(bitspersample[i+1:])) + t &= int('0b'+'1'*bps, 2) + if rescale: + o = ((dtype.itemsize * 8) // bps + 1) * bps + if o > data.dtype.itemsize * 8: + t = t.astype('I') + t *= (2**o - 1) // (2**bps - 1) + t //= 2**(o - (dtype.itemsize * 8)) + result[:, i] = t + return result.reshape(-1) + + +def reorient(image, orientation): + """Return reoriented view of image array. + + Parameters + ---------- + image : numpy array + Non-squeezed output of asarray() functions. + Axes -3 and -2 must be image length and width respectively. + orientation : int or str + One of TIFF_ORIENTATIONS keys or values. + + """ + o = TIFF_ORIENTATIONS.get(orientation, orientation) + if o == 'top_left': + return image + elif o == 'top_right': + return image[..., ::-1, :] + elif o == 'bottom_left': + return image[..., ::-1, :, :] + elif o == 'bottom_right': + return image[..., ::-1, ::-1, :] + elif o == 'left_top': + return numpy.swapaxes(image, -3, -2) + elif o == 'right_top': + return numpy.swapaxes(image, -3, -2)[..., ::-1, :] + elif o == 'left_bottom': + return numpy.swapaxes(image, -3, -2)[..., ::-1, :, :] + elif o == 'right_bottom': + return numpy.swapaxes(image, -3, -2)[..., ::-1, ::-1, :] + + +def numpy_fromfile(arg, dtype=float, count=-1, sep=''): + """Return array from data in binary file. + + Work around numpy issue #2230, "numpy.fromfile does not accept StringIO + object" https://github.com/numpy/numpy/issues/2230. + + """ + try: + return numpy.fromfile(arg, dtype, count, sep) + except IOError: + if count < 0: + size = 2**30 + else: + size = count * numpy.dtype(dtype).itemsize + data = arg.read(int(size)) + return numpy.fromstring(data, dtype, count, sep) + + +def stripnull(string): + """Return string truncated at first null character.""" + i = string.find(b'\x00') + return string if (i < 0) else string[:i] + + +def format_size(size): + """Return file size as string from byte size.""" + for unit in ('B', 'KB', 'MB', 'GB', 'TB'): + if size < 2048: + return "%.f %s" % (size, unit) + size /= 1024.0 + + +def natural_sorted(iterable): + """Return human sorted list of strings. + + >>> natural_sorted(['f1', 'f2', 'f10']) + ['f1', 'f2', 'f10'] + + """ + def sortkey(x): + return [(int(c) if c.isdigit() else c) for c in re.split(numbers, x)] + numbers = re.compile('(\d+)') + return sorted(iterable, key=sortkey) + + +def datetime_from_timestamp(n, epoch=datetime.datetime.fromordinal(693594)): + """Return datetime object from timestamp in Excel serial format. + + Examples + -------- + >>> datetime_from_timestamp(40237.029999999795) + datetime.datetime(2010, 2, 28, 0, 43, 11, 999982) + + """ + return epoch + datetime.timedelta(n) + + +def test_tifffile(directory='testimages', verbose=True): + """Read all images in directory. Print error message on failure. + + Examples + -------- + >>> test_tifffile(verbose=False) + + """ + successful = 0 + failed = 0 + start = time.time() + for f in glob.glob(os.path.join(directory, '*.*')): + if verbose: + print("\n%s>\n" % f.lower(), end='') + t0 = time.time() + try: + tif = TiffFile(f, multifile=True) + except Exception as e: + if not verbose: + print(f, end=' ') + print("ERROR:", e) + failed += 1 + continue + try: + img = tif.asarray() + except ValueError: + try: + img = tif[0].asarray() + except Exception as e: + if not verbose: + print(f, end=' ') + print("ERROR:", e) + failed += 1 + continue + finally: + tif.close() + successful += 1 + if verbose: + print("%s, %s %s, %s, %.0f ms" % ( + str(tif), str(img.shape), img.dtype, tif[0].compression, + (time.time()-t0) * 1e3)) + if verbose: + print("\nSuccessfully read %i of %i files in %.3f s\n" % ( + successful, successful+failed, time.time()-start)) + + +class TIFF_SUBFILE_TYPES(object): + def __getitem__(self, key): + result = [] + if key & 1: + result.append('reduced_image') + if key & 2: + result.append('page') + if key & 4: + result.append('mask') + return tuple(result) + + +TIFF_PHOTOMETRICS = { + 0: 'miniswhite', + 1: 'minisblack', + 2: 'rgb', + 3: 'palette', + 4: 'mask', + 5: 'separated', + 6: 'cielab', + 7: 'icclab', + 8: 'itulab', + 32844: 'logl', + 32845: 'logluv', +} + +TIFF_COMPESSIONS = { + 1: None, + 2: 'ccittrle', + 3: 'ccittfax3', + 4: 'ccittfax4', + 5: 'lzw', + 6: 'ojpeg', + 7: 'jpeg', + 8: 'adobe_deflate', + 9: 't85', + 10: 't43', + 32766: 'next', + 32771: 'ccittrlew', + 32773: 'packbits', + 32809: 'thunderscan', + 32895: 'it8ctpad', + 32896: 'it8lw', + 32897: 'it8mp', + 32898: 'it8bl', + 32908: 'pixarfilm', + 32909: 'pixarlog', + 32946: 'deflate', + 32947: 'dcs', + 34661: 'jbig', + 34676: 'sgilog', + 34677: 'sgilog24', + 34712: 'jp2000', + 34713: 'nef', +} + +TIFF_DECOMPESSORS = { + None: lambda x: x, + 'adobe_deflate': zlib.decompress, + 'deflate': zlib.decompress, + 'packbits': decodepackbits, + 'lzw': decodelzw, +} + +TIFF_DATA_TYPES = { + 1: '1B', # BYTE 8-bit unsigned integer. + 2: '1s', # ASCII 8-bit byte that contains a 7-bit ASCII code; + # the last byte must be NULL (binary zero). + 3: '1H', # SHORT 16-bit (2-byte) unsigned integer + 4: '1I', # LONG 32-bit (4-byte) unsigned integer. + 5: '2I', # RATIONAL Two LONGs: the first represents the numerator of + # a fraction; the second, the denominator. + 6: '1b', # SBYTE An 8-bit signed (twos-complement) integer. + 7: '1B', # UNDEFINED An 8-bit byte that may contain anything, + # depending on the definition of the field. + 8: '1h', # SSHORT A 16-bit (2-byte) signed (twos-complement) integer. + 9: '1i', # SLONG A 32-bit (4-byte) signed (twos-complement) integer. + 10: '2i', # SRATIONAL Two SLONGs: the first represents the numerator + # of a fraction, the second the denominator. + 11: '1f', # FLOAT Single precision (4-byte) IEEE format. + 12: '1d', # DOUBLE Double precision (8-byte) IEEE format. + 13: '1I', # IFD unsigned 4 byte IFD offset. + #14: '', # UNICODE + #15: '', # COMPLEX + 16: '1Q', # LONG8 unsigned 8 byte integer (BigTiff) + 17: '1q', # SLONG8 signed 8 byte integer (BigTiff) + 18: '1Q', # IFD8 unsigned 8 byte IFD offset (BigTiff) +} + +TIFF_SAMPLE_FORMATS = { + 1: 'uint', + 2: 'int', + 3: 'float', + #4: 'void', + #5: 'complex_int', + 6: 'complex', +} + +TIFF_SAMPLE_DTYPES = { + ('uint', 1): '?', # bitmap + ('uint', 2): 'B', + ('uint', 3): 'B', + ('uint', 4): 'B', + ('uint', 5): 'B', + ('uint', 6): 'B', + ('uint', 7): 'B', + ('uint', 8): 'B', + ('uint', 9): 'H', + ('uint', 10): 'H', + ('uint', 11): 'H', + ('uint', 12): 'H', + ('uint', 13): 'H', + ('uint', 14): 'H', + ('uint', 15): 'H', + ('uint', 16): 'H', + ('uint', 17): 'I', + ('uint', 18): 'I', + ('uint', 19): 'I', + ('uint', 20): 'I', + ('uint', 21): 'I', + ('uint', 22): 'I', + ('uint', 23): 'I', + ('uint', 24): 'I', + ('uint', 25): 'I', + ('uint', 26): 'I', + ('uint', 27): 'I', + ('uint', 28): 'I', + ('uint', 29): 'I', + ('uint', 30): 'I', + ('uint', 31): 'I', + ('uint', 32): 'I', + ('uint', 64): 'Q', + ('int', 8): 'b', + ('int', 16): 'h', + ('int', 32): 'i', + ('int', 64): 'q', + ('float', 16): 'e', + ('float', 32): 'f', + ('float', 64): 'd', + ('complex', 64): 'F', + ('complex', 128): 'D', + ('uint', (5, 6, 5)): 'B', +} + +TIFF_ORIENTATIONS = { + 1: 'top_left', + 2: 'top_right', + 3: 'bottom_right', + 4: 'bottom_left', + 5: 'left_top', + 6: 'right_top', + 7: 'right_bottom', + 8: 'left_bottom', +} + +AXES_LABELS = { + 'X': 'width', + 'Y': 'height', + 'Z': 'depth', + 'S': 'sample', # rgb(a) + 'P': 'plane', # page + 'T': 'time', + 'C': 'channel', # color, emission wavelength + 'A': 'angle', + 'F': 'phase', + 'R': 'tile', # region, point + 'H': 'lifetime', # histogram + 'E': 'lambda', # excitation wavelength + 'L': 'exposure', # lux + 'V': 'event', + 'Q': 'other', +} + +AXES_LABELS.update(dict((v, k) for k, v in list(AXES_LABELS.items()))) + +# NIH Image PicHeader v1.63 +NIH_IMAGE_HEADER = [ + ('fileid', 'a8'), + ('nlines', 'i2'), + ('pixelsperline', 'i2'), + ('version', 'i2'), + ('oldlutmode', 'i2'), + ('oldncolors', 'i2'), + ('colors', 'u1', (3, 32)), + ('oldcolorstart', 'i2'), + ('colorwidth', 'i2'), + ('extracolors', 'u2', (6, 3)), + ('nextracolors', 'i2'), + ('foregroundindex', 'i2'), + ('backgroundindex', 'i2'), + ('xscale', 'f8'), + ('_x0', 'i2'), + ('_x1', 'i2'), + ('units_t', 'i2'), + ('p1', [('x', 'i2'), ('y', 'i2')]), + ('p2', [('x', 'i2'), ('y', 'i2')]), + ('curvefit_t', 'i2'), + ('ncoefficients', 'i2'), + ('coeff', 'f8', 6), + ('_um_len', 'u1'), + ('um', 'a15'), + ('_x2', 'u1'), + ('binarypic', 'b1'), + ('slicestart', 'i2'), + ('sliceend', 'i2'), + ('scalemagnification', 'f4'), + ('nslices', 'i2'), + ('slicespacing', 'f4'), + ('currentslice', 'i2'), + ('frameinterval', 'f4'), + ('pixelaspectratio', 'f4'), + ('colorstart', 'i2'), + ('colorend', 'i2'), + ('ncolors', 'i2'), + ('fill1', '3u2'), + ('fill2', '3u2'), + ('colortable_t', 'u1'), + ('lutmode_t', 'u1'), + ('invertedtable', 'b1'), + ('zeroclip', 'b1'), + ('_xunit_len', 'u1'), + ('xunit', 'a11'), + ('stacktype_t', 'i2'), +] + +#NIH_COLORTABLE_TYPE = ( +# 'CustomTable', 'AppleDefault', 'Pseudo20', 'Pseudo32', 'Rainbow', +# 'Fire1', 'Fire2', 'Ice', 'Grays', 'Spectrum') +#NIH_LUTMODE_TYPE = ( +# 'PseudoColor', 'OldAppleDefault', 'OldSpectrum', 'GrayScale', +# 'ColorLut', 'CustomGrayscale') +#NIH_CURVEFIT_TYPE = ( +# 'StraightLine', 'Poly2', 'Poly3', 'Poly4', 'Poly5', 'ExpoFit', +# 'PowerFit', 'LogFit', 'RodbardFit', 'SpareFit1', 'Uncalibrated', +# 'UncalibratedOD') +#NIH_UNITS_TYPE = ( +# 'Nanometers', 'Micrometers', 'Millimeters', 'Centimeters', 'Meters', +# 'Kilometers', 'Inches', 'Feet', 'Miles', 'Pixels', 'OtherUnits') +#NIH_STACKTYPE_TYPE = ( +# 'VolumeStack', 'RGBStack', 'MovieStack', 'HSVStack') + +# MetaMorph STK tags +MM_TAG_IDS = { + 0: 'auto_scale', + 1: 'min_scale', + 2: 'max_scale', + 3: 'spatial_calibration', + #4: 'x_calibration', + #5: 'y_calibration', + #6: 'calibration_units', + #7: 'name', + 8: 'thresh_state', + 9: 'thresh_state_red', + 11: 'thresh_state_green', + 12: 'thresh_state_blue', + 13: 'thresh_state_lo', + 14: 'thresh_state_hi', + 15: 'zoom', + #16: 'create_time', + #17: 'last_saved_time', + 18: 'current_buffer', + 19: 'gray_fit', + 20: 'gray_point_count', + #21: 'gray_x', + #22: 'gray_y', + #23: 'gray_min', + #24: 'gray_max', + #25: 'gray_unit_name', + 26: 'standard_lut', + 27: 'wavelength', + #28: 'stage_position', + #29: 'camera_chip_offset', + #30: 'overlay_mask', + #31: 'overlay_compress', + #32: 'overlay', + #33: 'special_overlay_mask', + #34: 'special_overlay_compress', + #35: 'special_overlay', + 36: 'image_property', + #37: 'stage_label', + #38: 'autoscale_lo_info', + #39: 'autoscale_hi_info', + #40: 'absolute_z', + #41: 'absolute_z_valid', + #42: 'gamma', + #43: 'gamma_red', + #44: 'gamma_green', + #45: 'gamma_blue', + #46: 'camera_bin', + 47: 'new_lut', + #48: 'image_property_ex', + 49: 'plane_property', + #50: 'user_lut_table', + 51: 'red_autoscale_info', + #52: 'red_autoscale_lo_info', + #53: 'red_autoscale_hi_info', + 54: 'red_minscale_info', + 55: 'red_maxscale_info', + 56: 'green_autoscale_info', + #57: 'green_autoscale_lo_info', + #58: 'green_autoscale_hi_info', + 59: 'green_minscale_info', + 60: 'green_maxscale_info', + 61: 'blue_autoscale_info', + #62: 'blue_autoscale_lo_info', + #63: 'blue_autoscale_hi_info', + 64: 'blue_min_scale_info', + 65: 'blue_max_scale_info', + #66: 'overlay_plane_color' +} + +# Olympus FluoView +MM_DIMENSION = [ + ('name', 'a16'), + ('size', 'i4'), + ('origin', 'f8'), + ('resolution', 'f8'), + ('unit', 'a64'), +] + +MM_HEADER = [ + ('header_flag', 'i2'), + ('image_type', 'u1'), + ('image_name', 'a257'), + ('offset_data', 'u4'), + ('palette_size', 'i4'), + ('offset_palette0', 'u4'), + ('offset_palette1', 'u4'), + ('comment_size', 'i4'), + ('offset_comment', 'u4'), + ('dimensions', MM_DIMENSION, 10), + ('offset_position', 'u4'), + ('map_type', 'i2'), + ('map_min', 'f8'), + ('map_max', 'f8'), + ('min_value', 'f8'), + ('max_value', 'f8'), + ('offset_map', 'u4'), + ('gamma', 'f8'), + ('offset', 'f8'), + ('gray_channel', MM_DIMENSION), + ('offset_thumbnail', 'u4'), + ('voice_field', 'i4'), + ('offset_voice_field', 'u4'), +] + +# Carl Zeiss LSM +CZ_LSM_INFO = [ + ('magic_number', 'i4'), + ('structure_size', 'i4'), + ('dimension_x', 'i4'), + ('dimension_y', 'i4'), + ('dimension_z', 'i4'), + ('dimension_channels', 'i4'), + ('dimension_time', 'i4'), + ('dimension_data_type', 'i4'), + ('thumbnail_x', 'i4'), + ('thumbnail_y', 'i4'), + ('voxel_size_x', 'f8'), + ('voxel_size_y', 'f8'), + ('voxel_size_z', 'f8'), + ('origin_x', 'f8'), + ('origin_y', 'f8'), + ('origin_z', 'f8'), + ('scan_type', 'u2'), + ('spectral_scan', 'u2'), + ('data_type', 'u4'), + ('offset_vector_overlay', 'u4'), + ('offset_input_lut', 'u4'), + ('offset_output_lut', 'u4'), + ('offset_channel_colors', 'u4'), + ('time_interval', 'f8'), + ('offset_channel_data_types', 'u4'), + ('offset_scan_information', 'u4'), + ('offset_ks_data', 'u4'), + ('offset_time_stamps', 'u4'), + ('offset_event_list', 'u4'), + ('offset_roi', 'u4'), + ('offset_bleach_roi', 'u4'), + ('offset_next_recording', 'u4'), + ('display_aspect_x', 'f8'), + ('display_aspect_y', 'f8'), + ('display_aspect_z', 'f8'), + ('display_aspect_time', 'f8'), + ('offset_mean_of_roi_overlay', 'u4'), + ('offset_topo_isoline_overlay', 'u4'), + ('offset_topo_profile_overlay', 'u4'), + ('offset_linescan_overlay', 'u4'), + ('offset_toolbar_flags', 'u4'), +] + +# Import functions for LSM_INFO sub-records +CZ_LSM_INFO_READERS = { + 'scan_information': read_cz_lsm_scan_info, + 'time_stamps': read_cz_lsm_time_stamps, + 'event_list': read_cz_lsm_event_list, +} + +# Map cz_lsm_info.scan_type to dimension order +CZ_SCAN_TYPES = { + 0: 'XYZCT', # x-y-z scan + 1: 'XYZCT', # z scan (x-z plane) + 2: 'XYZCT', # line scan + 3: 'XYTCZ', # time series x-y + 4: 'XYZTC', # time series x-z + 5: 'XYTCZ', # time series 'Mean of ROIs' + 6: 'XYZTC', # time series x-y-z + 7: 'XYCTZ', # spline scan + 8: 'XYCZT', # spline scan x-z + 9: 'XYTCZ', # time series spline plane x-z + 10: 'XYZCT', # point mode +} + +# Map dimension codes to cz_lsm_info attribute +CZ_DIMENSIONS = { + 'X': 'dimension_x', + 'Y': 'dimension_y', + 'Z': 'dimension_z', + 'C': 'dimension_channels', + 'T': 'dimension_time', +} + +# Descriptions of cz_lsm_info.data_type +CZ_DATA_TYPES = { + 0: 'varying data types', + 2: '12 bit unsigned integer', + 5: '32 bit float', +} + +CZ_LSM_SCAN_INFO_ARRAYS = { + 0x20000000: "tracks", + 0x30000000: "lasers", + 0x60000000: "detectionchannels", + 0x80000000: "illuminationchannels", + 0xa0000000: "beamsplitters", + 0xc0000000: "datachannels", + 0x13000000: "markers", + 0x11000000: "timers", +} + +CZ_LSM_SCAN_INFO_STRUCTS = { + 0x40000000: "tracks", + 0x50000000: "lasers", + 0x70000000: "detectionchannels", + 0x90000000: "illuminationchannels", + 0xb0000000: "beamsplitters", + 0xd0000000: "datachannels", + 0x14000000: "markers", + 0x12000000: "timers", +} + +CZ_LSM_SCAN_INFO_ATTRIBUTES = { + 0x10000001: "name", + 0x10000002: "description", + 0x10000003: "notes", + 0x10000004: "objective", + 0x10000005: "processing_summary", + 0x10000006: "special_scan_mode", + 0x10000007: "oledb_recording_scan_type", + 0x10000008: "oledb_recording_scan_mode", + 0x10000009: "number_of_stacks", + 0x1000000a: "lines_per_plane", + 0x1000000b: "samples_per_line", + 0x1000000c: "planes_per_volume", + 0x1000000d: "images_width", + 0x1000000e: "images_height", + 0x1000000f: "images_number_planes", + 0x10000010: "images_number_stacks", + 0x10000011: "images_number_channels", + 0x10000012: "linscan_xy_size", + 0x10000013: "scan_direction", + 0x10000014: "time_series", + 0x10000015: "original_scan_data", + 0x10000016: "zoom_x", + 0x10000017: "zoom_y", + 0x10000018: "zoom_z", + 0x10000019: "sample_0x", + 0x1000001a: "sample_0y", + 0x1000001b: "sample_0z", + 0x1000001c: "sample_spacing", + 0x1000001d: "line_spacing", + 0x1000001e: "plane_spacing", + 0x1000001f: "plane_width", + 0x10000020: "plane_height", + 0x10000021: "volume_depth", + 0x10000023: "nutation", + 0x10000034: "rotation", + 0x10000035: "precession", + 0x10000036: "sample_0time", + 0x10000037: "start_scan_trigger_in", + 0x10000038: "start_scan_trigger_out", + 0x10000039: "start_scan_event", + 0x10000040: "start_scan_time", + 0x10000041: "stop_scan_trigger_in", + 0x10000042: "stop_scan_trigger_out", + 0x10000043: "stop_scan_event", + 0x10000044: "stop_scan_time", + 0x10000045: "use_rois", + 0x10000046: "use_reduced_memory_rois", + 0x10000047: "user", + 0x10000048: "use_bccorrection", + 0x10000049: "position_bccorrection1", + 0x10000050: "position_bccorrection2", + 0x10000051: "interpolation_y", + 0x10000052: "camera_binning", + 0x10000053: "camera_supersampling", + 0x10000054: "camera_frame_width", + 0x10000055: "camera_frame_height", + 0x10000056: "camera_offset_x", + 0x10000057: "camera_offset_y", + # lasers + 0x50000001: "name", + 0x50000002: "acquire", + 0x50000003: "power", + # tracks + 0x40000001: "multiplex_type", + 0x40000002: "multiplex_order", + 0x40000003: "sampling_mode", + 0x40000004: "sampling_method", + 0x40000005: "sampling_number", + 0x40000006: "acquire", + 0x40000007: "sample_observation_time", + 0x4000000b: "time_between_stacks", + 0x4000000c: "name", + 0x4000000d: "collimator1_name", + 0x4000000e: "collimator1_position", + 0x4000000f: "collimator2_name", + 0x40000010: "collimator2_position", + 0x40000011: "is_bleach_track", + 0x40000012: "is_bleach_after_scan_number", + 0x40000013: "bleach_scan_number", + 0x40000014: "trigger_in", + 0x40000015: "trigger_out", + 0x40000016: "is_ratio_track", + 0x40000017: "bleach_count", + 0x40000018: "spi_center_wavelength", + 0x40000019: "pixel_time", + 0x40000021: "condensor_frontlens", + 0x40000023: "field_stop_value", + 0x40000024: "id_condensor_aperture", + 0x40000025: "condensor_aperture", + 0x40000026: "id_condensor_revolver", + 0x40000027: "condensor_filter", + 0x40000028: "id_transmission_filter1", + 0x40000029: "id_transmission1", + 0x40000030: "id_transmission_filter2", + 0x40000031: "id_transmission2", + 0x40000032: "repeat_bleach", + 0x40000033: "enable_spot_bleach_pos", + 0x40000034: "spot_bleach_posx", + 0x40000035: "spot_bleach_posy", + 0x40000036: "spot_bleach_posz", + 0x40000037: "id_tubelens", + 0x40000038: "id_tubelens_position", + 0x40000039: "transmitted_light", + 0x4000003a: "reflected_light", + 0x4000003b: "simultan_grab_and_bleach", + 0x4000003c: "bleach_pixel_time", + # detection_channels + 0x70000001: "integration_mode", + 0x70000002: "special_mode", + 0x70000003: "detector_gain_first", + 0x70000004: "detector_gain_last", + 0x70000005: "amplifier_gain_first", + 0x70000006: "amplifier_gain_last", + 0x70000007: "amplifier_offs_first", + 0x70000008: "amplifier_offs_last", + 0x70000009: "pinhole_diameter", + 0x7000000a: "counting_trigger", + 0x7000000b: "acquire", + 0x7000000c: "point_detector_name", + 0x7000000d: "amplifier_name", + 0x7000000e: "pinhole_name", + 0x7000000f: "filter_set_name", + 0x70000010: "filter_name", + 0x70000013: "integrator_name", + 0x70000014: "detection_channel_name", + 0x70000015: "detection_detector_gain_bc1", + 0x70000016: "detection_detector_gain_bc2", + 0x70000017: "detection_amplifier_gain_bc1", + 0x70000018: "detection_amplifier_gain_bc2", + 0x70000019: "detection_amplifier_offset_bc1", + 0x70000020: "detection_amplifier_offset_bc2", + 0x70000021: "detection_spectral_scan_channels", + 0x70000022: "detection_spi_wavelength_start", + 0x70000023: "detection_spi_wavelength_stop", + 0x70000026: "detection_dye_name", + 0x70000027: "detection_dye_folder", + # illumination_channels + 0x90000001: "name", + 0x90000002: "power", + 0x90000003: "wavelength", + 0x90000004: "aquire", + 0x90000005: "detchannel_name", + 0x90000006: "power_bc1", + 0x90000007: "power_bc2", + # beam_splitters + 0xb0000001: "filter_set", + 0xb0000002: "filter", + 0xb0000003: "name", + # data_channels + 0xd0000001: "name", + 0xd0000003: "acquire", + 0xd0000004: "color", + 0xd0000005: "sample_type", + 0xd0000006: "bits_per_sample", + 0xd0000007: "ratio_type", + 0xd0000008: "ratio_track1", + 0xd0000009: "ratio_track2", + 0xd000000a: "ratio_channel1", + 0xd000000b: "ratio_channel2", + 0xd000000c: "ratio_const1", + 0xd000000d: "ratio_const2", + 0xd000000e: "ratio_const3", + 0xd000000f: "ratio_const4", + 0xd0000010: "ratio_const5", + 0xd0000011: "ratio_const6", + 0xd0000012: "ratio_first_images1", + 0xd0000013: "ratio_first_images2", + 0xd0000014: "dye_name", + 0xd0000015: "dye_folder", + 0xd0000016: "spectrum", + 0xd0000017: "acquire", + # markers + 0x14000001: "name", + 0x14000002: "description", + 0x14000003: "trigger_in", + 0x14000004: "trigger_out", + # timers + 0x12000001: "name", + 0x12000002: "description", + 0x12000003: "interval", + 0x12000004: "trigger_in", + 0x12000005: "trigger_out", + 0x12000006: "activation_time", + 0x12000007: "activation_number", +} + +# Map TIFF tag code to attribute name, default value, type, count, validator +TIFF_TAGS = { + 254: ('new_subfile_type', 0, 4, 1, TIFF_SUBFILE_TYPES()), + 255: ('subfile_type', None, 3, 1, + {0: 'undefined', 1: 'image', 2: 'reduced_image', 3: 'page'}), + 256: ('image_width', None, 4, 1, None), + 257: ('image_length', None, 4, 1, None), + 258: ('bits_per_sample', 1, 3, 1, None), + 259: ('compression', 1, 3, 1, TIFF_COMPESSIONS), + 262: ('photometric', None, 3, 1, TIFF_PHOTOMETRICS), + 266: ('fill_order', 1, 3, 1, {1: 'msb2lsb', 2: 'lsb2msb'}), + 269: ('document_name', None, 2, None, None), + 270: ('image_description', None, 2, None, None), + 271: ('make', None, 2, None, None), + 272: ('model', None, 2, None, None), + 273: ('strip_offsets', None, 4, None, None), + 274: ('orientation', 1, 3, 1, TIFF_ORIENTATIONS), + 277: ('samples_per_pixel', 1, 3, 1, None), + 278: ('rows_per_strip', 2**32-1, 4, 1, None), + 279: ('strip_byte_counts', None, 4, None, None), + 280: ('min_sample_value', None, 3, None, None), + 281: ('max_sample_value', None, 3, None, None), # 2**bits_per_sample + 282: ('x_resolution', None, 5, 1, None), + 283: ('y_resolution', None, 5, 1, None), + 284: ('planar_configuration', 1, 3, 1, {1: 'contig', 2: 'separate'}), + 285: ('page_name', None, 2, None, None), + 286: ('x_position', None, 5, 1, None), + 287: ('y_position', None, 5, 1, None), + 296: ('resolution_unit', 2, 4, 1, {1: 'none', 2: 'inch', 3: 'centimeter'}), + 297: ('page_number', None, 3, 2, None), + 305: ('software', None, 2, None, None), + 306: ('datetime', None, 2, None, None), + 315: ('artist', None, 2, None, None), + 316: ('host_computer', None, 2, None, None), + 317: ('predictor', 1, 3, 1, {1: None, 2: 'horizontal'}), + 320: ('color_map', None, 3, None, None), + 322: ('tile_width', None, 4, 1, None), + 323: ('tile_length', None, 4, 1, None), + 324: ('tile_offsets', None, 4, None, None), + 325: ('tile_byte_counts', None, 4, None, None), + 338: ('extra_samples', None, 3, None, + {0: 'unspecified', 1: 'assocalpha', 2: 'unassalpha'}), + 339: ('sample_format', 1, 3, 1, TIFF_SAMPLE_FORMATS), + 347: ('jpeg_tables', None, None, None, None), + 530: ('ycbcr_subsampling', 1, 3, 2, None), + 531: ('ycbcr_positioning', 1, 3, 1, None), + 32997: ('image_depth', None, 4, 1, None), + 32998: ('tile_depth', None, 4, 1, None), + 33432: ('copyright', None, 1, None, None), + 33445: ('md_file_tag', None, 4, 1, None), + 33446: ('md_scale_pixel', None, 5, 1, None), + 33447: ('md_color_table', None, 3, None, None), + 33448: ('md_lab_name', None, 2, None, None), + 33449: ('md_sample_info', None, 2, None, None), + 33450: ('md_prep_date', None, 2, None, None), + 33451: ('md_prep_time', None, 2, None, None), + 33452: ('md_file_units', None, 2, None, None), + 33550: ('model_pixel_scale', None, 12, 3, None), + 33922: ('model_tie_point', None, 12, None, None), + 37510: ('user_comment', None, None, None, None), + 34665: ('exif_ifd', None, None, 1, None), + 34735: ('geo_key_directory', None, 3, None, None), + 34736: ('geo_double_params', None, 12, None, None), + 34737: ('geo_ascii_params', None, 2, None, None), + 34853: ('gps_ifd', None, None, 1, None), + 42112: ('gdal_metadata', None, 2, None, None), + 42113: ('gdal_nodata', None, 2, None, None), + 50838: ('imagej_byte_counts', None, None, None, None), + 50289: ('mc_xy_position', None, 12, 2, None), + 50290: ('mc_z_position', None, 12, 1, None), + 50291: ('mc_xy_calibration', None, 12, 3, None), + 50292: ('mc_lens_lem_na_n', None, 12, 3, None), + 50293: ('mc_channel_name', None, 1, None, None), + 50294: ('mc_ex_wavelength', None, 12, 1, None), + 50295: ('mc_time_stamp', None, 12, 1, None), + 65200: ('flex_xml', None, 2, None, None), + # code: (attribute name, default value, type, count, validator) +} + +# Map custom TIFF tag codes to attribute names and import functions +CUSTOM_TAGS = { + 700: ('xmp', read_bytes), + 34377: ('photoshop', read_numpy), + 33723: ('iptc', read_bytes), + 34675: ('icc_profile', read_numpy), + 33628: ('mm_uic1', read_mm_uic1), + 33629: ('mm_uic2', read_mm_uic2), + 33630: ('mm_uic3', read_mm_uic3), + 33631: ('mm_uic4', read_mm_uic4), + 34361: ('mm_header', read_mm_header), + 34362: ('mm_stamp', read_mm_stamp), + 34386: ('mm_user_block', read_bytes), + 34412: ('cz_lsm_info', read_cz_lsm_info), + 43314: ('nih_image_header', read_nih_image_header), + # 40001: ('mc_ipwinscal', read_bytes), + 40100: ('mc_id_old', read_bytes), + 50288: ('mc_id', read_bytes), + 50296: ('mc_frame_properties', read_bytes), + 50839: ('imagej_metadata', read_bytes), + 51123: ('micromanager_metadata', read_json), +} + +# Max line length of printed output +PRINT_LINE_LEN = 79 + + +def imshow(data, title=None, vmin=0, vmax=None, cmap=None, + bitspersample=None, photometric='rgb', interpolation='nearest', + dpi=96, figure=None, subplot=111, maxdim=8192, **kwargs): + """Plot n-dimensional images using matplotlib.pyplot. + + Return figure, subplot and plot axis. + Requires pyplot already imported ``from matplotlib import pyplot``. + + Parameters + ---------- + bitspersample : int or None + Number of bits per channel in integer RGB images. + photometric : {'miniswhite', 'minisblack', 'rgb', or 'palette'} + The color space of the image data. + title : str + Window and subplot title. + figure : matplotlib.figure.Figure (optional). + Matplotlib to use for plotting. + subplot : int + A matplotlib.pyplot.subplot axis. + maxdim : int + maximum image size in any dimension. + kwargs : optional + Arguments for matplotlib.pyplot.imshow. + + """ + #if photometric not in ('miniswhite', 'minisblack', 'rgb', 'palette'): + # raise ValueError("Can't handle %s photometrics" % photometric) + # TODO: handle photometric == 'separated' (CMYK) + isrgb = photometric in ('rgb', 'palette') + data = numpy.atleast_2d(data.squeeze()) + data = data[(slice(0, maxdim), ) * len(data.shape)] + + dims = data.ndim + if dims < 2: + raise ValueError("not an image") + elif dims == 2: + dims = 0 + isrgb = False + else: + if isrgb and data.shape[-3] in (3, 4): + data = numpy.swapaxes(data, -3, -2) + data = numpy.swapaxes(data, -2, -1) + elif not isrgb and data.shape[-1] in (3, 4): + data = numpy.swapaxes(data, -3, -1) + data = numpy.swapaxes(data, -2, -1) + isrgb = isrgb and data.shape[-1] in (3, 4) + dims -= 3 if isrgb else 2 + + if photometric == 'palette' and isrgb: + datamax = data.max() + if datamax > 255: + data >>= 8 # possible precision loss + data = data.astype('B') + elif data.dtype.kind in 'ui': + if not (isrgb and data.dtype.itemsize <= 1) or bitspersample is None: + try: + bitspersample = int(math.ceil(math.log(data.max(), 2))) + except Exception: + bitspersample = data.dtype.itemsize * 8 + elif not isinstance(bitspersample, int): + # bitspersample can be tuple, e.g. (5, 6, 5) + bitspersample = data.dtype.itemsize * 8 + datamax = 2**bitspersample + if isrgb: + if bitspersample < 8: + data <<= 8 - bitspersample + elif bitspersample > 8: + data >>= bitspersample - 8 # precision loss + data = data.astype('B') + elif data.dtype.kind == 'f': + datamax = data.max() + if isrgb and datamax > 1.0: + if data.dtype.char == 'd': + data = data.astype('f') + data /= datamax + elif data.dtype.kind == 'b': + datamax = 1 + elif data.dtype.kind == 'c': + raise NotImplementedError("complex type") # TODO: handle complex types + + if not isrgb: + if vmax is None: + vmax = datamax + if vmin is None: + if data.dtype.kind == 'i': + dtmin = numpy.iinfo(data.dtype).min + vmin = numpy.min(data) + if vmin == dtmin: + vmin = numpy.min(data > dtmin) + if data.dtype.kind == 'f': + dtmin = numpy.finfo(data.dtype).min + vmin = numpy.min(data) + if vmin == dtmin: + vmin = numpy.min(data > dtmin) + else: + vmin = 0 + + pyplot = sys.modules['matplotlib.pyplot'] + + if figure is None: + pyplot.rc('font', family='sans-serif', weight='normal', size=8) + figure = pyplot.figure(dpi=dpi, figsize=(10.3, 6.3), frameon=True, + facecolor='1.0', edgecolor='w') + try: + figure.canvas.manager.window.title(title) + except Exception: + pass + pyplot.subplots_adjust(bottom=0.03*(dims+2), top=0.9, + left=0.1, right=0.95, hspace=0.05, wspace=0.0) + subplot = pyplot.subplot(subplot) + + if title: + try: + title = str(title, 'Windows-1252') + except TypeError: + pass + pyplot.title(title, size=11) + + if cmap is None: + if data.dtype.kind in 'ub' and vmin == 0: + cmap = 'gray' + else: + cmap = 'coolwarm' + if photometric == 'miniswhite': + cmap += '_r' + + image = pyplot.imshow(data[(0, ) * dims].squeeze(), vmin=vmin, vmax=vmax, + cmap=cmap, interpolation=interpolation, **kwargs) + + if not isrgb: + pyplot.colorbar() # panchor=(0.55, 0.5), fraction=0.05 + + def format_coord(x, y): + # callback function to format coordinate display in toolbar + x = int(x + 0.5) + y = int(y + 0.5) + try: + if dims: + return "%s @ %s [%4i, %4i]" % (cur_ax_dat[1][y, x], + current, x, y) + else: + return "%s @ [%4i, %4i]" % (data[y, x], x, y) + except IndexError: + return "" + + pyplot.gca().format_coord = format_coord + + if dims: + current = list((0, ) * dims) + cur_ax_dat = [0, data[tuple(current)].squeeze()] + sliders = [pyplot.Slider( + pyplot.axes([0.125, 0.03*(axis+1), 0.725, 0.025]), + 'Dimension %i' % axis, 0, data.shape[axis]-1, 0, facecolor='0.5', + valfmt='%%.0f [%i]' % data.shape[axis]) for axis in range(dims)] + for slider in sliders: + slider.drawon = False + + def set_image(current, sliders=sliders, data=data): + # change image and redraw canvas + cur_ax_dat[1] = data[tuple(current)].squeeze() + image.set_data(cur_ax_dat[1]) + for ctrl, index in zip(sliders, current): + ctrl.eventson = False + ctrl.set_val(index) + ctrl.eventson = True + figure.canvas.draw() + + def on_changed(index, axis, data=data, current=current): + # callback function for slider change event + index = int(round(index)) + cur_ax_dat[0] = axis + if index == current[axis]: + return + if index >= data.shape[axis]: + index = 0 + elif index < 0: + index = data.shape[axis] - 1 + current[axis] = index + set_image(current) + + def on_keypressed(event, data=data, current=current): + # callback function for key press event + key = event.key + axis = cur_ax_dat[0] + if str(key) in '0123456789': + on_changed(key, axis) + elif key == 'right': + on_changed(current[axis] + 1, axis) + elif key == 'left': + on_changed(current[axis] - 1, axis) + elif key == 'up': + cur_ax_dat[0] = 0 if axis == len(data.shape)-1 else axis + 1 + elif key == 'down': + cur_ax_dat[0] = len(data.shape)-1 if axis == 0 else axis - 1 + elif key == 'end': + on_changed(data.shape[axis] - 1, axis) + elif key == 'home': + on_changed(0, axis) + + figure.canvas.mpl_connect('key_press_event', on_keypressed) + for axis, ctrl in enumerate(sliders): + ctrl.on_changed(lambda k, a=axis: on_changed(k, a)) + + return figure, subplot, image + + +def _app_show(): + """Block the GUI. For use as skimage plugin.""" + pyplot = sys.modules['matplotlib.pyplot'] + pyplot.show() + + +def main(argv=None): + """Command line usage main function.""" + if float(sys.version[0:3]) < 2.6: + print("This script requires Python version 2.6 or better.") + print("This is Python version %s" % sys.version) + return 0 + if argv is None: + argv = sys.argv + + import optparse + + search_doc = lambda r, d: re.search(r, __doc__).group(1) if __doc__ else d + parser = optparse.OptionParser( + usage="usage: %prog [options] path", + description=search_doc("\n\n([^|]*?)\n\n", ''), + version="%%prog %s" % search_doc(":Version: (.*)", "Unknown")) + opt = parser.add_option + opt('-p', '--page', dest='page', type='int', default=-1, + help="display single page") + opt('-s', '--series', dest='series', type='int', default=-1, + help="display series of pages of same shape") + opt('--nomultifile', dest='nomultifile', action='store_true', + default=False, help="don't read OME series from multiple files") + opt('--noplot', dest='noplot', action='store_true', default=False, + help="don't display images") + opt('--interpol', dest='interpol', metavar='INTERPOL', default='bilinear', + help="image interpolation method") + opt('--dpi', dest='dpi', type='int', default=96, + help="set plot resolution") + opt('--debug', dest='debug', action='store_true', default=False, + help="raise exception on failures") + opt('--test', dest='test', action='store_true', default=False, + help="try read all images in path") + opt('--doctest', dest='doctest', action='store_true', default=False, + help="runs the internal tests") + opt('-v', '--verbose', dest='verbose', action='store_true', default=True) + opt('-q', '--quiet', dest='verbose', action='store_false') + + settings, path = parser.parse_args() + path = ' '.join(path) + + if settings.doctest: + import doctest + doctest.testmod() + return 0 + if not path: + parser.error("No file specified") + if settings.test: + test_tifffile(path, settings.verbose) + return 0 + + if any(i in path for i in '?*'): + path = glob.glob(path) + if not path: + print('no files match the pattern') + return 0 + # TODO: handle image sequences + #if len(path) == 1: + path = path[0] + + print("Reading file structure...", end=' ') + start = time.time() + try: + tif = TiffFile(path, multifile=not settings.nomultifile) + except Exception as e: + if settings.debug: + raise + else: + print("\n", e) + sys.exit(0) + print("%.3f ms" % ((time.time()-start) * 1e3)) + + if tif.is_ome: + settings.norgb = True + + images = [(None, tif[0 if settings.page < 0 else settings.page])] + if not settings.noplot: + print("Reading image data... ", end=' ') + + def notnone(x): + return next(i for i in x if i is not None) + start = time.time() + try: + if settings.page >= 0: + images = [(tif.asarray(key=settings.page), + tif[settings.page])] + elif settings.series >= 0: + images = [(tif.asarray(series=settings.series), + notnone(tif.series[settings.series].pages))] + else: + images = [] + for i, s in enumerate(tif.series): + try: + images.append( + (tif.asarray(series=i), notnone(s.pages))) + except ValueError as e: + images.append((None, notnone(s.pages))) + if settings.debug: + raise + else: + print("\n* series %i failed: %s... " % (i, e), + end='') + print("%.3f ms" % ((time.time()-start) * 1e3)) + except Exception as e: + if settings.debug: + raise + else: + print(e) + + tif.close() + + print("\nTIFF file:", tif) + print() + for i, s in enumerate(tif.series): + print ("Series %i" % i) + print(s) + print() + for i, page in images: + print(page) + print(page.tags) + if page.is_palette: + print("\nColor Map:", page.color_map.shape, page.color_map.dtype) + for attr in ('cz_lsm_info', 'cz_lsm_scan_information', 'mm_uic_tags', + 'mm_header', 'imagej_tags', 'micromanager_metadata', + 'nih_image_header'): + if hasattr(page, attr): + print("", attr.upper(), Record(getattr(page, attr)), sep="\n") + print() + if page.is_micromanager: + print('MICROMANAGER_FILE_METADATA') + print(Record(tif.micromanager_metadata)) + + if images and not settings.noplot: + try: + import matplotlib + matplotlib.use('TkAgg') + from matplotlib import pyplot + except ImportError as e: + warnings.warn("failed to import matplotlib.\n%s" % e) + else: + for img, page in images: + if img is None: + continue + vmin, vmax = None, None + if 'gdal_nodata' in page.tags: + vmin = numpy.min(img[img > float(page.gdal_nodata)]) + if page.is_stk: + try: + vmin = page.mm_uic_tags['min_scale'] + vmax = page.mm_uic_tags['max_scale'] + except KeyError: + pass + else: + if vmax <= vmin: + vmin, vmax = None, None + title = "%s\n %s" % (str(tif), str(page)) + imshow(img, title=title, vmin=vmin, vmax=vmax, + bitspersample=page.bits_per_sample, + photometric=page.photometric, + interpolation=settings.interpol, + dpi=settings.dpi) + pyplot.show() + + +TIFFfile = TiffFile # backwards compatibility + +if sys.version_info[0] > 2: + str = str, bytes + str = str + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/corticalmapping/ephys/KilosortWrapper.py b/corticalmapping/ephys/KilosortWrapper.py index 50de1d5..a192d8a 100644 --- a/corticalmapping/ephys/KilosortWrapper.py +++ b/corticalmapping/ephys/KilosortWrapper.py @@ -39,7 +39,7 @@ def get_clusters(csv_output): try: cluster_id = int(cluster[0]) except ValueError: - print cluster[0], 'can not be converted into integer.' + print(cluster[0], 'can not be converted into integer.') continue cluster_type = cluster[1] @@ -70,7 +70,7 @@ def get_spike_times_indices(clusters, spike_clusters_path, spike_times_path): spike_ind = {} - for cluster, cluster_id in clusters.iteritems(): + for cluster, cluster_id in clusters.items(): if cluster == 'unit_mua': mua_spike_ind = [] for id in cluster_id: @@ -120,23 +120,23 @@ def get_spike_timestamps(spike_ind, h5_path): :return: update the h5_file to contain timestamps in seconds of each defined unit in spike_ind for each file """ h5_file = h5py.File(h5_path, 'r+') - folder_list = [f for f in h5_file.keys() if f[0:6] == 'folder'] + folder_list = [f for f in list(h5_file.keys()) if f[0:6] == 'folder'] fs = h5_file['fs_hz'].value - units = spike_ind.keys() + units = list(spike_ind.keys()) units.sort() h5_file.create_dataset('units', data=units) - print folder_list - print fs + print(folder_list) + print(fs) for folder in folder_list: curr_group = h5_file[folder] curr_start_ind = curr_group.attrs['start_index'] curr_end_ind = curr_group.attrs['end_index'] - print curr_start_ind - print curr_end_ind + print(curr_start_ind) + print(curr_end_ind) - for unit, spikes in spike_ind.iteritems(): + for unit, spikes in spike_ind.items(): curr_spike_ind = [spk for spk in spikes if spk >= curr_start_ind and spk < curr_end_ind] curr_spike_ind = np.array(curr_spike_ind, dtype=np.float32) - curr_start_ind @@ -153,16 +153,16 @@ def get_spike_timestamps(spike_ind, h5_path): h5_path = r"G:\160610-M240652\processed_1\160610-M240652.hdf5" cluster_group = read_csv(csv_path) - print 'cluster_group:' - print cluster_group + print('cluster_group:') + print(cluster_group) clusters = get_clusters(cluster_group) - print '\nclusters:' - print clusters + print('\nclusters:') + print(clusters) spike_ind = get_spike_times_indices(clusters, spike_cluster_path, spike_times_path) - print '\nspike_ind:' - print spike_ind.keys() - for key, value in spike_ind.iteritems(): - print key, ':', len(value) - print key, ':', value[0:10] + print('\nspike_ind:') + print(list(spike_ind.keys())) + for key, value in spike_ind.items(): + print(key, ':', len(value)) + print(key, ':', value[0:10]) get_spike_timestamps(spike_ind, h5_path) diff --git a/corticalmapping/ephys/OpenEphysWrapper.py b/corticalmapping/ephys/OpenEphysWrapper.py index 042c5da..80b298f 100644 --- a/corticalmapping/ephys/OpenEphysWrapper.py +++ b/corticalmapping/ephys/OpenEphysWrapper.py @@ -36,7 +36,7 @@ def find_next_valid_block(input_array, bytes_per_block, start_index): first_valid_block_start = i - bytes_per_block break else: - print 'no valid block found after index:', start_index + print('no valid block found after index:', start_index) return first_valid_block_start @@ -81,7 +81,7 @@ def load_continuous_old(file_path, dtype=np.float32): assert dtype in (np.float32, np.int16), \ 'Invalid data type specified for loadContinous, valid types are np.float32 and np.int16' - print "\nLoading continuous data from " + file_path + print("\nLoading continuous data from " + file_path) bytes_per_block = CONTINUOUS_TIMESTAMP_DTYPE.itemsize + CONTINUOUS_SAMPLE_PER_RECORD_DTYPE.itemsize + \ CONTINUOUS_RECORDING_NUMBER_DTYPE.itemsize + CONTINUOUS_MARKER_BYTES + \ @@ -91,12 +91,12 @@ def load_continuous_old(file_path, dtype=np.float32): f = open(file_path, 'rb') file_length = os.fstat(f.fileno()).st_size - print 'total length of the file: ', file_length, 'bytes.' + print('total length of the file: ', file_length, 'bytes.') - print 'bytes per record block: ', bytes_per_block + print('bytes per record block: ', bytes_per_block) block_num = (file_length - oe.NUM_HEADER_BYTES) // bytes_per_block - print 'total number of valid blocks: ', block_num + print('total number of valid blocks: ', block_num) header = oe.readHeader(f) @@ -115,8 +115,8 @@ def load_continuous_old(file_path, dtype=np.float32): N = np.fromfile(f, CONTINUOUS_SAMPLE_PER_RECORD_DTYPE, 1)[0] if N != oe.SAMPLES_PER_RECORD: - print('samples per record specified in block ' + str(i) + ' (' + str(N) + - ') does not equal to expected value (' + str(oe.SAMPLES_PER_RECORD) + ')!') + print(('samples per record specified in block ' + str(i) + ' (' + str(N) + + ') does not equal to expected value (' + str(oe.SAMPLES_PER_RECORD) + ')!')) samples = samples[0 : i * oe.SAMPLES_PER_RECORD] is_break = True break @@ -136,8 +136,8 @@ def load_continuous_old(file_path, dtype=np.float32): record_mark = np.fromfile(f, dtype=np.dtype(' 0) & (er <= end_time)] @@ -469,10 +469,10 @@ def pack_folders(folder_list, output_folder, output_filename, continous_channels curr_ts_group = curr_group.create_group('timestamps') curr_trace_dict, curr_sample_num, fs = pack_folder(folder, prefix, digital_channels=digital_channels) - all_channels = curr_trace_dict.keys() - print '\nall channels in folder ', folder, ':' - print all_channels - print + all_channels = list(curr_trace_dict.keys()) + print('\nall channels in folder ', folder, ':') + print(all_channels) + print() if sampling_rate is None: sampling_rate = fs @@ -502,14 +502,14 @@ def pack_folders(folder_list, output_folder, output_filename, continous_channels data_all.append(curr_data_array.flatten(order='F')) # add continuous channels - for ch, trace in curr_trace_dict.iteritems(): + for ch, trace in curr_trace_dict.items(): if '_CH' not in ch and ch != 'events': curr_dset = curr_con_group.create_dataset(ch[len(prefix) + 1:], data=trace) curr_dset.attrs['unit'] = 'volt' # add digital events events = curr_trace_dict['events'] - for dch, dch_dict in events.iteritems(): + for dch, dch_dict in events.items(): curr_dch_group = curr_dig_group.create_group(dch) curr_dch_group.create_dataset('rise', data=dch_dict['rise']) curr_dch_group.create_dataset('fall', data=dch_dict['fall']) diff --git a/corticalmapping/scripts/post_recording/00_old/analysis_database/0000_delete_stas_and_repack.py b/corticalmapping/scripts/post_recording/00_old/analysis_database/0000_delete_stas_and_repack.py deleted file mode 100644 index cb8e540..0000000 --- a/corticalmapping/scripts/post_recording/00_old/analysis_database/0000_delete_stas_and_repack.py +++ /dev/null @@ -1,71 +0,0 @@ -import os -import sys -import h5py -import time -from multiprocessing import Pool -import corticalmapping.NwbTools as nt -import corticalmapping.DatabaseTools as dt - -process_num = 5 -save_folder = 'repacked' - -curr_folder = os.path.dirname(os.path.abspath(__file__)) -os.chdir(curr_folder) - -def run_single_file_for_multi_process(params): - - file_path, save_dir, file_ind, total_file_num, t0 = params - - nwb_f = h5py.File(file_path) - if 'STRFs' in nwb_f['analysis']: - del nwb_f['analysis/STRFs'] - - if 'strf_001_LocallySparseNoiseRetinotopicMapping' in nwb_f['analysis']: - del nwb_f['analysis/strf_001_LocallySparseNoiseRetinotopicMapping'] - - if 'response_table_003_DriftingGratingCircleRetinotopicMapping' in nwb_f['analysis']: - del nwb_f['analysis/response_table_003_DriftingGratingCircleRetinotopicMapping'] - - if 'response_table_001_DriftingGratingCircleRetinotopicMapping' in nwb_f['analysis']: - del nwb_f['analysis/response_table_001_DriftingGratingCircleRetinotopicMapping'] - - nwb_f.close() - - print('{:6.1f} min; {} / {}; {}: repacking ...'.format((time.time() - t0) / 60., - file_ind+1, - total_file_num, - file_path)) - # save_path = os.path.join(save_dir, os.path.splitext(os.path.split(file_path)[1])[0] + '_repacked.nwb') - save_path = os.path.join(save_dir, os.path.splitext(os.path.split(file_path)[1])[0] + '_repacked.nwb') - sys_str = "h5repack {} {}".format(file_path, save_path) - - # print(sys_str) - - os.system(sys_str) - - print('{:6.1f} min; {} / {}; {}: Done.'.format((time.time() - t0) / 60., - file_ind + 1, - total_file_num, - file_path)) - - -def run(): - - t0 = time.time() - - nwb_fns = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'] - nwb_fns.sort() - print('nwb files:') - print('\n'.join(nwb_fns)) - - param_lst = [(os.path.join(curr_folder, nwb_fn), - os.path.join(curr_folder, save_folder), - file_ind, - len(nwb_fns), - t0) for file_ind, nwb_fn in enumerate(nwb_fns)] - - p = Pool(process_num) - p.map(run_single_file_for_multi_process, param_lst) - -if __name__ == "__main__": - run() diff --git a/corticalmapping/scripts/post_recording/00_old/analysis_database/0010_regenerate_stas.py b/corticalmapping/scripts/post_recording/00_old/analysis_database/0010_regenerate_stas.py deleted file mode 100644 index 3e2f64e..0000000 --- a/corticalmapping/scripts/post_recording/00_old/analysis_database/0010_regenerate_stas.py +++ /dev/null @@ -1,65 +0,0 @@ -import os -import time -from multiprocessing import Pool -import corticalmapping.NwbTools as nt -import corticalmapping.DatabaseTools as dt - -process_num = 5 -nwb_folder = 'repacked' -strf_t_win = [-0.5, 2.] -dgc_t_win = [-1., 2.5] - -curr_folder = os.path.dirname(os.path.abspath(__file__)) -os.chdir(curr_folder) - -def run_single_file_for_multi_process(params): - - file_path, file_ind, total_file_num, t0 = params - - print('{:6.1f} min; {} / {}; {}: regenerating strf ...'.format((time.time() - t0) / 60., - file_ind + 1, - total_file_num, - file_path)) - - nwb_f = nt.RecordedFile(file_path) - lsn_name = '001_LocallySparseNoiseRetinotopicMapping' - if dt.get_strf_grp_key(nwb_f.file_pointer) is None: - if lsn_name in nwb_f.file_pointer['analysis/photodiode_onsets']: - nwb_f.get_spatial_temporal_receptive_field_retinotopic_mapping(stim_name=lsn_name, - time_window=strf_t_win, - verbose=False) - - dgc_name = '003_DriftingGratingCircleRetinotopicMapping' - if dt.get_dgcrm_grp_key(nwb_f.file_pointer) is None: - if dgc_name in nwb_f.file_pointer['analysis/photodiode_onsets']: - nwb_f.get_drifting_grating_response_table_retinotopic_mapping(stim_name=dgc_name, - time_window=dgc_t_win) - - dgc_name = '001_DriftingGratingCircleRetinotopicMapping' - if dt.get_dgcrm_grp_key(nwb_f.file_pointer) is None: - if dgc_name in nwb_f.file_pointer['analysis/photodiode_onsets']: - nwb_f.get_drifting_grating_response_table_retinotopic_mapping(stim_name=dgc_name, - time_window=dgc_t_win) - - nwb_f.close() - - -def run(): - - t0 = time.time() - - nwb_fns = [f for f in os.listdir(nwb_folder) if f[-4:] == '.nwb'] - nwb_fns.sort() - print('nwb files:') - print('\n'.join(nwb_fns)) - - param_lst = [(os.path.join(curr_folder, nwb_folder, nwb_fn), - file_ind, - len(nwb_fns), - t0) for file_ind, nwb_fn in enumerate(nwb_fns)] - - p = Pool(process_num) - p.map(run_single_file_for_multi_process, param_lst) - -if __name__ == "__main__": - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/analysis_database/0030_test_sigle_strf.py b/corticalmapping/scripts/post_recording/00_old/analysis_database/0030_test_sigle_strf.py deleted file mode 100644 index d455f9a..0000000 --- a/corticalmapping/scripts/post_recording/00_old/analysis_database/0030_test_sigle_strf.py +++ /dev/null @@ -1,50 +0,0 @@ -import os -import h5py -import numpy as np -import corticalmapping.DatabaseTools as dt -import corticalmapping.SingleCellAnalysis as sca - -fn_original = '180813_M386444_110.nwb' -fn_repacked = '180813_M386444_110_repacked.nwb' - -roi_ind = 0 -params = dt.ANALYSIS_PARAMS -params['gaussian_filter_sigma_rf'] = 1. -params['interpolate_rate_rf'] = 10. -params['rf_z_threshold'] = 1.3 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -roi_n = 'roi_{:04d}'.format(roi_ind) - -def print_peak_z(strf): - strf_dff = strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=1) - - # positive spatial receptive fields - srf_on, srf_off = strf_dff.get_zscore_receptive_field(timeWindow=[0., 0.5]) - - srf = srf_on.gaussian_filter(sigma=1.) - srf = srf.interpolate(ratio=10.) - - print(np.max(srf.weights)) - - -f_o = h5py.File(fn_original, 'r') -strf_o = sca.SpatialTemporalReceptiveField.from_h5_group(f_o['analysis/STRFs/plane0/strf_roi_{:04d}'.format(roi_ind)]) -print_peak_z(strf_o) -# print(strf_o.data['traces'][0][5]) -# print(strf_o.time) -f_o.close() - -f_r = h5py.File(os.path.join('repacked', fn_repacked), 'r') -strf_r = dt.get_strf(f_r, plane_n='plane0', roi_ind=0, trace_type='sta_f_center_subtracted') -print_peak_z(strf_r) -# print(strf_r.data['traces'][0][5]) -# print(strf_r.time) - -roi_properties, _, _, _, _, _, _, _, _, _, _, _, _, _ = \ - dt.get_everything_from_roi(nwb_f=f_r, plane_n='plane0', roi_n=roi_n, params=params) -print(roi_properties['rf_pos_on_peak_z']) - -f_r.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/analysis_database/0040_get_roi_metadata.py b/corticalmapping/scripts/post_recording/00_old/analysis_database/0040_get_roi_metadata.py deleted file mode 100644 index 52b66b7..0000000 --- a/corticalmapping/scripts/post_recording/00_old/analysis_database/0040_get_roi_metadata.py +++ /dev/null @@ -1,414 +0,0 @@ -import sys -sys.path.extend(['/home/junz/PycharmProjects/corticalmapping']) -import os -import time -import pandas as pd -import numpy as np -import h5py -import datetime -import corticalmapping.DatabaseTools as dt -from multiprocessing import Pool - -date_range = [180309, 190411] -database_folder = 'nwbs' -save_folder = "temp_xlsx" -process_num = 8 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -print('pandas version: {}\n'.format(pd.__version__)) - -params = dt.ANALYSIS_PARAMS -params['trace_type'] = 'f_center_subtracted' -params['rf_z_threshold'] = 1.3 -params['is_collapse_dire'] = False -params['is_collapse_sf'] = False -params['is_collapse_tf'] = False - -columns = [ - 'date', - 'mouse_id', - 'plane_n', - 'roi_n', - 'depth', # microns under pia, float - - # roi mask - 'roi_area', # square micron - 'roi_center_row', # center of roi mask in field of view, row - 'roi_center_col', # center of roi mask in field of view, column - - # trace skewness - 'skew_raw', # skewness of unfiltered trace (neuropil subtracted), float - 'skew_fil', # skewness of highpassed trace, float - - # receptive fields - 'rf_pos_on_peak_z', - 'rf_pos_on_area', - 'rf_pos_on_center_alt', - 'rf_pos_on_center_azi', - - 'rf_pos_off_peak_z', - 'rf_pos_off_area', - 'rf_pos_off_center_alt', - 'rf_pos_off_center_azi', - - 'rf_pos_onoff_peak_z', - 'rf_pos_onoff_area', - 'rf_pos_onoff_center_alt', - 'rf_pos_onoff_center_azi', - - 'rf_pos_lsi', - - 'rf_neg_on_peak_z', - 'rf_neg_on_area', - 'rf_neg_on_center_alt', - 'rf_neg_on_center_azi', - - 'rf_neg_off_peak_z', - 'rf_neg_off_area', - 'rf_neg_off_center_alt', - 'rf_neg_off_center_azi', - - 'rf_neg_onoff_peak_z', - 'rf_neg_onoff_area', - 'rf_neg_onoff_center_alt', - 'rf_neg_onoff_center_azi', - - 'rf_neg_lsi', - - # drifting grating peak response - 'dgc_pos_peak_df', - 'dgc_neg_peak_df', - 'dgc_pos_p_ttest_df', - 'dgc_neg_p_ttest_df', - 'dgc_p_anova_df', - - 'dgc_pos_peak_dff', - 'dgc_neg_peak_dff', - 'dgc_pos_p_ttest_dff', - 'dgc_neg_p_ttest_dff', - 'dgc_p_anova_dff', - - 'dgc_pos_peak_z', - 'dgc_neg_peak_z', - 'dgc_pos_p_ttest_z', - 'dgc_neg_p_ttest_z', - 'dgc_p_anova_z', - - # direction / orientation tuning, pos, df - 'dgc_pos_osi_raw_df', - 'dgc_pos_dsi_raw_df', - 'dgc_pos_gosi_raw_df', - 'dgc_pos_gdsi_raw_df', - 'dgc_pos_osi_ele_df', - 'dgc_pos_dsi_ele_df', - 'dgc_pos_gosi_ele_df', - 'dgc_pos_gdsi_ele_df', - 'dgc_pos_osi_rec_df', - 'dgc_pos_dsi_rec_df', - 'dgc_pos_gosi_rec_df', - 'dgc_pos_gdsi_rec_df', - 'dgc_pos_peak_dire_raw_df', - 'dgc_pos_vs_dire_raw_df', - 'dgc_pos_vs_dire_ele_df', - 'dgc_pos_vs_dire_rec_df', - - # direction / orientation tuning, neg, df - 'dgc_neg_osi_raw_df', - 'dgc_neg_dsi_raw_df', - 'dgc_neg_gosi_raw_df', - 'dgc_neg_gdsi_raw_df', - 'dgc_neg_osi_ele_df', - 'dgc_neg_dsi_ele_df', - 'dgc_neg_gosi_ele_df', - 'dgc_neg_gdsi_ele_df', - 'dgc_neg_osi_rec_df', - 'dgc_neg_dsi_rec_df', - 'dgc_neg_gosi_rec_df', - 'dgc_neg_gdsi_rec_df', - 'dgc_neg_peak_dire_raw_df', - 'dgc_neg_vs_dire_raw_df', - 'dgc_neg_vs_dire_ele_df', - 'dgc_neg_vs_dire_rec_df', - - # direction / orientation tuning, pos, dff - 'dgc_pos_osi_raw_dff', - 'dgc_pos_dsi_raw_dff', - 'dgc_pos_gosi_raw_dff', - 'dgc_pos_gdsi_raw_dff', - 'dgc_pos_osi_ele_dff', - 'dgc_pos_dsi_ele_dff', - 'dgc_pos_gosi_ele_dff', - 'dgc_pos_gdsi_ele_dff', - 'dgc_pos_osi_rec_dff', - 'dgc_pos_dsi_rec_dff', - 'dgc_pos_gosi_rec_dff', - 'dgc_pos_gdsi_rec_dff', - 'dgc_pos_peak_dire_raw_dff', - 'dgc_pos_vs_dire_raw_dff', - 'dgc_pos_vs_dire_ele_dff', - 'dgc_pos_vs_dire_rec_dff', - - # direction / orientation tuning, neg, dff - 'dgc_neg_osi_raw_dff', - 'dgc_neg_dsi_raw_dff', - 'dgc_neg_gosi_raw_dff', - 'dgc_neg_gdsi_raw_dff', - 'dgc_neg_osi_ele_dff', - 'dgc_neg_dsi_ele_dff', - 'dgc_neg_gosi_ele_dff', - 'dgc_neg_gdsi_ele_dff', - 'dgc_neg_osi_rec_dff', - 'dgc_neg_dsi_rec_dff', - 'dgc_neg_gosi_rec_dff', - 'dgc_neg_gdsi_rec_dff', - 'dgc_neg_peak_dire_raw_dff', - 'dgc_neg_vs_dire_raw_dff', - 'dgc_neg_vs_dire_ele_dff', - 'dgc_neg_vs_dire_rec_dff', - - # direction / orientation tuning, pos, zscore - 'dgc_pos_osi_raw_z', - 'dgc_pos_dsi_raw_z', - 'dgc_pos_gosi_raw_z', - 'dgc_pos_gdsi_raw_z', - 'dgc_pos_osi_ele_z', - 'dgc_pos_dsi_ele_z', - 'dgc_pos_gosi_ele_z', - 'dgc_pos_gdsi_ele_z', - 'dgc_pos_osi_rec_z', - 'dgc_pos_dsi_rec_z', - 'dgc_pos_gosi_rec_z', - 'dgc_pos_gdsi_rec_z', - 'dgc_pos_peak_dire_raw_z', - 'dgc_pos_vs_dire_raw_z', - 'dgc_pos_vs_dire_ele_z', - 'dgc_pos_vs_dire_rec_z', - - # direction / orientation tuning, neg, zscore - 'dgc_neg_osi_raw_z', - 'dgc_neg_dsi_raw_z', - 'dgc_neg_gosi_raw_z', - 'dgc_neg_gdsi_raw_z', - 'dgc_neg_osi_ele_z', - 'dgc_neg_dsi_ele_z', - 'dgc_neg_gosi_ele_z', - 'dgc_neg_gdsi_ele_z', - 'dgc_neg_osi_rec_z', - 'dgc_neg_dsi_rec_z', - 'dgc_neg_gosi_rec_z', - 'dgc_neg_gdsi_rec_z', - 'dgc_neg_peak_dire_raw_z', - 'dgc_neg_vs_dire_raw_z', - 'dgc_neg_vs_dire_ele_z', - 'dgc_neg_vs_dire_rec_z', - - # sf tuning, pos, df - 'dgc_pos_peak_sf_raw_df', - 'dgc_pos_weighted_sf_raw_df', - 'dgc_pos_weighted_sf_log_raw_df', - 'dgc_pos_weighted_sf_ele_df', - 'dgc_pos_weighted_sf_log_ele_df', - 'dgc_pos_weighted_sf_rec_df', - 'dgc_pos_weighted_sf_log_rec_df', - - # sf tuning, neg, df - 'dgc_neg_peak_sf_raw_df', - 'dgc_neg_weighted_sf_raw_df', - 'dgc_neg_weighted_sf_log_raw_df', - 'dgc_neg_weighted_sf_ele_df', - 'dgc_neg_weighted_sf_log_ele_df', - 'dgc_neg_weighted_sf_rec_df', - 'dgc_neg_weighted_sf_log_rec_df', - - # sf tuning, pos, dff - 'dgc_pos_peak_sf_raw_dff', - 'dgc_pos_weighted_sf_raw_dff', - 'dgc_pos_weighted_sf_log_raw_dff', - 'dgc_pos_weighted_sf_ele_dff', - 'dgc_pos_weighted_sf_log_ele_dff', - 'dgc_pos_weighted_sf_rec_dff', - 'dgc_pos_weighted_sf_log_rec_dff', - - # sf tuning, neg, dff - 'dgc_neg_peak_sf_raw_dff', - 'dgc_neg_weighted_sf_raw_dff', - 'dgc_neg_weighted_sf_log_raw_dff', - 'dgc_neg_weighted_sf_ele_dff', - 'dgc_neg_weighted_sf_log_ele_dff', - 'dgc_neg_weighted_sf_rec_dff', - 'dgc_neg_weighted_sf_log_rec_dff', - - # sf tuning, pos, zscore - 'dgc_pos_peak_sf_raw_z', - 'dgc_pos_weighted_sf_raw_z', - 'dgc_pos_weighted_sf_log_raw_z', - 'dgc_pos_weighted_sf_ele_z', - 'dgc_pos_weighted_sf_log_ele_z', - 'dgc_pos_weighted_sf_rec_z', - 'dgc_pos_weighted_sf_log_rec_z', - - # sf tuning, neg, zscore - 'dgc_neg_peak_sf_raw_z', - 'dgc_neg_weighted_sf_raw_z', - 'dgc_neg_weighted_sf_log_raw_z', - 'dgc_neg_weighted_sf_ele_z', - 'dgc_neg_weighted_sf_log_ele_z', - 'dgc_neg_weighted_sf_rec_z', - 'dgc_neg_weighted_sf_log_rec_z', - - # tf tuning, pos, df - 'dgc_pos_peak_tf_raw_df', - 'dgc_pos_weighted_tf_raw_df', - 'dgc_pos_weighted_tf_log_raw_df', - 'dgc_pos_weighted_tf_ele_df', - 'dgc_pos_weighted_tf_log_ele_df', - 'dgc_pos_weighted_tf_rec_df', - 'dgc_pos_weighted_tf_log_rec_df', - - # tf tuning, neg, df - 'dgc_neg_peak_tf_raw_df', - 'dgc_neg_weighted_tf_raw_df', - 'dgc_neg_weighted_tf_log_raw_df', - 'dgc_neg_weighted_tf_ele_df', - 'dgc_neg_weighted_tf_log_ele_df', - 'dgc_neg_weighted_tf_rec_df', - 'dgc_neg_weighted_tf_log_rec_df', - - # tf tuning, pos, dff - 'dgc_pos_peak_tf_raw_dff', - 'dgc_pos_weighted_tf_raw_dff', - 'dgc_pos_weighted_tf_log_raw_dff', - 'dgc_pos_weighted_tf_ele_dff', - 'dgc_pos_weighted_tf_log_ele_dff', - 'dgc_pos_weighted_tf_rec_dff', - 'dgc_pos_weighted_tf_log_rec_dff', - - # tf tuning, neg, dff - 'dgc_neg_peak_tf_raw_dff', - 'dgc_neg_weighted_tf_raw_dff', - 'dgc_neg_weighted_tf_log_raw_dff', - 'dgc_neg_weighted_tf_ele_dff', - 'dgc_neg_weighted_tf_log_ele_dff', - 'dgc_neg_weighted_tf_rec_dff', - 'dgc_neg_weighted_tf_log_rec_dff', - - # tf tuning, pos, zscore - 'dgc_pos_peak_tf_raw_z', - 'dgc_pos_weighted_tf_raw_z', - 'dgc_pos_weighted_tf_log_raw_z', - 'dgc_pos_weighted_tf_ele_z', - 'dgc_pos_weighted_tf_log_ele_z', - 'dgc_pos_weighted_tf_rec_z', - 'dgc_pos_weighted_tf_log_rec_z', - - # tf tuning, neg, zscore - 'dgc_neg_peak_tf_raw_z', - 'dgc_neg_weighted_tf_raw_z', - 'dgc_neg_weighted_tf_log_raw_z', - 'dgc_neg_weighted_tf_ele_z', - 'dgc_neg_weighted_tf_log_ele_z', - 'dgc_neg_weighted_tf_rec_z', - 'dgc_neg_weighted_tf_log_rec_z', -] - -def process_one_nwb_for_multi_thread(inputs): - - nwb_path, params, columns, save_folder, t0 = inputs - nwb_fn = os.path.splitext(os.path.split(nwb_path)[1])[0] - save_path = os.path.join(save_folder, nwb_fn + '.xlsx') - - if os.path.isfile(save_path): - print('\tt: {:5.0f} minutes, {} , excel table already exists, skip.'.format((time.time() - t0) / 60., nwb_fn)) - return None - else: - nwb_f = h5py.File(nwb_path, 'r') - - plane_ns = [k for k in nwb_f['processing'].keys() if k[0:16] == 'rois_and_traces_'] - plane_ns = [k[16:] for k in plane_ns] - plane_ns.sort() - # print('total plane number: {}'.format(len(plane_ns))) - - total_roi_num = 10000 - big_array = np.zeros((total_roi_num, len(columns)), dtype=np.float64) - big_array[:] = np.nan - df = pd.DataFrame(data=big_array, columns=columns) - - curr_row_ind = 0 - for plane_n in plane_ns: - print('\tt: {:5.0f} minutes, {} / {}, processing ...'.format((time.time() - t0) / 60., nwb_fn, plane_n)) - roi_ns = nwb_f['processing/rois_and_traces_{}/ImageSegmentation/imaging_plane/roi_list'.format(plane_n)].value - roi_ns = [r.encode('utf-8') for r in roi_ns if r[0:4] == 'roi_'] - roi_ns.sort() - - for roi_i, roi_n in enumerate(roi_ns): - # print('\t\t\troi: {} / {}'.format(roi_i+1, len(roi_ns))) - roi_properties, _, _, _, _, _, _, _, _, _, _, _, _, _ = \ - dt.get_everything_from_roi(nwb_f=nwb_f, plane_n=plane_n, roi_n=roi_n, params=params) - for rp_name, rp_value in roi_properties.items(): - df.loc[curr_row_ind, rp_name] = rp_value - - curr_row_ind += 1 - - df = df[0:curr_row_ind] - - save_path = os.path.join(save_folder, nwb_fn + '.xlsx') - if os.path.isfile(save_path): - os.remove(save_path) - - with pd.ExcelWriter(save_path, mode='w') as writer: - df.to_excel(writer, sheet_name='sheet1') - - -def run(): - - t0 = time.time() - - nwb_fns = [] - for fn in os.listdir(database_folder): - if fn[-4:] == '.nwb' and date_range[0] <= int(fn[0:6]) <= date_range[1]: - nwb_fns.append(fn) - nwb_fns.sort() - print('\nnwb file list:') - print('\n'.join(nwb_fns)) - - inputs_lst = [(os.path.join(curr_folder, database_folder, nwb_fn), - params, - columns, - os.path.join(curr_folder, save_folder), - t0) for nwb_fn in nwb_fns] - - print('\nprocessing individual nwb files ...') - p = Pool(process_num) - p.map(process_one_nwb_for_multi_thread, inputs_lst) - # process_one_nwb_for_multi_thread(inputs_lst[0]) - - print('\nConcatenating indiviudal dataframes ...') - xlsx_fns = [f for f in os.listdir(os.path.join(curr_folder,save_folder)) if f[-5:] == '.xlsx'] - xlsx_fns.sort() - - dfs = [] - for xlsx_fn in xlsx_fns: - curr_df = pd.read_excel(os.path.join(curr_folder, save_folder, xlsx_fn), sheetname='sheet1') - # print(curr_df) - dfs.append(curr_df) - - big_df = pd.concat(dfs, ignore_index=True) - - print('\nsaving ...') - date_str = datetime.datetime.now().strftime('%y%m%d%H%M%S') - save_path = os.path.join(curr_folder, 'big_roi_table_{}.xlsx'.format(date_str)) - - if os.path.isfile(save_path): - with pd.ExcelWriter(save_path, mode='a') as writer: - big_df.to_excel(writer, sheet_name=params['trace_type']) - else: - with pd.ExcelWriter(save_path, mode='w') as writer: - big_df.to_excel(writer, sheet_name=params['trace_type']) - - print('\ndone!') - - -if __name__ == "__main__": - run() diff --git a/corticalmapping/scripts/post_recording/00_old/analysis_database/0041_get_plane_dfs.py b/corticalmapping/scripts/post_recording/00_old/analysis_database/0041_get_plane_dfs.py deleted file mode 100644 index 9c4746c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/analysis_database/0041_get_plane_dfs.py +++ /dev/null @@ -1,427 +0,0 @@ -import sys -sys.path.extend(['/home/junz/PycharmProjects/corticalmapping']) -import os -import time -import pandas as pd -import numpy as np -import h5py -import datetime -import corticalmapping.DatabaseTools as dt -from multiprocessing import Pool -from shutil import copyfile - -date_range = [180201, 190610] -database_folder = 'nwbs' -save_folder_n = "dataframes" -process_num = 8 -is_overwrite = False - -params = dt.ANALYSIS_PARAMS -params['trace_type'] = 'f_center_subtracted' -params['is_collapse_dire'] = False -params['is_collapse_sf'] = True -params['is_collapse_tf'] = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -print('pandas version: {}\n'.format(pd.__version__)) - -columns = [ - 'date', - 'mouse_id', - 'plane_n', - 'roi_n', - 'depth', # microns under pia, float - - # roi mask - 'roi_area', # square micron - 'roi_center_row', # center of roi mask in field of view, row - 'roi_center_col', # center of roi mask in field of view, column - - # trace skewness - 'skew_raw', # skewness of unfiltered trace (neuropil subtracted), float - 'skew_fil', # skewness of highpassed trace, float - - # receptive fields - 'rf_pos_on_peak_z', - 'rf_pos_on_area', - 'rf_pos_on_center_alt', - 'rf_pos_on_center_azi', - - 'rf_pos_off_peak_z', - 'rf_pos_off_area', - 'rf_pos_off_center_alt', - 'rf_pos_off_center_azi', - - 'rf_pos_onoff_peak_z', - 'rf_pos_onoff_area', - 'rf_pos_onoff_center_alt', - 'rf_pos_onoff_center_azi', - - 'rf_pos_lsi', - - 'rf_neg_on_peak_z', - 'rf_neg_on_area', - 'rf_neg_on_center_alt', - 'rf_neg_on_center_azi', - - 'rf_neg_off_peak_z', - 'rf_neg_off_area', - 'rf_neg_off_center_alt', - 'rf_neg_off_center_azi', - - 'rf_neg_onoff_peak_z', - 'rf_neg_onoff_area', - 'rf_neg_onoff_center_alt', - 'rf_neg_onoff_center_azi', - - 'rf_neg_lsi', - - # drifting grating peak response - 'dgc_pos_peak_df', - 'dgc_neg_peak_df', - 'dgc_pos_p_ttest_df', - 'dgc_neg_p_ttest_df', - 'dgc_p_anova_df', - - 'dgc_pos_peak_dff', - 'dgc_neg_peak_dff', - 'dgc_pos_p_ttest_dff', - 'dgc_neg_p_ttest_dff', - 'dgc_p_anova_dff', - - 'dgc_pos_peak_z', - 'dgc_neg_peak_z', - 'dgc_pos_p_ttest_z', - 'dgc_neg_p_ttest_z', - 'dgc_p_anova_z', - - # direction / orientation tuning, pos, df - 'dgc_pos_osi_raw_df', - 'dgc_pos_dsi_raw_df', - 'dgc_pos_gosi_raw_df', - 'dgc_pos_gdsi_raw_df', - 'dgc_pos_osi_ele_df', - 'dgc_pos_dsi_ele_df', - 'dgc_pos_gosi_ele_df', - 'dgc_pos_gdsi_ele_df', - 'dgc_pos_osi_rec_df', - 'dgc_pos_dsi_rec_df', - 'dgc_pos_gosi_rec_df', - 'dgc_pos_gdsi_rec_df', - 'dgc_pos_peak_dire_raw_df', - 'dgc_pos_vs_dire_raw_df', - 'dgc_pos_vs_dire_ele_df', - 'dgc_pos_vs_dire_rec_df', - - # direction / orientation tuning, neg, df - 'dgc_neg_osi_raw_df', - 'dgc_neg_dsi_raw_df', - 'dgc_neg_gosi_raw_df', - 'dgc_neg_gdsi_raw_df', - 'dgc_neg_osi_ele_df', - 'dgc_neg_dsi_ele_df', - 'dgc_neg_gosi_ele_df', - 'dgc_neg_gdsi_ele_df', - 'dgc_neg_osi_rec_df', - 'dgc_neg_dsi_rec_df', - 'dgc_neg_gosi_rec_df', - 'dgc_neg_gdsi_rec_df', - 'dgc_neg_peak_dire_raw_df', - 'dgc_neg_vs_dire_raw_df', - 'dgc_neg_vs_dire_ele_df', - 'dgc_neg_vs_dire_rec_df', - - # direction / orientation tuning, pos, dff - 'dgc_pos_osi_raw_dff', - 'dgc_pos_dsi_raw_dff', - 'dgc_pos_gosi_raw_dff', - 'dgc_pos_gdsi_raw_dff', - 'dgc_pos_osi_ele_dff', - 'dgc_pos_dsi_ele_dff', - 'dgc_pos_gosi_ele_dff', - 'dgc_pos_gdsi_ele_dff', - 'dgc_pos_osi_rec_dff', - 'dgc_pos_dsi_rec_dff', - 'dgc_pos_gosi_rec_dff', - 'dgc_pos_gdsi_rec_dff', - 'dgc_pos_peak_dire_raw_dff', - 'dgc_pos_vs_dire_raw_dff', - 'dgc_pos_vs_dire_ele_dff', - 'dgc_pos_vs_dire_rec_dff', - - # direction / orientation tuning, neg, dff - 'dgc_neg_osi_raw_dff', - 'dgc_neg_dsi_raw_dff', - 'dgc_neg_gosi_raw_dff', - 'dgc_neg_gdsi_raw_dff', - 'dgc_neg_osi_ele_dff', - 'dgc_neg_dsi_ele_dff', - 'dgc_neg_gosi_ele_dff', - 'dgc_neg_gdsi_ele_dff', - 'dgc_neg_osi_rec_dff', - 'dgc_neg_dsi_rec_dff', - 'dgc_neg_gosi_rec_dff', - 'dgc_neg_gdsi_rec_dff', - 'dgc_neg_peak_dire_raw_dff', - 'dgc_neg_vs_dire_raw_dff', - 'dgc_neg_vs_dire_ele_dff', - 'dgc_neg_vs_dire_rec_dff', - - # direction / orientation tuning, pos, zscore - 'dgc_pos_osi_raw_z', - 'dgc_pos_dsi_raw_z', - 'dgc_pos_gosi_raw_z', - 'dgc_pos_gdsi_raw_z', - 'dgc_pos_osi_ele_z', - 'dgc_pos_dsi_ele_z', - 'dgc_pos_gosi_ele_z', - 'dgc_pos_gdsi_ele_z', - 'dgc_pos_osi_rec_z', - 'dgc_pos_dsi_rec_z', - 'dgc_pos_gosi_rec_z', - 'dgc_pos_gdsi_rec_z', - 'dgc_pos_peak_dire_raw_z', - 'dgc_pos_vs_dire_raw_z', - 'dgc_pos_vs_dire_ele_z', - 'dgc_pos_vs_dire_rec_z', - - # direction / orientation tuning, neg, zscore - 'dgc_neg_osi_raw_z', - 'dgc_neg_dsi_raw_z', - 'dgc_neg_gosi_raw_z', - 'dgc_neg_gdsi_raw_z', - 'dgc_neg_osi_ele_z', - 'dgc_neg_dsi_ele_z', - 'dgc_neg_gosi_ele_z', - 'dgc_neg_gdsi_ele_z', - 'dgc_neg_osi_rec_z', - 'dgc_neg_dsi_rec_z', - 'dgc_neg_gosi_rec_z', - 'dgc_neg_gdsi_rec_z', - 'dgc_neg_peak_dire_raw_z', - 'dgc_neg_vs_dire_raw_z', - 'dgc_neg_vs_dire_ele_z', - 'dgc_neg_vs_dire_rec_z', - - # sf tuning, pos, df - 'dgc_pos_peak_sf_raw_df', - 'dgc_pos_weighted_sf_raw_df', - 'dgc_pos_weighted_sf_log_raw_df', - 'dgc_pos_weighted_sf_ele_df', - 'dgc_pos_weighted_sf_log_ele_df', - 'dgc_pos_weighted_sf_rec_df', - 'dgc_pos_weighted_sf_log_rec_df', - - # sf tuning, neg, df - 'dgc_neg_peak_sf_raw_df', - 'dgc_neg_weighted_sf_raw_df', - 'dgc_neg_weighted_sf_log_raw_df', - 'dgc_neg_weighted_sf_ele_df', - 'dgc_neg_weighted_sf_log_ele_df', - 'dgc_neg_weighted_sf_rec_df', - 'dgc_neg_weighted_sf_log_rec_df', - - # sf tuning, pos, dff - 'dgc_pos_peak_sf_raw_dff', - 'dgc_pos_weighted_sf_raw_dff', - 'dgc_pos_weighted_sf_log_raw_dff', - 'dgc_pos_weighted_sf_ele_dff', - 'dgc_pos_weighted_sf_log_ele_dff', - 'dgc_pos_weighted_sf_rec_dff', - 'dgc_pos_weighted_sf_log_rec_dff', - - # sf tuning, neg, dff - 'dgc_neg_peak_sf_raw_dff', - 'dgc_neg_weighted_sf_raw_dff', - 'dgc_neg_weighted_sf_log_raw_dff', - 'dgc_neg_weighted_sf_ele_dff', - 'dgc_neg_weighted_sf_log_ele_dff', - 'dgc_neg_weighted_sf_rec_dff', - 'dgc_neg_weighted_sf_log_rec_dff', - - # sf tuning, pos, zscore - 'dgc_pos_peak_sf_raw_z', - 'dgc_pos_weighted_sf_raw_z', - 'dgc_pos_weighted_sf_log_raw_z', - 'dgc_pos_weighted_sf_ele_z', - 'dgc_pos_weighted_sf_log_ele_z', - 'dgc_pos_weighted_sf_rec_z', - 'dgc_pos_weighted_sf_log_rec_z', - - # sf tuning, neg, zscore - 'dgc_neg_peak_sf_raw_z', - 'dgc_neg_weighted_sf_raw_z', - 'dgc_neg_weighted_sf_log_raw_z', - 'dgc_neg_weighted_sf_ele_z', - 'dgc_neg_weighted_sf_log_ele_z', - 'dgc_neg_weighted_sf_rec_z', - 'dgc_neg_weighted_sf_log_rec_z', - - # tf tuning, pos, df - 'dgc_pos_peak_tf_raw_df', - 'dgc_pos_weighted_tf_raw_df', - 'dgc_pos_weighted_tf_log_raw_df', - 'dgc_pos_weighted_tf_ele_df', - 'dgc_pos_weighted_tf_log_ele_df', - 'dgc_pos_weighted_tf_rec_df', - 'dgc_pos_weighted_tf_log_rec_df', - - # tf tuning, neg, df - 'dgc_neg_peak_tf_raw_df', - 'dgc_neg_weighted_tf_raw_df', - 'dgc_neg_weighted_tf_log_raw_df', - 'dgc_neg_weighted_tf_ele_df', - 'dgc_neg_weighted_tf_log_ele_df', - 'dgc_neg_weighted_tf_rec_df', - 'dgc_neg_weighted_tf_log_rec_df', - - # tf tuning, pos, dff - 'dgc_pos_peak_tf_raw_dff', - 'dgc_pos_weighted_tf_raw_dff', - 'dgc_pos_weighted_tf_log_raw_dff', - 'dgc_pos_weighted_tf_ele_dff', - 'dgc_pos_weighted_tf_log_ele_dff', - 'dgc_pos_weighted_tf_rec_dff', - 'dgc_pos_weighted_tf_log_rec_dff', - - # tf tuning, neg, dff - 'dgc_neg_peak_tf_raw_dff', - 'dgc_neg_weighted_tf_raw_dff', - 'dgc_neg_weighted_tf_log_raw_dff', - 'dgc_neg_weighted_tf_ele_dff', - 'dgc_neg_weighted_tf_log_ele_dff', - 'dgc_neg_weighted_tf_rec_dff', - 'dgc_neg_weighted_tf_log_rec_dff', - - # tf tuning, pos, zscore - 'dgc_pos_peak_tf_raw_z', - 'dgc_pos_weighted_tf_raw_z', - 'dgc_pos_weighted_tf_log_raw_z', - 'dgc_pos_weighted_tf_ele_z', - 'dgc_pos_weighted_tf_log_ele_z', - 'dgc_pos_weighted_tf_rec_z', - 'dgc_pos_weighted_tf_log_rec_z', - - # tf tuning, neg, zscore - 'dgc_neg_peak_tf_raw_z', - 'dgc_neg_weighted_tf_raw_z', - 'dgc_neg_weighted_tf_log_raw_z', - 'dgc_neg_weighted_tf_ele_z', - 'dgc_neg_weighted_tf_log_ele_z', - 'dgc_neg_weighted_tf_rec_z', - 'dgc_neg_weighted_tf_log_rec_z', -] - -def process_one_nwb_for_multi_thread(inputs): - - nwb_path, params, columns, save_folder, t0, nwb_i, nwb_f_num, is_overwrite = inputs - nwb_fn = os.path.splitext(os.path.split(nwb_path)[1])[0] - - nwb_f = h5py.File(nwb_path, 'r') - - plane_ns = [k for k in nwb_f['processing'].keys() if k[0:16] == 'rois_and_traces_'] - plane_ns = [k[16:] for k in plane_ns] - plane_ns.sort() - # print('total plane number: {}'.format(len(plane_ns))) - - for plane_n in plane_ns: - print('\tt: {:5.0f} minutes, processing {}, {} / {}, {} ...'.format((time.time() - t0) / 60., - nwb_fn, - nwb_i + 1, - nwb_f_num, - plane_n)) - - save_fn = '_'.join(nwb_fn.split('_')[0:2]) + '_' + plane_n + '.xlsx' - save_path = os.path.join(save_folder, save_fn) - if os.path.isfile(save_path): - - if is_overwrite: # overwrite existing xlsx files - print('\t{}, file already exists. Overwirite.'.format(os.path.split(save_path)[1])) - os.remove(save_path) - - - else: # do not overwrite existing xlsx files - print('\t{}, file already exists. Skip.'.format(os.path.split(save_path)[1])) - return - - roi_ns = nwb_f['processing/rois_and_traces_{}/ImageSegmentation/imaging_plane/roi_list'.format(plane_n)].value - roi_ns = [r.encode('utf-8') for r in roi_ns if r[0:4] == 'roi_'] - roi_ns.sort() - - df = pd.DataFrame(np.nan, index=range(len(roi_ns)), columns=columns) - - for roi_i, roi_n in enumerate(roi_ns): - # print('\t\t\troi: {} / {}'.format(roi_i+1, len(roi_ns))) - roi_properties, _, _, _, _, _, _, _, _, _, _, _, _, _ = \ - dt.get_everything_from_roi(nwb_f=nwb_f, plane_n=plane_n, roi_n=roi_n, params=params) - for rp_name, rp_value in roi_properties.items(): - df.loc[roi_i, rp_name] = rp_value - - with pd.ExcelWriter(save_path, mode='w') as writer: - df.to_excel(writer, sheet_name='sheet1') - - -def run(): - - t0 = time.time() - - nwb_fns = [] - for fn in os.listdir(database_folder): - if fn[-4:] == '.nwb' and date_range[0] <= int(fn[0:6]) <= date_range[1]: - nwb_fns.append(fn) - nwb_fns.sort() - print('\nnwb file list:') - print('\n'.join(nwb_fns)) - - date_str = datetime.datetime.now().strftime('%y%m%d%H%M%S') - save_folder = os.path.join(curr_folder, '{}_{}'.format(save_folder_n, date_str)) - - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - copyfile(os.path.realpath(__file__), os.path.join(save_folder, 'script_log.py')) - - inputs_lst = [(os.path.join(curr_folder, database_folder, nwb_fn), - params, - columns, - save_folder, - t0, - nwb_i, - len(nwb_fns), - is_overwrite) for nwb_i, nwb_fn in enumerate(nwb_fns)] - - print('\nprocessing individual nwb files ...') - p = Pool(process_num) - p.map(process_one_nwb_for_multi_thread, inputs_lst) - # process_one_nwb_for_multi_thread(inputs_lst[0]) - - # print('\nConcatenating indiviudal dataframes ...') - # xlsx_fns = [f for f in os.listdir(os.path.join(curr_folder,save_folder)) if f[-5:] == '.xlsx'] - # xlsx_fns.sort() - # - # dfs = [] - # for xlsx_fn in xlsx_fns: - # curr_df = pd.read_excel(os.path.join(curr_folder, save_folder, xlsx_fn), sheetname='sheet1') - # # print(curr_df) - # dfs.append(curr_df) - # - # big_df = pd.concat(dfs, ignore_index=True) - # - # print('\nsaving ...') - # date_str = datetime.datetime.now().strftime('%y%m%d%H%M%S') - # save_path = os.path.join(curr_folder, 'big_roi_table_{}.xlsx'.format(date_str)) - # - # if os.path.isfile(save_path): - # with pd.ExcelWriter(save_path, mode='a') as writer: - # big_df.to_excel(writer, sheet_name=params['trace_type']) - # else: - # with pd.ExcelWriter(save_path, mode='w') as writer: - # big_df.to_excel(writer, sheet_name=params['trace_type']) - - print('\ndone!') - - -if __name__ == "__main__": - run() diff --git a/corticalmapping/scripts/post_recording/00_old/analysis_database/0042_get_plane_meta.py b/corticalmapping/scripts/post_recording/00_old/analysis_database/0042_get_plane_meta.py deleted file mode 100644 index e8cb038..0000000 --- a/corticalmapping/scripts/post_recording/00_old/analysis_database/0042_get_plane_meta.py +++ /dev/null @@ -1,32 +0,0 @@ -import os -import datetime -import pandas as pd - -df_folder = 'dataframes_190523104427' -save_fn = 'plane_table' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -fns = [fn for fn in os.listdir(df_folder) if fn[-5:] == '.xlsx'] -print('\n'.join(fns)) - -df = pd.DataFrame(index=range(len(fns)), columns=['date', 'mouse_id', 'plane_n', 'volume_n', - 'depth', 'has_lsn', 'has_dgc']) - -for fn_i, fn in enumerate(fns): - - date = fn.split('_')[0] - mouse_id = fn.split('_')[1] - plane_n = fn.split('_')[-1][0:-5] - - df.loc[fn_i] = [date, mouse_id, plane_n, '', 0, True, True] - -df.sort_values(by=['mouse_id', 'date', 'plane_n'], inplace=True) -df.reset_index(inplace=True, drop=True) - -print(df) - -date_str = datetime.datetime.now().strftime('%y%m%d%H%M%S') -with pd.ExcelWriter('{}_{}.xlsx'.format(save_fn, date_str), mode='w') as writer: - df.to_excel(writer, sheet_name='sheet1') \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/analysis_database/0050_generate_page_report.py b/corticalmapping/scripts/post_recording/00_old/analysis_database/0050_generate_page_report.py deleted file mode 100644 index ed9423e..0000000 --- a/corticalmapping/scripts/post_recording/00_old/analysis_database/0050_generate_page_report.py +++ /dev/null @@ -1,50 +0,0 @@ -import os -import corticalmapping.DatabaseTools as dt -import pandas as pd -import matplotlib.pyplot as plt -from matplotlib.backends.backend_pdf import PdfPages -import h5py -import datetime - -area_lim = 100. -nwb_folder = 'nwbs' - -summary_fn = 'big_roi_table_190404145421.xlsx' -sheet_n = 'f_center_raw' - -params = dt.ANALYSIS_PARAMS -params['trace_type'] = sheet_n -params['response_window_dgc'] = [0., 1.5] - -plot_params = dt.PLOTTING_PARAMS -plot_params['response_type_for_plot'] = 'zscore' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -db_df = pd.read_excel(summary_fn, sheet_name=sheet_n) -print(db_df.head()) - -pdf_fn = 'page_report_{}.pdf'.format(datetime.datetime.now().strftime('%y%m%d%H%M%S')) -pdff = PdfPages(pdf_fn) - -for row_i, row in db_df.iterrows(): - # if row['roi_area'] >= area_lim and row['skew_fil'] >= 0.5: - if row['roi_area'] >= area_lim: - - print('{}_{}; {}; {}'.format(row['date'], row['mouse_id'], row['plane_n'], row['roi_n'])) - - nwb_fn = '{}_{}_110.nwb'.format(row['date'], row['mouse_id']) - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') - - f = dt.roi_page_report(nwb_f=nwb_f, - plane_n=row['plane_n'], - roi_n=row['roi_n'], - params=params, - plot_params=plot_params) - nwb_f.close() - pdff.savefig(f) - f.clear() - plt.close() - -pdff.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/analysis_database/0060_single_page_report.py b/corticalmapping/scripts/post_recording/00_old/analysis_database/0060_single_page_report.py deleted file mode 100644 index 07db7e8..0000000 --- a/corticalmapping/scripts/post_recording/00_old/analysis_database/0060_single_page_report.py +++ /dev/null @@ -1,30 +0,0 @@ -import os -import h5py -import corticalmapping.DatabaseTools as dt -import matplotlib.pyplot as plt - -nwb_folder = 'nwbs' -nwb_fn = "190326_M441626_110.nwb" - -plane_n = 'plane2' -roi_n = 'roi_0001' - -analysis_params = dt.ANALYSIS_PARAMS -plot_params = dt.PLOTTING_PARAMS - -analysis_params['trace_type'] = 'f_center_raw' -analysis_params['response_window_dgc'] = [0.5, 1.5] -analysis_params['baseline_window_dgc'] = [-0.5, 0.5] -plot_params['sftf_vmax'] = 6 -plot_params['sftf_vmin'] = -6 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') -f = dt.roi_page_report(nwb_f=nwb_f, plane_n=plane_n, roi_n=roi_n, params=analysis_params, plot_params=plot_params) -nwb_f.close() - -plt.show() - -f.savefig('{}_{}_{}.pdf'.format(os.path.splitext(nwb_fn)[0], plane_n, roi_n)) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/analysis_database/0070_get_rf_maps.py b/corticalmapping/scripts/post_recording/00_old/analysis_database/0070_get_rf_maps.py deleted file mode 100644 index 0c992cd..0000000 --- a/corticalmapping/scripts/post_recording/00_old/analysis_database/0070_get_rf_maps.py +++ /dev/null @@ -1,194 +0,0 @@ -import sys -sys.path.extend(['/home/junz/PycharmProjects/corticalmapping']) -import os -import numpy as np -import h5py -# import time -import pandas as pd -import corticalmapping.DatabaseTools as dt -import corticalmapping.core.ImageAnalysis as ia - -table_name = 'big_roi_table_190423135026.xlsx' -sheet_name = 'f_center_subtracted' -nwb_folder = 'nwbs' -save_folder = "temp_xlsx" - -response_dir = 'pos' -skew_thr = 0.6 -trace_bias = 1. -rf_peak_z_thr = 1.3 -gaussian_filter_sigma = 1. # float, in pixels, filtering sigma for z-score receptive fields -interpolate_rate = 10. # float, interpolate rate of filtered z-score maps -response_window = [0., 0.5] - -rf_map_fn = 'rf_maps.hdf5' -notes = ''' - zscore receptive field maps of all significant rois. Spatial temporal receptive fields - are first converted to df/f. Then 2-d zscore maps are generated. Then the zscore maps are - 2d filtered to smooth and interpolated in to high resolution. After preprocessing, if the - peak value of zscore is larger than the threshold, the receptive field will be considered - as sigificant. - ''' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -rf_map_f = h5py.File(os.path.join(curr_folder, rf_map_fn)) -if 'notes' not in rf_map_f.keys(): - rf_map_f['notes'] = notes - -table_path = os.path.join(curr_folder, 'intermediate_results', table_name) -df = pd.read_excel(table_path, sheetname=sheet_name) -subdf = df[np.logical_not(df['rf_pos_on_peak_z'].isnull())] -subdf = subdf[subdf['skew_fil'] >= skew_thr] - -df_nwb = subdf[['date', 'mouse_id']].drop_duplicates() -nwb_fns_total = ['{}_{}_110_repacked'.format(r['date'], r['mouse_id']) for r_i, r in df_nwb.iterrows()] -print('all nwb files:') -print('\n'.join(nwb_fns_total)) - -# nwb_fns_saved = [gn[0:14] + '_110_repacked' for gn in rf_map_f.keys() if len(gn) >= 14] -# print('\nsaved nwb files:') -# print('\n'.join(nwb_fns_saved)) -# -# nwb_fns_todo = [] -# for nwb_fn in nwb_fns_total: -# if nwb_fn not in nwb_fns_saved: -# nwb_fns_todo.append(nwb_fn) -# print('\ntodo nwb files:') -# print('\n'.join(nwb_fns_todo)) - -for nwb_fn in nwb_fns_total: - print('\nprocessing {} ...'.format(nwb_fn)) - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn + '.nwb')) - localdf = subdf[subdf['date'] == int(nwb_fn[0:6])] - localdf = localdf[localdf['mouse_id'] == nwb_fn[7:14]] - - # ====================================ON============================================================= - localdfon = localdf[localdf['rf_{}_on_peak_z'.format(response_dir)] >= rf_peak_z_thr].reset_index() - print('\tnumber of rois with significant ON RFs: {}'.format(len(localdfon))) - - for roi_i, roi_row in localdfon.iterrows(): - - # get the saving group - group_n = '{}_{}_{}_ON'.format(roi_row['date'], roi_row['mouse_id'], roi_row['plane_n']) - if group_n in rf_map_f.keys(): - local_grp = rf_map_f[group_n] - else: - local_grp = rf_map_f.create_group(name=group_n) - local_grp.attrs['trace_type'] = sheet_name - local_grp.attrs['response_dir'] = response_dir - local_grp.attrs['skew_thr'] = 0.6 - local_grp.attrs['trace_bias'] = trace_bias - local_grp.attrs['rf_peak_z_thr'] = rf_peak_z_thr - local_grp.attrs['gaussian_filter_sigma'] = gaussian_filter_sigma - local_grp.attrs['interpolation_rate'] = interpolate_rate - local_grp.attrs['response_window'] = response_window - - print('\t\tprocessing roi {}/{} ...'.format(roi_i + 1, len(localdfon))) - - if roi_row['roi_n'] not in local_grp.keys(): - - # get constant to add to trace - trace, _ = dt.get_single_trace(nwb_f=nwb_f, - plane_n=roi_row['plane_n'], - roi_n=roi_row['roi_n'], - trace_type=sheet_name) - if np.min(trace) < trace_bias: - add_to_trace = -np.min(trace) + trace_bias - else: - add_to_trace = 0. - - # get strf - curr_strf = dt.get_strf(nwb_f=nwb_f, - plane_n=roi_row['plane_n'], - roi_ind=int(roi_row['roi_n'][-4:]), - trace_type='sta_'+sheet_name) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, - add_to_trace=add_to_trace) - curr_srf, _ = curr_strf_dff.get_zscore_receptive_field(timeWindow=response_window) - if response_dir == 'pos': - polarity = 'positive' - elif response_dir == 'neg': - polarity = 'negative' - else: - raise ValueError('polarity ({}) should be either "pos" or "neg".') - _, _, _, rf_mask = dt.get_rf_properties(srf=curr_srf, - polarity=polarity, - sigma=gaussian_filter_sigma, - interpolate_rate=interpolate_rate, - z_thr=rf_peak_z_thr) - roi_grp = local_grp.create_group(name=roi_row['roi_n']) - ia.WeightedROI(mask=rf_mask).to_h5_group(roi_grp) - if 'alts_deg' not in local_grp.attrs: - local_grp.attrs['alts_deg'] = curr_srf.altPos - if 'azis_deg' not in local_grp.attrs: - local_grp.attrs['alts_deg'] = curr_srf.aziPos - else: - print('\t\t\tAlready exists. Skip.') - - - # ====================================OFF======================================================== - localdfoff = localdf[localdf['rf_{}_off_peak_z'.format(response_dir)] >= rf_peak_z_thr].reset_index() - print('\n\tnumber of rois with significant OFF RFs: {}'.format(len(localdfoff))) - - for roi_i, roi_row in localdfoff.iterrows(): - - # get the saving group - group_n = '{}_{}_{}_OFF'.format(roi_row['date'], roi_row['mouse_id'], roi_row['plane_n']) - if group_n in rf_map_f.keys(): - local_grp = rf_map_f[group_n] - else: - local_grp = rf_map_f.create_group(name=group_n) - local_grp.attrs['trace_type'] = sheet_name - local_grp.attrs['response_dir'] = response_dir - local_grp.attrs['skew_thr'] = 0.6 - local_grp.attrs['trace_bias'] = trace_bias - local_grp.attrs['rf_peak_z_thr'] = rf_peak_z_thr - local_grp.attrs['gaussian_filter_sigma'] = gaussian_filter_sigma - local_grp.attrs['interpolation_rate'] = interpolate_rate - local_grp.attrs['response_window'] = response_window - - print('\t\tprocessing roi {}/{} ...'.format(roi_i + 1, len(localdfoff))) - - if roi_row['roi_n'] not in local_grp.keys(): - - # get constant to add to trace - trace, _ = dt.get_single_trace(nwb_f=nwb_f, - plane_n=roi_row['plane_n'], - roi_n=roi_row['roi_n'], - trace_type=sheet_name) - if np.min(trace) < trace_bias: - add_to_trace = -np.min(trace) + trace_bias - else: - add_to_trace = 0. - - # get strf - curr_strf = dt.get_strf(nwb_f=nwb_f, - plane_n=roi_row['plane_n'], - roi_ind=int(roi_row['roi_n'][-4:]), - trace_type='sta_' + sheet_name) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, - add_to_trace=add_to_trace) - _, curr_srf = curr_strf_dff.get_zscore_receptive_field(timeWindow=response_window) - if response_dir == 'pos': - polarity = 'positive' - elif response_dir == 'neg': - polarity = 'negative' - else: - raise ValueError('polarity ({}) should be either "pos" or "neg".') - _, _, _, rf_mask = dt.get_rf_properties(srf=curr_srf, - polarity=polarity, - sigma=gaussian_filter_sigma, - interpolate_rate=interpolate_rate, - z_thr=rf_peak_z_thr) - roi_grp = local_grp.create_group(name=roi_row['roi_n']) - ia.WeightedROI(mask=rf_mask).to_h5_group(roi_grp) - if 'alts_deg' not in local_grp.attrs: - local_grp.attrs['alts_deg'] = curr_srf.altPos - if 'azis_deg' not in local_grp.attrs: - local_grp.attrs['alts_deg'] = curr_srf.aziPos - else: - print('\t\t\tAlready exists. Skip.') - - \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/analysis_database/0080_plot_plane_rf_center.py b/corticalmapping/scripts/post_recording/00_old/analysis_database/0080_plot_plane_rf_center.py deleted file mode 100644 index d849ca4..0000000 --- a/corticalmapping/scripts/post_recording/00_old/analysis_database/0080_plot_plane_rf_center.py +++ /dev/null @@ -1,262 +0,0 @@ -import sys -sys.path.extend(['/home/junz/PycharmProjects/corticalmapping']) -import os -import numpy as np -import matplotlib.pyplot as plt -import pandas as pd -from matplotlib.backends.backend_pdf import PdfPages -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.DatabaseTools as dt - -table_name = 'big_roi_table_test.xlsx' -sheet_name = 'f_center_subtracted' - -response_dir = 'pos' -skew_thr = 0.6 -rf_peak_z_thr = 1.6 - -save_fn = 'plane_rf_centers.pdf' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -table_path = os.path.join(curr_folder, table_name) -df = pd.read_excel(table_path, sheetname=sheet_name) -subdf = df[df['skew_fil'] >= skew_thr] - -planes = subdf[['date', 'mouse_id', 'plane_n', 'depth']].drop_duplicates().reset_index() -print(planes) - -pdff = PdfPages(os.path.join('intermediate_figures', save_fn)) - -for plane_i, plane_row in planes.iterrows(): - - print('plotting {}_{}_{}, {} / {}'.format( - plane_row['date'], - plane_row['mouse_id'], - plane_row['plane_n'], - plane_i + 1, - len(planes))) - - planedf = subdf[(subdf['date'] == plane_row['date']) & \ - (subdf['mouse_id'] == plane_row['mouse_id']) & \ - (subdf['plane_n'] == plane_row['plane_n']) & \ - (subdf['depth'] == plane_row['depth'])] - - df_or = planedf[planedf['rf_{}_onoff_peak_z'.format(response_dir)] >= rf_peak_z_thr] - df_and = planedf[(planedf['rf_{}_on_peak_z'.format(response_dir)] >= rf_peak_z_thr) & \ - (planedf['rf_{}_off_peak_z'.format(response_dir)] >= rf_peak_z_thr)] - df_on = planedf[planedf['rf_{}_on_peak_z'.format(response_dir)] >= rf_peak_z_thr].drop(df_and.index) - df_off = planedf[planedf['rf_{}_off_peak_z'.format(response_dir)] >= rf_peak_z_thr].drop(df_and.index) - - df_or = df_or.reset_index() - df_and = df_and.reset_index() - df_on = df_on.reset_index() - df_off = df_off.reset_index() - - if len(df_or) == 0: - print('no any receptive fields. skip.') - else: - print('\tnumber of rois with significant rf: {}'.format(len(df_or))) - print('\tnumber of rois with S1 ON: {}'.format(len(df_on))) - print('\tnumber of rois with S1 OFF: {}'.format(len(df_off))) - print('\tnumber of rois with S2 ON/OFF: {}'.format(len(df_and))) - - f = plt.figure(figsize=(11, 8.5)) - - f.suptitle('{}_{}_{}; {} um'.format(plane_row['date'], - plane_row['mouse_id'], - plane_row['plane_n'], - plane_row['depth'])) - - #=============================RF center============================================= - # ON/OFF - alt_min = int(np.min(df_or['rf_{}_onoff_center_alt'.format(response_dir)]) - 5) - alt_max = int(np.max(df_or['rf_{}_onoff_center_alt'.format(response_dir)]) + 5) - azi_min = int(np.min(df_or['rf_{}_onoff_center_azi'.format(response_dir)]) - 5) - azi_max = int(np.max(df_or['rf_{}_onoff_center_azi'.format(response_dir)]) + 5) - ax_or_scatter = f.add_subplot(4, 5, 1) - ax_or_scatter.plot(df_or['rf_{}_onoff_center_azi'.format(response_dir)], - df_or['rf_{}_onoff_center_alt'.format(response_dir)], - '.', color='#888888') - ax_or_scatter.set_xlim([azi_min, azi_max]) - ax_or_scatter.set_ylim([alt_min, alt_max]) - ax_or_scatter.set_title('RF center') - - # ON - ax_on_scatter = f.add_subplot(4, 5, 6) - ax_on_scatter.plot(df_off['rf_{}_off_center_azi'.format(response_dir)], - df_off['rf_{}_off_center_alt'.format(response_dir)], - '.', color='#aaaaaa') - ax_on_scatter.plot(df_on['rf_{}_on_center_azi'.format(response_dir)], - df_on['rf_{}_on_center_alt'.format(response_dir)], - '.', color='#ff0000') - ax_on_scatter.set_xlim([azi_min, azi_max]) - ax_on_scatter.set_ylim([alt_min, alt_max]) - - # OFF - ax_off_scatter = f.add_subplot(4, 5, 11) - ax_off_scatter.plot(df_on['rf_{}_on_center_azi'.format(response_dir)], - df_on['rf_{}_on_center_alt'.format(response_dir)], - '.', color='#aaaaaa') - ax_off_scatter.plot(df_off['rf_{}_off_center_azi'.format(response_dir)], - df_off['rf_{}_off_center_alt'.format(response_dir)], - '.', color='#0000ff') - ax_off_scatter.set_xlim([azi_min, azi_max]) - ax_off_scatter.set_ylim([alt_min, alt_max]) - - # ON-OFF - ax_and_scatter = f.add_subplot(4, 5, 16) - ax_and_scatter.plot(df_and['rf_{}_on_center_azi'.format(response_dir)], - df_and['rf_{}_on_center_alt'.format(response_dir)], - '.', color='#ff0000') - ax_and_scatter.plot(df_and['rf_{}_off_center_azi'.format(response_dir)], - df_and['rf_{}_off_center_alt'.format(response_dir)], - '.', color='#0000ff') - ax_and_scatter.set_xlim([azi_min, azi_max]) - ax_and_scatter.set_ylim([alt_min, alt_max]) - - # =============================pairwise distance============================================= - dis_or = ia.pairwise_distance(df_or[['rf_{}_onoff_center_azi'.format(response_dir), - 'rf_{}_onoff_center_alt'.format(response_dir)]].values) - ax_or_pd = f.add_subplot(4, 5, 2) - if len(dis_or) > 0: - ax_or_pd.hist(dis_or, range=[0, 80], bins=20, facecolor='#aaaaaa', edgecolor='none') - ax_or_pd.get_yaxis().set_ticks([]) - ax_or_pd.set_title('pw RF dis') # pairwise receptive field center distance - - dis_on = ia.pairwise_distance(df_on[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values) - ax_on_pd = f.add_subplot(4, 5, 7) - if len(dis_on) > 0: - ax_on_pd.hist(dis_on, range=[0, 80], bins=20, facecolor='#ff0000', edgecolor='none') - ax_on_pd.get_yaxis().set_ticks([]) - - dis_off = ia.pairwise_distance(df_off[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values) - ax_off_pd = f.add_subplot(4, 5, 12) - if len(dis_off) > 0: - ax_off_pd.hist(dis_off, range=[0, 80], bins=20, facecolor='#0000ff', edgecolor='none') - ax_off_pd.get_yaxis().set_ticks([]) - - dis_and_on = ia.pairwise_distance(df_and[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values) - dis_and_off = ia.pairwise_distance(df_and[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values) - ax_and_pd = f.add_subplot(4, 5, 17) - if len(dis_and_on) > 0: - ax_and_pd.hist(dis_and_on, range=[0, 80], bins=20, facecolor='#ff0000', edgecolor='none', alpha=0.5) - ax_and_pd.hist(dis_and_off, range=[0, 80], bins=20, facecolor='#0000ff', edgecolor='none', alpha=0.5) - ax_and_pd.get_yaxis().set_ticks([]) - - # =============================parewise magnification============================================= - mag_or = ia.pairwise_magnification(df_or[['rf_{}_onoff_center_azi'.format(response_dir), - 'rf_{}_onoff_center_alt'.format(response_dir)]].values, - df_or[['roi_center_col', 'roi_center_row']].values) - ax_or_pm = f.add_subplot(4, 5, 3) - if len(mag_or) > 0: - mag_or = 0.00035 / mag_or # 0.35 um per pixel - ax_or_pm.hist(mag_or, range=[0, 0.2], bins=20, facecolor='#aaaaaa', edgecolor='none') - ax_or_pm.get_yaxis().set_ticks([]) - ax_or_pm.set_title('mm/deg') # pairwise magnification - # - mag_on = ia.pairwise_magnification(df_on[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values, - df_on[['roi_center_col', 'roi_center_row']].values) - ax_on_pm = f.add_subplot(4, 5, 8) - if len(mag_on) > 0: - mag_on = 0.00035 / mag_on # 0.35 um per pixel - ax_on_pm.hist(mag_on, range=[0, 0.2], bins=20, facecolor='#ff0000', edgecolor='none') - ax_on_pm.get_yaxis().set_ticks([]) - - mag_off = ia.pairwise_magnification(df_off[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values, - df_off[['roi_center_col', 'roi_center_row']].values) - ax_off_pm = f.add_subplot(4, 5, 13) - if len(mag_off) > 0: - mag_off = 0.00035 / mag_off # 0.35 um per pixel - ax_off_pm.hist(mag_off, range=[0, 0.2], bins=20, facecolor='#0000ff', edgecolor='none') - ax_off_pm.get_yaxis().set_ticks([]) - - mag_and_on = ia.pairwise_magnification(df_and[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values, - df_and[['roi_center_col', 'roi_center_row']].values) - - mag_and_off = ia.pairwise_magnification(df_and[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values, - df_and[['roi_center_col', 'roi_center_row']].values) - - ax_and_pm = f.add_subplot(4, 5, 18) - if len(mag_and_on) > 0: - mag_and_on = 0.00035 / mag_and_on # 0.35 um per pixel - mag_and_off = 0.00035 / mag_and_off # 0.35 um per pixel - ax_and_pm.hist(mag_and_on, range=[0, 0.2], bins=20, facecolor='#ff0000', edgecolor='none', alpha=0.5,) - ax_and_pm.hist(mag_and_off, range=[0, 0.2], bins=20, facecolor='#0000ff', edgecolor='none', alpha=0.5,) - ax_and_pm.get_yaxis().set_ticks([]) - - # =============================azi alt spatial distribution============================================= - ax_alt_or = f.add_subplot(4, 5, 4) - ax_alt_or.set_title('altitude') - ax_azi_or = f.add_subplot(4, 5, 5) - ax_azi_or.set_title('azimuth') - if len(df_or) > 0: - dt.plot_roi_retinotopy(coords_rf=df_or[['rf_{}_onoff_center_alt'.format(response_dir), - 'rf_{}_onoff_center_azi'.format(response_dir)]].values, - coords_roi=df_or[['roi_center_row', 'roi_center_col']].values, - ax_alt=ax_alt_or, - ax_azi=ax_azi_or, - cmap='viridis', - canvas_shape=(512, 512), - edgecolors='#000000', - linewidth=0.5) - else: - ax_alt_or.set_xticks([]) - ax_alt_or.set_yticks([]) - ax_azi_or.set_xticks([]) - ax_azi_or.set_yticks([]) - - ax_alt_on = f.add_subplot(4, 5, 9) - ax_azi_on = f.add_subplot(4, 5, 10) - if len(df_on) > 0: - dt.plot_roi_retinotopy(coords_rf=df_on[['rf_{}_on_center_alt'.format(response_dir), - 'rf_{}_on_center_azi'.format(response_dir)]].values, - coords_roi=df_on[['roi_center_row', 'roi_center_col']].values, - ax_alt=ax_alt_on, - ax_azi=ax_azi_on, - cmap='viridis', - canvas_shape=(512, 512), - edgecolors='#000000', - linewidth=0.5) - else: - ax_alt_on.set_xticks([]) - ax_alt_on.set_yticks([]) - ax_azi_on.set_xticks([]) - ax_azi_on.set_yticks([]) - - ax_alt_off = f.add_subplot(4, 5, 14) - ax_azi_off = f.add_subplot(4, 5, 15) - if len(df_off) > 0: - dt.plot_roi_retinotopy(coords_rf=df_off[['rf_{}_off_center_alt'.format(response_dir), - 'rf_{}_off_center_azi'.format(response_dir)]].values, - coords_roi=df_off[['roi_center_row', 'roi_center_col']].values, - ax_alt=ax_alt_off, - ax_azi=ax_azi_off, - cmap='viridis', - canvas_shape=(512, 512), - edgecolors='#000000', - linewidth=0.5) - else: - ax_alt_off.set_xticks([]) - ax_alt_off.set_yticks([]) - ax_azi_off.set_xticks([]) - ax_azi_off.set_yticks([]) - - # plt.tight_layout() - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - -pdff.close() - -print('for debug ...') diff --git a/corticalmapping/scripts/post_recording/00_old/analysis_database/h5repack.exe b/corticalmapping/scripts/post_recording/00_old/analysis_database/h5repack.exe deleted file mode 100644 index 3ee5da805053a168f7b43514494c31d0c5b93321..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1877504 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

^X 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

LyfLv(PPD?9-bw{asWjwrbt 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%0AN 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%0D9-HLJTioo6mqit%b!5b>FFz$?q-ceefWV~_ zRSbs)1l|b|oLJ!#c>8gI7%rm>;E1!EAy9Tz>8b*9p5(0kMcNmM%jeH99bCL_!qr?$ zXkXUONGQmk^@TYYs_Ww%6;r=$>iHHaVJ(l5p(dP=ZU-MnqKSME6Ea-Etv0ynSuIS7 z;pUTL4LvaOTa7Yu4*W3Ik9h_GlI=Qe$#%e*L(pV%YEMaW*|bI7FZ`n19q?_XsuYVZ zy5$ROU2eNaV5>>H%9J8av#`FI5dYHu*X>(-jwk@F{WG5_V7G#qM*VV*J3{TpVHc>0 zIM$7Fx{*ojW1H0tJ|K&v^lArz;89J8E#OlB(6sPXcQKY*>x zcBfIxX&uiAJcXPJkje7WKMoLPH_6Kes?39end?~k(mKWtGtV1GF=I(JtW*|6JIRcX`o;qQ;ML-n=e;Io;g7k z`QE2!N@A@w5~PWk$jOC3?V(WR7!{Y8;RXVV^V!i%ujKO`XLSTi^3&v!K^ zH-xgeK~rQo2Xaz|tM}`F@%8>&2(VJ(;Txgs&9!EZ#e3X&x~un+7p5Q%z;XZry`Ln{ zgf;R%+4Iu$JiUI+)1KZpI_2$ry}z)7$l(`wBu; zs)a{tNtEi%%T%d)(aOjv$_8zB2iGLgtL2+jsux4g7gq@2RRQ2u1{`{I1K?DzTBtll zH(Z)W`^e`Rl$hpnN+7N-pHSpRwGN=gY?d`uvrMj-C{@Ov`~5)qnwA(K zO=_BtE66urC(q*FE%Z;f z-ULqTssI0g4O#CHvhU;; z8d;`=HY(Y&m#xobQ25Fa=J$NP&u5uz_y6DHF`dunbDj5k-sgSJd7rKJ$j-Dv`=djo z*XI)c2J0copL&7AgkU)67F8_l!|GVhHq2hOif5V*o)`AP|8j!iz6;$zzO44x8k9E} zt48MWlqvYHREq=}`i5trL=zu14%#y;pPk)P(w|K`XnQlJTMJJcBT==MYm7RsTs*J; zO7$OuMx@XsN~0igq)KacS2Bl!=;x&WpI3~p_Nltj{FXZdUZv&Vp*JlD@vETz4$kyC z@g!G&!|L0IcZxdCKXXu2eYgIx6INm~UQWP&I#Cd>|39(c(*VHEO0lz^8!uKv_w)uq ztP`gWVlQVYKwK#ZRl@PG$#Mf_Ki#r_8)yGDBG6CS<@kcT%#}T1*_++SvMkD8tn6~d zUQxyN)_QEF<*X9`sZoc#<0Ji^(l{{VI>zl729Cd^p-GpF<;gl)r_uCY{PN*08oE zf56ncu;Q57$vsiU)|6NkI~OCs7CObQd+2@GlZ#-!KyA~Q(Kc9C+fH282*vNVY>+Qe zjoZ{7h-4ihN6S(s^W_JHj`#$3FqD+PnJI8q%L}vGrRT6`MlBzkIJ^dm*OUC!$&eY{TeK&%4S4 zQD&`PjmoT^87p?b#ITnLqSxyUAZ>6B79Wq$R`20jGRb^MyDTO5NQ z3b-qrD;ekTB>%?~zMz+DS4W!T<(H(t^q#12l2Xb1FgQZE_F@7g#N+LYZjnAU{uUOH zm5-KIP&AM9qiD3u>?gIexAF#0oF{l{R8cQ5Ev<=J9q$;#xF6Xa4Nr1q^B)X`UPfYq zDvEAJklwf52E8x$-(QU08T?A=SA8d?e-ooy7NMH~I(NxSDaQEt`A+HAEHSMO2KMMq z(%N@1Y|99?5?H6T&3Qs=d#G;I7L}3&t;S7vM_T(5W@XZ@6G}H^_@McWI?8^0ce|C{ z7H2+b*@G54ans!Puea-jd2f<0+&_@&i{`2aSR*8jCjAMWez$+1g}+7TP4^FA#1~X= z7Icbk++H(>KrYPxwi0Cgn=S3b&nDXlgq?`iruzpfyjA61UbnByyiZsclcF$w;wCDT2>t=>-k<@=>FuTPRf(0S=cBoh;Fuutwh0j=aK$#x#kq9`JREb1d$ ztF%@bxRVSfFxp^OSD%u^m2%6fF?qS;`9GEMISGLlCNbe!zVX_k`h0z?9w?5gHAcS% zyQMs?aIN@o88Zb%i}r5{Dpg189T@%##qlVyf@d@J)iWqG?wnM&+J6ccOTf5cAHU*S z77FlG?0=lHo{%m$S>|7PxGH#K124Ze6!@99?X|~wU8?s@_c$+Evqh$7s~T3DFY_De z#3SVifmUkr^~;gkQJkOd(YCr5Ff|wa4rYOa;ix}5AJW0F#J#DyEbVE@dG(>`9_eHa zCyhA0La7h;g%4c9nw9HF0nFC^zoZ7e5W7dz3kS%GjkMQ#oD~R9TraF2Rqw2 zu};UaZrDm^A#2uv%S|nhqPlTx02CrxAzy8cBi@JAZ$i{*2HCpCOKJ!zvWG(h5mu(sQaAA z-mRfA`vqxSp#7|>HTRLnwD&XG&quTeHlv*iX|KAYWuA`jWSjR%Nu=zKl>QrUAzc=N zwH@<#SO2TmkGe$&^+OxN-Jf6)T-;=-`EVK^_w zaA<^Kk}!;kF?{JdIPg&BPl_=-VqKf_V+=n7=M1+@7!Co0E@14l?IsH8Z(t*0u(c84 zl)zfWgtsuliy+MXx-sD;SZ%7VI>sT~ zF_MpmiIXd1BzHte{2f5@5evxzo!m=Sr<2o!T1IWi44RX{%J`^dL|}IHrnBRxVj&WMV1d_Chtl3OH+%Ku;3EiO zda!?SqfIeLylF$l@nbR4_Ck76Kz*}QqsY{?W@=By89&*MjQa%C{p7iJgK>69BYd+? z+R#27=bok9d6wH^C`@(Osj$%*I#toJ@8R$vjMqi|4{c}dwt;P|cI;A*150`{pL)*1 z{Ow6)jDAn{z@b&Ps^ky1TL9_rOATbsask)n>=?I+;`ml@h z9^(|gCjE7_bcwc%1WX z3%8mv%VSfj#7i~X;oVfR={s|06`95+7B}Pi4mxKlH9plew`?+}R<%rdb1QWDH}@?0 z-_KWuqTu&e@|xW5`t7Ul<8aEWxR7MG#asfG?rL~BBXx20q49aw;1AI|>=j`6^85M9 zR;2Th^t(gP@BH>nbL`RjGvap|%XQK5db-xAtoHO471^#_H7qvE%e=RP`>QnAX{t{= zQ^i`6w&Q+oDod1ql*;^6aB)ZOieHj=W?uS@)fMgXkU5o^#{ori(>>>pSW?auMB9pm zL64Oa5i|u*s6kJcX0l4tl2^j!&Z37Jyr;8M$Kfq)1D$?PXRErq-E=O0wyHFIHTY~* z*{1JP2Z!KXUGPB#GrM+f92a)2>^rf(GEugqqP9<$zS%y0MYdlT{#N(R9$jCN?XzgZ z{ABg!gx)m6PssS_RSny)S*p8~!oXRqu-3aFUpuHunXS}FXv6##)jO*ZRhPOKA%B*| znMG^!kGaI59b1sAWt?m4AQxQbDVI{ex8-Ub=lXV?!^NQ4aw+wTh6W>xTJ-ajyGCvuePlkbVHhGVl@ljU>#EAm_9Wg0iDR+a3x(I%rh zfDj{i8Z<$x`?x8$D|nolJPh8j*K-ZeR%v$o1MWUJm!LilKFjLJlcyT$Q)1N1O%>fD z)W1@;tw1Y4@^ zkciUfiCBomJev@M9VJsdJ)zVE5#Dng-gT##1k+-?Im25T;jL|k_o5JQL36wnNq-dw zd%4o3L6Li@?RY{fco@1pTdP|#}ppKvFIcu!#cuPz2 z(kaZI$9gdFv}xB)GlcQIy-|CS`Ko)Fv`NQE`wz!xERtE!J#6+FKXhl?(qL2gpx^xn zlC-zr1Wn#frnS@LZ7w^iAUmqC$LK>76LkzpWo>C;y0(gV@8?!JTDLiv(&5--MFBxe zX`kgZPy3ONcuvqyJGxTFWlWS3Z zoc=qQcFhj%oKItws!PYjzHlTUHr0_pt0Mzq4cGN}zL={}TJ6zX;Zd8DN# zOekjInJV(5Yjq1u1w9tFF&jFS8JViIIeEB1Ne76qW*r9eQVMzE@2y+JNNbCRI6N)& zJ81I!8#evD%>CX*zgy?xVt&_pUL(qR+3e;2m~*oe6LbYq+@JNPOdlt7oeShHMs-X# z%}@9IXoR2Jm28ylp)`D{x`DB}@9ALWh`I;zl)C`Vo8Jc`(S`7@^f8_FZs-H`{(skp zb*b)YwW&OnPM+#(LJ5v%`gIctE^-oFN;5?eHI`sa`%r@0c(N5i+}BmAwy%r-3vnyl zCd=OP|6za^1K2Q9F^(-(o5ky=Z)i<8NL4?bn)b@+po}W1-izBXTa4UYS+}~yvTC?< zk7XSjlYa)H4PCvWmDs3rqk`zn4U2)hKG`6yjv-n{Dt~;lusYqy6IH4FM5{`ZPKw4| z+g%m3W`X%?NjC=5-26jdq15g-EVZt+_ikpkOuuDf;ysk0%$W@+{fn!xsVY90p(^#S zT4UlJgLrCJx!^P$oqGBIinQAr2sP0cV!5DrEzBNBL31;m&BX;Vr1huL~&7LF! z@5Hf1<#)-QP6V-<=h2%&}drIkw|jwuk0#=x2g-Pyslv z_ZBFLA)+AO;jsnpVMG0SfzyCLW@0GupKr)_<&o)i#eqyxx`l`r@hJb`Pwiz*y5jof zZ$%5TPB*fzMP#ppRE|S(Scte;SCD8SqSoaZiXD&^HL)P8flvA;J--($$T}R`c~1H0 z?>xnlSxw@oAhtGL+I0g?)NP}JsD@g@d0aFb_Gx-HgS*1!pS4CZP|+vSx+&*S!F4lY zniVOUW_%qFu`6laQ+2Cipv#@^(CABT4IBr56HgSv?#EjpT-84^|LrHBU$>UxBBWvB zIiuZRk;w<+sbKvKp)`x5>GY$a{0;7L;5+*eP%B-Szm(Ke_9fAMRJJ#)?ewk!YESb^ zs!l6l0;xW5Y{6g~o^pL?%)O3J{j_zSzfTLNL>1YHZ#5aeUkvtEB-g$I>mV>fK#Tl> z{}9Pp1x!d7Qr$zohEepPQH4JI?=MmoR@< z<^SlO2>(6&!kvA}|116}U}j>J-{wyqkMkdG`43lqPTvaiKRs)csgkjA{+?z=DdoRC z&c6mrar{?_pJU?u-_s@IoJ8e6KhA$$l;0*mc8v4iX8FsNzgL|9HF*WokIgWAb9W@~ z(U$*6oCmRhx`}l=fEhxO*dQ<=YNFWRH{y^By*3&`OlB? z+qB8)asE>+e_!Rlod0Z|AQ;TI^H02bGu6|9mn91MK!7FIKkxpEy?l^%u zpYTa*>`LX?Z6)rb^^O*XR?dekr@A;n&QZ$A`VAwhSQ0K4Xg zZsRff>A6qv4S_y7k`>1KdU^hpcSX8>XMlJ+9R#_%QoVaVVxvPyUo|aFXY@ zbm5^v@O1E-o!I5LSh@MfIor6VfXDTYGko2)5swA#DG8qg9iJPHvF82SvB4q+obagB zAP(c_YL?ghf|Do*nilPJ>|kboZ%p8}h``(yp%NZ&PsxVcxp90HtjsE&0b}YzYGYk> zTP;+}v447FMP@Pazn48ut$o#>d8sC})%p9R{!3O%j60tCWXFt z#6YKjf!wR~&lFu1AKSm^IIF_X$JXW4B0+{tcxJ; z3?T;?WQ*$3lvmz4v$_Q*g^Bz+>Qbui;~2>+j!77=j*y%ZBIyJYhAEg!O7`-aU-*IM z8yn4DjzKM6$4S70_d@WZ=y68{1>-uCI}4F@Cfo8v?aZrWRA1@#$bJeF{@%yH+4<9WY@3VYB!C39zhsJVst%Pt8m+~jY2kGdG&$JkzR zvP%94+bJQoPGIBG2q!<5v&H+N_*@iRn|~?&0)+M|%({wYWX>or0%@wQ5h|`aYy-lu zpZn{t#&puZ@lq3gsCc1sm>s}kqD$CI%7nl^*`55CIgAOFvr?$Q&#gCikeIj_vm~0I zm>~O1DOR}LuXIBVF*KK<0}TC~Him8p3xl)VVIWnv72-ICCc{L?(6Er9R*s=}=$p7v zS3qFTlnR4O+*A)(ujGi_koO)Zy(E;b@zxu25dE#E7k@7TmxBNz(h&*8nw#l+LKth`>LO4r#?p_+r^cAJ-r-=-*SX0 zZvUA0MhMV1jEL{ijClKy_}!P7;zoiiJ4sX;roq9;8v+gn1swD+4iXbax#+}Rj^=KT z=8KZs34E2&JT#*D)X%ZN|KZt8VIN0RyBm8N&BTPH!~5oOCd+H(Cd)Z7-hB=4VG-U_ zo8dh?#QXM0ljUKo7d@pH9;x*xdHzeB7(|uTnpLj4trs|FbA|Uc#Cn#K<<~RXu?|>(W|EJ8f))kWY#lIt9zax~1p#H1b>mD_ENTj%9=pf+WC?V1FehWT6bU?>h^y3q~uTkAJ5 z$9}>k!7zUSR%18TKTl@2g;4vsew_a;wo|RaUz=Cdkc@*^?mm7|BfYiSc>T77vP#uu z^jntSCT^%1>m4}v%dKtG;XRP@dOgdVH$Stw4efHP%+l6g{^uw>vvdolW^I?@Yu+1H zaHn&;Ot{bRHi35RjF=GVYeZ&*CFUC3nLlDR*5Kako|3so1XR>k^p^QEZwWRHj&OCr z$G@cNt`3n9mj-#TX>e5zkmdg6AxK{bayu0zRd-zs(p4ajLd7*2>{?FYv9`+D=;l&5 zy#YU%s=GIa7(u}}%RN+Fn#q2#!Pz^ub#!mDtvCCaZJmFyisJq1OEtwboL!fc+67=e zfrf|m+A-F=20Ym`d`dsR3RRUcN5Kn*so2Xh)5;>wdkHqa zvv2T97>4R2x2Q^d7b@CWrU-*ozuNZ3UE5+v@)dZR=|Hl%#;04z~gxMLDZKcM(Nu z7r!MY=sqNw*bao>Mc^7o;2SgdUtaW%fHO%-s6EUthaR+ zC}DI9^_$aFF`n2qt!_t^!Er2D3owPN5Z!Q-|G#m-exs9`=H&ztdIuDM>`aRpn3bTu zq~YW+*%<2O2x=!m-6g_woo!a(T+O_mO=M-c{~p`ykWh0A9~+A2V-%9B;SBhy>A|^o zlqJofiv5{)%(Ez#`>9#tOEJt!V4PNFe+Ts5kl2Hbe~=hR_S1~S!Z`aSQTBmk2N3_# zmM6?_Q7<#UpAL)4_L>VUNTIX2KE)b0iBpI(IU6&oFkGSgLbyNXI98h3{W0Y{SfLDY zJLq)=(=GFDro82$M$Lm-;9B~_Myl={^ycIl^=;ha7{c?y{0%HM(?5P^JqB?;H~G*k z6mR=~Z1<|nnR9Ge5EC5LsMbhZGJetlFWT$aZDne99s?ZC0a?wf_+--XGne=-(QdlQ z?hH47cr{^_3B~^hi1L1yD9ry5_f1;#noB#rKuQ)3u9QVbjO0N!FE44&h4!4+^1H+q z6N(FX5svW&yTy&g3dsthi{&a5q9l)1^NmGyNgY{W6^#{aF9$oNSm8~>s<^0r*)GI7 zoDX19JT_#hl;^y>QhiPbpAB{>FM@pszr}BZd1a8I$5BBxJ2Qo6t#CGwsR77z2QoPZ znG%2iJoRd72*i zPDp0BzrQ9>6`aw3goREcEX058=;eTEozuOV%ydkT$D^fY*HdCzTFj3nSQnus_L^x9 z`9X5=Jj&*i1;MvP?%QC#58-bZf7Ser@vphs>M0w6SCb|80x}BGU$P*3oDo|eTC)LZpE>=ZhYm1Jc$C9%B|6v^xNYWo0;bM6L6B~bzYl<6&)fO$J zckyRsx^IMz#Zh^=)Bhfw+p~YP;rgSPpH2$J)QLK|E&m?Zl;YX01n$*v;ZLmh#t0p| zSo3o8`R{MEHpN^BiYxyMbmb8`#Wr%)e~+%xCV6rMJpQZ@ewr9cy1j7G!TLMaDvO7} zSM6WmdMv_~6fSN+`g>f%!1Yk~Kj-U=2v@OinJ@f1L#qbY=j!nOiJ=uaTukBo16&P9 z{*CS{i@B4zsQTbPNA&6hQx@|(jN++s`5T|L-pW@Dx&Z4N2V#C>&}v;^x`{tC1aV!d z=iO7%?{~I!oz!KTQanShmLp*|A4Zp!9ny>R&ao3D&CUW)C(5lK@SpyK4DReMte+^Y`fR0g2iBP=Fs490nL%T{M zYD2yL*epHU4lb51SjFv9b)0$;3fVt`y;ZPV0o!b)hwE~xpxu3e-cQ~V|ME4F-ukgK zO}J|FXTK8-Bz7ij;PiGGC|!}Y;|aa(?*{hg(Z3G#HuWs0H@dkxPo9%fB)JX(IZ=IA zl((Z7DX+b8KOyG+dPe}|eeea!y8-2m%1c~xJyk|%NK1tqC9XUJvv?mM)vRW!zt9zb^OhcE3KKM&zOF zog3^=C5E12@yNvbR3iQUCWDnoq)Iqq*=PUa!hYI{Mf`&`w826hty_lYJb&#r(I9mC zld5^^W92>J_@`Lk{S4))kHlF0WlSZl>e0fEU)cv;r0RZ%p`MIX-U|5Ktw4E2o!=qO z!2QOwYC&CBExdlFDE<_~z8g_|1d0u3ND(aTDjI!*oXH};;z~0@O?=-$-A~p13T(q} zp*A1y=8S9?mkr4Lh3ZDsHN_;p1hFfsuI`C~`tDP!7Cqg7zdyAjs1~CKn`hIYa2aBw zXNG`(bzrmE&!+beRO!ZnpSrzY+pmC?7M|}HeB_K-(aGmcf>UXY?Y2a(Xx&>V>DU7Q z20T0iQ1_&;Z zRlEzH?^O4x?%Wvmln8c)V0R&#SM=?hP7_(0DANQ%%t#YMATLc!aUBO~Vn5@E5qv00 zUWD~1Vb$Ryp(cJ%9R|E_3_j18BDfQR&jrY7qP@Ut0gp9NcDQLm2E4`qiS31eQzF2X zYcSxGA~mctHH@Z{kUw4Bv^wU0LZFi(pxXtc1J)WX5A%Rwo_>))kC6x7Fx#Fy;mv2c z=)&z-Z(z3b2AbK%C#pFI@jIUv*lQdgeTHS8Orj9ycaf@lc&9k>{CP!@lepwDRoQ!D z#;$hk;3Q_PMpX}xA15(m5XTWT&Z+cv{V?hs5x+)HC555 zHTh4DH@mJmPDWV;-Sr2^D3|)PGjec=teQ?FxC?d|V8;@aL=udEZ)XMvxTj=phV)kS z^y}0eb^-JaXbcQ#>?QXG4Nj0!?0$dn|62ipx=&;LiySUWbIS<-n;#jaWJ4cX-6{&C zpFbDRM4KkuB_m^?%+zko>~@i$XTzd1 zhCSR|?yh^TQFBIcWjW-$L8py}D#qg(jnV8~6v z#w&i~{-+MOEL!tgXeR!Zmpkh?5_BJ7hLy}VP+%S<8B~JRig4Jis)18&74cO8Hx2u` zUT@I*M38F@;2{eYS*`gl0;r4xdG9?FJQm@`-#VIlFL=9=sKLwj-#%YRu? zvda6;wI}^}x6&KI3Kkew%e}_~)*8e?`$HUcN^c0%Z9bC%tinJt8(UPX!cRBWXT~6h zN3355NW&Pf=)PC6_2vGh=uNdTXhe3sU$kb$fagZE4i#XZZuObpTBuqKP25zA0O)u>ops`*^VEr3$GF#Pemp#|K<6Bcl*Q(P--Jy2Z9iIK~=9FhKiH7c=v zWO}t{Wi%zA>mQiM{Fz0krr2#Zj}*J?J_$P}F5oqeJNRtRQ|@N`1B(5TlH_Dk>2;Xd zwhKm@Nvw?|>j6Z=zr3QpOVR66f0r??MhI%zy`qze=@dm!YgYlapRRVG7VYTPrmGU| zX@n-lASXtwO$Wp|veS7&h3}|gtSzcPC{p3){Ua3~LVXHVNXPtl4RExdd;C}R688OW z^_OU7QFRSfS0Kq-v6#2-e-k-?5LeI(6!ahm3*6xzw zg)Xg5lBQniQ&_-%T?)vh5c$wl_SD(@J9OzX)EBC@a9|Y;@ApI;z{UH5H zDKP7yG-Q9CNe7pZ`deRYmdIfEPF{1rSbO_7(Nu-M5htxW^C!WiMXW!dCW7uBq5B_E zOBYT3O2zjlaltVWE_DKPXZ$bLRyo#g+C%7MzI5N5%B5?6?+Be%PW#qDQiD}OQf;o`;z?f&Puj)-t+(|B&r z|Ak9q!QRGKlKh*h?C0U3j9ScR%SWWpzu=2loiNlEy^nuN%}(lNlM;9&LN|xs{&&=J z2)M2=T&g6S;u_>|5w(1Y-~LC`axjQ?{|iK0IYdM)=ZXmH6c>Nk$C=`m&B@V8!^2dDIb$qI8k8&95Fj zk63_fRbCK++d;rV6ipQq=UW>%6EV57G$zw z`gg=;PeC1`wvT>s(Lba`oc6m;+3G6qZWI;14n({Q2*=@C1~^W+5Aj4zt;^^3E zcRe<8+7nSi*j3)7&nHv0_$HyMJ)5?MkPSrX_HWA@g!B9!wsF$B{ok+>K2@hpEdf6* zB7XLSi-t?l_qNADs_4cA))_dT8<#^xY-7Mj#lV9j;Qa;MOADPYMiEIw;Km2(Vh0q7 z1JO!@TlvZ&y%_vDrvZxleMVnS7q|0-E;e@!*x{%o=mQiV6X{~K7zDfOs5M}lbkm}q zuzP40$gnuYqoMXcM*lD_N_bs=MDDuvLZ69ar#|;3;>vGslf86t*xH*cQfL z;V@Ol(d?l@KZ}Gq1R$BzQS;5DcCC&zxm;76by|WO6vI{#l+QRPHmQSlb|y9H0#l<* z>O7;b!t43C)9YjLVLAEjU<5@Kdg+@xIb0-k0xdI)I1&+-k-MY7qrLT z5-n*AdQh|ZWES=gGl4vI@?VtDuJ74-06YA#} zN_ODt$@hZNE@ZDLZ=PxLUD~*2>uj#PP0IW&hFu&{yiKtC1FI@piYC6;DbT0u5NP+( zWtbXwM8>0;p9Q2KVo%N*9-;1bo^jVE&e9Z7I6;}^2Sp(bg%9=@g{6LROyL%zuvrW~ zO+eQLKnhu38vw0yt+$r`lo?Qm80aj38U}@AY8*ti`gzLg`Jw*V!^}VwZI(8_(3`PQN(cf2yU3cxjlCyz$~Dk{~=fb10gRE(7^$*ar$0+qcLlz>if(4F``6N!8VwY{uaaTF4&E#I8%xZl!Yl;b-*_G z3w7c`e>+KNDqr})R>=35UR;G5tGjxyYY9{-&wk~O?Nzl!;bfygR5Rpm0RzU|E%xpp z8_KoHMsURIIM8Z(v7#CZ2uv}|{$U&%rn{U(2CvPNGAniA5BG~oq~0`pW3e<`r=F4j z2~A)q>@!w7c8*!;Ayyd2fi%pcaK+e(k6`p@=#(Wj%S_NO(&RB4!Yk1?%dw3b@{ zLB)XvK(P8CVx$ZuN=>F7?3c&$drQOfyRzH2XquPK%uRap(mjbBUEJ0H8@`YfXc&DLCr>w>#I)U^kUUg4nI$F zAbN=%h~BI|6K@*-m30I;&*+<|8rLJZUx*q5N3D(ZcNvGxwtdcuINUtqaQb57u%@ed zoFifmcNK>mRH%h{D&oPYq3s8*zCL@t=~W}^58hY3J0n)Zo(?tbPYtLWZVAkz==;DW zUglus=j-{Dsy%=tRHgFe(Fi?$fppHk_e#2ZTU2^; zQ(pUS^*vu7bIRB@HY}W<^3tu#y`x*FdM+MOen~^-=ax08a<8f|{v{^PNR*qQv{d}~~DSPT~%6}JX zk#3TMC0IcGQ`$SKJ4fC1NkBdvI@75z~ zI1*<1mHKaCxa8l_bUm^;kcarhtXROnpAtvUOzdQb{cQ(nEK<0TKLy^~Q%@4|G?RU;-|a$BWW(S>6P zYdTf-=-nkTkx>W7hc8Y{P$OxdCeV3zpO{R-cXFY?NyEhN(ICzv75LW5p6@5FJOe_Ts?20?Aaa{vD*ji6uf?OtMQObqg-~E_B&G4rWdb{HN?nq6 zNtH_mxMZ+PhPq_9OGde5oJ%IVWU5Q1xnzb*9(2h}m&|ray-OCkWQ9vsxn#9Ve3z_u zNux{DSCo8aVoY}OESEyk#U*Myc`I>=xuYznKHEoi%J?nqk}8)BaLHho40XwHm#7IK zMm&x`0@{71^2fDReP;0YAb&IYn~f{?J(_BL3le=I-42lK5>{@$HwRIIoZr`zJym+`A3ye8R%rMr`jyc@6Ad!RZQW} zW=dao*mz94$3h;f+#`dh%mDWYlbOLhQq9IMPz|C#VC_|~e`dR{74QhwCA8;7B>aE= zV4FYWP(Gd_f5WEFu(E$wA3BundCKhz0E6{{!*OIATPCjh6?m^`{C_Ppbo^QP1h?2r zxHfviwIs&Xdv1uUvv7S1Y^K*~to+KW>FVdZ1qTAW{-1ImD`UX(BPQm*1fVvl5?CtD zG;ly%IGy$3KLqqy3^XL7b~-=}3iw`B#{>E=8ubhhZRNk7q9AZyYu!;8>MtI^Y*nf* z7b98+xQd-KvxVpnbAWS<@+&bwth2NOrA>JIMQan5?`CbnIHK!8KYL(L>t}0i)PE2A zT^P%H9M+%iN?D?-1{bd)mUj$~R(R(Vn859d^Ic5g9mpy(rMG%u#Q7k|H(Wx=9n%XM zMJr}n+~h7?wiq>8mfgaLHBEu{fNX1$e8KX zhc=u6$tvPdwj;4C@1~t&zl%(}wF&4(m!_SqBPLU@mg__TqM*GGr4Qe9h3v4U6|7qt zZxGu=D!EV)Yao$nN{&_D$IWVV#Y>?3uOsYzl=o@!(%-Te#F>`2BF_7Aly__8%?fX? zKA)*#?CyWn7`3aTP0z>0fc=%-7WvjzS~%-k3_CU3M->0(!{tvZi~DQSl3G9i6TWc# zMQV;zqkDDK@#vp=4hPume89BFr8_GV6V&2q1iPZ<^2)>$@Ad8Z-X&E!bUIs@&dk>_ zIIYWiE+6r_SCn}iV_N3-A7sYF-A4eTkzcNoL7g2lD?K=)D0&{Y?{V37Gnod(*=9xA zHX~cZ6`*=ulRkB)#d)SidA^uK9@*eT%X3PcXIPYHp7I=Q_pM^MI`_r@k!{G-8il>e zFi4it#efOOIzCSTkSC0HYo6%Rb=}2`ckZr{@!ml!CJ1|dj~&NhFB#@v-wzgymqPo^ z$O$&H-{HKgko;@izC2l2ndx(C%;!mtJN(jknv*aZeSQyB@O6(`nbZ%AEBLTUDvz6D z_@^THiNH6U9d>&k`M0a!7oToAl5<#%z#Iw`!*+^vw4Y%Aq26rEYN_)FJY1^owm7RF zkvW8{PMwQz#HjPzq^WZRS`B=`u?I!!yjOzEXLq7H$GH3AZYvpK>}WRI^PP)}QeJ@u zqw^Vi9^EK&?73pZS_@7<`%g9RJVj0fH(k%e!L)^POra z%OX(aE(XB{oL>tIvGet3_7|-@F*#wP10IeD_)G>VoI^2_0G_y|TEftoEqi%4&Q6Dj%El z|I0ot6si#Svu2HHIV`8a@*c(rWC3-SXl<0fgTZy)!V1p7}y8M1n z?QSJHgV#SR&lT_ft~{S(8E;UYkMd~cxzrsUp?W%Qyi~Jgeq?_)I36j_7iPuf`4EQY z747+emFKG`#^pI2)|uaq;WLr69|}Gw&xao0q&!DhfovB;)kRc2K{YSWhex{d9ElYL z<+V%qKfv~JLs0J-u!fTuq%&wMM-Lbr2W<@uVKq7FKW%d?jeTk6*x7nSGRRJT;! zt}(9{MZ{XUCtBkyy>`lT+wH78yHTt`c@}n!%JXdL@DJtr(m{Vxp3U2X-`B19TzkGJPbMweZkNuH`!Iwl~^11o!@7k^@sj(vEG6N z90?x7KN<<$228m$@Dn#!)3d?!b8?(D8)bc8q~sB3Ecer}XfK9n<^~=zpi^U@$_VIw z0r^OZqZ~nWquKba=UA2dpcv}PNUHvV`Y%wSr@xqu?#{=T;^WKwgE88`d_H3eEmh}s zBAfX<0IoTFLSM~Ea)jSer-;Vef&n^PxA4X92;5C zjXOx0BVxAqjp)sV9+a`5BYDDt{4Qoe-)<9G(6YTF3))r16>F^NaG`~b5cMSupG zkLSF_6Hn45k*O0ob#eSxtCZ2pfBLP+km~uuet=5vlWt8?*kBr`(>c;0#a&3{UapK) zy;VBf+FMuN{x|;@3BYh z{T!a#>u$klDf1jtW(!o`aF2$7+v;?zxy8TPorTLPy?TFEw@yrzYg8NfL)^Av-QJt@ zpt*N*7%gzuir?6r=L>;Cg=tH$WBfmvg%?czY zXgQhkf4i&lFKQ|(Jw|3CJ`)qQViR63=LlpJxYgEX0qc~YNCwRdp3qjbZ6NN9w2F6g zFPKds&wNe#5A5l5yA#zdiKumb)63w5+pq#As$eWalSlKbHD~Ib$f@^BcXq=J(S}?G88MwaX`N7}Yn$pz+#=%!zWB z$AMy@$x@~ z(-7mn5ytTj<8ST*WqPP#>=t8O`VTNJYT|Iwg({JH6#^B1LRYK!s@@!Idb(ZQb~?wK z>im*QCcSid6c?ywMwjMsu{iYx7PS^X%}r7DHCt8oNR{flN+A9|)&W%WgCK$*7PiFT z2`F}4wXL1$c2{RvCpE$Ac(a76EjnE6R}z!z_#`8}RNYmu?=}aV&pS^^x`i#BAjU%& zJ+b{D|d#^S$%^@CaqTed?;R!qgHpP@t=?m-u5-`GO? zZT2V_bbBJ?$i1w*kwv+r+!z zA#6MkIYsVbk>AoaV(KVfi0)E#dCUz5UTI{+QavNHoVHxEpRkGa6`h6%W{=JTmsn)t#*Wp|k9S$jz}EP~ZLqd*cZf_3ZXnH(Sg)q4Hn<=^^R zWTC71(oIuz8gQsMc+_kZpP4qAGlQTwM;_1MjpSaJNT0mfR1{ZgxHp@Mdfv{Q;5VC! zdfr?YZwp+OMJ{>OB`aL=rc312$f?sANLIV#Gnc4$$j7f-vfd>Oz4futCBL{t67Z3= ze@a-_t3(HHkZ4seNoSXIafyzX;A7GyS{2J%2??@yx5=(Kr7lUkq{<}&Tr$`tLtQf5 zC2E@hALo+EE}80*X)c-Jk_TNf(p_3v8X~vb5x+LwADwhm!$zYcZb;)p-j3Qwlq+04Kx?@P{zE5_^RF_P1 z$qbh~=#rT(neCE#mn?G03YV;M$!eGQE?MuAMwhthxjCAigLsJEa>DyhniXe#b9?h2H*{3% zZGFpM?vK~fH{Kh%o9En9(m!rzYbGrOCx(e)<=_4R=U*@_I1YXNsXl6pwtQAn4-jpS zOR1u6-2RR)cBRbaH@olAVI|A&)mllzJQxkWy*d6d*x}L~*B6JIjNp1%xVA=JA%_jd zp|)rIS#i}C-G=<(zKG^1Z-`JX7Rm)!m+NuKDaxU4xrcM81&Z#BUm)*^LsffU5Un?= z%Je#d1|P|3;9pd!)EtyO@Blvf{cvhQ6IC!=O%(e~!-Ck(shojS`8_mIuK>p!-4c#8 ztxuG_g166K%!{AgC5YhC*xWeY7I&$ks+%fw>}C$p{Su)&5p)fgYNkgS{V$bK%Qpf8 zct6Uxw=y2*<-bfg0KLE08(GykaxKt*ZlVS9mU46|?@8a!8tpScdA2W(V&9+Ogf7}1Ga8QTCHU?VSYKHsr}MPC9KLCN-vS)O5%6xit;7Y_aE=0 zMDNC&Q^%>mi3#`cFz#>`*xYJg zHq_{-h?8@InGB5W!l96k^>*0nM*4C2hoME&Kj)*X2+F+$?6||!lKy!BAcQKT|Ai1- z7ZXiDl9s^}(O?Tu^- z(+-@0Jcfk;OPjm2K*@U>TIvpI|*e zdwL>-D_;ugO|M^GMd91M6&2bFP9Nf)H`PD*U~v2?d<63HWFa8-<}7seW}(XLI$nHV zX13J7j;>6mvE`UynZ{OQ^D~Y4HD~g*Jw;+62UN4Kb=!802|On?_9xd((~S$&qVKF< zxLcg*?1+nncar@NGxFmN;Gh`bfC%6j0Gvw8c%qK%=2UtZAc0DkZU^(rrO};t3N;$+ zE76dH@g8HGhgoQ4cNxiH%*%`}%**EA%s1|`O%-BtZYWnmg=**jlfy!+I@ztpf&Us7 zE_zq!2%M!A-gxzjQZJ}5Drn$6tf~(v%e~bT>vd;vj(YKOHh*8fX@d57OeT?4Y%%3! z)f@EY{kW><+>weLJz*MnFOD1?|Um8Ru!J=iU<_&l*5Z?9p2!)Uo{#f`Y-A4Q7RS^*?9oKMj>pFbu^71 zn%G|^;p}}6*h$s3XSm?3{v-x{W+s?GE1vn9^l#m!CE*;EL#ECN-xEpw8$K2d1lLH` zbdj4!$K)@3g~_K3L?OF?xr3yuJ@_&ElyqW8 zW5W>JJNU1%w&V4OA-VWwg~Ny^P0)VK{4Ksx4tkiO-9AR!Bnd}>wk-RI`1JxotpysU)77hs zN6`bwEGV_$O5m?bn+I1{3od`}`IL$|t#Z$wSYO6v$le+%2e6|HnzH#F`MpMq0!mRC z6mFp=gUPQo;^@EpG-Q`ia-g#SGu6y!KuB1P2Yt9+K5Oq(b2uEQb)oNWyqj8Hr zrNb6^-um*1U)K!q7NvGw&KlQaA-&r*)(1Sok^VUD1Fk~595fb@ZrzOZ%v%G}M~0+x zYw7h=cpZxSGTbkKtzq5j6-Ca!{0%2esU)H|M|e-=UaH$67+Z&%C|UNzQb|<@EdNp4 z)j{uX0Dq?mCaJK};4;c^D4&M&S6!KPyF>g|m|qaZluMbu+yts|o!ZS~fuy@sovwBP zAr7V+LWFhX<68oOMs6i!Kj!8mP8v~4S)ZMl@CpjlUXN*Lx58`9I!|}Qx4x;$6SN%8 zSYZZ_xP&IY*1NCOWvG8P&wN@yVxsd&17G6kf?uRZ5wU=;_hY!8xl(ZxX%r<6UD-MB zD6jSAC@oOY1lz-UlvlPneas1VyyP)nht1POK&A?zbmr&&Wj-x2LHmQ7C7u#(mjV2mnN>%N^Bj5M5%SWi;CUP!#HkD0LY2|*neX2Pv+UgU(X^hSz#|Y4j7p;17g(|VKY7Ei0;V2WN5zrJ?&-I z^S#HIHU-t)%Dl=ptW3vvw`ES~u6<)c104IZ<%^R6V2vOpoegrQV&!uV+U1 zM?67ThcvCOnyZis8d-hidI`CdKf0UR{NIoixqobHa!*x(ArZyC8>Nw|+cA>+>6>G@ z&*0hQZrsx3ZqT-%4;-e)9Ht{;Oz)s2cd4(_;Can3Z5?8I++os!oDt@df|~TrG8&Q4 z>eXxh;T%_HUdIf0p?eyOW{}`O(G}|$HsR|wLR?AIxZ^B_JT$&fQ?h(WvQDjTKcw;x zHHr&!%hU>bujCXtj-%PuA0wu=?F&GA?aIB^-6RLC0vmlxXcXr5^(L!hrM2V9zFs?c zNWpA2e=Td6{9^((VQ>zMTln$$Hm2geWntoz@d-R~)K;Y8JF(UsMmKd z>p5@CTK-E+ybowxqudvbR9jTo#cDqU%!=OlftB>lQ=n;L(*hjEvaZAYhum&rY*?Wt}kvo0f*WIamC0+cz2X(;ZuGRoT{$O*8336LIa-&ps za6qB8qmWW{Uq*#?JG_O3RuS;&uTHp*?(HJssM9!=BxyHlI6SuFgLD)8N7&z8$;qmL z%Co2O`|t9qUVVjO!%1yx6Z{)7&or0wn#z<#d z1XQxsB?WyaepWrY%;Va_1?UFF_(g-|UPcdr@+U|QdLt{o}xdxp+aK5*&VYP*pGS;YOGirJLT?eU#C$f*wrGtyk zaKV%X!~(hhfs;@rCc#r1=>_K)!eMntn^Dc#=JVR}@OH5EOkt>(kX z{hIsdN_pH@q?A|v70ff?bNAs&>&(LL1jBk>BY+W{S)<4Xv+Tpz!AU=JaPyngVg631 z!n{^ic>76{uJCq;^;F#(kqS>lfKY`k17ZAII+_X_Zj(e&e2PgahIL;46|?19N57UPTz|>vgewY#p^Pq{Y9&J%?dhKA z|8`KTVQPb*IknP3^GPwyY^pH5Fjb&lS>WefX}a+-+v3cAdsDFN6Vnbg08%VVt0~G{ zT9~H^;dcI(qoQ$fIo0|9gc&$8FYIlYk+EdHonMyJ2%D&PO+tY0a*ou(K58=EFgw|N zO3?C`2KlRNl)sMrX|FvXK(OC|a~Gj^3ZMMn##vlbM&-cgyrZgZuZ?Zsw`+nbd{cX~0Ts)pCz~do89x)MmEMv{@AYg;Z~SWBvb|e-<86YZ zcL5pH9AtZU7+0s9Eo$06Cq^6gDhqzHKm)fBv6n41*Ke6w)9Q8qjZZSG3tO#7WmdQD zUE9CqW)?wDoQM#mP$Nxr6keM zult$REm@SC4s$eIl=-Q!rdNc7+W~)vq)Wpwe}|zv0$qB@OkPiQyh z4hZ+qQ*i^?TsFTizx%DfmC@R92Wsecn5R(O=NMsNl_k#du*Z2*!m2ymJte8_PyeQX za?v?WtQ+BcvI4Wt+}PGs8TJFruwq)O`%g#tOA?(Jef6c>-tdpPt7TqaZy@!Pw)fNm ze;;mHZ`RzdrB;A^fJ5F6~GC)$U#dJJ5K9G8R~ztq-otm8{2){m_=HP!Yh zN!5ug?GAlj;Y<3T(~N2)Fi~r*DX$vb2%6f*(fwG7qx&eh3AaRaTQ{RS^XfoTM+S5+ zhwgJ*Itl$V=?Mo?t;6YqeZ?;()%Y0uSSyDv5%&AR7)o_~h&|WFNkw>5QVox#5~=1= zC7e_>$05QT{gDjRJCc8dtS4$@Gl%R>U40|GjZNCH*X9^E|@h3Jz1<_POBsV-GG zLf1P&ePTj|M(7_Ap`pzPbq)zVlJB(l0lH$@QDVXyJXEOTM)_Bt;b?o~B26}}pwP62 z=5CA+`L=UpUsdAdy9LyYb|SLhPLAcfoo6$!1030gq3Ir%bsu?VKxS%0#^1HZ1XN$N zz)^bgXVc`FF{OQt(!mj>tC~^j7E)T$+5}W*X*i4ioD#KE?Yi&e3~(6;q@z@`w%DZ? z*aAVOIUV7X_HoGQPJD2c6Cn35TnVu>`j^s;8cG*X8+7_6J%}hiUZ`c2t_yW2i^J#y z#tzf_QvQ-IVKzU>Z~i+Ca+a&YQNjA>I+ z$-MhD4v|X#YJN!7T^J+k6(P!i$cbIf6MDF#(M;rU)DcMkDl5@&(pK>EUAOv{^(LjF zx;NJdcXf>WKFM|$V{#R35X#kLVrl9$SFllk*v^Nb^}CSeT~T2&v%xijKdCgc`6uv; z-{DIcS-px$TTa8%fu$?-e@on+&z6uMy(Nx7D81*$r zG6dZlQwGD12#IL_Q9^ixm4MG9EVJ=&=^`o5PoQWml5(zlO49B(<^ADA;E-Sc5e-Y; z4TdEQM$(x%@+L6)8b7Ifywtz&cx!UhJ?@Mbfsqe47$ch-BR0~0z!*8GW7v>(gM8?n zKh6Z+Yb1_K%vv=~P_! zI>GTZQi=1bos6%65ns2#OUT!eAzvRgwFvl1Yh)9LZ6c;(f266Sa8UQ~!)V|7mfhaR zhPj_Wvvb(+b^PF`*UBB)p;Kyaj)O(!zTqTbZJ0{P#u9B{1nwXBA++Jgt~48doDYEw z_jD}gu0jJ^Cip&;4F{~6UQe|J`rl;6I4)JP4}o%ce?r_ z4e`4w%vxvt>fX|3cVew+?*?VS)O%kci|oHu7r%RNjXXVlCw!VM+q?_$)66SLVyM=` zUaB!Xz#-(Su_*ofasfevP4I@X$DH-3tU4V=c6Vbc=bWkO^Xj{&TUn$@+Wd9rmU&GJ zab7`qb~RW920zCO>+Qmtb=hk>Zp|lklb=06v{)o*Hj|8mM+23LUh0rR#r(7-AoJB= zFaOd9DS!=b(n|gPMq+*#%tDkd z`B$b1W|GIBd#NnKmfvgZWy|waVi(ZG53Jy9SJJdV-Z(3Bn&ZCiDT&z}@~xRopYLUA z{+qvqHDR#YWyO8M^_HV;J^Ld64A;i!$onJsL}VhS^_Ig6t(_uTYba1zTEq34l2Q#u zt8b3M;u&`gsKg+u{Jncglh$${<#p@>EZDmqXLfzd`oN%_hnpvK=HVvGQfiB`-%)FV zSn)_$@e7e5=jE$Aw#@H$qi&q%w)@q7cOFq~QTfErY6`WnvUN?nvdI={ZD{*SM#^j{ zkkT6*NaC4QmD$_INyj_ziE+5zC-a_o2I$Gw4hIt*iAm>Trb3#m-ql}L#e_gMvx2N7 zZqSmccW)ZlG;E*FKIMLW%KiG3`}HaP3V+pY$9ZbyTUXX*g3iXRN1+C~U!Uw^l~Y~G zD>(9r5*)S8N^rai(pCIp&it>6)MKC+#Y zxK%@GnzQqp7<30;S~l#XFF=jlt*l^<)bjWGOTAD?o^S>#KjppL@J|ZQiYw*46NgX; z7_4cP+V$l(J_$o8Wi2-=W6EtB@aePs=a~7*OSf8wQBMbXs$Ro8l`N#hib&+&{O&1p zI_AZ&Tany>Ei9DABuDaE6$;1*bQc=Q`iSIo$b=*Zha`V&gyaZFR%Vlgl?`!i$Ja+w zjj}@-?7J@>Qvt&Z{cV{YbOCSWtIQ5yC>~@;cTs3uO;0Tj0Dd-nWh!X;CgNutG&%Mo z;-`b-0oSwgvOod<7B`=PJ1D7TGL*G$aCm z*T2ks(lRrzwXLpH238uD?db=)tvKs7HbixC5xIbibT(ZQ%m>v3Auo`bHOCRJ^q!4T z_EBAf$Lw&*%uQcwP}%{tO@-$drAO~ZvvLZr$5WjN_PmOb(+n|Nt$DlikC3ZOWR;y_ ze&#VN=0t-Pa~D~kGrRq)nAb)M?u8+T3eI6yX6fTLaHFbD8M{;1=udw9qg+@bd#%IX z_^4WuX1jNf1q+ZkuCE;q%;vTL8?%=*d~%bO+5IZBXB7Asy%#yeN#_gGde{m0yFJ2m zPlV~X0287g#>z}ukRvf1+P+mkSlidono)e2Mz{ksOqB{M=}>6#SJWoD_^nWAr5`q2W&Y zEf(1~kf7%l_umNDvp!AJm5kS3x>G{t0wKNc7%JG3rq{Ch_wti}D(ioP7F?G#55YCT z^gx(~)i0!HXEB#RI8}E-G!NDuVM2>}_L9J2j`_b98W+GDMKH`wbDYkQXpyZp_7DHw z1gn6~_KwaEtcQip*qF{*@Uqx9qH_=4XdmUdh zRp;M-j!AQ^s7MUg6R8p!>xuj}(n_RBMHKWz&V;f*N%-r9pJB09a{o@ESmkcKbIKjf z$2-~lqj>kfCEgMQ{NASKq1A+wcB+)?wE8Qo(9@5keHw;BY0u!9TuHz2yRdKf$5GDj z$2Y-xMEb0++zu+n;%nHRh;XHvK~KB&iMCYMf-o;*3*N`s{9DM(nwKiSl1kYuZhiJ6 zGVeQZJORVQT@g^lH-kN7x@>J|-cQz;GXIN^fq5V97|;D^Rk7ZMBbIz>YEi3rtW!&O zP+$b3!&36{F93I{?rzA!G_>vjb*Pr3cs8}H`PK$N4f!v%A)}Z7)^r&1uCZ2+INjs& zj_nxTy+u#`W#$*PrYD>K1i9QQ!Y#~Kjy~1PzY}uoZ5%bkN@+C9gr(N*j`l8&9`$!c zwF&q-?79U7ftUke#LGWINCcLudkB7VZ!4-oC!DB?J8bnl4qLsrD+jJNUmq7wotjf} zgHjui`g2#QC~O$2n)%*C#$8WwS6fsmoxvboh!0^fRaeSvp=0+Z$r&wiqOxePy#Pd4y}V6t>vE^t<|OwqxBg0Qgvk!t^8)Rp1e4qm4=p8%V1dA z<)g4#9t5kPPn>0<3>SLI;HqVv;H_Hzizj3p3f-_;rXlICW37^_miQE#+1ykds^#zM zgZxf1j6=@4b(7E)(>Bclk^kXG|Co0omCHW=^`J_6vksBenmCYYb2ER&61>jf>({w5;k;J%Rx&is=u4}|b+z|nrs!D=5M+w%Hsv8~W zeTlrzR$soz)Og1yp&EzrfucYEvq+6Uf9<^ApBMP=$~%x}$+S)6c`uY_dCK#%wzf|D zknl=*{?bMjc{EY?l~wMe89)TJ>#B&?|3}%Gz(-MJk3S(92uPd=j2jVkTv4N_pb99TT@*!x1X1L25@BoyQE|N$cU{+8*8_C0N+C(bWM7ogWTS_ zP5qzDLr*dX;E9e5(h3YsR^V-5HUAAAsoy;LEKYhFlU7s4$+~oq{nkeNbyKppmHiuV zDgT<5AMTbnrg^yFRMT92ji!Rl64LqSQ%S0Jt;^^5yQ!bMB0&4Z_Mi7<0*;Of@xOeu zm%iJ=531MZ~;ra35sB1XP9WDa~v-a$?-_WvBX#6#u91ASJ++gMy}oM zMP9AgopvTJ`B3dnl>MR$1^aJF?Bl>5pYgv&_rLuy=l-Sg=-8a{=tSM=JOkyhe8>JH zRYlE34A@+GY|uj35%sm>JC~ivQI!k%-NNr8drlS88P;KCh*W2HFgND2Ft$?d{r5EU zy|U40&x=Cxprp+mQF{lTI zms-A6z|($2sr3%=l8>@HgjT-;>cW4kR=@hGLi`sH9ht2FF}GSnlQI5#K<{dGjYEok z0H_tNeKM7m(Z)p4wCtV~2KducrC!6`dKvW`EQM7)Pc*4u^Klds1(g07-c$lBVna$| z1Dos)BXw(v%jRtqJlJZOJP@ElSVUOido^2QJY4cw2e(CJ+p- zHjGX3>LJp~tKPQqs=1r2S^w&<`<*Ir&0LK@-VH2P9ENm=kc>; z4Ug$@nJ%pk&1M+H_bv#|G4&#H7?t~J49uzLv9R~-xj^`9lkY}; z6~c4I$@!zbujUnS-uzqVwD zhw~if3(;1ysNa}!>F4gUKC(Z4A5+9>j1f7cLsleVI6f4t3`vuOE*3>Ol2|BpoXR1l z`Gqef_6Uw%Q41o(?EucbH(4-+stQ_z65eJsZpq?<_}hE*9S`9HR?wKsCNC&MGssTP zehK~FxFtuj+(V}S`I4J_Ek-%UKJDK2R85DDqAj;QAhh3^r2Xd0q5U$M!Aum$XUIPr3^+F znx9nY72QXI|M<*5+@Y(O_L6g@z_Mh4d_Y^EFhfqx<`emk^Rv$}J)x0|eC!i`lLZTly+i&3z@)-ZbMvK869;=LHS}xJ>@f~Ca!Q9#Q?Lp4`Q8RE)VxNwDutR7 zpoAJW3pF}Ri%^6q8xIu`Dj5=-^6W@qiZqc983B)s&H#x2J(UL3BF+ACxH`)k!JpNc z4w!GC!GBas{O^qx{Br7yUfKTDCxYnlB%*nMH}!p$oTM=4*`zS%_8)6oeg{8JTkhXt zdQ{Bbu8_LVTsog0S<7_0JMQ@2xvzvx>}~PHG#B3C(LdehIO|R<2GN9y6q5pwbW;`1BPO0^oA zwcafGfJ_|ec~!y!;)z{eKPVi&0Y7rx1rid@sL=NibtZa|Jn<3I7M56niNaosrSYs% zt4Vx-Qc!7HoJo}VtFo4jcrM(4VJNC}(IsH`D|zPJBXF~md5-hgKZQJo{q0G?{`)>s z_IKXwv48HTo;Ur9x_REzJ6}Nh(-;Et%T6c-4tBTr0e0kZ!Lj`?gIjSXC33CR3}Kh_ zz-0cE1!Q@)i#blN0`ba2if=3tmlm#Ya{Z5cbVeb@L%!d_Lxvp>X|n9&B=`N8^pkWx zpxQogp64gewX(-wtd07`ck8I&mJc1Jfi$){{ay|Kbd0f*6n)T-_|A)Z=!I(K4~CkD*R&v_4%Qm9}|=PT@FKO7@8F2B}RaV zy9NT{r-fMw5+E|BofOjUVj1NQMV_ICbCTsw^U4)S71RRTl0pUk^MR^B4mEKsF!nKZ z*euR}G@CKBE@9cqlTsbe6gA=W$CCCq>DHF^IGSfw<(oK8z_G{EPw?!q3Q9r^Ga%P^ ziJFVB$3hB3inC@p_PFO}2SiUZqmDf`^Gg1x@ZU~$?D1sO*R{ug6PMs1d%HpyQlDf< zHexo&{w~N?14bHoaTI6WC3$Bh^EMln!So`@J6}*@!y=%MU>*@R$-HlPdHv*#Ung?M zu>T1I?A9+Te2E$r4@)QxPe05eWQhdKSLH8&&%XN}Z z-r>phqT_|K8Nwu}U~D+S&;$D(xB&KT<4dIY)OwNYWlg?35~>=GXzV-Z|pz z-uZ_Xs!taBuUFv%pvh2o?Ed{(00vi(=M^5}u8*1_`=7pkYoTgkpiORM;sWZ2fJT6)JV*@G~ z9A>`-wT8>h=fi-$Ve70smce9}sk|WExCJ&;r03doU*WA#WpOzlP%{p!Ov0Gr!T1mu zh6{I+Labd37h*|;?D~PYO=mv81}CY^byaZR`d4J-$}1R>W5$El5FYxLgbVxT)Lzll zR}ON(+|JFDNgr$I3H|Fg)SO#s_2YoYMY;YX=qVsVDPI-jo&>p)tS=8RSf6Wm$VXL- zwvoxFavlhGCQvfizv%6Wo#Kl*N9t7J*b)%QuXBHzV;@t*VY&7(mIr0~Ev%M1uIS8T zJ)Is2JDAV}hGF)N=V2eyC=e;`auZ;rACg5vLA0BylEd%)GSi3*uJ^@>YCoh9HF6dF znPIBn{gc^P)lPZg87=Jw%5o^Qvyh#P)15D|3iZ!VRLYDS;ey{{jiFdsdTC*C?-@)hjE(wEwc{1?A)|eatcUC1mS-@?=V6w!{igsVe5I?i0+ygi3ePMag+Uw{S5de)ZJt!xf zQF;)$n*6imBnH53EG!A zz|3B3-z0#A8rCE!TaQ?bur`?@i0XEya5J$bTzrR`i}M~|$`Qz8l_}N-I@6o6S*CpY zet;o1e(tk><1{9T=fc^_u8nu`A)y8uahKi=1}sdt%0qRGi)x7ELsrp|c)0<2M1#Tl z0W3tnV~so^-1vphjx!}|eEg7ta5S^Ce8zE*zVVPOdC-$u-FH5XYtQ{9sgWfIQi66|2$!7yhH!!lvCrGr63XoEu7p9B_5Z)yuI@>4b;WR3fCrlt0$exDNFTMCx=vMVIY0QMbJnYB#WO<2 zAB>k7sL9@*>{MGc>jS-S#Er8)z$zPJl=Xp2`{H?7ADHH@545#^kXNbe1CraGf-@DL z!}`FsDW);_v-JUiZsLKok<$6Dfnipd?3>QeF0$;>xDLY-dw~hk&q{ipSE+HR;TxI- zqs)i%?ApTOp^hz_yUw^Ah#Kid{agg$!+|y^&uI%7GFI6SEzwmX(c{f(GI2mVcK4*= zE_%GsK!)QNIyjnd7Didavj7-20PPo;Fvjtp0<7B<*7r$R4sBu+9;{9dEbg_3D*LAw zl@t+vi5CAUS^Nl(loP$;@1E@m|9K<)lPQMq&s%GR-@d6|3Hqab8R{xBN@a)Bp^@X_ zgA$69J9K#RXr#zCREAqu%d7k6f5{J%tiLe_z$_xWTCHuB*3X=y^E|-iMacTt-slqp zl-Tj>MSzDx3Ie>y5Oz<}nfX+JYv}$O0bW2YTmhy#HL~x)ahcs62UkO6t_W_VJy74A1LZ6oosEUD{q#Wp#N5-Jt_$@_A zRlBZi{9EeEAkI;NYYev$+si|g+o-_#CKYU6Eb^gea>IIQ@cs2HO{K-E(m`SG{OFI= zL+SJS$)wsYGR8pB)3|BU9>p?Y@nS#)}emWTaiJJ@ag ziyR8}EK@eliTSKtWtaU`*>0~f?TnZY;$?Tw84%5OUtzmW9(-Lzb+nIkt4Re>oFm_X z?W+>a_e#ZL**6F}aGb|!8x>waLiQI&DvmmX;|-Cp!SNB0uuTugO&*TCB#t*$8ysAZ z0#KxY)#NvN$qx}NYYN>>T$yi%Wj!;n4H~|_*GJOQL z%6$zoHFBS4Qb9P?uv$6wBN9am4i)@z!Ll(hMn5f$P;Je~%xGbG0b4rlkJfiW$C+9~ zo;p_=8!Rg`cAs9}Hqu5AfNr2c7ZC-55A-KLh87MM!Lkis!aTc9VH-SG!9a_&c#Z@5Ir4w4{Xn^nW;pV{FSV4(Jvs9K>s9X1d=KSmLjqs}4EB3JFPM@9nW6~V z#~Z+ovTcZ7IXgs7`-7FR9vWaW94 zay1CYn|x7W{hs2a?ff)xloD5@9{NTohfy2fR@wh5GNB>}W3&q6N>3QCp>M7*E+VbM z_;rO*k`yxa@&fVDdQhfSSsmu|H3_d)FZVJBVPoQgW2F5!53B|iUQklgJ1jX1&n>9! zfi~@xJ1N*A(PY=J#SPG8J0SMOLm|(pWyNWd7ybW$jbEn=owl$q4)VT9ezR97!R7MDM3m2vqeij6@FZeMGec&~-u z?^UcCT_uiOmR!{7!v%X&%Y0dqFX{82Qp<-?71sAC$d6%&Dr&0mm)Us8??iuIX)i8$ zi?mxz6JMZI`v!K?%4M{y+@67M<^D;E${mnWxjL!b@l6aHui~a0&JPoJD^0+S>dCUN z1GV5gr-R_z<>1?{fP5bvs`&0)(Sn(`diYK;`0hu;TxQ-Z_+I6hzJ2j^)G80J)+T|_ zSQ_pED!bf{ypz%#82Y4aN|PW4PZL0su!b{1WzPvpVEV8-Mtl9~+VhpzubWie+lkg2 zb$=A*ahk5D?yJj_>OP*ds#7~ihAr_iqN7|{<0$FZt2=SJEFNFrVNA0P^e0>iSiZ;L z0b4aa=;KnLcXFXWvFv{sf|n~Qgb&00tv~U3|?N~ z<6c2-p@xS+>-e|3*zR z!zY2fZm37%#|V&o1u_Undp$!^er53MbXDFcrK?iHu^;+@uFC&R3^`4TbG}h={y4^N zawO8&CSP~b%Gxve-)S!{&6OG24w8#M{9=-Z^dtYB{ z*vlC7{3VeoC22JTVKDspd9@CIWF_KUt@n&1f~5v5^*)JI{6XPZWO7CNb>%YQM*rF6 zSOPquhMSWmZ}Ce0+#ilrE60XbqntJUV9=SnndFZ4tBz+R!WA-aczhrlT`S@0k zLL(~4cQ0~d7LbLYa;ur^#Oz>C?@W`!RDm`805o(9YP`xnX;5QBRSpqQ|4XA%|Ljuj zLvC86L^U^MiT+HJA@S2H)H?C=2H-2EFhV`o1DJr9QmgngVVuF?n5t->u+#*19Y}HG%1@2vfS8}=&b0NT9$P`v{C3|d8 zpKT@R;dKHXJ(LDkCIAvB0f6TVVCFw4?=Wo=5KZ8Wsr=zQyDG7`ucH#@3i$7soMGHF zo55SxXKE_%B6?}^UHx-9Lq0iK{eSzu-ZvjRsz`Nw+z##+HlZI0dXYiaZSQNK#icQpzLq7ep>KZ&RYz@diu z9-@T+cM(l?5%~qtqdw@i4mOJsVXKq9yHWkT28^D_v0Q{=(HY+zq%#m2mkrthmqp$ohh>;p7-XwMgbA{&%B|WygUj2D1{IGj*j(6fxq@l zr+$|U{NJ@VJ|cBa?r?5>b{IHuq#~#=2z*Hd#{e+YfE#rXybVy7f#X~RK0$DwBIs95 zB;RCY1lvk-CHMnpuBX3<>{;hqWW6u(GGtD(FR1O{5^x_fqlT+q*BaJ|MjBnoRs!gS zI|PJM2-tpxQ^Pt!nwk%B2vFoMVyMu?MTs4j-k%}|Sem`1#zX##VeeT6dAlU?xgZNQ zFjjStF9o4n`|&RFc7pr?nHJJ+2AcWHdg-RrjFuOiC7Ybi+M^#-WrrA?AHAj(zEd>E zR5(X*%DM^9xbUhJoO_C$3f~FLW@0`ta_QO(62?|C(xVB@uop097u3AtD9E6jh3|QM zPq+W!eCJ~?hvq!{IMix9z}+{pG7YWc^4~rpL*mqK$6Rn3taMkM&p~K z+CjP zt0_ola3=x@HRQQ4iRm^}Qxk=!gV-MnO-v)>z zrD4k5fC@GA0=7}e-vQ$)V()$YQ;Ianr%AjiviiDWD)wX%QUl?JM<6X6W~O6wvqSp`~nezNc& ziWq5Un-qrd;^{g9UH!5uNC4ebEeP6wdkL#HG5WW`0hwi#p+1fJKA}E3ijwYsR;s*? zxwNIcBwksWeacmyyuQisLPuUNkx&q8IiwZ+MqZD|HzTia zvmAMCeK{$w;U~9{*M-IU*zS+KV1S7nbN3k@?A|*S$yl2DkT|shuU~jIuGQbhQ&~5$ePw$352YxNI}*c zFDhAU&?ZNXexl1Q`@D_;Y^3<-QI0Ha5{f9poN|aPH_A67%fs6tOAGH9KnacQt+Jy% zjX=ck?xKwfH8dqzwZmi8KqxUgzN~Py^yAC(v?G7R*V3)|KtxJp)Le1_rV8CQb|a;T4~8UE1m~ zo|Vl^^W=9QbeQv6jQrlrhgZ^uM~cro-;vB&B$R?HzH_atNWK}#)Zt<^dTk2o=1S)K z6UEAuwIZBdG&U{Hml^7@$RrrB>Nr4N}Ub#FPD`MEhik6TK3al<4ECko4=?*hSq% zK>p}6hOd{NC#AEKrSJ4gUm(P)8nrViQKL(rS2el}Z9SW+$u8h$NdeSvfaWMrPzLSZ2Cz}d*_iVFR8sUYC0->`+~HhwhW0uv zA#}TIClLcV(ow7&bKK!*sOwD&0OB>zX>IQQucxUypeWQZ53-H$yYTFGPtr&?`5ptD z{3)F=0sz8JAC&IrRptmA31(qwIg7{r2Ot;aLAfc<1_XVkJRt8p82| z9{h(Lc9DLLpDA}?WRI6Rr7h1q0!{OxLagP|2But1mu&wWd-Mxs^j)Ri$(amuRgrd6a>cuT$sQ88auy5oBiNZa*26OZGY(@amN^RYnk?4y(lsJjLDS_>whIlgjv0nD=C%L9^Cn+RV@vLg-FZ?(STUXW}o&M1^Scxvm z>~jf03di>8AIar;1bTm1*!zp&tka++oA}kL|7cE;5-s@)o;%vRkuPjaN=~r9XNbds z_nzpIxy_`4kowpzeQzFa~Zy%A6i#Nu#uc8Ul!yyXfHTM1}6R} zTMtB_?FOh7q#1(ml^p@pG`MQu+5%%ODS`3}jf3k<|?Ih|``LxPpf&4%Np+-Uj^N~_Tr70gu zntbbr8f`bt)L$Ix@bFJFdyJ{-a!Ht@@J%YIU@kp(3IK3QcV zrRSI=J=4JzYFO{l^Di``$roxt&n(_SSrB@mLozk}%l$cNkuB+Uixm3Zhe?6$$pUX5PXUdnmgD(e=EIC4Hr+l=!W}05 zi+(}M7T{sD5G_t9XeT&ju}gP@v-~rG0)*m-50w2d`NvCvpOQG=hhg!{*oR6%ogScR zqpo=bhRl#$*1J3f?ccvtos}JniDL$vl(WgNqRG%(4XP%)? z1@cX58}rTl&C#dH|AF*X_K}Ar_31JUD%9`@;2eD#DH11CjDo}1Rkq4r!X8#fpX`8S z=2{##a}UY{ChTWy&=qB7bn2z$i)VZR4L+N`l>(b0G*vKF&Jqldf`|^7oWi z5p%wb)J_hgoIxJuuFSQ{1bm3Bub~6xIQ)yP>}EJXwSH65UvQ~0Ug@i2)#CA#mPrNU z*IR*n!M4hNe5F}8yqVIg50tt*>z1a!44<1ffZXl&%E-xQmmVTQ@keC`4aGJ-F2xok zi#-4_MlhEPLhbhEn3S|_a-I-VkmLTN-QK(ZrPG=Iy1NA$6ph#-O6-s7b}-^<75eK* z=nhO;&W^y0mq3A)EfhRK9uB$XzC3;Wk344cXwK35cM`%}kG{{3ap@aR3fe-3whd^z z(?!_%&-66Yb*_BO>!-D@=KJ&yBE?^e1JnNVZ|nzSXt%+2azAh+4H_h)rN@5s^!hD` z;O;4}!~Dh3@P7?_i4;$-bo6>62{b3oo}+w{36yI2#vj0*!0D==od2lXy;@^;35qtF zS*zfa!m4XQ;VAbMBN*7Z5p40%(yKU5y3f*$*iaz3Cnj?b_j3P7a`)CbkWGxe?6;VI z1IhIgBXp-Cxh`4kM=~3Y7->?>Htg-L9G~?s)rRVSdmOKSM8*MT*6Ks5;cd;^ciO6@ zD=CQ-Us$31-lb>w)z&om2J%9g6)Mv)o(%SH{#9GgGH65#*U1m0(QOYw8e4r}q#^Y( z2y{CvLr6FIejj8Q&3Defm~T2|-dwrqy6Y19ggdUePcLt@|lb+GF7~DcANVG^^bUZ$T&P7v6 zo>ZOC%YLsGkRWn3++$PA``5Y{{6v2jhlYk5?`j_I$0l`D&%`MDEozVP`a zT!tgltA&YyGIAT4-u_qdlYx)O!3(r-GA}|6BY@@Dd%K4{D|h#rGN9~9zcMd>L*bBK zle&i%Dm!~DDUW65CKiVyOR~aN$1HlAFmG^P(U4vfzJi^wi4G_Ak#3bXgl_+C?OD{t zG<1z5wH{ma+zE9EcYWtMglCmH@w0~f4M*Y-ZsIXgJfR#Q(s8<+go3!BR9S&T7%JcR zgDLm?_;T19QJ2n_puLFhonylR?AaYzIqWdE&ZWxtHSuA|_TP1KD~Ef2 za@ux}Eq8;JTYspvVasbl*fJkVBgN;39kvW6p==q@&t*#=`NkiWn~Bp^KRFq3yKLFk zRY=fYm~dulSU7VUK;ox(oRJSd(>jD23gp8fw8{1`6O|>?^$q%|51$;Sbwh%N$Vm`s zn3hQjru~A;gK1|a+ZyWuk7-+=%QcpL^=C?%^6Ep`VSE*7t>#62wVJZS_|YFa;jGS< zT>OO^Zjd^I8W~<-h(Lz3j=+ES5#zMN9V5Hj*7zd3IcnZw1DP{G@vYX%M3SWk8@W^Z z2}+yFoKA|$WRaK3%uH71&HIfZDKs_kRC&Kbj1~KJWqRcr=7D(9THG7bTF92nPj^Ty zZk2reF~OUp<8Ff;IvNjm>3EJ5bUbgEx;xpNoCX~&CP>6ctx!jX4>easc4cWpx0U>% zhEI|xF7+7oCV=Dj%MM;?tqbmxj2|R3_VY5{MaJf9bRb(^f=Bo;IpSU2iZ|vIq%9rVB?)X)&d2$a^@=fiFZ_8gQK*vh} z>5pOV(g<@1abfYm{!CC@-sC%jU*+@3CCXULe>$?IaGPze9f89*7G?nz? zf0MH`cJA-&hve2yf1R9)#BNr9-6#^J)@PCwKIuf>`=xR?9hUwy=R+1~cT>nw*23 z(l(dIuHB7rO8j-gV7aDVCgUpjm0?AHnOTv8@k*_o^r-7TBq>+CTc4I%w(iHLY6D}Z zo>?gqrEEKmbvZDcvDW_lkxtUTpA#j&R^NO_r%R7R6}ueAUe)9D1EosPvg3qv2;IQI zMRKZ&{nA67I0TvibxN0W2msLA($i>X-|v2q;Z&7;68N!>ZS9$S33E(pI1LsaVD-z4 zW-xsjGILdtWmz<}uyH#Z5cc}(&wzVig(@sKKM`4!P)^jh<$VS5{lWpcCtNu~3qJr_iwT9L~fz+46NIkNdsehIboz?VI|y0!_M+= zLYUZ?Cf{a$kwu!lrjMG~a7?W8HZ`$fkEoBlKz@vgtzi(B8kzUwAxRValr+1`Zv!5g zgZ8+8q^E_%$iBrm%pP!43E3YgmBrR%ABxlyf9|iBmJ6H1uI5t$g%Yx)VC>r8t;r`u zzURx$d=V{~#%~BMVaO^9Q)zADb_44(MPrPq13Sz+1lg*7xwl`q_5JE|B+5k#ig=xQ zMzzNd;V5^-ly;!2)K@fyd#mX{N`e{2zG7(|f<`BzncT`++93}M?4UV&NlLR8NQ%79 zPHC$RR(DX4uBjO0LFiy#G01~@kr0DCs6fK@4-8rDLslp=6ip9B%h4g(>)x0tcLn?F z|04F-ggyR5XhX9O)WsghdhENHR0{jVyk=0xQfmyGt;0DX?U9E>FBx!${dLEY-?c@~ z!0^}Q@FHY=-{0EAt^|MGP6}6A+`WvZms>x3{FF*n#xBOh!X@Q6r%EfcqcGfmL>(pe zcRs+JIee0$)*p_YmiwP$15#$ka;u9tIJ}iItE4>Ig?A3)VqF|N?RbADEgridor0f< z5vx6RIYfNHclU`EUzoR#t!CI8`5?ARRC@>OKx;r|v~BdPOv4Lqo{B8bM1w*_+x<5k ztp<5%_Y!O-6j=tNztI>H*E9_`WfPt;Sx2ieRZ(T#*6rA6k>GS}l#7S2A*l+CLB!rE6k1*AAANtq<`Ik zgo5~^Knu$_)%&%2Tx$$#bW&FNo%-&R`tbI2{7#TEAHzv>*FbQb45 zD4&=KaLM@|attBm5JHLp%BFz!YwmeKeaDd$aRw?E-r;o?Uj(9EXGTB&`(l<<>gRK$FcGS>eJf3ToOUmWMe{ zB23Fw`*r=VT(HgPwwLvZ7oDCN3DFex(!_Ecrjo(Oj1p@>@^u)C# z%rmjvb`_pFvNV`_V`YiE3tPly`Tk8#{9Av^u3uy(oH)QkPNv4u%GuQQ@_l!GLs6@*X(9w;-eU|>1*bC z{av~JokoP~-wB8-?Vc#FU+S;F8Myc&GbdPCw8dX1u0}XIURE|Id#!t{_+LfHXY|f; ztJ_YQjv2>=ujST?L}20zXs%i0k)V#!ue4dY>wZ;@X%BhL%IVM;YUmGmqfJYYiaV1r zi!^V!|3v7eIe7&J;U#t&mc!Qw)7ka+@pfRW%UAs19adCCN9UAA$7YpA2X*9MF6nL+ z(TVJ)AJi)m*!*9yD4KY5UZOLf`cetoLtmLO!B^<4V{Z?EJk%g@d4{m#QwaMGsVQOg zw<%%qBO`xws5vn5M@9|X`*kiO-j0M&l*ouA614|Q$i+cSSQOYNZ3x6$9k`1 zt-xNhk2R@a^JYecDaI)FsP{9ziNR@$K^LoZF=xd_eNXXfTF;{FLv#L;Huc!>=tdM) z;`bOtG>SE5M+(y>Wm(+MEeBoe5>j{xq%46H^l&u;3iv!#)DaFc%zIM#yw&6LZwDn6 z;AYaw=fmc>3L7?4E+xZ5a&b+Vd)gt$`qrVczqP|9v<>h5bBG#AE6N_{Cmmx>6r^%W zVXXJI0#RF*;!j;pFW6t#n{SabxDx#Y&BSFKvZ0-3V0%U3IF6Rl87Qz#Z#TMu947_5d5!+(24(eVjO-IA{jURs z=kMB`mx?xhCNuc;*VB5163faXOS8BXEnGAp$A8l>r$w7AK5l$CGOZVVa%xu7L#Eff z_8RQHfLf_g&&#!6Ujo?Zg-i(jo>u$0ILYd!0hyxD(uW;mvoTf}URafI!1J(THA6m`J#X1b{5>zGB(Ck&S3CXzCyoR*$t}WFR-T_sIl7M!&C~~PXuviWoBl>~$7tg;O0=tkBI=jqd!>iyP!G{E1SXn_ivMk%s(1u# z#9S(7t9Hfj2h!ubBEumj!zH4wMv}JykrUEEVdVK4&|DiB>p-^K7)Z@SAv@WIS-v)> zr`med_ajzZzj9jp#@)Hd-fCQerI@xvN}0A~4S7nfMqNXbCa*E-dyQ8iYfh$k?+v_CrVnyhv zMWt+sv$`y1OR(zik9Uc{zQVUUwN5wOLx11x;0kQ2Nd@&N>HL+K&n+!%Bf;?;NXSt- zK>A+zsF2zwm$F_lJOWbGgOu+=`a(RCdN@?5gA73jMKj-)bG*^%Xec)_JrAXukW2tEU%_rRFu8BE&V5Y@_|cw!4m0-s7llXtu}NL zy3%o|?Pw3+)*T0QVnwu#%YfUUkj}sCRD}ZiP#a8TVZeN-(twGfnx|JXPhTT=^4w!6 zhe^kfqTM zBgy{NCq{BOX}Q=Y96PbLr&HOhmNDvNenCP}B2$?!xcQ%&$|WUJ+m+F=A86<6YC2yL zUX(}AXKr*#Ze?`z7VUoZce`IT$Kem9=frGg+sKrxw3-}?26YaqjNp2-Ywh{K~JF2g_mn+&_F zDXcyAT2koD8D!%{P)g@BhGql92m^Oh%Zm8y&_ut zu~7AUp-#e_hftp93?*3C98-ZWzReT(Yp8dqVXWsKK7-4yz*mu0fnPF{36Hh*YmP-} zM~xBQMN5cT!Stn>X`+ztY#!{p&<@7!Btwz6CA5UQ!+iykZS%APSX2l#{+1hxd_FxC z`7JT+`O>1myfz5-KipopO0#AQkTjXHf}wN6k;yq}HPMi@*fiye*2u%G?{dZy25Ehm z+Tgt{Z@N5sGMctNE(igraB%R?a7}DNMU)brINl3=A zunPVp8>C}k7)-$j?aIJURa+&Q;^_X$#JY}+m`NR|W24DqfHsC+jVd)^uC$!dzD$D3 z@f4wkzXLMfHq>ybfadm)7daKH>4VfJ;*EHSrazIV`Zb`ZP zdHtl8{NZjB-{ZWqUxUenkemhjDj|K0|2-QgCgerCeK04G4>b4u@=OS_|Hp_HGWClKrPrYlC*7CiAvs z7s{yA2Z@6nm7-^G{&AvG?kvJknMF8@o4oCOy35kl7k%2KL=+ON?s(ky4Ye@(@pRZS zI#H5og)`+W=K~TY4u`@^EDnq{J%|MO z?P zoMfIKygX&(F?`vGNKu+OQRU0su*c!cs2f}I#q+;aR-;5xy8b4;Hs5~U#3)Dz?Dip2 zqjfZKj>JcTs#(}$0uHLF0g;=LT^Tj`kzH+SPKxaE)f^|w<)SZsC3$PI^cfz>^B^?N z#5QYU_I&1rF%1cqcms+#HJ1|cpa$7;m@Q$p59#R@y1yl&IX+FoTnr?m9^Fj}kFy5J z!G?W*lv8!Z)0X?*Yva&@^kdcFAlt z9-6;_#t8h&KeVP-&r*RuHruP|*c+tTBdo@51oOV(AuZb9u@IYD-%&i>f+v{zq z?~_TE_cQ2G>#IQ@FSC3Bs_3<(SyJCYD4f=a@HV$b7fqImKbr*9)&n#KK&D2WOpWd| zHF}R9M|SsK@7Cyhf$cDcHXVPYG3rf zc|ami&Wah3^JWw=QXJNOOlYCR3^J7#^3PkA2nvfGwS2$vG%TB5<;)g}?3@`LH14mKA+iSY=F;*5|yMbNZCm z9zSDB`f0V@XH02xYVF~(5gC_tr3>FlUrPa`I5Ca%g!tG#SJieEK?O{<3~);(a!g2z zSISG0*UEJZ+(UWH?|LcBc>gOmEq&lGX_rUS{R?hdPHJ|j4el!z_~z(8$pL8poB90D zIxp5f>t?aw>>K&1h#fuWQM$bT{#%&K=G$m_8NHOl{9-58$d_2}t4#)*13$^|RE3f{Yw3epUB8Anqc|D9LhyeDn8b`QX;>GvJjjGA|M@&&)d~EF=Pt zAn^LcH0}yU+_I^TzF^E5sKo-uzSSv>eRb@qi&b=-&UzEMCFe+4@H@I?cG-UyxVfGR zdf7#fXD}h2R&)Hmd7W#{0!P0Fg%c@R0?ov@Jg#>9qg8Tf|7q;gZQ0JWv8Vec{P$`)BbAkf?KdZ1@}{fIc^@g zm(Q)i#VNph{pdfhl^XcFEb^bX?(%Z~v~~C^7s$s5&gjyaF06x+uxgI~(VxSKHK={6VRa?HjKbK|>SX%yS^q1(!EYLSxI9{hX8ZceV{Bgc z8Bj92EInzentAU`$Poq**ewMTfsSrv9>$%jPgp1o+T}En0;1;7Bm_aNP__U?N*n5H zTBSZ`AL>V;ajj5)g+~x~vrF0`-4J)p?VZw^+d^2W|IJpkAQ3o=3~Gs6p>;nxcD$SC z4f2R*qN#1^!wk2?jhIT~M3$GP@2yo%B?8TJ9V9m@00_Ywh<9TJQG5e;1ab0ABG5=a z>Dn)<^*&6{50A@-zHa8T_1!#qcTyM@0>J1;BJdV3TM}m!fZfV;3Y;kWTF2CDV*A&prLKipJ2Gbb6imZZ~*;^jt44;K>^u6J$ zjq~1mKiv3jr|=aUO8WZKDtf#f-nq3jIyjS;e~JD@78gU2;z5KBr`6^p0%JkejN48pA~9Y!3Qh5EwI4#(_%D2preXhyLY2N(J9iiO)3Z> zWrmOy{4nwV;nfn9_)envbyc&q_KJm4137xTHjA4&fgYdlf771`95F{LUZfT8&8yHt zp@-%6NOMUduqy`Y*g0+FwiTNG9*dcO_AlrVPAr~Xk&~!fS^H!BV*i464zv1WUlf3m zMYt1xX{@9K=A0m;$tv+{FgY9>oX(N{;U4QNtj#RRmr6g99&6+5(hdpscK#SY=v6}i z1+D}bn3;I<*Zqh}IEHKej?G$o@gWtli&;n)pUK0(SjCa)iNMiK6IJ81T;Ftwo74Gq_|A-kN-G@FdS1j$8pu%1I^sG5pl+VY*F z!PkvVPI5?fnOt%GiJ6}ZSwx6_Krq;xJAh;bj^BDP>)D(@s~QgeqdybDq-kJ$;$0D!dz3U-waH|-iK6t zz7nl-p@u(M%Aa-Sa0KDca3FwsfI;0;*km9rq`bKw&hRIVXO};VwmbYe$D_}S408Dc zzPxf~TDmN77BX5+|C{;PAh$)qKl`GWkb5*ksSP^&bF_Sx{T@dV>HB@khb@NRBbo6x zN5BygEs^=Lz#gedbn*)RE#luo{yopX$N4wEG*(9V?_?$=KV*kmkU&Yv`ayJZPE$!P z>+SNig@3mF>}(CUJeP}~B@_Eb>4w0kHP@?%+9?wCgrY0T7kK{hHsuQveaNIZ|Ktj1 ze#mULrOBHCKu+0UUXlsi3L2nlsGj5iq**2|SMZ66!ub-?|0Nw-$k-kFz$saJiS6>6 zC;tWQ#4IKEfKbC@Blr@8Nh- zaM()Vjna`2iutcZ{xj4G*7*i&N3c48TaaAgfqPcK-TUX@DkR1$@WBlNOU2d#R+HBF9Qc!q?u$2PmzWp+tIGCzD>W&Tg$TP%>D zw2gUjv5g&MH@30x+oWwgO1jB+p};lYgZ9aPcdeMI>+C^%vQPSquy3~i9Ki_v6lz7> z(W?@uZTrq>pBD8UOCI$$mam(eY)Ss=Cg0wJ4G!ay%=w6H8<`k9M|%M?w{3xd{9m~e zvee?93eKM<(247|XCdX1f|H56Cs8SHN z`j(a2oXA({8QXtd+@>be|LXSkzb?+vMAom1Gd0n{+8A1h#i#jS{g!t3w%Ib6-U2_b zO$6@a!OE2;^6{JL2y?_vDgTWWeXC-!G%UjzQTGk-8&ew!TVVU^@*`?bEC8$xHQ;g_ zwwE_2*?uBvas}_N<7*m(Gy)Z!BN-$@^>%Q9d4j>b zS$<3f_W&W(Fu|+fY@S`8Tj4_g;!>dhOrFwcb%AWqUqmIDa|}0?>9tYO>tJbFI1xCd zo_01mYYf5-6X&AAvEP9V)H|6sg}2p7)cxUKsDa6Bhqv3mNupj)T6sH0P%~*t*-N#N`XGb(5-km6SrIj^XHW* z2Y$IuIdGv6YncDNQhAR@;<-p~mIbih$Sa(@}3ep^Zr6))G=lmC1Dq^$7 zgP*Tu`j!67E#jB`Q8{h{+__+b`_~)XpUaQIJs2)<>Vt=SN(%0wF79tHNhetT%gJme zS)B-s66wl8Q+SYjzN81uUeFKj6+I)~{b?O%$vt#gz?9=Mw?F~6Irl2JCK6`xg@hx^ zQBO_r4bU|nNVxD%p@V zLlyfL`M;=f!WY+l6S8KjZr>sQ7ri}Uo#rg{m#qj9-{#d_66{wmNqtknNyzz&l5eDa zBO;_$I(b{DJ7PPsR>gLu{20B+?l_wNS?T1^wv`}UwH{9u}wNxmt2Gn+7q2r@qI+?S#p$2 z24Bq7jQU<=Qt0IN&pqw#RHJO_#}C?Fa}j(tDywSSg(6c&rV}7xRTj`@ms{%- zfzPLlB`&)DyhPxYnXd9J<7KEp+aZv;N_VWIgram$2^vGtHiI03-ZUu)8s!kwe5G{e zB(kaqtr3%60Xx|LGCKR-EeEGrpM;B={be7{6^csb#`gCTfgofEi|Xa1phVzEc`V4R z`B*mVNt2ie9LTHul@@a!uTO(e$}+lOs%}1CB4bOq2fDG1PG5O+YE`JIzqk?XFn&XY zwM;I$T5UgkDcj6ie!M{JVxp&x?|i`FBRwpWIVzf!?U}g zi=)cusTn$t;MpPYCER-RKdGIl&LXJW$H)rgQ~efyJr}a14T@zRX^iVgp47PXqFXD~ zxMWJ&Gp>4w3pM0}+0o@*pf*IcF)7$K_cPD9mW@~A`kEh%>n}o_Pbd)MYKv{PsTr4I zTC;BSOly>=pY|TFPO+x~{#Z73-S+sp`m7zHvd4(G0k70k2)_ z+D-j{8noxqV7t~8zuqw`yP^kL@r#bjrdFZah>3Zdso_b)3Qy&5$N0r9G+{}S=_h~$ zjs0+(tDif;gu?DHFA%{rVQs_7kmaapwvgc1(f`Se%>0WUPswPW=((B>;i4g|(=HJ* zt*CT+W#MlaEw8wqszjju^(wd8k%>TClDH5F_Bs?sa)+0rP7E_nfqJ9C_Q#rn-ZiAC z(O;;5*grq6{h#YLj?A)zElbcQ0?$p>Ua`b>JOEol4UZFG_rbduug?BGFOXvN0IQ7ovZ9^U~z)FAtT zO>H^}x~n6_%ZNtCV?hejMiI;;Ef;TCP4{BQUheb~W_K zjT6=?LRIBe^FurLrtAff{s!aSp&F7AhT~z&w$YOu<)M5pAgg2BW9Plfmq-Vemr6xZw=`FiFrdn2w%pxRt{pSSR?%tV*bfk&~WDO zhM4iG#2nfh(EStB%O&QE>ssf`QFUm{{>2pVhMeP8h7ab`|yK=)6~zXXJEW<+b{ z;LN1ye=4Tg42cY9`Ws?Kq!N?f8qoa{bFfRyN?FU(rO*_$hnOcu{qbj<`JFzfA?77% zz+!=w{CJ$HX$|Q9iHQmb;Y^>_%4PiGug~`3DMv7us^-nr792Bc4AQgiD?Ex0Hx!+o zO3~-FtyB|=er?^lcS>6!6g}BmIX0rzPr|c>9JW_UFt>WsMO3JdD)|s;(*Y}Zt5az^MD<}TunD&20%v(C< z66ZI@5EJ9a<4ZwnK=-dZl`b)9t(AkAVELc=n^QbuHkl?aEtQz3CjNhMW|4pp&P;5r zoG5ZB9Wi_Rg5Uo{k>}G{Hl`Ia6piD@W6vS2LEgXGoa9opbwVpO5k;0g+4cc%IxT0b zrgfl4(Nkkpdp_pJqi9xZkoQl~0|G*LG`O{LLQzmCIyd}hrgbBIbi<>7p=e+#MZa9z zN?rF)Q3sczH(Dzv6v;s;_NLgMQS_~jc0{SCU#UD=$dAXP(XBz=KSh%Tgz%_GYvovD zB^ZBcD6urM>r(%%KH@ldRU~Js1k-Br6M_EYsDCW5<_aMsT%8DDN(hN`?<9oe$zcGRxm5app;5_>vUCpl zE0Nw|WE+ddzOBDFT|+|cm@rHP##}A6sIEr*{yN!_?Ve&JM;P4$A{lXM)Z)kr*0ic* z-&(A9hx=rUKyhb&F!fvi53&&w+UG=nokg-w z4}V=mUP+`)-ZeT38Rzy#>F_23`4oh+X>e91*yjnY))pq1YWnC3aA|B{8>VPV5`k;^ z)F7*2iNL`?B8$H+j^bBDgJI6wRXVv%H|m=qNtV%r&5hDiPb_IB)Wc9!+u;q8@-FW0 z(}S=HYrCW~_$7w7-1>w0t*ewuCI%A0iS4M%?9x7YP6Tc^(L}tGW~lv)3`aB&T}~Ki z*<3?vXR+geb{Q&fR|}Adr?? zvLC_ffBp7~=+Se@<)61m3r2mnNSR%N#wqiLlwn_Fi!!5HlsQkzEKMm>FJ;giDbtfF zqWbOS)-s5C$35?8V6>uFE(g%$gp0+QO!!{0H_xC*x%CT&6qQ>LAS@P`x2%LPVcZ>I@s;)e1SCbD#lXE`O@bDKv%Giuu4Xg*|(xnjuX25 zvd(FC#X9Cm19QkjNOwUN#rW+dzp(RICo{)dGe^n?2~d+I$e_(Wf69#z*xV<;!V%f` z%%KuEID6+>`B*4*ie-O<%7kN8)rmkgFR+WT@_#qdrB-(RZm6<+BOuP9s)+3N0tK*Z zOsiDgt^mW;txz>h%PpcDR9*XlQgyjY)x~_Ye4WAA=LvQmww2+_(fJktmUUpK^ zgZOShCIXqt6bXWi6_>mXftW#SCU8z7u>0@Izi%&f`4^;K@Xx${N?tQ%Op}sr(=iP- zsL*(WA%v^@$3nZQ%paq<7Tc+ZUA;z@^o+{MG{LRiMM*`i|8W+mW-b*imTS$Kq}pnl z13eD_4E^V^qjLkN=#s;pP3}l><4#nYxr{qRNus`f>O!V{|;LehTm)JL5 ztHCO=JL@_!hZ>#%YW#7#>o&b}NZy~W@8yt<_+0z!3-!SJPQ2fVrE}*6@0)9}O#2m< zOj&u6PfzQo7vPFF(XMe0e2-Pv4}=nsL z*c#Hj4cZ6vch7pin0~8SUQCIq0)Jkl_-Mgja}v$_sif>z<}zy<_2rR<=%~+6f}b3I zqQ2jL<{{Gh?rV2HY^3;Ia#3@=kA5j>t<-1y4_{@5@4b7qQva6P!~%z_*68%&Z4jP% z4)Tjl$x1y;y2)27p!w74olxplN+(JKSMd>tc{M+fPFr~tXQX77$HJK=Ub3{Xy}xcg zrD;uBb?g-`T09|6%X9(-WEH6@T(r)A(-z4P>yx%HNP?rn!bQ9E8m3s6>EXuDGwlT& z=T%_=ANTmM=Jge!TPW^(>Wu=GJULRD2GOAg$aJLCA*DiVmOE0qte;GkgWd>8>Qc1W z9~murWgWmVqb1>t4YESok>2YdW*jTl5zIJypCLb{;a*HQDhHL8gsqHlG;pGP$XLnQX)BmO22be<@NmE^3Gi^h zED7-R15fD+@bm*u>54qnZJAaUeMb8~dAUoPy~F6R*tt(FPiCPVt~^f7Vb@gruTkE7 zUZ4qGPzYXN=PxXC1Ru1^`np1vU8vb(UIO@?2Dq5@49XB^0Py%De7@pEKG!5a7wYF~ z`FxYzapggv$$pTz#G>vG+U1~V}~0W3yxyOtQOZhjDS z0xjJ5MO&7fa=EpOhiFYMibEoDV{SoOjhrAEj+N%-g$qj$DPXz9syL*;K2=WpVt`wx zZ9`;l=A^69QgOC;)#;arR~-x}N+dzmiep&Z?O$yx_;ZTSM{zOXy zD<8N-R}McpQ~44vQ}D6UL-G`dtQs(o?O8!Tcf5Qm$9q`A4k_qoRm|MeH(GJio{FO7 z6TaeZ4yjmEDHY?7QVfr(Qf>yo=EjIkddGYU<8z0P&_N}~d_F+9u;>9@;!=*{#eYt0 z^40ND72#pC>9(3`&YLGq>Xnk6KMqewR=Cou@O6t_kz8g{K`PwIRQM@=ScTbkhA}PZ z9B&Adv)c6Zs$Iber+}%GGW5D6{Otx744v-5_c`$64@IWrrPa!rhC(rZsF!%vF~m^# zZ<2u$opp4qXZ}iOd|SaX73WE2<;kJf1-0$VoA|+Q65^I3jcDJ{UK2P{eM&E2m8d@s zXCz#I%&zOk&)eF^-XiW;!~aD|dm+nE!`vjrzc(r$pg3kysKC(wN8G!>Njbic|69A$ zu4+vVn@9{6k%)EJwWXb!t(lt5Qc_4uAs->Nt3_01m)6q|;^Qm|A!i@jB-?R2P${KE zh473ia!N_&|GuvKd1hwg`}=%fzyIt1`tPgexu3&*o$u?u@9RF9lrJ{VP*v2w%gsm8 zGGr}8QG(7^)W0)W$K3i972gKyZtD`RyR-yqusVj<=6{D-=Vwd0Ze$_W&9lZpVLKqg zIyZHdg+8@nXru! z@%qul!z%}; zRqZ>i+*^?x7_X!GQu0RNr^0*U9PgchYPY?!TcDoX+2uoQ2iRi z*wHLhzcnkA={!!CKmc&QFn+KdgTrn5UqpRaU9c%+H3Z~ffLKw7_n72cRo&=$jjKv! zIlGr271>i;)>lEmlp#vM0YV3m@b0%loxlDJ0t?(DnWyHZy!$<*>pBbv9PfN~d+0Td zh-!K&GLdr{5)LqH`4O(Eh}1Y|dd+?@DV-AZ9)$V5bH2{;Mtae@3|rT;S`wPStwqQRXR&V?lZht@9E6KKd%9!h$Z5o7 zNdb8f0$zMQPmF}Ny)P1zARhM=_u znbnr<3Eg9$O5xgk9kiw@s4@%D9Bj32QD*ha@h8?0p13i@?xK~~x^N4Ywb~N27-i13 zpGC2*I^(T);@+s(6CcVZ^4{8Uv=!>w$$nKDn~``UJ6BO;(RV6tBkkPCA~lexXBp<^ zuh(cQXp2~{~F4IhXkgsE+kz}T8lbL>e&lqKF5z3R@42p&n zdhZ&7cNZ%Xxk+3%-I@y6^PXbm} zb|~t{dNL||u}Fy43hnvjqt$wL1q!nW^vDz(gt*xrc5X_1ZfOW57O};1Nx1l`!qIof z7E3d0Sv9`!B6jecZ-o!b!X^$An>b8uLIl1PqW!H_gy;pR0Z1U%SC;v4nN^nIPh6;a zzdVSATLEuYL{Hp1J!G|zz!kpBReQgjGON_+-YZ+YRt8k<=Y_27P+h4Z_)W~zRk2HO ztPiqtZ{$n&gV^a(WpZB7rJ~D8DYfjJl(Eu<0`nfJ{Q#DK%Tv|1pWKzEPB!gi7))v9c-X&5c zfCzru$PU9a-nKB|RQdf$4o+Rko$?#|SslF04BUK=@cXuWr~GCUl6K0^zYT8`y7{wR zeqS>w;rH!}RqVfUf#0*)hFF|D3=a}IWaqhFh2nE^jToG#Y_#o<_T?0-^>%+I#Zb*` zg^JnC9q`v5n!^2^!2|ABlDG>T-0?fLTg>4rm}$G9)vqLI%`a+Kc@;5w(>=JOkP;X5 z+=?n7w3Q(=EEnTW_Q09HJ|A@4gChiFb;W4(qj!~8pFNx{%99=7bhfrg=Rw-qOs@$X zoA2LBQKn;BW$m#`I;dX_5Lr0=`Y2|_T&*M`c@ciOt!Qz31-iRyW0*40$V2-3%f)qHsjih8Ti699aULh%;q((>z{~tJfl%!~$1`e;5RACrm zLe{u;)LV(l_USViB70hoZAojloph{uFXTtY9q!8-+ftY_eGP4LF2qiu_D=DY!9>48 zRj=u=qjFrK8X-(GWx7rVV}fn?&qV2ipz0 zC=2Ffz3~)_Cj0F$q*2-494j5EX2Zu8<{;@vb?c1E%t))|O?-yqzba+EIZj=so9o`; zB?Y@mtuNWaKOkrD?Fkm$L?!k;tcolvq(eV~T5N*wX>FGNpVd0k{`FVGUwe75E08I5 z(P}=S@NO(%cIthkYj;!*l=Cxd@6@~{bQKyZZz*N`l;uv=O5OYWU&vL$7nWz!r_>>4NNWmDm>_6vWnr8ZE6#oU zIax0>Pw~vnpT=9e!z-ADj$`Ti>U)*;fhkJnCn;&4q~ygnjp$;s2s6wc5IQOm%{xe= zAWQ9y12yn1ef1L*o-S}Of-N&p5bVDV6l5{jffpYfnX9mxb8HhN2cadJ zA#v#|Qm@^RB5`4o#QPyECY!5EB*&5?U$g8BrZe8Pn>z7ASr)!cf9P+m?WDfb#PcXVLRx2)ba=uu%jD_7(fNe}% zB<3$Cg3!rK)hv;U*Ov)`!AX98*Z;E=e*w~}s-fQ{ z`5VCGx<=2*@)dvVa_9zzEKjcC1^&(vOxpT5-lT-Tm-;Gy*TE+I%{u9y`4fB&e=o`` zsD@kMua)6%DJ72j$Hi#)D==Jf+DiSb{{w$&kEuZfXJ_Ktjqnj-g%4GWG*UKJUDN2f zo`=eIyHz>@DyJ9^VZIzZY&QPuJGmIQ(+>0|W@_9{SBP-kPDj#>o}&Z@f4ukE{~^3f ziHl!F51GIMVa49k*6I;rcE!!7F=Qo^GUkVV$2YHGHF~GzESSn%8mv1|MzhR|PRsR4 zt2nKDhMrn*`tH79vtpX@`PC?vS|BG})ND*}Z1wk~T<%DdI-y`^!TsXWgl$ z%!;{D^q-=)d5St*v4P0jRgIpDxT;(-R;pYsL@uSw+NbwB*{)FvFyvAy!LvT6UGR>?&`J?4xqsjMS{$?;mHNDEE@q8NuS6 z-n$p*vz78}&tQFabF1j>&7wn^$7Fok%6(a0ZFXMeegM#n=+Ndd-47)9E_pfMd3l5e z+@eF9#jCB{k@DsR^JZ*zK|&sLmiiERJjHq3GMS+_`v%CHo0D%GJn_y}ZjrpXEcpg= zg@R`BLpTpe-t^$j2UG$)%#t(qULdLG@70(@qH=zk#+8-(@nd)ZtJ^!PP(ZRNgZ-b_xtc~ssI3W}OHSEk;K zl{b{P>&%fslt- z`BHo78FE^6vKg`r&(NeU+YW!mih4=L|Bz8aK@*UZRIHP@`eo)E(XfRQj-~rX|HD4W z)Lmy`3jlzkas~X1OOf##zsyu`f!~;DQoh)gih%XnO}k-b^m3klJ|+3`~fmPH3= z#o5AfZx)uWH_WWtn=>}`O^%xzZ!#njYgWk;$Yzx?Ml=q*{`!MlAIx6rsK$`{Op@Hw z9CCH%6Dzmm5jD=DkFcR5t=!J$_E@>iwQ`Rzx2?Ff&wj?(kzo2`mDtf?DNJ7^1#?ak z)8gk9yD#ukVsHEot9B-$$rEJO%6iB&@iBKr`tU2UdK5_|j2H(+XPa zWM!RWU2mDen2?oQ1`J^T91_?aT-e6C9uinu>mFxPqPkIhNhje&E~xIMV?}lGOx+;> znoUs3%op5$w9{!%rRuN!d)k{gy{%vuuA74*bO4kQlNZ+*zx^avwZGufq~DIRwxq7Z z-MsPFcXLU%JbjD^@GX_}!B8t=xzePBfySURU~_?i*fDB*PXn8M5Gs3#@xDQ%q zJ+Pu}h77RC{>!<)J;XP=`zz$JeDe+WeDjoiLs80z`>ot{^35lyZx)+xUXpLb|HeC5 zx&M)GUgsOD+e_y4d-7VQywM7N;i;5cyY3?~$B`*n^-IcX<_x^kD%Sw*pE`@}?RNd) z&%MPyZcJs&OJ=-LGCoX3ac2|2nY6i(*qltk<;Q1tH!UGN+D4Rt&E26e$G9+!%^d?Y zV{@-f*OV_7RM?ICdo%)K)zTO+r{Jky)AoQYaZU}wT{KX3b83Jr-M-HHC1}OvvvS$U zSe&oijFKA}A%oBoc`!nL2SwAej9{8Y(r#k zqACoA#T0xnEGbnCr=>7VL0JBJ*`LT@*p`N2lwc6p*}yK?6x+z8Zh>vLO*)l>fqj8G z6`jX+3tvR`w)EaTOg?44@JD|ntjUjE*3;u`LrZQJ0AyIJ*$1FXGXJSwL$9W0aJiKQ!rN;Ft;HpwgMC|(*a|odQ>Oje1n*@5Pz|LBWJqB#w)7c z9SIE{P&Fu0-LYBKg<)_As-Wd5Ym{<1)sPJ(VginlGtS$Q*`(QqjLV4o!A#vL2a=EZ zIsu_#tzhT?hBSx|F8Mpeu)qpiD@||D0Bq#|+?1;T!wPVN0Q}IPI2SMGuiqk1=ExJv zXdqoy=^Ak5rn0}m917sAJw@1$dgf_){8nJy04yW4*hx)gkl9ci9#FLzz`3u@mo1nS_q z5x3K`BB@ zll0dfEcEBDMGlz7RyA(ME{vp1E!h4^q@^vd7oX6SG+dT+H~zmw8!lZAml3X!oD{z) zwwSf(mgi0d*m*1f`T7z0nx=iBk@_-$qfMcUNnwCC@BQ_}&P-3=XK!hz^~45XLDm6E z7V!;37IOfAZb01ufLN{hY=M@66H=e;6Ux<$1yAg9^V#G2*>S1Q)+ayfBA=bZX9coa zY_RxQ(VehHYf?eYFmaW z7v>cIHTsiODK`p~*abTJFGc38$2qviR7jwC5x zEG&%{;$eP~GP4g{F@6ENd?4|qWTwS#ro+gjVkk148yRe1FB&s{IS9Y@HMwG&)dLBp z*sa()%&^Gz4!dFV3*oxl{zbRXGNnMoR&IZ(PhtiV(Xz$o%?!%_?h-(i}^075e*5tsD5~peE5p{;adH0n0#pT?tFWNek%uk z#wH5e3<9h~usZuh4>Mbt$Xj=gajDh95AD`t&=_d?DOY33Dq?fC7QXgT^r|KHce;Yo z1dW9i?h5;6-~u10iM%@h>d zYMQsZ*)5O6K%fmP`W&bbc-D8c9M$r zVK|CSGe!CX-JaxLd2+9L@{m5UN-K7*1v}is91meK`lkA>>Q|d%3Pk(`kF9!zUpF?eaWk03EIt+61?zS;pQ#* zqBCEhUt0*E0Opl58a8e)_Ph*_UKUpROvdOY+oZTf4w*dCtKTpyHXwn0OWd7pk7@y9ODh$L;fKiQkRUS-)WeQPp zHpy$?f4o`M&*?SVG(MCvQQ_8TVQZNb1?j42JJ!hRJu?O(a>{@{x=6f_byQ`nwBKjv zEI9;|vOEEJF)9cn=2<$~(|%=^C@a~4dR`%H5D4eNS5jFu`ZZf5si$U3VbRhRY`>GH z=r=X*=aRR%%4CIoAL}SE6w%V#s>7{@TeEX_!umjc}&z72G+Hp4sse{&xA>svsigZKUe z)#f9O80!zss-9ESf>C1T11@35Q#psay$W%6N{I84ytPa6_Wb?Ir4I@go~itu0$#jV zXT@QFnzoMp$6(Vc9wm}I0q%~FUFHp29{Y27>yi``IWO1mohsvfy^#vyEhklNzvz4* z1;TzpZx|3ad!I58-oWFbESk7_%6VsaGTz|-ssc~OZmwT)?R=&uW5`*ajAywHI@gm? zP~ypWrJE@5jF&U%KEDF68CB;tdPegU>15dVFILNwv58(oOtXY? zF%;3T{DTjSULtgiV^Zxzy4@)7WNOwh7sP>YsZB98t6bjk2e1(HNaAlG%n}S`4t`KV zhHu|PIl}7;w!(TbzftlvIKbK7Cn|j~P^sUh$ znMx#+HU*+I1spFSH3o)Zt+k7`OA%Hd!xa73$Oa9cQ^W(&zGIaFQ{(Kp52Mf;=Wdho z#rrss6P@AF+`e(5NX%O&l?E^-%oaXwO_9_HAx3Hw1%ei%Vv`b6D>*|=Z9W&Iv!E50 z1DhckXO2NsNWN<#hSUM-wIoURaWREXg7*&~5kuj*ZZDL0+O zAU5KTXTR_s`f|qWA0e$-y1$`R$asBCg$%^pAz$&wZqF%tx)~~<=NhOmTwY}8Sx@1k z5Fd?jokIJ0%e3)q7Kae5V@e?&p{?8h@bid%v_7rcDWB+cI+>5~f|liRDbx!LLQwli z#UoZpaK90Iwg1132go2)kgdga_a`;r&)5cv!Ki;exMJiflnAzUGaY%|IQjy( zLW?gM9y@X8nD6$e6q)-W(X6*rHg!_8h?P>i@J#T|Glk+F_kP|m{ z%83?d%83^3Inm;1PetggO2CI-5P!^*}(j81XZt60&y4(SS@3us+jmA8|0l zsqzy6MjFe@tZ(Gupfc;XMpN&Lbrp3kq)wEH1gCISdt22`7eC8HfToA}QQotdwxKkPg&!-a6Zn@r^kg#nCDen@04JgHUGfyjj|_MR*SM*4%&| zlL9?g^(KzP=xu2l=o-?hxAwq<<5(D&Grg4paH%nbH%!fOYzp9Y3Q)qG2H@BAYa3=d~!xp$oli=S6Rx?MXF{wL?!DOX_J~Lnq7YCUoTrNgrg(|Y0Niwq1$e4AH!>p2*?azm$=VZ?2rEN3Y3bX zk@>@`M(Se;?j2w+n#lPmNZS!_2Sd!|hL}}y;VtI>ISBprSrivTOnw?MTPHc3k0s2Q zF5YQGlciIm-Op%Vu$^H(BgK3a1;V^je+*6G8>#rPc?aL?;Bc8A<>JfU4__DXS$XSj zW$Vpw{&0I{r{w0FqSXu700~@on!4&2xftH2C@zV18{RHU!&ZadS71qW_S{Ykz`NC>pXI zBn_c!WT?6X@jQm`!97fH8@WsaFgCl4Cq^)*@xWj2GgMHie0P_F`3^A4daOaNgkn!} zc4?4)Z3E^j%i9LA{sS=+IaY~?2nd;pe?d55t5F#kJ3*QvW<_`$<{{^k(^M3tDXK11 zB2G+-;xUMDEn}cd(2nN&6VzG=0;NPyWXx@n5JsSeAr0x$Pz1`XMM9Q#$t9>IBqdDD z#-ZsPLsN;N=?%H?5v8NK($pzQQ;Rg37SuXI8vHk!s)-Ft&vs_>s;rq=m9h}Na*G=2 z7pE%Q{Zc6IRurctQLF{5%l1$g#ja-ivz-SDDS{J(-DuuLpNaNY*qx74|05WWMu+3ZZ z46JuO&08b$8+e+PFApa>4{NyU=3<>RzqjTm`AT7UYri4Q{?|xe%QX8Iy>YWF&l6v6 z9U#UccEcX>Vk-9F)(7yNbl$zSGCV@tdI>5IQNJ_~am+-g1x?yp=Df7#=Df5!*#8Ba zanbboKcM-!RSM082F*~=L@$zkxe|FjeVo5~(PgCZE1d7ZZ3tsF2Z)I`=(Ni0zm?6+ zq<~jidp`%+;{|0T^2(0JKBO0YEUJ*IcSav^P#c_SV1v=&t;UTOpQ1XPB^P5a*GI%& z2sk?S(u{johwtC%*vkOwrKX-@3_M&tB~ZvtZ*q{yoOAiXL=$-%k5XDVgxFdUvc8D3 zgTjiyB+BQYX3}uKHc)s{<#4H3M{vrCZJF`pNitx}xHk6W_+jSipt#+Ye^(r5fT-f8 zwG@f2LU;BO_*p$QLXcu3b-HF&VLo=6&79RBp$eASKIN z_l~f^`XC-tx^g?IwC<3Lk(Px-{PnjarL}5eN?P}kR?1Fb+_$N;N^SINq`-dP$%;J#?EZS1IYuo$yK&$C;J<|_2@_2d>ScLy1Bs-J zPc$nWFJ^rkfI<8N5P^T1z-Nr#8Ia3K;$JdBKf9av{`zq##Qpw;_<9gqo`*4+CTjDK z`JdGm%W(@r71QMGORy}7kgW-(ROqekqJdJU71aBD)>VjgFHGY~th}FD$_Qh>8jAA_ zV*^iW!q{oNL@x+-$9QGzRo?sS>r#wW(Ohe)u&EGWdBpykFg9ZJ|IApMri@L*2!ydc zz0(=Hn5vWYKQ3HMJntVs%K1Oyo8HfMWyP`PvJ^D2O4>X*g527^t?#N4n=39DA6oVIdZ$>KA z-}&qt{)R0IHp}ylSeG;pQPiw^;IDr*C5Cl-_7{Wnn1rMe_M@e)eMQYtU=l?f-k8JS(=F09z5uYG>j64= zeoHQpO6zC43j04`;hva?Juc`pMG%tylc`ab#Jl7=0Cv)#sb!%>OIKCFN zV3cs}Wlq^&Cta>%njIh_6$G1sj7WdXR|FLef}30fISv9=Y3}4p zj^?P$3)0qW5bsvJwUVJv3!*Nxa%V%7zy2e#a!+`(oe-&7SwMHp zsKdVacO=5tvKim?JNAmF zlHlaeY`4SaUYM^VmJY@~D2U?$8GlAs6Yal0pUT%K5l{AB&CySMY^x#*ndlF8;ej5g z`#T3V#SW7`gC3%}n%c9HxWUq3rxomM%T8L80*9Lxq{W6pivZyW3HfxS7(cg}eYbO0 zJjbNAdl)T^aE9Yyn9+H-65`=G>{a#}@5tG4q2-z3CV|oDd4N0lGZ1BeVp+GWF~V|T z*76iSNmdgh^zlUY-k8SK+K9Q8a9D-Rw%*FA&i2tl#R?s-jqGmet!+hP!@lG-Mq%@2 z{!7p4*fD<}#Ia;~C)G6rnhZz>RibnAIaY$AwX3R~gmi=Q!vtHV!tuW%9HoMy+*KWq zxa=H(m^%=N_U#RfC(87dM0Qo^k6xb*KjpOg z#m|U`XXjP#ysYd*-s5eNe%;%DmWbU)mb6i*JDOFk3P)$<%fm8i%5qj%$Rf1RoCotm z56C~}Z}G@mI-ecA{A?jIj3DWP;C0J5Ao)v{Ed@W01v8fi=f|3}vMIYy-Q-2105xt| zI661Knv}ISWc|Qa9yFFFO@v}T@U+Gn~Z^clQWQSvIg=^-ax*|EPEIE z+lAmtaxWz}8)0#q69ggGHeB0qMbY}1kE@USj$Au(?ZTDQsj?-v6n4$s+sWPA$=%z@ z-P_6C+sWNqb5r5XM2G}Z3jq+B`-Je2QFWO85ldaI-1Z|dogVY{F`-A_=uUNev&XHc zBwe~mO47t<`J#jod?`tckL3P}pRj+lr6gU&yLcb_PC(hW%TKie?Kf8ZN{7BwF8=x* zpfwAC)l2uPa=rACCgqFCAx)O&a9~-KA~$+xt+xMoP@-wZfMxq+Eguv)DIem^r3i{Q z@3C9Gxs*}=P&0%5(uMZ+mm%mo`T2?;_Ts_Tq3R)AHK&;+sZyW#I(;?czwa_BU%b2h zvgCbUex6YPV+?>Z6~H%50o<4bQ0f3+L#;n(9#gvF>R9dV>-Q#bsVd+qr5lM4IGC(#ew}Tvtb-}Xx7`xYJoPow2?ue zf@KAp?AnccSr!wH_Ur1-jpnEwX=-*XBT~k-i*&2})^5Mm+$nmit?*P|)ZeTwFx7|e zZk||}XbeRbXp!1D|LZJfCPfyAM@NK!q8^l=F8nV{i$D0SX0;7|Z=fMKY_}DjYXHs_ zfb8rr?k~1MlW_oZKuKhNb};hg?qKG}{zOA4GC#|YI~e!AGp56SOhI-2EKhd1{o8sN zJ>*cmcVv47hwF-WrU~|kU9({?vx@SL7a8gdW(`svoqpzeG8aO`8GnU(BbzjBjXJgy!0=VFOE3m||2!`6+;mRiCeMnnk&D1uNdP{@g zN33Pn{Orm+cm73U{*vah-2o4)eoib5MHXd6aBX+9rB{@i)Eu@dC;yEpkpov9&yh5# z{l-(%?Wu&#OX^>T)kNNT*o{oi*~ajceGzx+Jb@x1&z!w>Ty(LE+I;085;-;Nd&IDvKzLk%fYx%x)e zRJxMFRCXMtO=F+E{bL*ttJ^}-6qjW}OvtTv=VeKAcRvm6FMl(V9iw7efU(hHir$Vq ztB$k|M2-j8`|8EAtGlOodpQ^0WWxCps;Gt!Ww&2)f?N zl%6rPqMooDW=rD{Q6ZuqG^l$6wAe4sKnyeF=V^YJqhe%nsIThRow?dG3q|rrTRPd z;J^E$fi&FXiWR*`&ntmFUs(yl|beFy(_L>U-GTm6%^2$+=^I0%AN)~|L8xbRzl2BS}kQx6v%S9$ZC9JC*>7DW5(u@8GH zk=Ru`g;zP1M3LO98w10fs{-OS-33MlAGHfjgSon~; zMGZ`cY8LLqfg!CkLc`W7Nx=yF#~#P7ZwJe*N4BDYutM4gr5pWHQW|pnQd(9-{>UD8 zF#UB5x{K_oP$-?!q1;biFQAzT`Kh@!c^`X<26U?JA}Ls7hldu8&D$ZQUlx97`3vis zTmG(fJ+dr+NnMW|%O9xg(c1C{>w2`Y{NcJDhg$yfx*qMEL#2gZA1gGSeH$vY)C7n9 z48!+hwQ|JK4G>h&pevr3dC~PBS|w@iwNmSp{LrgvqKakF-Jl<{3%j;=85&un1z@T)&8qx#ta&dMG zHlv=>(UFhrADJDi@6Xmb&65=on|ax7SLV=*F~AujsysYQ4@XD)3F$fTn+wc5nS*Lx zI56X{U~wEwSapWI`e|%@h5Rhx2ity)SrW{9#dc$(VJY&#R;b9RJc0V@cbinO_!rL1 z*_>Fct%!XTnlEH3y0tJip3m(G0_ryTxtSk2JpxDuw5-eCE#xqmz~KSka~6GQ-zGE1>$nB4VH3O1-g<*(*f*5sv zY0VGy8S+7DzYkXzopV?d7rpE%*G0cZ3Kw0$G$Vg~SjTYj-9G!`rEq8Vd!o5b0|?O> zADg^&y_B3klok=2ATnZ`{em6Rv)X5OULo?>R2sdiSvYz#zJ(R#qQ`Ri;gl}w`v5;9 zlIzX<)={;a=nsA#92U32v&z-G%qkb}a=&OJGWQ3^yD*a;;-}^}fYSaA(NI<-?Rb~f zq~!Gmt|(?WwT@d>cB3RyR?J6}^&|U!){3Zi*}&Qq%8DYgEmKk&00U>cO-a#wcoGNr z3j$DGi+SMyoFZ@{3HEMbtQJE`ROSRT=l-Ayhq9%HvU3gTa_3l6iP%gKqF=)(U7~vm z4z(s;hgm@R)e8=}C#8`~(Qdg+i?+gJ#N60WiCB$*{1Z7C8{(0GiNm!N=Sqf}H&q@> zfmH(I?IQEG3Q69Y@A>7}o*!(zZV46d3wwM27%JYvWiOqR5KU=zbHMv`|0|IwE8bTo zudT%rtF0!UK7o90D_&0bu}3_Hp$?Ou%lWZ9XJUpFc>kedhP6`qF$t$FeabM`Seu)a z#9&U8yvNE<2LIap^Nk63IxhVK4rFK5xS zy;y!4_+c#A0RQ?Bq3(gE!0$~0AMOB;KjW4Ra}|H%H0`CUBAb+RE)wTj%Upt;_7{_M z{>xGJ06}=Z{Pf`mX}q^k!s%~=k9|glksB)4#~|9S*~d*tU=et=r4T)YER#)1(jviPckOG@DH8PS9fa$V~^JaAlV z_eG|iYjFI%u?x%EzB1l*7%NIQ2CM-S3rlO)&|p{-8uo|ZqOUc1k5c^Qf*kN~kb~>1 zhS~jv%6{?_;>YqFZKw+ib+d`93>UQ4a!k%P$gTMYW_;N^{y3nl!q&ugr zer8?movAj=qfjQTHR;+lRhQd7O!mi68Vxx5mmzD9p9w@%6@!;hRSbBtP}W;i1W?8r zjiPu1=wmq^{Ud{9@<+y_udSve%? z;sXiJUbr?V!`fZvS;AXRaO*aYE2D6InRr>eyQ(G05}HBM%cSy={qXTgYq7;FP>h%tcopSz3LBgIa0sI3ClV!!o?{mAFqM{S;H zG;rHWYh80uNeSGG1=hhq%U!1~uzCszryuC8mE(h`PY2saPQ_y!CO-%B!eM=1 zrf%b23_s0nY~iVK8~-7N+qk;5xQ#`0KpnR+^6a|czsa+YG_L+xo*!V2t~~oA+x_MFg@ZZJ|c)Qbm+ZfrMN@ifnnx`?6lS;NpCEGbkzsBo>(c8MF z^>3|g9gbKS9M*Jv!&^HA6a?^Rlbaf;??3gEy+ez7Mbf9#yBRg6()m`J&8&W8>`=pFlzKs`WVNHw0reoqN;VU(Y&XvmC%?&BdxE! zw7%HD1EbIwDvlHR(ee4m`px(Qt#3?RVB&V&eAe>Zzzmw88OfeNcuTcGTi(8!VnH)r zm+p)HP0L+UBc%C0DjXfw)vRCg6CaR1)DOX7`xws|r0w^)gQWd_Ig*2qy;3b^cx%t+ z61E}|&Y^L-mr&5;{0Y2CPUBa^zKDR??JVL~5flD8Hj#J5ZwH|O^D#De1~w;g zESyUsuN&`N@AM_5iGo(*w5r)i$~1uo&rg-I9TZp7s^+ychbKSb6tT5W?qlLl;JpFX zgM!(mF2s10-MC4+XeEq>)F?Zqk&SCL82&9#wWG!(fqECflB`z^N)OHzvk=f_j@Vw! zufjmySArqe)!L!JFTagW?d8Ow~)VX<^RWeMlSWc~FIO8vjp%U{TPJ@U1<6a8$ zZ}PK~A1imsU@g(DF4ht~>j1Yz>p<-LJSBY0Zif$&f{;Kq?LcP|(|{b>nZ{s$*^{XyurY9G7b)f5!2HDgeswdlCLx`=nnN47^l1!a zmCH*8AhT$hZguwYV@|#Ps>&{0fRu50Ff z*K;usu4dAL_Z?3^l9T+*XMhA=OF0qANRXn7&^L{n;A{~Nf8(XPZjdKPWP~g@D7QXh z>6)|#@bj#rFzvGJFT}HeP*jGpzuTZg>!Lxa6w|k;cJ!-T`%m1yS_`-5Fqsc!+)4va z78hY>VXQOnxj+OEFr@v!%b< zW*3n|YLMxt0d8&dDJcXuDNAajD^lZ;hU;c%XI`#b zmH8REFM`}lH6vH4W8^9oj9jI5VN@hU>y>4FsS3sjS>!9v-P%UdGlAef7cc0hUr&23 zvUi`)PJ7-Z?Ri7m^TM>}OVgg0q&;8lK1a*IRk382oc~iv>Z}ev;PV?xy&?fhGTdUT03QHvi{WLvz|mF{!`SN>ghO@Zi%> zZ?$=RZSwKEJfPVSie6qQQ_B3-Z{cX?e-Z6AXYWB7wYxY=okQv$;>!TzJ*<&- za;s;(S>VcP8D>awc_MG5!SM}>G^qO<9BjLA6M+5J7aWvE`wRG&2>eY!g@2a955HXj zhWDMkis4X!mslcWHi%$ZJQI2?N=GPq1JhhSVVN^`!{t2f;r_a8(j)wJy*P9#ewHkW)5* zpAP*gZN$p!dKSud<%BuhQM{rbK;anUUpkW~k=H?%<7MT?YoO|-d=v%^27Y^axLt2&7`m|Y+Bg2^>YjR z9Fy|J#B&O~n6B8lC19_v1=I@pS;CLydBVUuM&ZdRH)+meY7*YRTzEEO_^$@ww+^da zLwx&_;IKce$#W-=N1Dhrnxc-P+6b*F`}NhXtjKOxib(#5ccaD_}nZ%8~L$3cf$sS zMK`UH^BfZR6VPujOYmKkP{WQ!w;K zUu_W|%W|)@KJI(4X=;gyhT${*l)&s7C~#s1C%q=U)!1oDz^r_uF97TOo}z9oyAy0` z%k=dxum3AyZLt5fHjyY^Xksk(5FsG<B-* z-b73`m$6pqUInt`rQAOCM&?>l=7Z5Y285#(>`*h}NUf5>)^BcquWVc0`P(>0k}>YO zzWC0v44sN`+yhJ>&5k#(^D=;*FV@`B_O6i{Ei;}SG-pPIB8zY^3z?j>7)Reojl@1S zuZUMIVZxC$L8{8SySDO6<6Ycmoz_Qo zW>nqcDt$`OvR)T%Wx9jCWeFisnLXsOXJD-<7eCMz7w=BgU=q>Npmnb(Me;F`<7|1) ziCcM{N#G-is!dZFN_l0yc119Uu^i6N&`>x&UobG;E<4GGha@#|E9PSM88(`Ds+U`UCYzKmwi;-WZj%Lz zZ22_71-N&g0%Xgl>*XDPfF4LsKR%r*uMF*4c`FKa=wOtss}h*T8TBlBUTlrM0bOG_ z(O4H|FfobT>mZL0WXe&M)!_q$Bhma4qHaO^v=Zr;%T7+-+Q-3c<$X~qcJH}bp&yfi zz6=HpYsWdzsd#-@WdH|`;$-}=rhsi;AK)Crze zkIFWLR-Olk&wMve$CjhD0GsFdk;{*IXg9`1hagSN^wnWr>a@M!Fp` z8sTDxa!^JVX;tQUbApE)l(CIYKmiCm({Xi9`I@1^$Sa~?7~UJ{Nc!t#O^6}yHSSIQ zSJ&+==CRu6%@-teQ)OguP8(4sf-aBt`6-ci^4H?SMA%ibIo;koE!AfVx@g`y5hFk; z)jueb)Pm|;T_nikJkosSv)46H^Cse#Qg}+J5v}tJ?JLhit^mIh;Lp!c;Gg2Y0A8H} z-VE*e>t!2K!_h@)z`vw}tiY=kxI~?D5Vjp+M-gEPX*(mK1q{M2wVQoH4c(JZbefv; z%9ZzYrhL2do=RHf?KMYZiM%UeU*+9bXf&Lk2{Dkn43QY>E`}n1{fwl%yGs{dr-s*c z>j_YMbzkVYhIBG64 z!Al0*#&;0eWL!|@x=_KUS zfft`4)@!j(nAQ;FhX>a_GY$Rf4}QJgq5nogCRDm&pPwA;*ypC-lJ+?jpxEb0pfy5% zw?u^gr_m!8^ECIaeNG3ceJa!^{Xsnq11O~wCGv)BX=0y09HeT!QE)}`mWmWik@!+E zBn$>5cD;)MY^RgvE1$h|uG(ik;H_+&fwwsK%QKQjc`|-MjB>tUk9w?!6jhc%H6(@V za=7!?duYNN=6;~x;Tq)<(u!(`qKaQ&|5}tX%ED<1@{M1W`j#n>4=PAG$kTxQE)C=u z7i3EXnKa5x+5c>m4{}JFl)Q9jBl_%54bIul1tU}Tm`@ssW!O)%OEvlwd(1+@oS`dU z@#n8U4Wb+wzi%i(y5G@{f>LC=juiH|>lbC|9E9cA<7Mxq+hZ$x+__0}ynwI#^=G62 zw=oI=@Uwu6v%%&H`%m_{N;5}i{cY8C&{Z|E(Q_>cC4C#`?@B)n(nD=jymuN}UKS}s zq_qDa++QyX)QosLg2mO?p`>a530(eoPy269MutZ2wMiG$RZ8+wZ=K?;^CMW=Md)4L z5W~0lLu+$a*IhO@lSo02J>#y<2$Sxj5VV&iS+tzsd==TFoPxgKm-r zqMM}2=q71IYSLUj_P*nFQbDV4wRS4$bP|Q`TS8qLxh z4r%{Z(KCx+&ICviVJ7m5Hy*?ssCxL3bw4F(9f}vbidtr)mRNgf>(uoxLE{opA+tnO z><^^yKcNEz5?SGKvfU#7!JThCdz23&eyNeKJ8eZ5ZLUb_qJlRv0b`R)D~fFUk?}4L zwPm7FQHPVSrRbM%9w1lM%%djW)fvtF#S)de4mrG`znC)A_n_El<(p!*{w%^#$<{D6 z2a+fpLcGMIPl%FyrSTG(lDL7?9EnhB4=|C}mjOJdc_Vw0&iF_Q!=4n5!;&~&6ddcw zZh1~UwQWZ6lBz)j{n)#mn}Rj=t#}qJUw~|xB0%<~fXqn(DHkA9gqp^lB~VeqHRxQc zxn{&pf_Zwtea}J#CUPVD7EgX$KxRuuIHnN&2*{WoO+^LXZ-~Sz^6q;SQ?ynxUDN)P z1qHPYcU)?kp3%HY=at(*u=j4(K!K23f(17@v+PEQG;*y!7c5^2BNCiwj7)$+Q4Xz$ zYHda2cSdXH!2WH8N%ptiC-9C)(fVEz-UR|rHff_J5>!OQvUuxzGXo$et)$1Ti>Kp*xu^o|8d)Do}UbC_Wp3eq~&Rfs+cJ zy2nSEZxVT@@lIF2$#|fA5bCtn8INQ=T`T@)9nxuORdy(`kOt~@!jvMeJv)WaQ&NP^ zf+-`etBWA{O$0Qr1R({izB;;I5w={6@2``%|D^*0MVFS3?nq%C)HB+(eeqx39)B^M zHvg+k`z_t@sKHxQHz4=cPem3yk3h2Re)T{rwUA_8mJG9N@vyXgR#-}}afn5bV zipG@Rd_w`n71|!Ko_~l%~Fg_Dg4I0hDb`#sdJrg@%XI zxpSPx*`W2;{|k5q;exYMj6X)2r#}01aKIznFg04jUm0MKHo)$`qy}9iRYHZ2M^p*x z>P?j}Tka<=633rfbKus^a+Ug_oeo;+Y`l^JHlb&d?6*Z{oiPj#GP==o0#|V@_A@(_ zk)bfsf)!dYa(7&nF+%u(M7-a8i1pV`PBQWhof%hyACs;-YZDCsW#>u`F_Z9->2wo# zlU}a>GG?@K42<;yy7S(3A9!VsKBo1TiSMc|i_R>PUA|iv*|}3yl^(_&SotIdUw39U zOOHjX*Oia{Smt~WVwEZ5KT%ILbVBvV-z(K`3h{>O2bJo_l2o6QMs<6kTEze68_>Iw zf>mgL%2W@T5?W5ZB7k226o9J@;K3=tmn-0>lYqZJGbN4%q!sXB16Vk-JF@|pDA>-| z#0vXJy%LvTqdX$au8@){6#cpTiI1l~C$~)LuDX%<54Wl~JFbemjG$au3#Ihin2O*v zA(+`TRF*lg>91cPlV|nf0T4S|C!f! zKZ;Yb%u2jnYV6L|O^$ZU~h|>I{z(T4o4sp@gc14TT7QeH$7O2#=<4 zq#z~I8%Z~MssywUEn55YHAG^flyumzkP0!gnLbDKuLF|osG zrb_)2>2{m|)k7+26|wwyw=`B;xrE>Ky-HXbtMM!)e3q>eR{9}?qVYV$`RmV4626=+ zm0L94A>HViJ2Z{(o)9jL)vMJp&dOg38M05x1g=awV6Mn0N$m(k@1U_bBfpwE8EYL)9$FNB*t}OGTV5ErIaZ3GGl|e@( zij%+0L^`bqX>1tiuRrcqggg?6PXj2OiyBIra8VK}-hK12;Z8k$*s848yX#oldaC-0 zV5i$kq)u3z6MKsZYuq|t$V`wc%tr$Y63!=6_#|96rh>t?X#hs~E77{YzC>ELM(H=5 zu7w;s%_J3Y)R`6xl(^T^cKpTxJXKc4AL?X;&>c0 zbzAz#Qd7i=&T7Sk+^XvvJ&DVlLVT+^a70eh+0snQ!t8I9BEJx3#PS%V`s-`phkJxG z@3d4|c!_k~S#JZ5cRsrq;?458kvc3>V&*)TYqenAULN_eS#a2v>XY346s$6QQ0t9$ z1Z_F(^jr-FBv#28Dz|J7TBRlGpJWRJsam|R%@1al`huB^E0k7vXve5!OoXb0yti-LyE%0$9+PDiO1s$w6vRNmhVZK$d{shCW6r(l&> zJsI*8D9A8e+;For!@q}I=CG&ohLU*OOWwoD%jxh|1M7x@zq12=-$54Ji!AM9wO(dl z|CCulkj~vd$zSHXCzttYz~;_N5Uvd5$+&F5>Ur44YE1LBT zhrz1&>*9p;H9Uc@Rn||+1ts9GchQ3~qo77TM{w`@>lUONJ*@>Lf4ukE558#p_0K%8 zI00u|{wVwC6GcqBMI*8=eFcIB)yA(B)fp*N?;#g|{VqgiPpZ5EUxx#c~=H^e~YnfVlaVYX(g8Ct3U0xEh z`VEr4+mcdd^wQd5GS)A_PM-y=)nRKnYG7uTEdT#B#UE? z!OUNRnekw!y;ZMEBLG&#BCFFDCnUGYptMFkM`Oq^zV%CGyh!*rGCmLHsLVYs<1M&% zW&CU>hw&nY-Tl_0_`_-C?;rF(Xg~cQ^$+^z8>9cPQu=2nJ7~Mi(DtNU3~jr>@2~Hg zr0odqUD{fZM*o76KcoK_{z3o8H`TwTV)$vR(SHindnyJF-F5Y!hU)Q?UHv-<9Q}7l z>c5ZCKP)Bnk4gN0q5u2-FZF*88b`*v*~1%uQzZNw8TWuWfBldo<1M&%W&G?(uKu5I zqJLB`rORiEzt%*FQdvIqGxbrx`mU&3^+2kms~bHdxvILq+p6mBk12+kj0Y*ThLl(3 zVo1qWQf^O@(h2k~DTfIu-HrjAs(R*gM%5HZa?>ccelNP6-pT`duVaW?-1(Z5Q` z{^3mDwpt%ff%CH`bN0WT4rrrJD$FUaDj&UGicke5mF*-wCtw>;xn;8oEGI?fqe^8> zlFD@*Q&iqWTItI%^u?LzxXA1&DIEvuHHxV$ft7xi&b*>()B60s$g|@`MxIAmgi;?HMP~)$kmZWski784ON!NAzcz{Fipc(%!<@tizCA-G_Px4%k z1d-f7ljq~vxV=|<0!0@}PM{sC7>XTOhrAoWfUu0gJTno2#|{B4jWtwt}AYJ8v>b7{N~#^_^(t*=*@1L15nd?600ttvaRQ zGSjTAkn%7>Yi|hyRfnw`Iu>m5Ti2gLeCWcEb$bZ`&N6FJxKo3PM|j_9(}Od8GCF-+ zmCZ(1gd%saUz#4aR>szevZ{K0$otyTP-a=7UG_3Ffg38TWkxFjiZ4dL>1P|oV%vLX zZ@3IuWhK@IiP&UhZYtj3?Xf|2!aP(M%COJeAw3rv?f2F`!2<+wz%FUv?UWm1%vmr= z3Zr>AUsE(CFEc4$j8QSk^a-}_ueVc~yva=0Co`SsX1d#Cx?3}So67W=`2b9X$xJ^V zXKW^ZQPAquFoACL2F<~tIsKP*a?6#`LeFZDRwd2 z2RMY45K$#qGO%OV>cIw=OQCT^FtZP9gx1@0o;MT2v~43MA>LW0_qpy9gK^s3al4ND1?{s3W4nJHrU`!r1X!}eAOGcQ0mE9`Hdlc1pc zsavAc3L{DJAtoN^OmsO{@WG07Su(1|5D#?uc_X0wRKU+Jc91JztCXks#^K~$sIBX^ zub*p@n~rf!a+yi_5Wu}g0EZckmXlpVH6rR{uuHh^`8f1H0K)wu|icR34(AH-*Qy-dcuD;cBHlvRU#xVVQ$<5d`^l z(>#X_pD{ly{njJ{ISvCc#%dG6M+h)eD(@u_4eta6IrTHOe#Idk`G-$xXFo;6fb9h; zS!9t_nH$-gRh4CJh-cAm>lhpsMt!|t& zd}!85(b{4K2h(&Gl>2A<5T||DbgbICyj(T`(=%25iT*iGh_p2>1cm3CBK&R2;xC3E zfBkJqVn3y0;}ZKcX(je-L#$)GBb#)7_p@Jin+hf>+PP1uSq91C{%IfxTNe;E2;`T@ z1L-EJyyQlENg^z{ndH2|&T8@}I5$3j;=(qRu0c)10URcx_&O%>nforO`-v6ECYj6m z-j{sOd`<5=J(>mBD3SjcX3pQ5<7bRH&}u1+(CL|Fd%FO%K(a{6YjAU{9S5#*RhjUX9}0iA#7zTXUt)IFgoo)Yk3XK9HkTk8e+>m(CXL=l*aU~Z+Dze{2M!C(P%*Cgf^E@n$0 z5sMxH4?%05n7TBc7y?V&A|a255EjOF#|D+zk8&}_HwziDGCwJ^t?g4Xn@L)YuRDd5 zJ?&!a6)d40u!M<$m&KLozVcDk zU^$P-YJ{okqmt|CK>K^;hogd#ShHZ=)DI^?`YmyFS!B{x=R8sNThrb9R4~ioTr+ta z%G?*~^qaMzYP!-T^V-WpiA72r#~^s`9Hvn`(H7fRPa{XL6I-pQ{=BtosjA{Kxb@Pf zs>1iys|ruy&QW1AjKE)?NUHFlBT_2-=5R-a;~rKO3Y#K+4bTbYU=%GXCn9>sDih^} z615U@!XEorxzzxLh<#vZD_{C)id-~d<<1f$Ix=;$N%=6fLkyiYTxcZjL-10|HMbVZ zQ4UhnWH^+ImO$grtdx5;{w1xxbAybteeEcvH_T8c!KR0$Og6RVms-DD5;*MQtr^OVIDNgjkQ<}`%y}>#wjLcJrd?Fvt$8(FD$nT42hGbf%=W=b zXFurtm~EP&^8&)l!TDQdI+v$Ubifhq`b5KR+R6D|)L*-UMLl+VYh^jQ)DFVLCz|%> z@CLar;m)5}21`E5ov#eKm$KUs`wn?0y&_-In-J*i*)%0wthbF)vN|3oXry8@DFOA#0;xHoU z{||L<0v~m8KK>^p3qgn*)YYhQQNHTU&4IQLR#9wFVFpPUTQQJiv3;hyq?5>i*u(GoND@!M60<_W$=~*qP7gGc(Wg z%=0|+%rno-bV4*)7o)Z@LAQ2-Ey#~0Z)%qmtDUOuM`?Pwv@nIFr|zYN`(X@%X8ab? zd(%QxdKc?Q0Yf_urYDpWMNZ{mmHm}9nll5xakO6^1%d}wno`o9=v3tqB6y}K%?M7A z#*MZNGh$hZVFX9@lVc&wy>n>HiDyN*v9|lW(Y*HtCh~Ra8?~b=DhAs3FDLMM|wfV7Ux;c(nVHXx?kUvY1|Bzi`w>00!FkD>M<2 z>p50;(`f(hb*9nsmSDYXUuHo~o8g!h1zLff!=Wm{dWpdGfLk=_aRIKg;ZK8@RbIIQ zxml?=u9%c`d&BL3^xzVEmB`s!Qj?7oODEGiO61c^?x1ql3L8dREsFJb zn)(@JgLUiZQE4a}X&MT~=gF%?V0Rh|H{S0flcJ*3FSaVCvzE%sY5vQ#K#=MrgDU$G z^ku?-IY(Z81&ye_t_vSk*`K6B=HmnP<68gYU&_bLhM;eOA`U_S7eCHw1d8rAJak4?jarYEwGvnTHndNlsl%Bcm=tI-=@ic83ooE_j zWbETKnM#2XrozoHvm4m>O~nO2o;olmIvO;3`DmP{XtbqrQ{$l-XnbDepi!ToM+Q-G zr1rbqnhV(AfhJ}omy=xvj};En;7#@k(SbDc@M5Lb68SM8-%7RN#)G_OUZHB2oo{uU zIY638s#v!qJ6$OkH3#EznH&gQW@;W_YOeNc9-=iL=GA;rM$J9knqOs^DRl=mmwz3+ zWh&Aj7B=?K1){fd|=|ZkN6Uq zd&ipfL7oJ63ODsn+7x&70xhqy1XA0o7h47V&6sD7k%W% z^u^^+g>XNQVx>X9FShOObopI2WAFLW)6TZC<=WX3rAr|eVL976*V$uLALwS10_LEx&Wb!j`%0gqAGsjZUQ3~%0M%EIku zIJMAxnQ8v#{J{2WZc~9viPxj{rf#`418Q#?kyBk&{`G`I*c?A9X3ryv_~BoOYlrin zhL56qJ{YAFtzYu6HN-MLCe|mTzt_FJ8 zuIOF5-duErtBU1+=rIC2tOKTJg^+1fYe200QDTb9F$!a4VmCNqzwTK_BOvV$3xu?9 z`J`P}=#utmlY+EcIvNguv_BB#%Ba+iVvu%9+|^|Qn^|&GW%g3n5j==6(NS&f%#|;t z6m`64>X;)xhC8Ovcj3nOJnnd&23_u$>@=R-QyN>GPL)7+o%NcnkoU87J@Y&rE}vp* ziVzpIM{W|5VG32DPYwsj%vmpzPFZi_dwW-ltj>M$rTcT=iK9fI%kJ0L6Z&SK2SdNU zElqKm4;tj#Z}6>0@wCfRnu{c&ft=(B?#7iH63R^#;}&vgsWu&~k1;^%EVL`+fH=Qx zQ35|AKZZEh(+}as&pqPU9em=PPnxevlHc5_#1VcHa>#rbBGBQ^d{epuY3fBBB=n@? z6EqNR%wu^=n=$jvlcYMGZ?-k5l2ieME@CnD)vO-&yiGE{G$S%sXOWaii)9zlzzWsJ zxlFs_L%Mn1LB8jHxJsLuh|OuWL%I=8DKaH-9^LNH6^UA|gv+d7{IlY!FL6o`nrO-s zhq>A<`&mLf|3j%+_E$PpzLtDLALBODwE%UTNu#CnXNvGzW&I1-ho9LR@Ua`ZOXhhi zI3<}=v4m2=tR~#8=5yzue+Nc+XW4`G-X%eFdw;S!zm^riQaM|#^c#+rjk)XjF=X~q z;#R*dVE0?eJfWVhfz^E_05(_9L1^+=Z)?`NymV-|`0(H^_H|M2db^6ViJOE5!Dr@0 z5_>f#KfIj}h>#3$-cxHJBD71-i`pO9^CIPQ$K9t^;ojgLmNV<>*r!T$^`_GbSbhka zW@{%Aq?8C3R2LC%ZgXOUljpwZ_@siAf|dx}Ob^4c-OXrnjz9ih&|Wz9*HjK~VyJ!u zZmu{#MOrzwvYxiNSF#=KXfG%3IN664JQanF)JVn4j2h4LD=yHA@AWFaJ)`0?+=^T4 z5QQZ;HPh9HO1!8|O#Jx;$>(3!_7d9O%H$jDa*#8za7(?z2?*s@ym#w?Ty~+fARttB z^0tp44E&mh@iM4a(>@x1N^~06iRNlT{0iqt;DM~1p~k1sUpQ&K_@M072KdWeN-MGK zsfO|__VgBTdkF9JbOFvY0KeT<2(&WUH3MMd3ATT!#S?|=_#~Mx@0;}meIP)pZ!wi~ z6^76J@a*>dDSSy7S;S7NuNm$qxH&wyws$Vq%#lbBE=F;Pt8**;L42Z&m4}Kz!)w=6z8l;{<-3qGh#oa3hmz8vRQCwQG4Wz z1`+yiYC5{I94~HVd&+sR?oIrCDGFhFSjtm3GB(EfkvQb5&K?c3GO}Qi{w7?+|b&dm+DHFdLu@~ zFh{PUc5w`2@>1!*BKxe~GM*P-JgsyY4|~LHhMY+i(L`iar2FQW-6@t=G_ZWHcGn&m z!%=aVoUc8LQ*xRL%9oE@7qdqeP*n{ZP$V{%5ToL7qHit%6<)ry>AX2Qrfe5Yi@X^r z56@(Z3IU2_eAx%@*{ro~9Dma31<|}F9@l(@Q%_@DSkIU&4YBgZJ*|oBI4gew)P_X!tIxr0S5>;KD$%)?0jdhGYsAhC%YkaM9(^4`5~a>*M|&vm;V>&7(r@g35*i!}c6G_k;@px#@IPYw@gP z5jO%>Hk?nNLbvV4XS%@jO?ktX(>wYIp! z=JH9st0j_sEcnm_-EsO^=mkFGL5bYeekbrJU%Eku%arUKK%ubd zLGG>Y&u~=EIdO76Tpp|7*Z>?27!M1td@r z8BCGU&Jqy4BzcoL??6^vs!d#74}mukIzUJaEm^g_8$iR2(>>Zgn&Z> z&a8sU@^{CfgGOSht*LpG6+mJ4QGzGPDk04I{ci9hRDQZZxgtGVsXTLzN97($WtnUm z4i2Y7p3ZJhTB-a1Q46xTbRTWC-e(A??NpKbVLfS(+$_PH~zxI?2;dR%#J3ln9aLR0B7Kr za2&1D9YDlbY7`4EH+WAIOyi(eX%E5HMq%KCYpOn+3pT3vFI^UsaPs{9B4Q)YF0c-- z?6f(;LT1gUF=h*VShF%)8PSN@ALbEFYK+uQEXb)I2vRFS1Ei+R((Y>SBXuKP5^lT% zkH6`zyT134I-9g2wQ-VBIN&2|??WnL^hcz3vT+F0&v>4iff+l)4956Foy)Z?ke0P7 zl%lTZF~)3S70?)fq7Dxyk{o|9KscmMnwBx~+n5vv;G*vgG@!A%Nzv#7HaH80I?zph zRT`2jCd5^cC_I<~$wM2ZQe(10HrXunCyIp|@9|sP&1>x*(*~`b=d{*3O+2V$1p^j= zCQ1YLjlIR2I)${v0aWl?XXeG0vyCl3(dBP2+;4cpCDTvGt>*vldv1L;{q4_Sd08i~6=XeKMiLLa92*?n{V)({^pPd>^5 zC3}a#1KaSnS71`KsB>XZJD$RRbjDJ?4mUnYj|n@b21$*Hz`=Zu_l~Cy*Yq(3;F?7g zFhzf9QYESV1f2K{Ex1=^03&seu1^%63@kD|tA9!;bO~rcp)r5YqR>ZtW6*qtRJd`eL!s85kSCvSIselq z&%uTikmo6{+_ffEf>6~-UvefoKM2(iH7fF{p8<`LXN~`nLq5fiMfi{I{^WQP>1TX_ z#S)m%r0G?Wa&>vb+o@=<&jwv0XCnB6M4sKp7>2UpL zMd>PU(PQ&wUo$n47FI|OH(*L+0`=;KJHmsa_Q-DZ%IFr1>tNlTys;8tj{kU4XGt2+ z!ZTUb9U;Fk?JE7oB2uJIk-XI2{eiBf+U|{f>GZqcz;AS$jHDg8d z)<$Z#hF*jO1tEK~G%Odg0Wt&?MkHzzu*|L+9 zNr^sT-p9%C(yIH0a}(sP_`cEuiX&;_cKUXUl)q7ZKn&YsUzxv%`&qi3Lx^*Q-1V>O zY8tEbJQUIz5#cAmnRoLj@%ahiMoMfx||$CYrT!} ze6F;p#`Cv)&4qmcD7JG-6adGk$c&CkbQ;J^ZO0x2Clt>UNat!$=SpE$g_U`eYdt_L7GfSsUys;O$h_#tO!^5u)419QGk^IUwD#J8zJr zl;b>6$}yjk&j{N&M*7CS9EllFEDVn>sL5TvHd?+Rc=IPh1&J833)2S){reUZuQ2X9 z%MNM2e1RM|QrIf*@(3~ur6eUsj#RuXwH8Xev=t95;799Tz7`iJUXSFT#VW81AD8!b z=n4HSe}SBlA1}OzTx%Dg4Az@4FTwDn4XI($H+JX-Ei<4PtQRC3ILOINn1XfBDqj!n zhTXkhCesWG$)~TC3DrnGF07(le>=K*vv`w}gZ@Op_~>pjaI$cZ6Tfd!WOQZMe2YB| z^;>wto(4k@hZswJVr(MtHxjQ>Vq6GFlD8aUyenxX#@mt-Vw4Jp+J!GkJ|1|PpKM}0 zs0Kvg{qo)p{Z>D7iE%4AOaVJ|3kd;9(f$SGbJ2zZyf`xp?Y~ZtYthdYvL>&Gc zM?yJQc$2FmULb{5IAW$7c46^=F|v^d>W3;xzIhcsxbLOK?PRBhXEGhxnf#n8apn;!krnyS3 zq{2(&y&W2(pSetPI=P>apmHtaNYH72^;v1g4kYM`YEOdlDCbL1;bZ@X1pS#9+Dr+O zyemQXOInHXYe@+)HW~@JPV(`<_55TLzwZlc0{2^Cf8SNB?sYbhax&Gl+;nf|Mymh91!fsle73M20j-LdG&XaM8w4 zPHHKRaq~3wJV|x~MeQ#m_P5E)AMB758+}FBe6~kpR?%OWGwXv5BdJa8D`z};t$*2Z z$!ZBWuIVT8z{EGnCY5mc0`_*;kZEp|J!ANbcCcTJ*+_7DQ)_!ZmgHSc^-Wf=5&u}bZknpO*B2m(9IenHXnf`nbg>RW1p2-w+Qtk)lZma)6d=tz0yIY6 zDbHLiDdCxyjewk{r2_r<$>y0JYN-?+D(~&kVfvZNGo8q7CqVt}lalTbv{fQ|881~A z2c8$+OJaTm>pt)W=xcWf>VAzUK(i_53(%s6C>X!In+!c-#OW|Jx`GjCgF6BZ^hY3Q zAuA0IEyhw)Bz~GV0!iMLpK~Owv^ZN*LW^gOe4Hrxc%Yo0Y+4-joZwKnpS-t2`|D>e zEdu0Je)37M|K4C7RO%V5WfB&Vy=aMi59?lIMSjkc5oi%1zK;A1>y~`;K|YRO)h&`f zt;ijGGe@8e1+92pPJ{6Vc%u{}5o1ieaw8*=yfK4NB}TUJG$YdnTuJMl5$W6hcD0OD zj7a+-Phv;aUD^>BLD?|kUC$?F*=-F^?~tQ|_2u#n=i@ym(q^^^sI+~%9%&nmqtiT1 zoknu|Q77F&WNQjw(M*=T_TL_FViG=`VX$LsJ9$^8WPFV->+_phQJmkplx2Mce$^222w_uor7pZkB$ z2Gd=huk@j+OzxMw%l$`5T8VL_q=Xo?M$|h>J{~CKCz}{wGIxR)pGgR`9a=9x9x+yu zQ|^D0gvb5sp7haXX2^@|`AR#`cDVm{l=Hd&vETmZxZj(vSh%?S`O1mp;Qc$E6yCqv z=lw08!21_*fUJ3%vPf>v`?-|;|H^zLMzC+D1ZdvX|MJf0e@UtSHypl?mI@T}lile* z;7*~_za;`!^uPRgo&G+#pOAosTE^A?%s^cU*rEQv#FK!#Dd$VTZ-4V|NI(TuWlDhL zT?sf+(nkMFO7%bd3bMON9{tZxHZeA0ckupGf@?+p%a2ElH^>?NPr{Rc_aL8(_KxNo z0nm2!KjnN0c!XW7|EU?fH{UpoHQCJh#_{Au|35A=aGx&&UwA?D{JA0_z`p^5XP9?B7QyMhyNJDpdrwnkoq=W&cz@>C>do2~%jh}1=*zkzH zPZ8b<1AHn!9s{f(_Y>0avX*hAp@rF`%N9G5hR-kbq@kX2zBJr9{ojy=W2q`r8YJ&Z z!vT_3Vsw_25M#U%{qG1@MH;^6Cz}`_JgmfcTi$bTihkyrhRx(u8s?Gkq+u!KbJ5XY2DfW=B!(<%!WFb_@yCO;z5@EczmK3Rt}4CQE9 z^E7olNnE^9yy^5#PF30DuK70vs#s*ouacP9HzM}@)UhH^*|i{MZ;P-f$Ub*jd-d+6 z`|{mD&e80YJmI52&cn$-&ZDh?oCiPWcVi$&y!sU^AQ2v65mv;Kk_36_6G_1svk93fHuEI7T^N*ja#8UdDbMV$dh8l^SyU8Rg#==w!8Fhv7u*9G=8`=O3Ym}ekm~0e*97z8&uTX zFCPari(G?>qw%W=8=yNV;IE&!@d2xoH?u-R=%s+x4`2Xoq5*B4{20)F0Dy4gF(_gK z+7sm6(7Qe^aI4R9fz!Bvp+diNcay+HLqAT$o0Yuq9wF2KhkksMC|vzE>#R@Wa3K9K zY1T{VlcMCWcvQ+_n;tVOJmH?tyAPG-a5nF0jpEke-XQXruUrTCyX!mU=tHiC`j{wQ zd5V@;Niw;BDH{+iBrTlW9C*R~AW?XffYAO|v^U>bFOoCPHaw+0HT93$Q_JMXkmCbB z4>z9Yk>fYyO;7bQsgmTP5md$sL9>Q`2omSdiTCDg7a~DQmo-;7-7~QudF)5x7vO#% zJ2aHiv^#~C=!W)wgX6TpOPvN;cI{?|7ObIBE_;HBPT)&cs7BDH@HH=F&FQQ=1>sBp zlnq%_i%(?j3O|obN!)cMh0x~@k;UYr4rmgV+xiCus*WF8oGU+u7*{E8%=d_K2hF>@ zaj3v3E3+>TcX`8_qkU>l!N_ROUrWwjx*FpxU428uRMcLoi&pdLc~HH$tt?vAP3^`$ z$!OlJaC&WI^yw@gv=WORwYSwTn}kHL5Dgo!|0c!pW&}5^sXj#FLG>C_#(G&8xB-QM zO0z%G5QomMOXSl#5|KZ8s|0$qP(Ijj1*Iz!XLD}vao_WRlYZ~lnjQN>Rwb@rW0(C# zOpb)8X1h6|Q;AqNtE_X|>R|+Y2J3HVCtZ>owL7+zhSBY1xsy43m*pxp^~iqj`m_0@ zH7I$@ES*+DhU*nrO?4Uk?C=e5@Ed0CjGwv@Scc zZLsbXlpPD*yz$oiu2yF&M|rK08jQ0wNE6DetoKb|RM)&?FKr__*q8`gBqb)oU?b_P zB_9vG#ZR`0(8N3rTpQ)R9ePneb4`SY$hGcEd=g~q+ExdiRI@})*r8wO_l~V}zf6HO zI5JrGt-jZ_KJhp3ZA)_YNRL2gQO>uOMkAFncNRyhtjsglXSJvgj#l>4Z`0BlOMTEG zW2vv5g6c2zY2Km3ml9d6rqV`92_38l6wjrSj|Z0VlTC-2_v`y-<-HwxPCs+$a6dUU zm42gdou$4cxcH1!vZVuaK_zCN&&P$+ zDW@DR-z5K-@HspZKOa7EIx&q^;D8oR_s>sBY-x-b`fjhtFvOaJGQL%IWY220>2>az zZmvz|O(vvDkUq1BO_+{^c5JKtG$7s)*KiE8%9qhI0H;0RMB_0dqkDly)N%M5R3v`xr#ySJD}=qS|zu;O)BUVkhD_Us%(K;}mLh#qmNo zNAnIFULo(44KI_Fu;DBtZvC}XU;sbaY`kBjyA#%!wd+A$8_zq^Eh%@w{pRkYEr{xS#Bx6(&DPE~})5YdYo z61*FD;LcR4C9Qamla$~+--y@gl8*<5@{^5s8TJNvA1m+eP^o_A;@yRu;@z2qT*50% zyC8X}lFBS}OjU3(1%zu|wKHJ2aIG9YXBV!^aZr!2W3*fHQ#Po2I5SeQ=uz2~_4e(A zaBtb!_+u&5r#luyeDzsk$Os_Pv?DPL-nxWEL13!_Hcu1>D%%i_9bIc(u`thWB zibY>&QYFcU2I`?Pgn(lsj>(!b35*ykA7ZK)E2U;NS78;wEVB7 zpc2>M>i0|02s<>m#trpUT6>B(${dxp=dHdn|35Rhe)k>S?JQV`!KHae!`~+FRMRy` zN;KVjI6~l?FhoZjes;3N3kWha&o!tLaL~?Zn_J(eE9d-<^rKYkKU+;2Lte zN1)_3L!ixSk!})p-9>}zm()buwWskl@6h39d8c%kA}OK6O6|YGQCcc+0YBMv=y$i$ zsY2e{p|F1D(xE#!)%Zu~TgTw)1?60TP=C!Km25~VG|=X;@T+hng8f#E*m~X zPTBB5ed`F{)4z}s#_rgW!L?$bCw!Mv&S68eQ_am3+*O0Cn5wc2u6+b12k$&dtN7(g zO7I?G#A_WP-y;xK1LM2|bg2$+!+H2;gnFhc^;~F1B8TT{WvF zcgV4m1f#X^K->qJL?BJPC2!G`HrIFVT&=B1l4DVu1U`hlKJ3bHZNirI@f zE2`377QuNGDeIhX&gP3r4D!v!po!U?^TP(j%i|M3tpG%{DP>7!~l%w*)fh_7g*}6vG{d&x|2a;g(-@_JQM+-iO~VvDm(v~nq#rd* z@el_iq=zuYqpz2KJP&|xSAU_E9$1u)xkMO!VA+u+)g5p*l{gM2 z!3yFr2kz(zqIOOs-VvtDb}$Lhk8KFC%I`Rs{t_3oO*gEdJ?#b~itnE0^*{|poMD0< zxam5+gYrtQi0fTCs*Ti?;q1+jy$tWtkJu!AP@5tWn@=h7AqojoKMjP4s>x$-ClKENR7hZ%GN(R~m8p=5zA#z_hU$U2)xB)HV?i>xtU;ioX%H1HeXar^xW` z9t<{)sJ%!zWnC4uSFhz`6*bP76%a(!tb9iaIC=pr#P1wsWXES@Mi@@Qiqw8qV5M7P z_7~2$E-s+KqQK%^FkM{J1FIld*WvSYy6p0jV0{}B7#-2>oOz%j?Y*=W`%wasIa5^a;lg@O)k&{Bao zKiS&m>YMfb7eE+_r8x$(?@#N1fVZhp^;vRS5$&T`Ur=ElKg zo%Xtpd zOZ3|oB3`L%tNeI!{vo-Ym>Wy=dq?BFO&#vQyCZX>O>d7tzoMM4@gBUKg1cyL^ixoM zb3^kE9ZKb$(%~3M2^|{Lkx|%5O9l4eCz}qNb>>yLajnu}ll*vec$=K+yEXdOF*iPD zhU#LyBXgtU3El|zAmtpa8R4F~%vTKmv*n~BK=RCu{bjOk@7&x7AMcqPtKQ4haqnO1 zbNK(n+=u{Yi^uLPTBAbed`F{`7+7=33H?VI8XRG zQ_dH@L&s2XSIv$21gd748!bF=&5h?Jt$05tDZ#tFa#-PAl8*=O<|iBPNzBYpL*wMV z9U8Bnxp<#TPVqj6gl}$Ktfcx0bHgt6&5eU7s;C=tEUz~x`f7RT05Hmu|jewdPGF?fXz@s0T8-HK^Z+JIG5iGZ6;COen$gEkv5raeXj)q$x?^M!T zBqfr5ppoRqv{c}6ezGN9=66VXgS@vxasAAd^zr0&VsMPo?;V|YIkQ}M=-rXQ@r$E9 z0(GODuk(%_O~G9>INn?(bnp!h%{z3ME$@^LO_CBi?1L)>JRi_ffj{t*O@~`$wr3Yk zk@t3Js($9u;c{}S@h;Q1j=@m_Jm* z6N>!K4UX4)cv|if%K2LEnhSgm{}&7n+{#4n5yJm(2Mn)zGsEEcKxeHx^l$7A29C4o zVTw31N@qMX%Ew?=430t6=8B^j9GZ97uw33L8exI#y@NOmH8ysJ2k)JR)_UrB& z9Dk;$qHYY1#`Am;{7)GiJ!KJfnsHJ5WWexVf|l!QJW#RPxxrC&geQt`FV7UkkI&6O z>Az}loc>Oxf8!J}IObtn#AG#I0#*2bo8I>rU1%(h{^H%0pbkP_E2ZD}lq22u4cl$X z+Kl5^v_5QITB7$X$~I*ac@nkPQyELY5;$DLdDzB39Om%o*yy!#(xt3w2OAD!Q7@dB znA_jENXl8R%08bg(;xEgkM=W7(9<37&_xXPGwsFR1`~r4vv;#|!bI5~={}J?brl5> zcCPA<>Kejt-}3i64WJ6M-^uZpT%vBcKE$2$H~XEI>3*j>unt7Woe7cP_cc4)bN=BkUA$+=d`k!*9iqH9HJmaad9p3v_d#rXnLO?UkMcn7O8 zYYz43GLCY-;+%Xo1$WVEDN#^;t3~q;9XiT8rNeHL5;|OfM+Q7UeNE}m%1<^OmNIeT z{Q`M!hZgE*E*)MVr^Chb`qr^pUV(Bh)*BF2nRRD6g+DsP8z?7J&cT|2@|Lrt;4ZJq z$PrW`p+v0taVNb1Aaj?~9-4QySL`6<TShR-z0~H4q*yMRcUW<%yi-=ZNm9a! zZyJ$1UrPl>@srJpr(LA1c&fa&Llyd&%Zf*kQ&v1&-#P+#98+Z1RdUe|R%XsV*b~5w zi#%3DF>M z2QK3$8|%RrD%KHsZ-@HpXD-&qkyEToNqDherzo9_*>bgx2iXF6;m?F^by;-VLI-OJ zOiBj8+QXKuZRSYP?Y21WJow9H>ns6$9>O>RsOCz8=2`Xpsc4i70n4*ju2upVDSW_` z&XU4=5Avk&NCzF`AUXcbpOnJZeI1eHu3|@ZxX2U7VX={`Lawghw(>>k*My5?Uz3bX z>}_(b0cY&zMDmFmWSgfRkv`~()FGH6Qjf%|j1;rPu}D~Y!3rVp^E7?fM^B|lok!;4 zX^(9Q#Fd zqc6fLlHJR_t+I2AgLQiW4iPB@60s-yo~A%V6QKgf5!G@-aZOuJs4tfL56zCI_*sOw z#3)es)_h?**#*@;`SDQ7B?>ROUmls=Or?Wmqdw>d>mEU=R3@&?mGz(~%@e-58N1~; zHs;!@ZzP6k7`t{SZ!U-?I=%rAFa}3y$8YZ{Ou_XP?AgQ`8kn_bQ$p&_1c+#SpxLuI zO6RtQyH`+7!gr;-1RzPM)h!2#OlzoB7s@%t2BB7m0e+O@3MD8>Vxx%YV~-1$#+97} zjPGa_P^}jDa%!%?y}}mnd2XB(LVm4)vY-Xz<3mkI;v39O-RQo1wfxX-0~7flk>q(x za@zr(dbx{o4k)OX`%mWsjddGPl#bE=lJcfp(Ia+pNwJoFS-DoCGkzrq#NzAW@Fq}zjjUxu=kPE7*hLbT@O=N>vIO>lQ!#>B-cU#4!1~L zA|RXa-S?UW#eTYN=jW&#%{y9qp}bT5HD6Mqze+|6vW1UpslXHb(1Le5^j$Iq zvuogqMFT(+N2Z`;Scfc=;Q7X@~CDZ(MwDA*cA(>sx1xng)g3 zfw`l`j%nzQhv9;t_fl}+yy4Wu7VrgNUU0PQ-Klx z$>a!xox(>Jm;bOeTqclm#74&U$&8~;RON`ip^_`)!h-6q#pb3*b11qwQbv!JSP8eG zAA+xL(PJf6o#GxV5gpwxpNm!|9_yYrBiB4|IBo4oO*z&34758o*=_YShZ}a26Y^Jc z8q~OVBh0%dy=fF#5wo{oonDj91gWiP&&#AS=5eXfB-{o{9s4P>{(Yr>&IvW!7svZ8 zysa&)$ZR2`m9oXNEjt~9^=;iY(u|^PhEIMtpaX54pKm9bPffhY+fT`DT0&hATAfpE z(ctQ^9nmci+?B=EM6$JDr&|hI+m$wzFE(2sI{sC4uAYAEGMio zK}POp+j0M8P}k|dRJmVz;&D6b|9`%Js<}PD>6_FxPIz*A{p2io4mbS7b#YjF$ z%<6aAKksD9k7=O23Jhk1k)Mt5T}u8D*?G*$l$|kE9tNwb&!o#%ITCY>Covi4z3)JH z>X&hsylfyUC|@io-9eNoK7Ua0nQpq*!5>gGYf|Im)*=U4$>{_(iK2-y+) z=3k*tw)|YF@{?IUQ~zbikJEjrlYIFJHcTV)FSkF^uVG?egKuX0M`Xzho}E;ih3x0A6z#bum|i>dsC4F2@=A@w6K`7InMAoq>2Y2R;w z@rD+h@y4`}@5)O%9d9-W!|krm79=NDiM*VfZ*P#X=2CGlW{)>IfUYv*jgg)uHHWD5 z=&rk;9&eWXSYR^78`HhPx(flQY&^rwFcKD<`-rm_f2xs}j97aeZ;lc0h4@FaJ{eMqW%O_c8L4S)TDl_$Ou;Ipa&e zACE7}QH(D@X?^*tSV*JULr%jfRGu~|>n=!k>ytVD z{8RG87FKUG+QAUyYzh4*?6)j=l6@9Ae|BKM`Qwi#5C2U2nd8s)@A5|^tW@yX4Gy@zgcOoVyb(PKdRQOs_@i))-Cz% zpD^s@Ae?SG$K9!Zn7gf3}*~fpFQ9*@*U79XCdFx+8ITgWso4H!{Dj9`E4V86S z#M23f`r{8X+*v$gzP>s+r4K_xdX81zncZF<`JcBz$^gfFV8 zx=W;de$1Z5eR)i97pFs4vd&_a|1d5vukxsLj&w%5Fz&sAI7j|$XQOc7BW8+Yqj(hB z-X1X*(uRdBEBeXYR%)Q z(P)CpXCwB=o)P=<-Vu9zMa2HV^O9uxNd8&=h|(VX(LM88@-BbK`DJujux_H^515Lq!x}Gk5S$HvBu8nf>v6m`39odKtxMV7L-K>zJw9;QRHOnz%jKT3z5mHa;p?#!@ z9kGbA1T50gVG%Bo_F3c@+J^xKm^!*s_x3CjG+!KUzSt7Y`a)S`Ju43X5{rmD9B3L+PHe6s<+*D`x z0drHG92y)q)!BVYqH!(G6RDLdDu$Z7)=Ns)REY-VNa*=B`&o&T|6*~R9>-JcUb`^O zAS6?@^UjxhiYvJuUDmk)N6+C9aT*a#9P&Qoi3O|Hf}xe@@hUmGkw(un0Lvf#sDqY4!;}>&yGA!KioJwdr2Gm5yDe>yp3ix6S?@x2y-?iQQj=Ues z`$P5pL>Bv*Y%Q`9dr^30QS!b1G-LDOZT&AoY0AU)_|zAl5|<;=9&N$YHh369C8pGXvtxCgT?HqkX54*8PpU1 zPn2t3&m5jHZj5?)3TRc?sTjut=$R#@=$nVSuH|Vuw5LkRD(G0Vr@8%ll&5&kr9YtK z2+RK3^{2@i$SMkQjk{&zGhLVtoy9T;rbVJEU0J@0qs!~pOak|XGL%-h?IbG)$ePVL zJvc*^BUctvV>&b)=5~&8eoUGMw-GQxR{J#fI?ahA`2qQ5xHcCJo6}&b?8PE+eAUET z4%Cd;D|t|otix-^Y1w(Tuj#I5xPM`?2`byo?#<<9$lXq#`s06vj372MmenFs6JAF7 z$@wojHJxiEPMTszw-qH{zQHh7g10>An0!(nOq@ujAo+Wf;lSSLHq6NR)q&(m+SAc( zB_`X$$#yf@1IUKcuUhH3a#>={UARZA!#l{(#_%9LPFKLwVmXX~QWbbN&{riKd>?MS zg{807P`I&9e%po{Z|0YmrMwia#Kmt-H?z2v2t+Ks7?E=he=`;9DG_=g;@*^a01uoi z68{1L>6F$w&ot45Igh;dn9306a*j#j3W&gf-KC4#myzjU9PPZ-La;oD-G>Q zL*d3h`_-T5RsRx|w*KCU$SM^M*c}HI03JrU2Y)W*X87gWdgbaUm%31#*J9&lUy<(N z{}h-R^p7E_=vS=w(0@bDdrJg%H~5?i#U13QQFpj;Bh4BTJb#lzaSoe&fOK=<{U;60 zcwdt2!7-dhDgi@G``VMIqBwJF7+gbBRD~|9X2MW-59#zW7huZ{T&^#h7Fd{L;dyR5&w_W@Jh2kj7jeU9LlfWG{l2pjXUP5ObHT5| zZm05FscXhOut?~b3bV5;$dgfazkGg1q=yxVP&9Wd?{2qD{&VY9b{?xX*67Vd$@YqVeF+AWiw0{-|bz#ncr6)Ul|+Tieq zj#9R|wo=6;S>>_Sy)GLeRda7}rD~8#l_Wow`{!yGakPc$G&%oRI#F)L(?vNJ=N>c2 z!0}_{Hi+|#)v5YXW*=k2GW%foURKj79XfYkv`TH$D52WmRt@(XT-p>(M7s`-Cg@np zel0N=o}uTIu=39s`1c@?*q1wy%n|b{B=6rOeJcM>qo=P zN5jI6dRby3@cns8i1Yd?A>I>u@F6X#K>Nr%=sA)1IhDZ~ z(Yzw8tUQ+bnx$24DN&P)n$ygu4E((4K-$!)Rn_zY?!R*-w@uV_uuXq>}%z1QQv0~1*YQl#3(Ew%)ooXasd&xed z!(LS1p(On}VsRCb41%pums$keOi_rXu3$|R8xMDhhJn`8F8ogFh+?@EK4DTN@WU&8l$~=Zz%>!aaOjG3v-DHQ)erlUvOCa)8?W(L?K0qS?}OOe!7%w0qG-k+ zYfcnjleptrlDB?Wlmle7uUgoL&V3)y7gveshw4L<7c+PgG69_zvZPSW>D9{(!#e)t zWUsKy4vbHvRy@ouPKcR>KgC|g%xUZ=5^mLTR>XciVlN1%hZw?w)YDI@_&0}@UayN5 zF#LE7fQK8u^N_lCqK{OVw93z_ubiQs%PpezFV@m?wAO~!Y>vAQM=%l?JP zRJ44z>OJH_^xE0Ta!Z7GjnA0bXzZN9KRaHbLeLr;;k-UPEPAaCR%A-}?gFBfpwqyT zu)R!Ku0Ob32lU~_lW`t6IQ#)P>9IZ_2e@$F>(_?hqfm-2jFIYXZhaxr|qBtdVz%5CBMjSVs!So`qj{3rl18KOmzPTMF z%b|4b<{z|l^|Okfpu+Iz8J?I!eqTwSMeVm^1hfnl6&XI$xyGInzZS}s5|;KM_X5T2 zjdF0dy(!93gw1`6c*QZ(FRtY$c*_;YdL({%fm#2s?Cw_i++ck`q__5ZQDINZ*WiNc z8c^hhwh19baWMtL=6_9w3qDg4sv47HwZ&<16!oBkD%w=*sY{AK*8&5R;b9tb9TxB6g}u#NR7Ci8y+KNQBJ%4Kvl&_!4n6 zX`YrO=WT?GUQ4I$mYNfRm5*y>Ii|8}{L1!&@iHqrKBKbttDVXwnaWxt_324f)YcwG zhM%81ML|2*uWhY@*4+awNe!+By2q`pJGDU~u~wu%yV9Qw)-x5*9(Pa~u7OMRr^l3J zr<}*yR1d8br}mrD-Jd6v+rxI?3?LOT?F}T|fW#1E0<9ofcY-JnkgP5|2sfS}zKi)v znJ2ld4$4%O&WF+LUyF}IDqViETj@cj(i6Q(TgFSJC;62QHFdJuR|)k#j9)uWr1+L0T~Fd*UBpV%66kNhd=-djp{eU5{&JxZCCs1 zU~fi$-pD!X{my#g>R$O({X4tLUKLG*Hmno>DJAEz zm?Uk>fVSJYGDP1?6Hc2~(x&OoyG;vF=}b}pc$ES08OZ7M=i7RI$(o2+_drn@p95M= z?AB3``Z|tsl0ok0duuo2s|xhvQMmC}ev@xfeXAX{=g4@7ZI2334~k4IPlOub#^3oL zKMM^EeC;S^PHoF$grHB}qO6Ird;=;9F7nd%R9(Hb-lbfWj zfojn3#B%NA%Y~Gtlh=YqxN*7H$vv;}JNfOgPA8A&5V}&a5tz6JXVj`96s%9mt?3*A z+X&d6*i=)bm|4>!0PKGIEVwL9)~;<++mxU2M%k1=!bIWl_oRzDvg8sT16x!RI<>$` zuVtyMc5SX_O8DO&rr+n!vMY+E-po(5zIM+0Zz_s&tM{3Tmd^_}*0-g%g2_8yR^g6?8*k?UBs~oxirkbbNsEQD zhNN-GrX0#}zwT-!AzXZ?NtGlI<(5C!3{#?d*K(1=%NrNhtoUTb8QZ7CgVvE}q1nN} zD;HP+a@1~+FlGVyY}d9Dz|R1}6~F|9R~iT>oajOL0kDJ{SAvJ(<#O__WPfs%)9E+1 z`f!GuX+y0%u@kj3_{Z>{*jcNicC@R^Dq+K0-VrvOBg#e6H|q z480Q2E8N(ooeRxVK;S}imJ7{>PhDu>L6;X@K%8IcPiAqz&;}S#^8$l7p~qrUj1dH+ zMKv|XNU8)O5zC^cA2faDk2+3{G2lfMyeMk0aFY_Nr+}x{{_X;jTKg|usi_j|`yqgr z-I>7T+0zy1bnPX3sS--4E*8ZUK#HMl6e@L=tKA)Ak}1XoZdaMe4WH=17#Kho(xEF( zP!c{Ypz;N(dI^w(8|BnysoD-)M&2di-U6i<22Z6FMqiaU`r3`p&G46sQ*t6x;V_qX zBm&Q`=|s#uW?#~>^IPnSsRy z4>#Thd=9{q3{Zgj$`z{Ji@^yf5tNI0fa-4Mfuc~7{E8bhk(kt6TF~ySEofuRU>U?A zGpl&X1FvY8V~u7}e9~DoIFD0)h{ZuDe{OlV8~SRiBfW0VbP z*=c^+5tL00)M?NyM%bFFPsCu{pzzR9uUVHb>y2}$&0b+exeK)nr+2s()UV|#silBg zyy42T#4}vLnJn#3MVzh?qZ}q@WZe?8lQuW=t>g6c%hSChc{2r=N}}_V^N!yQ<-iJW zQ;m>bVd|!r4!a_Ih20Ndzpwp!Tq-{Zsh?2v~OjK!h6?0iOZt89;Ga zcccSWa?{7MD+YOv>EFx(g{aC@f{dAE079^;^R24}A9Xhie%i|=$R!vqvXQhU>2*!6wO-WaSqi9U$b zPLlbdINxP{SQVTRDH1=IbdSKIT6?0TP>8UhP6?I4zqy~m+d$h7l7$<4dC-mqLVE7A zV_n^{8xPPemxHIHTc&e9o#>VqX~+1tu+^3%uUY-$x#bDjb4x2ewtzLlA5Sgoe>Anc zRai~*V%LnR<)nQe{8>KmrvqE+6xA{VN}~2$jkaBq9zGbgBnoB5+rs3%LC(RpN($^r z0R|owIpqf7fpc&oqDaj6%vAffKhr)DC*0&2wP%N?Y{{*0tk7U^#?4tKn(Y6W9!+!^ zT(D=3)xCu!vPk(HY+9z(c2R2{>;Da%w1SSaKE=|V)uv%cx;2jl&ESl_JsWcQ_n3yT z{1i2W+k~ggl$LXAcApo{%b6F>=P!`cS{(jniZq&Ey*nT7)exrIzM+ON@xKE~#FDw# zG+TY1>=U;rW&#HTn7S>5V{=Unq zm=QP*fWmmc$eQumSGwXUukgGdx_+2)JRK8yNIZ4z^#wWAdoXj*)zihv+Yw6E0FF;{ z74w|l%+1Usf80uvWw)%Qsp<>q$QR(gaAS+#k>3GbYcX%e>zh~oH{&i*xX}UAFH%+x z_jFnLR35;}5AeWY<;Ck1H+S2?;2*hq@b5(8BMWfQ6h-3WizD&t&2|F(E)R8`0(g;x zY`|L`i;rH5d)(d>iT7QFyDPc;TMkvSTj&9Ca-FPDuH}1&@)n>#U7*}_p+Y&bhYRJe zcmR}JkM^KE%z?6>*gLqb?KidiFw!htKk6xy@NS|yN|M`fj?<`2+y0CUhZ{fi8(l-q zt#QNpnW$!rrBX?;IvSM=I+bO3op`90(9@l;9dehSt$O>)l6MS?{Q|M$_any%Q!I z%bZ?3Ar;@uZBsK8?^SNKsN!oultxCFMrIx5HL{F~n2mUi+>+7Av2G*t-!qNe$7dph z;)H<8Pj-EPmL6+UQ}9?tux<-!5jF1PRHH|RpVtwjaHA~xIXHb{s-#o9m=wd$!k-&l zQv1kvb{>P8Xtp*MUNK3CD-9btX21G3ayemj+(d&_a~Sgkl5R?f7p8ly9;>ZB>bH8O z*XpCDEm}R(ZS@b-iB)qdmb0{md#D`9mmmS*#>f3S4)^L9?bXrAsiSpN=^CfFxM!rh zD(%+**nw4mlrV{s)s-j*+C~N+aE6l1ZIEOweDioDhA82x^ctyQtUql&gA`bzsL4UilrBY z5D%}C0b;EWW|Ifz*#S>S_NXuv!VP6Nz4-;seNWgN7-SJ`-jn`FDL$l)9YNyjT9 zPQdSs94tOu(L6vn6+&Xmb*FNK6bOW{cHo0iz8u_1TD$*H8WMi6=TbdG?KcaI4P+PY z1-w+ho-4h=jY$^|GwYfz#iPs*3Qsesl2jQ(pLdluL>=*?c#2G8TIOTA9>HAigC)l2 zkh~4!j7}E08)T3wHb_Ofdq{msd$`t-T7%T?5`EVtjX&TQYER_{EPOo>XGl$7qyGx_YQ=wuZcN zpEw(vEn1W1n=UNhSj~M)ke?i*JbMb+oOt2nr8Kwm*J5TkpErlyDr&tYAAjh~VBIhi zm92(z?KhaPBtM=lT*Qg|OiP9QhMC$Z-6wd_6~8>sr+XjL%1o=@GWu>K z=tRwOF3Uf8i^U|wWyT8776g8|EayU-22uOb^7hri^lWUG-exiP^VY!MV744$oy6R~ z7_mi&hZ}$@e1Kv!8g9JK11OLI(6i?{09gXiLf6Nvla)}5GI)RPTY_C^p(9s)(Zbh< zI=C7RpCMH!hhIpFL33_5Mp34xrSrp$H_~eIv(=s^F%H3h!1NA(xbyqr*3{3YfG65R zqe4D~Q<4o!cF)0!R%a%QM+_Fxo8qruoonzymiHkA(9-}K%A-mCevU$UAW%96NYucW z!c&&Rbg+dVf88W%f&`eZm@|t6Rq5%m3DP9Ke*(N(GA&=<9nDMd7EBJikwbGQG^ zZ173R)}zv3)+tM9%3+A@H5pSHiVKQ3`vzVA8wQ-Caoec0+d|s z{oPrP(BA!~5|8<_HK)|9g^QZ?^{eQ!VzVf!9Xy+*%LeKsD*L_`0rj|fRrb0nagc2+ zU6UNYPI`8;49wEA_q;AN+1DM=?7-1NVI3i70RlbH2e^5FRE^;QJv*N+aeDTfH%!ky zs6A_(hEDUAy}ME-CTqgYm0~zoV!@Oo+Zf7-Iy)Z)Mhj~XQp#N8cC{V2nU=zhU4Y7f zay)sLGVhOYC^JbKUkEa*Nn!6NC=(5&Ah)O&yWh$;9G{PhxDQPr~otpEv(A z{d*GY;ZFbl3M~T`M;I*rbgSlm?u2!!gdca z_k2;?%e8B=Y(E}l&|7B@v!S;&*Q0mDqxZq@X3@Kn;XT~g!PhbG8JrlUt`W#okPb8{ zq4$#qDuHSsJM?~JnUSYC(3`PG#F(%OB|0MMY8>dU^mmCbQuQR$QPrSA{G(Bd)IHeL zy{~ksA^y`ejLQt@$OF>5XZpmyjWkb7k_QP8My_Un99-VZF6;~%)Lg$v(A(2R&k%pQ zlr;9QNtL92_K%3aQ~kw^*U=!ETI=Pvv#9*;LII;r$YT(jWGD(s9RLB`qg`CB_1qb% zwf2M)9j#Td)KK|BCGQTkR@L@e>pLPFp@dL(3wnwcKJBbM_ctN30b&s?g&U>Q40WfF zcUk*L(n{SquY1%T@s6jpdMHA-`v^S=8vj^px!mje(@+47MO2awJ+z67;nDH3y49P# zS^)g4=+{JO8O-4T++6|QGxy`| zn;w+Ueyojh!Tmys!X{vgPiXA$UdKzzIyH!Mvwc6DeV(E3v{xaE5lBq0J>}E-Z-RB| z?5B|mv5G9q?y-?rRvA18t5-*S042l_dFR!gV)rYE+Lr+6ppt0tnY99C7jHkBWgGY^ zGvJHHuP&*KUk!Q_3L@PP~`m_~4`FqV$ZLo5Q5;9)+7LOE~ZsMP{kY zc+5dr6~(c5E~UTp7H-}C8!E<(&(ABq@cDU+Zf6Qaj6v1LHycI{5dqxI8(TgZ?DO-V zNGm`0S)9$!dk86%p9?f)IgG5d5Z9~zr~MO{aRUv!C~RKe6lR|+6Sb0f-~pZ{H9iUg z1+ChNxL!GNg12m3bx3Ob-9A3~;N!$V3=B7(7@*4%fjil#f}n0TWANd=C2_Nh2nucw zg9yX+szDA)S1-y$30&;Jk-((uNBBt#tkHdoqwy}qJYx@0g`D}64)ysQipKlG5PB?C z6(FQ0qPB@jtSswWJmFZZ=uCMW3I`A9NuY6YB>2qhR^HH(NV+K!Kcjup8a<#mT0U#s z8lwTS3Fr1FTZKd<5wUL&{jmLcW!W{w!CUSVMPZ~;b{rvuBcgVXizlPR<#ObR{f!&1 z*RQ7??OA|ygf$L2+Or6$6n>sxy|>7Nk;muPXX=Z7cx%*jKc7hd&F zmOYZar)z3jUEJx+o9RV5v({KZ=lFy0(t*OIk9!dgVe;-U5g@HxdjEoqE`^4^{-kpU z0fUfHH9|x+!jAL-fu)h2$z%5xbx^p!z-{Nod!|FvJ`);=f@o`ysloT-sfJC&>#|>Z zo-E;c&X{;rizcc{NFkT?HOe*-AT!tGRl z*R}fmg8nZ4C-wJ7lLUcm{S60&QT2ge$-8))w2X5EipPhP*h31Uc}?uOyHQ?=^puA# zr>7?rCqFdH2nFHB;i#3?11WzNA4SUpedL3I$x>r@RD~PQ;RE>UbPNvo>PusOc%K9CQP&*$h6VE(|se4%uY>6g_&rz`}_F#{TG>Nw8CJ~x+A!{yF`y_K=KP%xY$ZJSoXMr?3vYkktkfMA6{3GY|#%-{)0SbA3r+?gI0661n!g1mg#59lYf-2u$^g;rb|V1 zWFk`g84n6C(z5gaA7N(#A7zpJ{e)y7TuxBXsHmgHCEjQ}h=^t|=maJ%-Y6n?tc&Ne zkhrL*!NAJ+?5ymn>#caKx~{Ib63=i5C~|l%%Btw9`-~%sx{C;E-tVvadFB~F-#?!Z zndj-JySlony1Kf%y4v|^Y^u97Hl(~E-N_?k!mJA&w`D5dTa@uiQ{Fih4VeV$=Ty}u zbu`$jjWzHib+it3RL`^bb*5j!@(gLm&lVwBgHE{@T3tkl#(IFEk2dhy*>As3iuKWt z>CA4Oq&H-CTg4By_r)qdc*q$ldy;<~r_`K2YClqVI>+pO+l2aPS5oQU{kIM0vmXuH zL?8Wjt_U<_7r;s%4cS)kIr^OGeUzT26B5455!KY|r&@{#cT59AQMgQ`OG&msZz$G8OKeOD3!MLK&< zD3Gan{st%JIxtdcP^SSkf4^V*wb4iE%K^gtTIn(3h>R$|KN*pqel#esq+V`j`dKu;LpV;ZsGDH(tBV=Rzspz1cslOi1@5kNbE(&G zOP0apkAg7X3V%N^E?eQB!dXJ+9X+sYBU8!{a{b$jokd~I3f!CmA~V^|#EvGlv)`(Z zs0!9K3w|vW^e20;b66lKMM;Md- z)Hf8VYe;}ev$rlRk?hbB&@r3lEA`krbW21h##4liKP+hK4}guyK@Mi|J$M^jM@+KSE*uu(^22sXQW@4baiure{C z%D)#niDKGB8QNECMCKgm2sMN{a#P@YC+_RTYZEp;NIuM~2KwMt3%`qbvMdvOhE)H5 zs=)c+A4?tYZ)Fpz}Uu77Q)I_YBC?ixIX_t3`QnP*ZH{_Gd5iJ0Dj|2!da zG<{|cpM6PE_A|cq8{>=gpe>Jwy9B23j}IttBG}ysHR+7q9d_?R>_B8{4Lb$5b=bX_ zjIe9t*|6IV>~Q)ooM3=rV{w*PnaT`GXGYN>dTO4(bt+TFE7QwS*aC%?Xx^_JO!@Vd zOhq3z#veb(3yWonR?rPhtd8PC%V`p;)lRHL^Ut7ivi;0({nv~A)nSZae_L;YJ#NcT zu%99U!TtaW1UjeT`Auk^%1W62>G!0Dv_chiG!L|ZGCT~>tZMfm{soHNKHNlacLEk= z*pcpl08wkRzpU{0;0S#vd$&XDWc%a@$2lO9J1LMoD;NMVszMOcPl5c6q&}z+tTSkt zfnXD{LrC=%PrFF|PU~axcSRqQzjd@b;P5fjmu!EouoCj#;Sq=Lhp=($a3!A6Y} zzb)o%Dt;U5&-#>i$+}c_;yMW*d--j168^<7@^lng|qy3lM zHVoF+0If-9V+TM3Wd5>BqsXTs3~j|K9vxBuIazj?QGn6tdiTVB-LoRBIEs}qs@ANG zBJOr-Jh!#1ov~i5t%QM>u<6mux?=V99U8d#FWL4hgt8@fpWO0ILCJCXJJR#TD{if{}o z>c3aI8LR3KrKEa-~hXOgGv-WcY|6@IcY^E-i)%3j3 zgB!D{3V%FvacC8G;?gZNfUJxV{T^&`#};>D0RdLni5+Po`V{O9sw=qodtU6R@i54nTdbLm zsma9dAX$ro{x4*!E4$`f@bC9Oo&Pss{;o`(iu0eyhbC{4^GL8I)(%dCQ^OE_h(A2X zZ&uf_8j6S=%40FTds2cy`~_!NF+yI6aK#%IWMW_a8iMySUat_ZWm^6KiIeTKx}jut zBkw-sW!h5d$FMSkW!ekL_C-&L#C>E7_>nurB=*x@p}pHo0up;c?@)wmNeE-W&d0^rmPZF~Yc@C221<9b8$ty__Fg$YoH%#OJb(lN0Q@R94t-OQm*O zGPS0;e$HqJ=aY(DK%^nFr7Alc5#M7-X{^MS>O&=v)^>RsEB#a0rx$r-%B}6;#x{umH3(#EkMb_}lwd}D za1mna_gGNiZsd;gsvE0BhjwZjf)Hw2BbL>#yg_is2WY5 z{lYyZP;!fr5NP=(KVb6E6A_7ivhi=ey>a9Bhb!Z{OB#zz{Lv0Pd{}*OSmopHDUtVF z3xoZe|EqoKzhXt#&VHv;HGJ5((*@sK+%0rE7s>Zj=WcrmPhz+9?(DaFR4(}V}qm-Lwq-CtZy4~DQ`%@J1sR0UhMAO z70BpUB&nAZ$7}&y=VzY#>EI{djeL*u;9otkr%q969c3Zo2`m`rq~=c93h)Ob;TI5# z7*Ykp_=|%gaXy!4awYuI$HQr>b(Ikbh_f&`3JikoBOw9XRNIgxXM$~$x7@J(UyZPR znxp{RDu-r2_p@b)ZH$M&kEkY%%qE&xJL|QLVXw}(v%~%|CqVy>28U^eeR;v?8(4(> zg9irKAMsd-{l@-8hOgYIOAOhRA=@KD_B><@(vEjOzv9P8JBkN3iu`}=Tfpy5FoL>! zIQ))?@OuP&lI`gNelx)-R2IEL{O*4Ar}1Nc7TI4TH7{A`eN(r?`Db8a?7|s@0V3`x zyotD(*mDy5*faKbHh+=j_%z#JZ(C0=(kscN?aa&M!`GmALgREUJ0&?_lx%Pgfi-%X zth9vx_b+v<%K}=gv93OqcI1cGv8>y?Yh82oWb@OUO=1ZFuSuwHE^Av{&O?J9Dl$81 zLq(x0rkG`8ICCD9su^)ej6YsIT%zwtSyWv7d@h} zBLwOe_kdfFzoyfRaf(MN%(3sN zzL`)U|3`XRuy+yluM5UWUSa=DyQyuXd3Ef6UMR+%wZHBB_0x`h>Oa2)K1jdP!xZq{ z(O`Bc^(5Q(E127B_K)QAe4ed6Wku~NLdo^__r?bJbT@n!^Xl-~+c4SF{_f6SbA9j{ zNBQ$3yt+HQhC94^MtF5V)MPsgw!#3ak1E3J&HVzr&ihA1=OVmBEB$l$4GHl(r&{#d zvw&Zgre|WF{mt;#Pk&?LE_(e5OrY1??nbXk@H&u#a>MJ%0$v^9687gqL%iO2=qK@V z_&ju)$>$1o9!%@JzN^r>j?_Rz|3?*w=-2M&OMdba(T{lWN;xMu*@@{vtVC!;>`J8D zWc#;Z6recAu??c_>JUOk?;PkzmOr<)qwPC_hvAa(YzaD_J&Jh4JkfYF0bT5`Jg6sJ zllKOu2SJxQfk%zgAV;!&k8qOUjE$#NgBcqKx~By6yQ{+JR||X5m(M*I4EVu9rK87f zV*>ekc^47F}J8)6e}K$T1e#bo<$z|F}`0%St| z+`6wLf?o?Fo1G2_<)wFsPo3ej;nWbHF^0)#`+KtRIW&@&J82>KyxYy%dqjlK3hF0> zVga9-;1J@|E5zq+Vt)S=K5qSXQ-8>jZ9~ohDaP0j@f&aO@EgxbA7pkG{YDhQGbLL5TpR?3|s#NJ&3{z+IH$AEVA^RDq{UT5! z9gV?nYd~3m5HtlcJwaR9lT(3jIBIs}M!wI|AEEuV;S{K?DFpNwDSC+7+b2Zr_xR+b z^>85=8)AP4^OwQb9MQ%aMjvRO%t`1j1&o^awq`=HE<8c8TO7s8k)nWNnZJdSA-gMk zJPRpj=S)k9a4R8gvi*|?_KA)Mw6+F_`MZcz2gc3kX@zUC-+zsj6rC=%lB=VV{R<_( z*b6%S3e%M6ZPgYJ$4y{-k+H&UhcrIhJmkd^+0MTrwRxWK^y z;_(5*{DW@V@?7;JG>3*;kaW74UKPl`^X1gk?@D;NNJskKf~}~{2zFHg+j9%B*X`l3 z%{7SwFuY!zoA@8&oS^&%8A-MejG&z1S_*~-6rem7Lg^+b2tj>GvkLETC>$6Se#aG| z@HMUo&1(q@zrq3yRGTVy7K=XVEq!KZTGQF@-33bO_vKv9_2>)!H490B;5JmKs)r5F zF;P_)6{@N(RMjJ_YOL0Rvi*)#2Vg5;%e@sNJ!t%|Ru>vS7tSTye-#1!RRQS11)xtd zLjsdTt_MKHl|NM537Xnh{T>g2r9xbZK~v{TE`wN{>hEns`kWMuMML^a0y|F(CGd0+ zIX2e*j^S@@2O(G_oRahKpBvAXP>2PiIZ_g9w-=b%1u)wg0F%ww);ybqeh_flbUD%9 zP7vc3h-+aNfd~C;?*l&Q!F_S{_bM*}OE@JaGleeTd<3?q@TBl>kI?H{U~IBg3`Air%E67EU41Yr^OySl|x&=YBAzwd$(cVyw=z9Cxd*u6oCq+&fu!qeQS} zOCBUvE!}yL{1~F^%LMX46?njboXw{pGFUHDm5)Ld9i(Xj1T!{ z1oAmb(aOF;MxS@F&rj>~38q|$Fy#+pc@^_U1M0h7RPQr<52PsOBnAEWPi8lH6kEEw z{+T%S`KNtmYDwPlo)vpODt1_**i4EcYN*v6c|x1s@uPHRzeFdO54bRRf4vhSv_Sn+ z=S!MG_uGy3UjjkH0aB4b>kb@$tdq6twvnKo$QvaQ(b=jw_LTj7g1@1xl^FkWtm5Y6 zbjRYAKZK2{U|@g67_=r@n8PTIVe@<@|gvZw2De2O7$&Nbhawe!XBlXchp+_6z6myz&T@F56b1F zGla_lsqAr;%67f;iV*+qvTS`L@J~AO!@q8T&o>uF)RB?v#V3aJwiL=A9(-TsPv~tn zrG#b&1KMBTck-@v*Dx>hclxxz{x^;N<_ZPP&^@A=qu!lN?i~Af;tBRo{@&QX^A82~ z-+Kq_U&=sxYqfZ&kej1_`h;V&|I{|**Vs~UQNkdVlkF!l_$7%>3?5axV-MTkhxjXg zH#x7Z<7kqT^YD4*woR^+ac(mxijK&oXi_9MX5LZrLG&R*q+9aF;TID3dI(wSbvH!G)4Hk z5!~n-?WaIpP7ika{kF!ASk$(zSNy89KxTV?lm4zVbz_4!ZR&$hb}dPl%}Ejc|J zdlyMj0_*ylf|%#3RmgTE{Y%bM%*=Sk!HJz|CtwtMC1_q(CoVLvLU`JRwamAz(`aio zw?Zt#jMl{;?i9DG|ITD~o-+JZMNo4)zVSW#3uT(hD@W(q8f9KmjyB7& z)bC|E*qBzJOAFw@`C}_M3?gG#0bPg%kS;{*~S;Zgf-frW%&$JBsmQw(EwQ>dHK~XFJw6C!H zNI7n^983KLmLsTJ6r!f}*bOe4N&aH0d3@VK6Quy=+gC+~&5u?cZ+^|eEJsOwZn4i_ z>hq!6WFGd7K3fGXXY>6)`!0Q$@6YR6Q#Wh%+MCy9Mn4Ulfedtk^G?K%-zn^hA2A`K zD_+W=!Ps#t5Q46F&MkCBuHi6I*1l)Bp}E83T}y7_f7ugVOD^F575x8{|3@IO$MV00 zZ~A{#(f`fle|^f^lKoq5jVF|#f59K^=+-EZe&BYIbw@D{RMB`BgDMDq#Ng&|Pk}M0 zeg(!PF#PRALV)xWHy7!TXtUOQD!rpC2r=uQzKsTMTX}x!Ak~$iuKb^)cG7*@3LCe9 z0Uqs7an1c5S;JOaAJdEhCT0%X#g3c}1#EBL!LU7|fbDNd2(axMU`us63pS#{pU?Of zAh(|(mlSe$JCq8eces9_$*tE|;ot5`387j;Xk0|T_o>d&t%AL8yhcGMCSAz%Fa`B_4FU4+Ft?an?R=+ZPj+w) z9&YM?$zk#|b&sQ@DAJ%cMQurpV<;IRbx&%-|7wJ>K#Kr7I;8J&18Zx9`|~tC*{(1# z0_4S>WHM%`g7%+yHb6B6q&M0cGKtzGK9f9^nDCV7^nm&VZY}k4Z6SWsa(){cGsmqW zpj(AE&F*_(vqtr%c%20$nzBbMVmtehVVM#4H)Naew0yTIbDZxF*wI>|#kD68zhwLH zXxQ4Oz`XC6QoxxmG!mdRU^xkh#h;>4`Lr91{CR4`H<^A!AS{2|tqi)h7``YIbj3{x zczZfD$j|a#(*0<(YxB?wTBWOzfNb6P?TYPR{`Q^Dmoq>%u2e7sax-8{&;X(7Uh#BT z>+hq)KbQz0F|i&d-U}0xv$3WuRD_B-mg;^UdX=r~Aa1m_dV`7nL;3*%YUVRgCRi|S z5NTNx-!@|D3p|sW@K5+lB!{-n7z&x@=S|*gYLS%KU(2nQrOv)NDyn6g)$&ZCmMKNG zj0$V{@_LybY?ZmDhs(E0f|xysTZc=(gL%=56Uno@B zy{O7hs)B}1u`1aBScptB!?FXaToa%@0eVZR&56Us2!2I>q;Cd!wWpdA8}xr5{}lqY zl@B}?IP)*;9J-xj$2F+}JDx+xV8@f+89U4#q!{cNjSwc=^98Jr1JkgF?Hgjf;JSz% zrhN)MzF^NL=nVcAC0&tdDI$j0V-N-wzBu9OukDOkdP?baq_-Zef(;UTnx%2CP zu#WxhYmH@`E9B10-b*2-|IS+l-y1EWP4s2|zAua%#V=sG$CK>et?da_@REB<*vtqb zIhT$G1sp5(=a{~_d24Co4X*O|)7;rvS#oyfs7`T3mvDlsjz1RpgOqPUB*7UC@gH#r zA>Qm_|6=rRuo?6b)q>)({|H4tL{Z({>wSh$vdy3x_mn_>c5~f%DL<^fKK$p%2G_|$ zZ4r;#cucqPJR#Zm`UhmdOlWc>M(4OP4zIjo`Qc#RE6RDJ_zP<@-TbUQijkbQTX<9@ zD59YRAaHWzg-(-^KU=aA%~cGKF}u6`mR@eI@cXLKnZX+SmC)TUvxG}4GVqt}Ueq_gL+fGcSU7L>hQdT=h@ zb^>Awe;fB)UHkf=t}VOjU?k1nbHhgbOP1^xN?wR&p;+{FgxU=7sN3_b^YaawU z?(SDEmghWc1j|!HUakzn+Xg)vGTpn33M*$7wIOtZDSN z@CTWYEcIuh?iw>)$qz#(Y`Qn(V!{>2#u1mrU;H!9S7uab_l=(K zzt!zWqBX8aw&lAxw^*J^o6k?SZRpZ0Yp|cYc^Du#EA4Sj{xGGVHt>6vd*ajLLB1ck|BQ6yo41c1*W|ZW zzP0lU=^N~EO{(s&__P%)r8ROtsZI}$^ZV@cV`tL*3=xCoUvm{`6%A%O2bX{?obCNF zeD%Lvre0*;uhR50IL4A}KQ=<6%~c5+{R(KTtaMDzt)L~Q>6g&mi}dv{?r?r-M#Jvv z0GO!Yccd~d^XClH@6l+wuBfp1v=|ln&*>uw{03HdBuO1bY4v1O+JmHZ9$lHchhGs&vv-vlxXuwu;#RF^BNX-85ZUPc zAmt-%LwZ5i*|_)={E*stVW|k_OYMkYCw(Y_RYwFn))5DSeH;(@_JDgz5lw6XN>l3nQ1gW(Z$oRB}i_-Qbp9pdbX`M$_ z=B_RJRu<&9b3)!yzl}qpxrs#?KC+e1__Wh3Z43YFwGs6`b>C*(m92)IiHQ z9iA>DGVYNTsqE1e%?)X9iCy0LmVX}gU}EnN`NsWMVt->qzIl#RkT1JMNWSyjQzCE9 zsj5L@{Qu!q544hawdE4=$~=unP}$!<5U=itD!bPKrm{hW%6{zagqyKs#5bl2@8Jha zVfxih!{rH~h;g&z=j1(Ppv;)pbv_aPr464z;T!*sIzoI~>_0q|-%MAXTOKE!KVCW? z#gA7T*AB`M*~}k+`QULujHC(Xd_fS<&zMcbr|pQe>z*aT_;O(Q(yH3d+SMGf)2ETJ zu)!-GhSobaseOpU_*);(Dk>+WsiBR@_DU&NCWl_EyugO8jvi>Ak9!5({z*F1iO2fZ8Jf1y8T}%6Ph*A9(uc z3^ysj{X*;ch5K~ad77p7Hg{3t=y}%M{5Ce!W1xd7}IGV%KzCFW^d{>V`-`Z)7 z@gcusec23sxa6I zu+Y(>MG@hP_>BM3(EgKtz>dl1*}zM-|Bi<9PHW%p9$80^@l0yMZ~Q&&w z9fa-99=8a-vWDvA=;bT;zl#69?{S+0gp(0?KMhN^-y5-UnQHE*G z1jKVX3Bl6a8Pg$}E*X>4fYt-**++52Jv`-g6LAm5@aWIH*guHaRwCgzC>)=DmHI_C z)($M3z@zFbsNN2a!r?&@g^})TLMP@Q6DnIZhz({<4@3_ATP2so{;~^e2ZRjQ;TzGO zfE(`tZIz4)If9f6q#+i zWfWp+{VVp_=O)ax{6M$)`hipk^DtOyyoBGqyM^60-!%~9E87BfsU5fC74u)(jC5!J zwJ!=4pKTSp?FSAKUQW&VX&T{NNr=kW=KQ5$k)8idr~}$wq9(-ZmYNjXMfAx^4O%U` zK^Z@ND0Iklvo-K~dp@c{JSwb}*dr-LwKiBZ8~Wlm?(Qx$X+!nCIF zYJ{Sv)lI03Pd|_*_;FRJS?B8J1DIA+qzJ_1{YxwBjf3Z>;zL*XOIFA5yJ0zOdCFVT z+3&EsD!D55zZ1JA7c}gJOvoC|!^wr+0Oxj;4EjHL(>z5!a^3x^)!)0Ag0fpS%v7)V zLcDWqI>a#qCXJ6-r@Z*Huu~pR0z6$qLeMFfOjW1k{>V$40IyMc232|2Rk!MAkp`j~ z%^r#8&CV>7cbijRysbaKi?Y&>PTK}ZxA8NyOw2Lm?bw( z>Xp%`>q0ggL@#r{A1BiWC9B{E#deLxpM)0Mt4^?6stHy#_48SjQ-Jz){5lWQBsVq^ zp%5=qvepw$_2ZV}7{$N;*5?1)1Upj_!02*feb`O_J)u8+|CbsffuOo%(ZbG$rCzpL$a(2%!yKv6-Q-H-M+l(%z0Wnl};@|g51?R#N9VGGzXUhOrC1SqsW;>w23qq z^@fBa9jra2OGwT^?kSOLA)k>`J9iGY)|R1G_Lx!vlT`=KyZ^x#8<|!GeXW8%Q3WqL zU{vs3X;{HO-BTjhmwahD)hcL$(H4M2riNvcrBu>G)|Gf<0KE2>S5OH_*J))7QMCFS6imepT&>6#^^Lfg^T`4{EF7U z+|>wJui7~&vCzCZe=^QZ7Nb>5g>M|Cb~Hup_{`x9RjLZ@SlijxR0Hi;`Kh(zESgU{ zE}P<1h2{d9SwF?E-HWimDwKQ=zg_yTqeXL_PmO5DM3r4usO;~q3g$YQptAg*Qf-%4 z(PO93W3!iIeak_Pv;-pX1M6 z{|<77 zVvP(Avz{+Yueo}>1Wm{AH7uRFk#Heq+OH!{rXp?c*7yH17^Tz^lp8X*r&J8>parO~H!4?{dspyb|pvSiiMCvZoLAe60%12DjRk4c-Fn#@5T2 z4#pB2K>P!o=ad4-v?s;m53Rh(Sqj8*Z^amProBbCu5;G|%RkR8It1Dzl^G_BOmI7> zjHp&9dlsIO*^yoS@{Ah`Q|d0NWrRuH|Dsx+`kq>JXh*ZlQ4RQ+yAgGnKN;_fu-KF% zgxq&gv1KYYh+?TWr?iy(*c+0a`2~j*l$!JCBtd z#WVK`9DYttxM^bUU(8O(_lz5*m-$cLY9o{o-_kJXL9U=1?k)_TqCj(Fn$cQTim(rh zK7*@9i2aE}g?;a+Y#)`qL{p)lq+m|q@-B6XxWI{TX>XDDwm-Gjq+U4u19>7Gbp+?8 z4Z!(Y&G5SEK5SW;q9e;cK{)LI)H9(Oaidtoy*Th4QsBoqlLVBxZDR*p)Fo;Saj2Q0^1tpCBetxoRQWXowayZwbx&h=XQ9 zZNL1tN}U#!+ETS`rBa=aq0X2P(L^7a6J}T}lT9HE6aE`_7!nuL#(G2Iya?QR>qDj0 z{vCkl1EOvWlMF@AZ@a9dG=7b{OvxEr4J?64qBJ)pRv>XhimLg(hhZ&my1yCLqo^i# z8-FlF>*Le@Xs}D-)2H*03OM6;J?95j+;>WO!xCYdq>W0jdYyla7$=ta4_sByzB^eB z7e@_Q0Q%BNUhH4%>7mS&!+Y>)PPSXC0L=i| z1}So8fJvn|nF+PZFmFyIEN4eRt~DSBMGZepKxFr61!Mwk#ySQ(r=?>bu$GSb$Nj*J z1c-%9hY4o;1A4N*-1!fy!|nzflI@2@P|HQmsXzswBM?H|$GXpVuG`69=LN2hPPPw? zihc4eLvxc9Z!2XRu-)oSEGg(#WcVUk4a8I(;T5;wuhIJ`16Sl+Bw0~gxX*H}NzL=` zg`@W*5*WQdf4?9As&g1tv-d9HtXs-cnM;Ube@TK)|DaO@3`H87(!9^&m$|sGt-GYE z1wM2q3Xet{#*4>>ISM|Z&)L{$BUh`I5M0ZiaqU62JHCOtuaj*UD=t}BXga{(8SfJf z%o3vAOy*`pr9}hLmOEKgS{@8>EQ(V&uv!&wtLL@R>xH)^(g8lIvW9gh@RBje*Ajo| zQATw^|FuECHiF)@K=o=tm$sp(Zqa@95{(SD!>N*Ej9)iIC1(`y`s`~;evfdcyl+!= zmsVYbb%`t{U$1JnV9?0@US+JPXnK0O)}mOrEG>z*?Vxg<3sP+_4NMK5R5fu? z2;^vh6z?Z%dYDSK%WBZI8)Zv0GexTpR2?#a0i;8@3IBC`4~YqWbTL>g<>{)(rUzd4eh_E*nH;*U`LS!gVxzEo=#dmrZJ!$Lu~7 zu?@OR@HN1CihwnN`F(WnQ(^wIu|7BdR~BZqLXHBjXB16Jfq&j7GW~vkJ3Q z{T~jqPS!}RccG_PsEul!BQno$my$C$`^l8$)o^@~<^3kgYkWUhwuf4U_qhtxvm{HC zbdTe1R-lUvb?`=SCcryaLxBkXSEU8`TU++;Blrh`OYS@Nj+hZ;S+M)by$I2KlkxNC z>D}fw+%!^D9H5p@ijceEOIm(ub2=a!TP6jv;Xr3bM)m%#D_#kelkFE%Hb1CAo{et)(SoZogOBz{j@2?bmT(E60Rb7x7Ku%4}FX8T?Eu+Awj_7925 zU18-+d%EWQL*@1|v32FNp)k-^5Dj5Xb1?#@gp;Bz{DPG=qE+g0;#-R0iHE zeZjz6`5QCvM19?1jfoek2K!~-MsAaZf4nC)em^$B%1YfaiTHK$3@55x@n_zHKp+7G zmA|@vU?Got-*2OL!jujQ6ZGAyuMc0jzeVp28@+2w$(yQ43Gw;3dnbJ+J6*|&q29Ds z%|i-#g@VebI7S@{(u4Pb#qVFoD_gbVpn}2o*5dD!uP=V*Bz5@e@M}GFllO5Oy|cvS zCHw9=H-cZ`l?GU9J<0{q>F%#NMJLHjUPa_O_2VSuiO)4IK8YrBV*lW0 z(YQF1XHpaXfL~iLwDqq|mcfeaoAs}57|827TcmCmQwKGk?`q6OHExktjc*oeJhG_9 z9m5)*JR4OTuDk`Tw;xB(88abB3YVznJ}Kf>5h8u?E5GY%ZrSrTK475yUh9kk{pzJL z>uWpZ;6Fje%Y?eR<}EW9v%HOmytgonTB&~PS}@$TU|!UM8P)>Hi=$aU^&5xHS zv#Bord++?v>8%Nbe*8Xarnc&WN)8SgMMD?=$IV|0gU?+g(AG&VA>r?jzQrvh*9+qt zeJ2Hn%5o^It*-+Nzly(w??u9v5Pg*Z5Z0l!(~Fn z_8cAt3{N#)@2q=DgSIjYKwK3km5?|h7=#x^XUc5K_)|dF^;lqt? z7*D77$G!(rZvQ(?7hNTB=DHndF^FXKSkXyJiIt@HB_K7)IV_&DPsO5FvhCjvwif*P zme2ytI8m7sX=quNiNj){oT^n*!02?dKMF+x+i{wn2~ z1MgHE(yy~EQwE!H8FrtWD*Ok`54MR`9q(F|`IjR>z5f9X)?$i?hr{W{lY*aLPYKu8 zxc^r6ULtuy^2DT#dkul_WZJvO7GY~wwjEO1=;e~$y0rHh=S||%6-FiLby)nk=HBe( zw2}~?em#F1z3zmE?rg;W2TiEuhu_=%$9F}&Z#T({bt>;8`r-9_ffJfjHeh8AVu3|p zQ>4u#E!p-VxWbhtH4Z~mjxNvOFb35HzMFx+Cjvj$QItNrm%uA3PLc)r`uHCn0-U$7 zZB_ZSjv}nh=tg4*?NnwMOM?#Yr7$qrZm!r=&+v0W0q`3i0{A^aE5e(FzMsdN3&R7{ z$Kvn_>RJ$}wi+IdQ14c#;kVR~zucCG+Agl>Y(AjvKV9mlbvAboh~2WQe`32?Zkjzj zyIiQwjR5rkQ2xM1Z|k~KDx1d`7%pt7>(X+JfA}$`@3fHj;4&4{MGJwTZlG4R!Hd1N z%AQac5A%dVxqqoi^@scfYU7|Wri=ogAeKDs)+V+#r}^mkV8=-@To)cIn2 z$;By{ez7PdPLJbDV`j8pyR!9=eer1zNY z-=uYFKk1Tme}0FFhTJ&&AR178xGucU_B&5MfJ6vK!48fq0M;?8=7UI=l=Dn#!oOv- z(sWhtT3!o+2cQ4fR!uwH8H}h;Y?HAT` zSWwd{@;@VQZvP@S*tH5tzEQG*j`4aQ3`iRHJ#t5t7pO|U2L27F38oDwhw?BY{Y5(1VM zn)ktbQS+YV*_wB3QH`gnM)=+e0pYCkaR*}PjQ)otTMyr-Wvk?%v864&(`64@*4*P-4GGHCzTTKk$B4g7 zUC^+(d{9+OcXM7x4NhTqaAfn=!f7B}we?kh&6Lhg^#V;R#%m=&pSY2ivvB^$s~HNh z(c&{6V_V#tC%2?oV5e+OFdZZtJ6V?MgMF>cI+A$C&m3^XG z-}2ZC&d=G3NKiURh9-XIT`7S0jL*p-=w&aFZ z@>|7kl})rW=uK8+c&~wDszCaTAKvD>x9j*3G7q{sB$^;$-VOd8mpV*zF9VqDU1;4* z+D_A4uJ*}6`(74hvd>lPt3WAp}(*1^U8j9mH9um*IhU)QkqbhYbsw?JS-{?N?V@-C!AX0MRYp%H2wp(!ndiE>Mlrzg({~AI;Sxb2r0b)sVjxy}al- z8w1W+V8hPU`jH0lVr9&nlkMk#jo9PGe!U{n%e(SSD)!&;0Z_StjY_t?+L?P^BPsqj z4sIU@cmD|Pvy@G?tKT`eU;Z! z-D6X@9~I#K=k16N^LREooU%FGF%ItRg$DQ0`9_D=_2cMpCOC0^zX11|BDm*;aJy^{ zH|F3Da&S*}a4(7A_B6Pk7vLUT1b3?t?gQK`vk4s@QcvT4J0t))lsdS(L~tLcY_fe# z0qzHHMRa(UXQRWho5MZ9!5zd6(7^rMJfp)a`f+qP&fu;rz`d#n?wKLn^~Y?6?+>Q9 zV;#>M++!Tvi4olU3~qGq*uF(@dxmiD+8pj*9o!jC9=@|ktNpf%;BGLu+8+mh`rAL> zjOg$<&qjygo5O8#a4Q|$J00A)`f+p^0Zz&GWd*ob6u})G!u@*WW_0);CE(kd?Tv4Z z4(_=T+)m1JJ68ejfFihMA>5lchbwm~;NIxqel^GVwq*qOK7$)wU$N|shz@_}+2}BI zbGXAC+8#Z8OXqQ29S`mq=S1#1b2YZ z_nQLTor>UYcs;=P&zr;5bQ|xGs?fu4dPis_rsEBZ^BiR5>Z`@6Y_MiKUO^PVoSBoG zW$7i+i6w!qXCBM$RP5Eu49D9#jHWN>$F=~2PQ|mf zJud3fxsOPqstgQ+9Q(Vfj*F_QGBO`usH(E4s?T2w$kDcWRlf(Wt41x>uGe^`9`FAa zL%q1wtRy)PjiRPs3O=t*F?{ZN(V9L_Kd$NHt?9k!B)r4+R&WTLerkx%H%$zv;RvQV zD_hi)OPaS{JFm2*Phq6};c?7%eSFR)v?a}Z=lkoOT+J*NF#xtL?xNE9n7yr?*QKS0 z8^-5SG|hUBrkUEnw`nNmbfu=FyjR)ptA}MZCaSJ{>@LF3o zJ|u?$kG^0HeqBGV!DrGacaGjQ_?qQWgU{oc)P&!K25Yp|VU`oMJ$|}#A;bKsuQ&&!8T8$_mv$nAVyuzuAcjZX15-~1nytzfh-2;I>^`Pc<%a@pt<)u9>Ups zms#UjDw*b<5*intH~Kur53blf9dcSKaVMwP1f8edC7mxg%|IV5b?PYDQ~*7q0DAWT zbpCE_?u=tqT^8*0mGSAb_>~v=_(~q=qgqKYF~+k?@YHWxNu!wa?z6)If$PH=<^T}S zd`1RPc^u*=+s8zx%yo>Tl^sjNR^INO)XI4nTQK8QerV-xgjLfvmSl6|rh1G0`xm*r z5Vg4ZX6@2w3Kz8aS=TzM$rh?PIH)FngLa|SDStSX+SnerV4?*e$;6pI0c|bG$@WnZ zwD(*|pbaQM%PnydXnR2%r9R1(J+?$;heu^!D`2wBm4K}eg=MSRG0+C1?XPLAG|^hO zuc9(>r4nB2^I8Y*+swkl>&;t%?QbsBT%U_&7o00y(k-}LY#=LRVcei_&$1A2(EsQ< z_16_zF3}#`owgkFhusPTE+_Wh*!gC!v@nC&RJcdm&Z+!JgKwkKmZ7 zj=2)Sq_f|f$gre`wcmx(WLyk|37F>JKqP>`YvUAu4-U-x zyYUDAUC6-+#|gLTZ1IQ-A?HKr4m%0yXt#x<7mS|2*mTPhz6WcZ$EKp-QnZ*`htOHQ zHE$2k?e;=Rz}~W-yE{%u7x)XV6~Z;oX?gMZB!6lb5S_vwFZM3AgXqFr%>b;D0?hVH zZj9?0P)!{ue3_P`&S{h}+zMx3FGo79|Vo9fHN|+6bS?bP<1a0iUl*!RK@S zc(LIQpMxDfH#&v(C_hECd;OIVpBX%HGi8L&Hk;wYxBm}5iMC`f82C=h02aK7=bd67 zJlnfe=sic^4v{$9`xWF|F?wI&D4G)s31P~cBgrf4O!oRlGRdMv+Ut_uv!ifdTgA{r zdX+FJc6KOU6TAQ0ceJhO4CPmIe%3bX1ks|#AbS-SMLk&9$t|iJm<1YiJu`A#G9MXBbbw>(a=O;H^gsqhl{0L)e$gWOg6;klH&b!neGD@0JeV z+5*101%~gRNeJ-$-4R9j!r#J8r;=Unw)Bpx@OORD>^^NVt;3Tn2tVM6dmdGA+R?)T z-AZlfp3+`r&xJ|ZAtiO)T6*%qMdKb0G+UA`)Z*G1{+0Yz+HHOE3a;RAT9L=fDxf7j zvtCm)Rgq1!9n3*;q?<$AZ0kUnmafUVG36~=DJ0JH@#N#RB8Yp$iq#_k@lSX}Bg6^J zeB5rSOyqi27_sf8(o%A2toN_s4kZLi!*5@xm27`HBJkl*(Fw$dFPT7mG&>ZC86+SO z`?VJcy!B8caITg)Nqm|-Q8m$O|2TG`#sNitzJ`*?cCGD0RWdG8Mlj?Y zxJ|`m1MxJES~&nac+%Jo%v-HlCp&lq&PgI*& zZ}tC$A*g1FhO?nivi)75TX|29pS3StEJ1WuSZ6_3n3LKnx zXNTf=)S5Ckc}rN3mK<^dg9Tv!o!!h0Mnt~+J4wFI#pUXZ$aW&`$V6)J54vN*t$I3s zPC64xue^9m&Z{>=!na-O7*fHDg?p+R(SzG`^|1XD9a!JTt0z2kQD3{ZN!@OpJ944hAIlm`D^r8} z#M^X1w+5easq9|Irg<2znP#g}=X_GG_*v7}w(Q2W($y7ggUS29^KG4Y?rbJ%S~m<$ z9Uy2N+`oF_Khq30<2B-po4)p9x#~ZIfMc;;`=l8|Z|cWMQFmm7GsK1I{?2nF1NetI z)=Lxqb%&{~xY5B{G~8sV|DSSDH~Y0-TOBfC*+h}L5D~a4Q?`M&FPrzO?TqgsZDZ?iEf^2iqpe?<3qR? zpY}J}(TI)Gi0w@iNBaMKfNiVus|RL}D{aygdMO7+twSidAhbyM!&po}Gp1<4T@RX( zx~FTwp-~Gm)`CY1Ey#34E%+7Bv@_vP|`F!vE!IwD=EPH9fHD3w7eFQdx+*MC9F z5FVTk>N%oB58J4t1#J>}D>k;@kj5PL?jGm^&PG_r``UkktupCVg>slRQ(Ea!Nu!gU zTO78up@k2O=+FpWg`I$7v$v5s$;@3El1_R5r2m8f>!UUEfL6I603jbozrF+Nu$5KTw zX_G~>y;bvZRLvHJM$MuPG?b38BTqDM0L^3AXyqqp)L##AY;q_SY>l?bl_kvwry0Y> zX{x!*zwIdVfRP8zT?eXNxv0XIT{Td#^LbjBg$MT{(wcLe_}fCi1GWC1blF4sL9blz(Q6=CnVwo&J${sRB%nKMB{GYm%g{dRSX>y_%PS9~f%9f5cr zq4aCCA&m!7fMcJmx?_jgnPIto@{?5qWJ!z_#Z^ipJ~sqU0i!cq}u8$I$JpEHzH{1 zVo{uyh?6%M-1QOMt3fxvFYSIwYQ$UaH(gk?(vXe4Zv{#tBPLsDdj1(}`g0G5O+SMK zn!cEXpy_K4Dr&lr=U;JuFboIznVlW(KdDFy@+02)r_G84e>=nNm6?28*ckXxE)`No2cc_-KQY&H;^ycenJF#E&y4cU3i47DrZmy!$ffYC~&cy`nfop z@;HY`h0YCb4JEWrKY=guyObBhu-etbYS$*)4lmWzh;{09Ql9U`r@yU_UU{`vc~gGp zQ|1P!17^q5HInUDtDDhACctmdB-h&g+!N~agW0ZQAd<(^1h^5S7|XY3A1JowDp(Aa zvb1%%#r}kP=f#C|5a{q-qfzW}Bdfv^SVf>mtoEe>Y-{vW+*2ag1?mXPkxkj7a1_@H z!^kKA#Snw?=Mah)s|I>b(RgGiPk5%4FaE&3svmRF>R$_F8v5+t)KD#c71e)qq5kim zHcS$^D+SBrjJRt4J&=O9zp}l@8VqN=8nI5Hd`^=EA#2extJV~&1lAXs_C)@}ZpKfMlHul3mg#BF+obhV+3--0< zxeX`_pRy+;t;IbhkhISpG)cRKYLK+=_eauJDz5qoIvk(Y%MszGqOAe^&r`ZF3TbTfYpJgE29|dRG|6^8+FiLuahYnBewvPk6G= zdomE5555<9utwdP4s9fDQ)e4V>bpgAF&j$oZ`J5Ojyplpl$c2+wJVctbzFli`D()M zY0QjR(UcjnN|Oq)^p^ok)3Ef2CT~%LH^j)x$q_) zhc#jgiGpKO4^YqPEMHV!EUx$4k8lMacLkrZf{Jt&6nu@A=FeekNL50j0sehoNRPxb zQ(cKsRzl|yf)WF$Du3|hze*C~kyZ?pQmSE%0Yvb}`OK7%rUspGF4?XqC_%x`L{t-v z2cNLvVD$sxaBv(6D4=N+2>^XKkm11YFl?JYE*z($^P89lj5es(A}1<7sey2P(~1u!loUvlvBiLv-orP?>_pa_VjT)y>0 zdq}m->B>UmII`0k0c9ua9}DSD#J)?Hvfhv%AP5I&1&Y-#eC5cU->q$ZSy(I?AKD?H z!6ud#9Dvc|HljmzBQRjvdx@{uzM;a^WDM1TmE!rX++v@^;3de+hgjJJ1;A+GIY{DF*(|AL3xy%Zq%nJ{W*#xQ5}FDO z^1I#gy<5_+q1z#1OvX2kAIdugaXj?osGY>F43;Yt->atWkS;BK{4Sgfi9dy@*3rex z<&;HKH185hYSsH{L=>V;y5H2+$IwpfM{C>!@sjP^ zFs|{=p!d$BrZLbRSGlJI`mH}!g0t{bZoaqi@zjm$hiBGP68@7L!un%9T>U*={byPI zHb`x2_1CF>y{`aY!e*eNI>? zD-saLa5Zts!lbRzrxqp?;t^M?n9M-SP0kN#emS9bH8RFRIrckN(svvom>r+42`lne zA+4-+O|&ov3%EZEsZ$))S(bL*BLr4n#fc-`tEC&b68^h=Z23okyuxquEfIfQyas>LUk85InhlORXX_r=bTVzHs+h%OfoQFVpMLKXqzq{M?DA&oxxV`GZG)=*%sr zC||DPOYUy>9Q3DCiwaKIsNfkCOnYNc39$iuV7>CuQS95Y9h~jgRXJIfqK1Ka`~hM) zCv{sCLEB{`Xnh?tDac333$!x}&}OJ`2u<^zUn>FH(wctH8sylUL6U3Kf=6`vR*ISB zDYx9=CWS>iVUFUWH=IR~2#3pdg~cI$*UM?sD-R`X0q!Fn($bi0Z@KM(VYMrjgX>LM z%cgMJ`=V*jkJ8>N8WY%j{4895JjyHYt9t+rPI`y1hQ>sBWpk|2E6q!*5?}t%5#I5= zu@>h}Iw1Jeolh?h&z6^x_`98Eo6cdu4KtT|)F_s&So=$!$~q$ZX)Bv|YVt}O2QQzr zjo@_~;iY?*2t+DdUe`FdL`Dgxya|9-Ni_WU4K z_C>0!GgY>hY?D3|{#!Q2AAZke!g}>vl@G+~)y$NZszw^`5}V!4AP#oEoOEH-&U|_D zZcy!&^qzBzOY4)_TR2+M9(y48oIAzYoX;6xUxAyroR-hQkbIRz3=E0Z1`IhB4^tB zKvjhDK`@UIUU?s?`SvR$6drYyH*!}}V`k{OmXDxq_NY#pW29XnA{%+@9eLjf$h$nc z-{oiMyX8;Qx4-DyA18!q!N!=|o*I$u1-+RXYAMOJF;pDE)+dX|3d~v9`?M$xSXI~A zULcc;Mz4QIlXvKxrfhXbWA@NFs>0@MoFdWCt$%3`L3hXIhI9TK9dw!D|{9N@c660;l_}fU|#|k(+ zY*L6rW83@+nm6&GsJ?IFS8gL^MxCKNZC2mpi(FgtsxoTnbRiMcJLys}t|P2l4NrL! ze+q|`Nho+^Tz>Fr&PMpWAHVW@{LbvEI)-_674oOco)PG#Oba&6e)%{tbo7BsNe*w! z^si3s`JCB1n!jp-r)En)(tAGW_Lb*!mZi#GN)gC{91@|%IX{%8GrQ*-lkKMfiSh7* zyUetk%?~5u9Q`=c?!x;5Qw|I0v3t#wV^%T4Jth2|v}zhyb(>CiIR#rTdnne5linu04=`INldS})Uo@g@uf<2Bq>%d8 zf0;d$#S)iKQZK(rm7B#c-`T7PdjdgE4`6_lu#eAa5rq{v^WV`KA|2pc>JQqTy8y&< z1{}$FC}S2@G(bjvwL&<}aD<3AmhBr#xpk_+-%NZsU3xydk%;y&RB<-()|m1b{1r8f zQwBdD$gt3jx5<9e4MP)5V9U5sr+;<2>|lxR+yEGIi#GvrE>;)8n*ltBZy>4;;Yk-r zOa*$Auva}c)JsHzyol({VI$&KvU=T_TZWM8xT9b-m2!r6 zV7^;gzfK&*Qjxut-!$TBtVIkPC5o9r`XmcItw%W7h;%Yz`)L6}IxjYPEOq1V#-lIx z#lorMP&)c&pHj0c-CGc*2Y;@S%-~BJG(ok;`qG9OLz!{9moB{6m-thuvf7Rr!^;*7 zEiYX#G*+@;XjlGs<9`MJyYs&X|9kSk7yoUUm{G2c+AdZ1+-sEjYV zj0rM!2{O)&GA=}(lkEp6qZaOg$W*f5osx2@zR6{jBh-bKaS$2v6UfC?_x(l$xh6%q zDlJ!oat$X}J-Jp(N`TcA<$8>yWV_bI^4Q%+jU?AKL9PKVmzFG#w_Is*g>pU1WEa|h zaz{A8-NzFWS#t-KWczieNHE3Ur$55B)J+XFP!u+v!|wl+^KWxp-bx+Y^YO}jXS5ca zO&YY5Gk1dZxb0nKx2hMo9`__zB-^VBJub<6xULd&xU&AUy(VM1ebk_AzuWO|MqZM$ z6%Wh5XcxlLtZw13M{{!UkiYz?Z%IpC?d$A{|L4}iu6VQ2wkY8CfC4@KdOItQhh#3T z$t{726kD#2fCV_+#t`Ht9h$=}>C#ie<-w$rbB zegk_fVNj{RJ8aLo&D!^#kapM}1a`^x(FJV(2!^1nf*@mFS3+iR@!4~8Q__VfQJnfz^({Y7Q3$HjFZiazBXTS;87GXJIQ50H3; zYsn~UN!{d%%cLQ$MBtL`EfIu1j#+4kYi}`Go_tHV2zWUObi4oDT9E2qRmM=Mh9F1^ z3OsP9M8uqtGd8*tWs~v3dH?!dabK0dF809Qss{xvp%G4 zr8z$6*kr)HGpQ(lJ&wNv1MWAH|4hp-37UMZ5Gx9sT}%EPp|gr=gC{}Hb1HPx{qq$j zZ?R8a0B^T{-a$%}Hvx98*3}fmW;(yRq0u|ClBG8fpSSN`C{bsE03(y$dyU!42l`L{ zLps6sV-&Y1a)sV}69pns@*bdy& zD5up87%5uk9qFuz?tb;d5=o4&`sRkVE7ELOXy2$;L*_S{5jE%o$)y%Gslr73>j;Gb z1qSX+#W~zhI<+Qy5GzB?`_Wmu0%m0X60pC`gtap&`l`bj-hX-{Me!f(`ID3)WYdYzvPX32{s6#*S>+t8Q%C z(El4MDcN2o3eYq*1P(2fJ45B#DM$E{hUW8Gs6r}JHU97aY$J&kUw?>-_lk<|U0}df zDt-yY5!t^#z%c2op^AfydlIpj)m<4CUT&?XNV4wsHDKD(t+DK>*?hOU-Rc~3Jg>gS z!nD|hYG6&gHQRo*FjF&7waNBvBcSI2)QQBOZU7^ny>gX))jxVC8FuEeAZ!L$!5yQ5 zJ;XkF?KdGC197?Y-eQLsN6cmZi8hY1JyD0Cv0&CNwuP^(pU}mU?T0JtUP(F1>&R-$slyl|gC_aAl$ymLm3@`v zKAqfw1U~bZkYTH?x1RgS^`Qn_OakI`zzu~yeDapohqd}m!04rZZbt(wOTDWB{&fU+ z2>f@Fa20TJ?`s=x<;548aJ9{rz;$UEHw*Yj7Q=5BFT~q*r$G<_D&v%w}Whe1TuwxOHB8$Y zn$joRJ}Yf*PPVNtW%S6#{%H*Vua4)^0yM+X1-{&R9pt-@A`-C_#>Z@@wOP0KXKy2m zm@W9G=^wKnM4yQd1JR}am+PJ1f_AC^@8l^D`22{*x?5I}+T69VZuEra-{CSpDe1lD z?>*K;>euAn&X8HD+&l33czr&)#y(4i78|Qyk8r&R3>`Oaz1E%>2mZ(t4D5EDF>nn( z!8p*b(qu@-O++X;(TuJnA|hRVQlim|V)Kc<;ndLl554sb-c%8jU7q+ft-B^|H%^OE z^5myO;&*4r?Zv_7fRNptP@v||1f~4&ls9!91yWPz+>a1-j;v(MXq5E)NRIR~Pdad& z?I9iutTPPOHW93wT=RhS_@Bdu{*@;hdeF7jP_;K`=w5NnH)JgYhlosDZS=l5CQHo- z^jf8dH_p0AO5`6ZseA{&?o|}wNR47&x6M|pF<#6WO;dTlHsa)qZN6lgmSBF5zb@{vUg90v=V7{S7B1fdC0N3Q<7R zW>Et{K_eqEqPa-}-K2vEE)f)>aS#Vb5fTW3Y)LTj(u(Woj5_Ez<1Ws~Ai*tR3p)rd zsE7d?NYk~X`!|ZTUTkB3c`N{`iba=?sRRjp=If5aBnn9-|OD`XXq4h;{F5VYa)AFH@Lp?cn?S1Fl#f zMOp~&uJG~vL)0Vpi(r*;5XX)Y)KIUsm9b-pkXZZXgp$iaiC+CVo1&`QbCdueiYpX8h~h#EGsLwoD3$ZT1Jpa_f)*l+ z&#*c~Ce4hIK0CL9jZ;wL#eJf=QM( z_Xsb0-j8J@vzQH1SN(3RqM;>du!!yekk~;09I6(}0nXME#TIC@r6M+bj&*Tj%R|mK z|M;r7a1!_Xet|HmNKF;|&9W6nsYJ9Rv?zBYjH=>B#$%pY&RxIW;O}FNAB`iK=VxG- zMoAxU)j8deFe{+m(F)E7JD(0uiK#lj4c_7!O|JK-8u*hU81ku|g`t?&-H4WssA+v> zPpu*r`qN1LGg~NzkWRF9R2SNh@c^~7X_nMdh=Ni;_W+b$&C}|Ygl@*OEuq0k`M5s_F6#Gk4=Y4v-9(XaeEu>8rK!6ehA( zi zbGj|x<)xyOhsKlAPM~xRj03ah6%M8HjoGSRVA4pv1#JaeWEqg~#HnHcGOF(}G$sh{ zuZ9Tjb;?f(?i3N+bQD#B>uR`yYlpN5?)7FOIG$K~Uk$`&PQ?*hmZT?VynX^P5_X8uVsOhg>y58V3;I?Z|Oq;@%rnSM8C#D5{TinwABAhue- z$W1g-s~}w#iCry4=%hJIiag~OK>#u@xk0nZJ4G}ib8VVPz|Xq5>h43XtLU47NcT`; zRq5uZn6t{3kYHY0^QmGy1e0GAX>P2j(KX)><^tq^!lg1lQ>wns1z7`-N`&7aasw%J z`%R4D2qtvBPo#b+gl{R?5lyX@*dWq}lg!CV(c*e4TR;!5b+|1QKK|(<9YQ{;?3`U8 zN^p*$r{ruikV5vgDufAwk%aG_p$O)qXb2l;Bj%ceoXTqU`6mC6&u_Vq4}x^Lu)|jP zba{BNlo}N|kR32k)w^Xg3nrJ?N&v4B&C2hL&&{_n(86t?m0!aKf>UTKMTt&|nb=vwo(&%_ob;NQX2(5tU>0mrIJgI1d40B&ak<^~Lz3xYN5%j{WJT8dCy zz3eF1I}LQ>(9m-3|F9NiL(Qe%A47;%(H)rr!Fyi^=b(ub`HCK543Vb0VG-!nx=Yg^ z6@JjP{a%~d3YGGNYJtPTr{<|4xjiQHuk4Uo#Udf$0Wou_zh1#}B(=x6n6g#fD}5HN zgjhci5&yx(`liAFtbPZpW@Dw%Xkmqe|CE4nFEJ<=_uVa0V(Nr9Wa5dcYOS~IJyvm( zl986=;zV+BS~*XL=VI<0eC}(kqp`Hs{Bbv1yH-Vg3+kE*bq~1IUGGpgb*3$oyHv^( zdKHkcD;AwvZJ5TAa!+%}C*ri>SiS3C>;#Xw&`x)t-Q+;)Y(Wd(Oo9#HWN7AAWe1y- zjLn?YWg0M)+;$ArR|wS;vGkL0H8ibW{gjL2c?A#hTvK6F{GLjApd32P6yEdYp4GuxFjl)i-dcIuq^gLBu0obO27N z;Gpc_fTd=?F-PdpyN6cV*eSC9^S5##;7QV<)b5XaKPo;;HYN+?j6tz zaQnU^xK-v97o2fF!Tp3Fbz_sb8z}27UG~wiPNgcyCE$Xgzz&-7NE409c3(zxz$-Dcd zB1$l)z}(c7Rnls_(E1tPn#;Dz;KJ#79;_*KxQ+$+kb(lfy$9RhNe`7TphmGJMo-uU^-{Xnx+Qd<`==&3I1Tjxv z4PBD(0rTNM1bHKaMb>o=S<`3Ojpa_HKvJS2>HRWipxt-$sJqC3-ZElw)|stJV6f8a z`=oCwfNC^`IS$KbpK&^x!`ynIN2RxU1?_93fYi@Fl(|h97egj&u;t212w+G?*zW z;t-N-PRRpZ&jb0 z0Sg~JW;J5sAj=z)Aw*oPpw1*QbMK}yJ3uRoW6El*qU+Uj0MNp)T~!TW>*QIgYu!`@ zGK~GO%gx%S4@a^V&`JpL0vU=#-9gNnoqJrlQ8H_5IQebz_~C1dQZ7RcB++wgL2d0;PCVrQJt# zQfY0Gf|8sLYzW5AHL>* zl9%ogp@xYI#UWI$AgOhb+!BT4Y8y!yw>r8){R`c&n6`PV=4?VaYl4=Q9J;d&9G$b^ z@1#%X^fk(>1mtZni9uUAUm?H4MP3I4di7cdd0rIqk0)Ck-q8U0Sr&4j;G*1g7t72l z!BRI_SWI-W+$2~wI9R3wvfXd*vauvKz{0D)?TP38d3Ah8McY=T7_`uB;l*4i||nZ z)vG^q5PmYrXxOLNe4v5*8%&4w8*`yId_s zTDTh807(lXF^b4lCYJlbRXzr!p5|9)IXtyf=w7;8gcv4P#nT|rjiq!4-6K)xuC~#I z_eJyc7qoC4!%!&{B3XF~<}X+os9-QAa_b+eFQ6S&cieG@P$!WJK9(m{1`2=QOFEO3~T7UZBj%2={4)w?y}Ex z>L?$!7v$JfA?)uWd<8)DYTjh45bi2<*?b*oy!Dv<8z9_|*|O5^$eQ9P0o_uRiMF_yRK@yB^meEewuqfa5eUNY-j*Mb+aQ>H&wnih3?A<^1G= zCgLU_lPwhi&rJ{}za|}u$;*X+W@vnPMwJeaB4D6Rz}I`DndCX$M5D%2kTE5^7X|!j4)~5y;7_r^zp#hk!#7JWu;H&ZpP4T$vq}Otf(d(I?SO?ig~RLO z_)&1Qb#T0ciHI$=SCJN}c?Acymuclo&^)Ae*zDiLgT2syBhP*-aK!h|pTw`#AN;+B z_6XefmWcS4lLW*Mj*lRF|uPq z6=yzySPOLqoEu<@=uz~Ol2ZYu3HrTWT?a6sQbE#M#g&?O?U61->dqQTR4ZLnRStFA z?*h`RQBc|B=MMt$0?n6oW}vBz4#Hl&_r6NEuQ2av+4Qn21FIGzQ2Nx@acIF<(6^6R9q znS^){%)zyaEd&MspcMVmEtW1$&>v!yw*J~`$EEb7QL2h;#6 zP6$y$gW&9wa3QVYJ&?UfvF~F%1efLLK%YTkE-g?YpfNqIRdN8s$jLRj^2yf=# zdX;&}X`&gppcsFvVEOKkLR%{r%Nc^@JO|6YQCKF}SXx;O$k06qDrrOU?HFGxR!lj? z>~x#x{TuY^bN~sT>N8fFt?CtGy}AcQhIzeO47fv~e}KHIHt?j44w6h#=pf0Xw_EHz z8d8#z2oXdQ@EBA$QhBCJgYdsb{G&hqm7ZXsGp`Y7^1=&g!85PA(#sPa~duQ%}{7MI7|Y>H<%4A(c} zvr*%34NqX?0;AqWVcMcsKkq``so(*!j#T(LIxw@bv@+qw1oR_Bk|M5&RC~-v2jZO2 zU-oRO6;;O{KB%nE&ZohR@hQTy=nvn5C*eBt8{zs!P;`GEX&u{dm)5b1KdO5Cpa+{J zoQRq~j&|GN^+?wxJV4<1PssabyWM}(X zgu37lF44~+U6=4FG?zp4g%;5b0)l87z&~AA1nhe!`(u-4RDaBaHHmBW)r3ziIE32l}k z@EYajU2Z<0)-(xxvodS{ewzsSBjP|YI0g3u0Pdo5fL|E}{0bZRM_&W@R0TZIhrkDz z5$1!~DS@6q=yxdS{T1|57y1(d{T2uM?olr3TagyhO9{QBKv%&Ym%K>9(08s?^hK4x zhdmFKOD`__&?>X(J_mJ%QoE~131de{fnw|-k=h>})Z?R2kF-%A!9LH>KY${7t_E~J zs&mT8+W=UPqcpq(y2ugv=vcWYDOSrfcmRD`e2$DB0s9TJ}a>HzekE#G#|}tIA%HVXWVB zXBZncOj_aOTScC)k~SsJKY%5@y22sq+$f?>vx#~YhvPK@Snoyr&YV(3w_^boqS?Y2jDS=f!acVVv`=u`_;%Ef~~KYX7tOD6YS>x*`K{TS>gob*whU ze5R^T(DtyR2V+J@q|h*0sCjN@OyI3CPSmr9&z;Y1tm+<2(OFQg&WF&Ku=2Dh+cd?Yc(Jed9DfV#Ac) zJVFz#VU@XVb5naW&EzG-UF4R(#3_Fd%VS#}2dRZ`5vhHp=9CByv86V4>(9p=P<&*#Q!j})P81lzPf{wS zkV-9BphQE7j#Ta!uI_iavhad}=Ycmg5nS2B*kCEX%6!Q!K0#(av)$rnDY8*~6pLS~ zgj#9~HJfqDcOzsUYLJz3-eL+>?0hAFZ778l%BQhB)^DufbZb;~+Fz>6C)?ls69|Ym zB3P2Fb_i*$Kthtw6p18X8Y?~ZZxEn~s};FD9C9ZD zw%h*57u(NNRhF#Y0(5#cql+vG_o9SiwGUFN{b6$sOv_%})HTW+TjR({6U0m1!V8_k zF)ZAnk;aEcNPEJMThoh8F~b<3a!@Jgf88Z=_zN4BV&`qZ(yPA)8b#Q1H@j-(7Nqgk zV;wDIG#}aS@Yhb^ zYohSI#p+i4Z3cWyEF64GfWvkamfQHwH~~ImiLa_4ktZ5)*0@JYN$6UW!r$bpfR~pJ zFzA719{K?-*ZQb|v>N)Ma~6dn~d5e>M19j=%Nzdz|?^<{y8O zowY6XYR>ONe@7X97{q@R!A#lY&62}VK-fQu(0b*PZn4Nop@2Kf0k@6duKi_j0Z~Jp z8iBRpuua-Ggw^7gVa-zadIwVz4me(r7kZ2S9DHq*)AfW9OpO!bab9$7pc%#lQlEy%AfNS$EH2QI>^%Vyk zLm5L~G50T#HU_XBe}}LV$#a7yQs}^Q@g`uaXfHc z6gU_eJZL050^`bEhiELQGn7$s+l@-`T0Jh9u=OC#PMH%Y{2pnmN~oL37|hY={=0a; zuN38m4zmL<)wl>qtwl(9qjxY_k=EHz1 zS{6XwY+unj+yttXGpcbwusd$9gU4k+u!BcViF^)k7qZn=^B;fu27merU)KA)@lMNG z6v(O>m(bVP6mExi<67!(F;n04Ps7daB%<3^$v*sz+x*7L$VzSgq7C&pqj$x=^`1Cw ze&3i4Cf;|#%NB8!i2YxT(*pfv2NTCOkJR}hOG$SiZoR+k5Z^4r6*s~~dJRdg7z&di zoFD}2?+~>FV*83d>YM&8f_^rB9a&>6-M=13E$#9Z)%nW~B#xhwA1v4p?t1%c!*PBb zB55qv;|}{q*Bro^g?ijxjrUVx>u7O~Zmi7K<}ck)zi)kxC%%(5KQ6lyo&YzeMSQ26 z%J@#Yv23Hsuij3v_^fxvV@?Y^4GvNvaIX{ZBE z*m_DGzytUKc%bM91TV{sI&bd(U1$x=x*y0uo0EBl3^@MahuZlLRpJ3xC!`J!ml zv1cZ-W2DGQhm60(hQQ=l(4)?_knO@Nim%nVxgar;TC`uoVRrv-k;Tae4!vI?7vCaS zkT#6}C-BZNSk}J}H1~{hpdIVK^7xHixQfGA^n_kk-wd&i?90Yhn(;Ali)s(V`HB$0 zw68c<-&fPDxQ{Nq)%Z9+eYrntcR2l&W7!)qHHglg57?ZmV8 zd-IIeby#Er0b2vcw|%L!PKN+kX*QsV_B zmEQIdefn0K041Mc1sa{;(HaWEGkh@%_R5EX#rVHUu?F;jk<=b*V-e$hz4LlJ)kGHU zTkq|>Qt!0BQ*Dby#4Z4AXt*K{@=8(ubLgE6_BpJ>T!i06l%22EM3btA0{X;jtn+6r z(q=w_rbB_Wz(3310&!>)i#W6_9~ksDJy2d_EcR;NRU7KTXSemal~=dNjfCjGe?!iZ zv!#i7*dW`NGaPx{(dEdOOJ3Q8kh5fScoNwg&!&49TTy#r3XY9z_!h!nmRoso`wix$ zi;AzIa{Ds7$(G#84(-GJg(kcbjjGN6R!B1zV;B!6^~~;OO6jG!mEBe8Q*(kzO^uCS zW7URj*-bvotwas8O01TKwM7t-tvy<2)J3W5sCWbl%_HNBg!FUShAoY@1|m%3Rt1c; zxa?uydW=}v1A^Wr7y~|{IDJ`*$B!0`r94&Xa>R;#`T7NIUUfYFzIYV3Jfq4U*- zVA|EvXz|72hF{1!K1(u$1awxUKV?r-Rs&ID|$INAQ>8@G+i|J$=U*PJN7oZxI*cQ%4_V;|-2I@-zN^6Eg+W1W1IJ`tI@V--1C&o54M4#^SOso5?8l1ay=&lI?LL@PE|9($Dk5LYsb&4v!!LCdKVq4k8(HR0H)%wE;Y>XPJ z!Z@230~Cg%H82y^0HuZ_SaVB5pO8C7kuQuvDo{1AOCf&E?yt1KXtxVX6D0$Uj0%wNgO4gee*cxsmf9BI?Ev{T;MknH`e;NiTN4R2BQh>+EDvD+ zKmniFxgv|q8w8)YSmj(!h3Va!fGL<-#~;o&;9mqX1Z@B+o-uK#0ZQ8!REjh!+0<-& z51?DZ8?N2I1O=eD0>&zw2LnN=?t%Ls_Oh|H$v`{F$L4bcw?HL&%%|~M>gfroD7T)( z;^@J8>K0W`{;XXvIB-CE!+P@4+VH2t5Lea(Dr!r^in?|~R7KIIsMx2Ke@a@9wH-o> z@IDYs$^ah?nb@_5XV8)X30$;z_!MuEJzY4!P)f@-s>KN>J@tLHQLN;r*G97f1Ili4 zCuRksVng!?meKJ{AYQq}ibtTAx(I_2k859ULErKOEM#o-N*h~(*)?i@4S&#-87Z*U_C2!rFc052(4r@=1sZM8I4(8*lBsy%A)ler;_Z|auWTG z!xp0-gje&Qj~#!gS}!TGxXN7SY+0F?gs1q*+;}{-sLTV9yoAbJT%VNNyfUu^o|;zXHpNq7WnObUHLJ{R zR+)z*q&NK!Aj}0V`|I%kj~rTE z0u3YSe?_+dqrai!=ULr?({8O|7B+v<@+p5Gu~1?!|9@PwzOtRMB~_upK(Cag#Jm2o zvOGp#HWO{qv@#ElMO$AcROUes*!mKR13E(VWo2HxqZR(63R>Jz;e%C+ISvhA)nblA z16ajC5ci+Sqs82>ug7049M%5chW}Nyf9ywe+W&887SfJuL-%o8V_Bq)HbbU^KWoSF zJ{k0PV9>Yj7gED7{QIc5CY5>kk@j1e*Tkv6|9*sQ{wsvWg`isUzwDOGt!zKDCmWHT zwAs%?+_$UR?A4qH|5BTUKZWB%Y-LRlQ)QsWi-;m`Tq5 znf#;%!lA1<$*krESaE%Ddeb`7j`Y&XzeBZfdK2kwO>fk`03ZCiL-3cA;>*%8;dqv{ zmpQh#Rv}B>nRg+@*e|biv6DKS~F|^%XWPM=>J{B>;#VyvlQHc<^d*7XHPlNYBXg z0i^zf6u4?FkS=i{&2S*iBc!*O&Cqdra*nSg2dP0w4JyInqh5eiLCFP36(UuLyJii2 z6jGyb3A>?>L267%ilLWg=9HEs8v5kSoXK+P>l`l4S7*2=H3y{*l;OOplKAlLplyO= zybiu(-&q$cTYnX!{z-G(2734cGI0YWkjUgqTSAP?TbP4L=f;wCu#N^ycD0<7gG z=!HYPt&1-T>AW0e$ZF~sxG%FC+^cs|^M9NhjQPJ2%Iq~A+e0}0FTpRWBF5Z2+?oD& zY{lvS2z z1prNv4Siv6FJ4EviH=7^JH3zWq4l7MS5!>{Slrymt-#r70b>vj_RYnwFU9|UNXB{| z32BYL*oHutYH#~KF=nM_9E&H&pS~QcsqG?I2f)F=yoN&F`6D%zSGSAgXVs2f?~nUb z`tX~fM*tLb4Fg>&a#kzP#4GmO{(MKb1Vb}?#vlO7Q~frR{gx3&KIyGrNs2v~&OJj< zLw`YM8=F>qDvDPu#mln0(_pr<^?D6GN&3R`c#ytuA-bvC15UYtf?VMWa+*^U|GE@{ zda+D0lM^n9P|KPdO!|(fG#~C2R%TS9*~0`ptA?$lxFJe0XdU1b=3o& z9!8W)T$ITU$}4~}oUdaMK=pxfl69V&bsH*IwUai?iazivKL!(?;b{%34=i%>bU+@} z2VTmQK9C-eJ`fMERv)-<{z>!!S;}!%QT1!F{_P1ET?4peD`4D|!fm~~Gbi>O-F0{$+Ane4)~ZNqIx_jI`oc|)9M?+@yiHYa6Xk6#-hwkW@A3y1pLJ663Gyo5o+nqaTqQG}f8zk>bgKLr^B;Gn~Xj z_n2ePz<*sApTY159`ZK8Z}bO;K4|U!?CHJpjSflqMiYdt=N}E>&>jLM$jrKvKp&*2ipA6VI?c%)^n2n&EuXVFBEJ3hyXGb5JkTw*J~}2@n=^ zTyKgJVJ=d=6bv#hKt{iD^{H(|vS`E}v%@;st_w-F4ODeV!xF7l{uHtypsB15H-l0@ zOB1u^b(w>gWAEkWXQIO)iF^@2$k*%m)#23!voYSRAGMilkzj?PiJ=L}DUtX8vvaG$P+x0QOcmX3WV zoXc*!7>*MMef4a}3O7`zpw>RUmP-*bWNS{X>Aj$E`@#~m8@^t4j|VkQ;z>>L8t4Et zV~7utIxSLsaUcfWEiFXSwX#ZKKzJgbZ2Lp{X4nitudo{&m!U2V7&`4|4E4=g28I%& zscKSk7H)`1!z~gZ=_FkTM}rCe29&(Br2bSa<;+hZK`W}csfZ4=zr7=vHd zsCnzPVsvm{<0}ccgSzVc5O!-|qbBIGL_g|cyTd$`VRx8mD&;|^>D5m<%|}|)j1ZP_a!cXyx(q|+d zGB+h-MQ9JKIRnRxz1^jT^j~s5ZI=Tib&P1vZ}@$!c{bkjdJ?=Zl=o}#-kUi(vG__J z{z2xkYzr;K3rY3>Jl4W^l;amd+*K}de^7*gxaAkw+|N)cPv}Je6-8{$ZKE{9TJ!Hn zu~O|anBpK^w8_R1 z2>K9esz70N9hdqJ0DjPm)4^{;=TR*x5I0L&886UfvkIsD92sLiOGVOK(5*OLkkG!y zj~Ly{O1sBsGtYv|xnznn#}CT)qChIwt}3Zz0!ATp2szw6(D0s@;;a1zA(x-{;>Pq2 zWcEwZ?yXdEO5{~T1-#XsRft}NQ3LS>`N1}^>@Ne2zED{ zakycO7ttVhn#3ja#qG2GjQGdR9|o`sXq60*9$;L39;}sq&^}4&zPJw~sr}Yq?J{mS zWP<(gBA9ST$>rqef*b?lZUD_r7KR9pu_dHK0Yjfd2}AKMg#xUD@*C4#n}IXt&{8Hd zC+?Jx=!X7jhOaw%i?8UQR+%V`)au8^c7N?x3BIB>3la8E$vRLC0izMeY)Wnq281M$ z9L0urn9L$-#;_!pZ{(u!ikL8Q9OrMg^Eh+e+!0-Ne5Rmu}uECNLJ+ua&nncD;@bIf8mU}WJC z4y`|KHF-tdC5`A0OoZPSZOqd~sG-rU(&Vo8b@}MNk<=Y4H7>~)!^T+_Mx!M-bn7_8 z2!H7-`!2R*EjC62Mh(g$4rlt3X-Fs|Rz?W$@jjM`VdJ=b>~Q#K@9^=#`N9!(byc?L z>Ta0iLRSxaNpyAi&rpFo&!fI%Fo;&Zl?d0Go8{Gdr9JVALmB)XP32tdTLD6^?(Je8 z>tLRv@L^6p)WRJ4kosG#9IN7KQv6c4c!pE_Ca3rrcJaqhyn)tUXI}Ax+W+}2AqNu_ zygn|xH&j_v=qV1oRo#^!Lj6>shtH(+e&-f?P00*}Hmj`Q;W4|=DJbN`Dlm~f1n0tB zi-7p)JLW(7Y-Jz<4{VE@3u)(e`7^{R5@zTmzafJBqmi?(w=p z>g2I}$JW^olzR0!SjJ($GZN0gvmJ2L3~8JY>IFP}^q7BofpI>#M)9BwxIWBG)Uf}Y zG{gX&>w%X8@mcVvb07|IAm&&X2b6mCw_Gj)EQANVKs}LU$15I&SvhH-r+O9}|B>FmJMY>sad5e%WNiMluV;_;~!yV|TfiY&=WYPxhiisEO_vZ3A zPr4-onUe$Puwp&oEOF>>`Zh)(d|SOl4!-P5+q;ythl{Q;?mgLM8q&Iv>w!xFfemo^ zN(jRnNHDh#eiNm-m7S4qeB9UA?gc%C0sJ$_n>nbvHnSZ@Nj{FsH@=`XQwf-UV`tR- z%wLq0#ob~hka=9LQ7^Ygcq=F#XcOgmDAdpI&zx?gd?? zk)q+wO0)Y@Kb7)?UWV=HLV86gup1Yfkun4VGz6Z9)X_G4wO`|83=DZnJ_Cx{yhmGL z8A-QT2pHQCJ}dOoS`gC!ttO7fmA5O}inmLOilgPwPiugq6kDr5ZgQ7Iw1b7Pnhpli zVHfA7u$|-uJG8LdP$Nx5q^sJ=3p(3L=`F0z?(pv9qlGz*;_za-QONN2rFc^rOg$zH zQt^Y=Fp+lRd>t@ZdbKSbsWz!DTRtJ&VnT_>V#2OhZ*(R8_Ib9%+p82b_y^}&sv=W5 z=qr}-uh0a_T>TyGcLE2=1ZU<(J_k}ej@bfx)>si>3jP^{yi+HZw5 zcR?U?B>KpM=+j=~&h7~D{duR{~Uhf-eqG z#K-P!o)XAfKfYBUy~b}W@uwq(fBgi_Ulsyf?C@9`+6z0Dp3ll};RmTM5QfEN(x0<& zHJWdBcyFaRMold5tauOOgo}iS+pvN3bfxv0QzFqR@t9Qtmddc-iKVgw;i9k}&zxgf z9=C!P+C#Gb;rdgr;i-Af;py4f?k!Q6`)T!4eKeXw^SV*}yM ze4=#uF!%n~;Zhn@gM{<39>aOuUXA~L1qeYv{v zMLtG8IIr_ku*osDnEy5@Zh?Q})pAE)V@P+LWzVe`8;b9aTCeHN{3YjW@3!e3?t>b* z5Hg1)%IZ*Us!~ZCa$ndHZn#Ath*yMbbFrBR2f#Je-fI&Zjx*ZC;vkiH|bx7B){MgtOsl$>FuISOT4voFvn|@g5{=Y*_#`fm101ITx{S7`jIT8$(?-t^x622yATkND9s8Rix0!@6V9d)-1>I0bl$S4_TUL znFy;eEPPko4!+++^AJw(q62deetmWQQb>ofIP?&Hcs^_hr?F7&M+) z+I@Tx`#&a+KFS+ct7P}?r@b4W7(Q3B;Vo=6e8qcw)#k4AYVXCzVv@+D5g+R>>I=C; z5=F~>>C27yg>b@G#ow zQVI;gMAy%V2~OFLQom7(10MI~8(TTUK>+hXnu}MQ3n|V~WQ!z^E*yawt6rVi(rJNB z+3u>44a<+kIH&K*L~|StaNiraAL-Uac%lH>%?wEPHn|7U0%e@KD*DG3;4YrU$l`Q|rrNuGW5OCc0Tl|; zaYo;m^k$_!vliXc-G?TRX$>4cqTagw=KVeO&6AJcNZx`o^DR)D)u z3aHNq%7xsZ3B0t_%sApZh!;jdcilKz@!6C-nMPvH7L3ozH0wiOo7Z8NFLMd^i=#ruPc7CX0#EpD}?wzQvLLok=+<)skwEW3AdcDXtA_I>z(D4)5_$gZn9=G8``bha!rzGkSupw)R|~U zszFXT1H?ZG8Dm^9b5hy4XrvbZ0evq+9_gqm9%dP*i7>f?2{|HyxquhG(&@td&{aPC zlOKL3hU7Z)^8I2(7UApYK9t-%myD4>9&U>-H?<^zuex6*cd!Y3slB8yklcb429g`k zmx1KnEN37IKSqmbEe2?wP+9u31`PjqUlyemWZFK#0sZMi2In z&Fpn30QKFcDq#r7UWf~j4$zf2%7rVOqG|+F(eJ0bq*mJgF%AdgTWm}6@`nh zCVo8Tz5nE*EY_#W)hRqsy&TqFxQudHOpcUXdO)n9RN-*BIrwp&jjC155MpMDbb^Dh zz~NVPEqbQGa`@inMk!k4uB6yncn1%UHOyPbyl56sJUu{1>EmqBbuJxeqf%9m zd**ad6hu=;D=Gk!;4&`_?2%Eq-RH9N4@Znmdgg@Ul0>?Vr%4`L5q#(iCg-4cV0SY- zzJ zmggzML7|SE$OIn|1%%f1$L7vys_oCAv_E!{kT->7?`i1TPq0Z$19fa((oVQulR2h$ zaXS$tjG|(U;YsMXaLDytgrZ~Uir{=yme~suIjSfx61^5|a}1wi>&(wusolR62^tEE zuxA?Dbuo73CPw`Gcf)#P`>P>tjCbRHPoI)%gJ_imADjde6Wb)ez{q|GMQic*seeEI z|0DpJ5(C#hq?O0j`r@DdkVN@D7Eq5+np}91f&oOB%uFYjY3S z;C*q6dqHMdv5AE zO8Py0hLAU$Rgpo1&_nOGtAipXD>C=aPNQ#xc}AhNRrLcnw4W<7S+&hw1@nhgyK392 z6xz>*Hqw4RJ5SnAcT8igxTfnd%ZGKY+RuE(n>#eEB2AG&K5J2=av;6uP_HrYpF`Eg z6!$$QZ#Hd8_S@a$IhbIep(6ayrEMS@n`(A1fTGX{sAk5uX>-v<<|?}oR&9w7GmMhl zg)u3Fl6<5sh&Q&G-)&&WA%c<-0RFox*l{|!z^4PG0^XXyagAfdmj&Sc7YDNH$02H| z9>87)Fu(DkzjjBGKe7ZXiU{#pV&>q;N{jaL@nY_(nn7C>^@b%ukE$Y7xA`!jP!kwA zBZCUSaAg}*hDb%LNQ8yNdYi*~gNN&a{H!J8x&n1CpawF(@e%I`<7ac_2evNI%Fh8_ zdJ7 zsjY9TzQdWw5;I#0M74VDgGrXP2BN|7LUWh#c%gGa;3L#BMhysL6BPoxMiKZjtYN2N z2Ft~^_630xqM%ytZJvQ6-nevGXvqyUx5Vid0Xs9gsVV#*3%C^A}+*( zujO(d^sNWkz>vz1tht!iAOpB#p2wN%I3L@pFA)B;sNxQJo4rld7@J5=vi9F|Mu6Lw z*JNx6q%VUY*81btLdh&OGal7=XCJHH3%j`#>{JpU6dOIokY_em)o~08R|tAi!0?~p zL@F*w{Ue73V~}#OVdGGHJ$Z%BTa{I z=(!ptO=rDS3z`nUOVb&p&7^<9VFi_WfX}G;QI2Rk;uJX6{HSAVUNWPyVn8G-ag}FfnZqGzcwe50sa0OXmx!Q@`%|_p ztl`8Oz(zgE?WIQ@!R<_jy>vIst9P3@b1?cp=HgMskQZM{YaDyTo68*R zqn>2(VZaJsz`$?k!#ncfQGQ7ER!xe}s0s5Hf}=->`#~3XI|p}v;D&H8pvNKwgRq51 z2J~SUI0JfRl?>>C;FDcAU&7^{J^jY$-hN|Tw%_>DnQ@8J%ko#_T1h&+8oH~;aGsX& zncvvmx8?VKVf*jm7Z_s9b9%^}iM#gyutkfhI`aj5!SvCs z)bkYGsMM>HP^r8E$Bsa469-zCqL1xS0Di_oEJ1SG1cE*0u;(z{LW;-H!VrxR{$ zb0yi_kz`g&h(ZohL8s_saK6M~)IgUsHBs`IPd(CLlC<`xpRk^@3;pJNajRG}q=6i| zJ?5B(nV*r&CMHSUQ6obP>VW+&8Ha#e)yU))AcIQ{0+YS0AhV!YE1wRb$l8Jm>lpy| z9cHJ-;52{M@&N6QU~)d%HAJ%B?1F7goFamQd?R@wAOh!gy@)f`ZUqn(F0;K9-;ly( zwlON@K_^H(Q#!%5>FfmNy))4PRz*TjNUiMkWj-l3)cCO@XLBtU-14RfwZJ&~A2QB! zM9!z=@@0OB@H+68ZL4?=$!c(B)gYj6sZIk47NPl8XSV#(fegbc^u1&BS~pfb&3ADcN9Y21m$0WjnQv(` zk2_kM+XBy3Dd>PBiSo`9!qt>m1N#Kk6M6-Rfuj=^2bB9?E9dz?+yvH&@7P&p;00;} zlIVx!8eh-8S-zgK=V+F6yk%}R|Aj&pWxNKs!gm-Oc{o%DUl68mdd+OV5ufPGj4esw zL1eXwde-}6cT|l~TQXt<)(RKal@6@wgoP~`JZJ+I-RdBGX~2S!p@);FO7R9RPe%%s z-17{nh$bmcD>~gCA)( zTtuw+%rm!N3Ke%tRl*neHT!iF^~QiD;}dZSEyruUy2B|TU(wxPQ49TcS2K|q=pLa` zY@pAz7ux@wCJpqPGHcoJA_VRzHQ1Z82dKd;QG@Ln(1Uw0MS>e$EXhtAxWd75u&HVT z;l~-RqGBk_F0snH&&qy)x^|6u3SLQ(Ld)AwjM)G{?L*+*r~|E9sK&bY!FqyeIP&AUL1{`id#vw6sOQ0QNHCcz}) zb#z%pgfGRSVuhl@XDn5O)bjoEC_=heg!~{H;ui@K0)W^bSxX9to_c_^VP^1SAP$Ev z&HV;WU5t*f|FgsGY|@2l@kF8^KS4~9RQ)+F?ZQCBVXxG0j5_NBGW2Rh_S?;8d4k=| zhN={rk4L3Ep_fIsDi;_-U^r}khZ@AyH&6CF5UJQVrIjxWQA2)X9}dFX#k++-G@g8M znB&1&lDi8FWobrEAX3AlWbWl5(!R%aW7hP^A2fpjyCSlctpFYnUuFT;{WtJCFNIv~ zPyc}7P0oH9X*5~LQ}!D2;DEJqG_b!RSs9PJXMf~bLS4IH^Q`SHnY7dW++)WrS5Jm)bAJWS1MH&Jacfo;3 zXoFf}cmP^RuYS^Pee>h()@L@cmDjT><&h|f^QFXlZi#ZI#C)g37^?)--M>>s-7V`Z z>TdrOrS1ZVdGm#S$JQK7Xunqi8w{26N#wLN>G=SSh*aQ0kAXg&4zOYzp-UuND9q<9 zkRcPA0>kUYFjnksl27r~8nYu_DQjFS=gk&$-AeFZZ9NVF)O%_bSKuvdub;hD5ixmb zfy|q*aq=68k0yH{vk*_5*%?>Cq$|FZ_sX#=?>U#kgjda`za-8UW?if;%xhH26WYzm z49wCf01pZP3~DxjiyZ(n9RO`?0Iw6kRRJvfwJ$pqcTcL}3m_7wRoTI~dzyQzrpAZ9 zVBH53Itx^&T^s5aB{~SSrvvrf7$MMOPsO%~j~M^hgcSQOW*JGzA~aSA=?6gt~3^cD)i&S$9OY^jgcX4!HWj6ph) z=(!z++0E!|dv?QH&k$Si(EH6|UR>8KCU$+Zn11-<^RN9|LKrB!9)ElB$LC-BXU7)s zmqsIYrdjhX{3bS>bI~fX!>2R*+iI~~OkVOT*E9DIkhsVneFul~^$ zv`qwvGw$;4`&0)zY#sJYr$ohbKUZ=j;*v43^hFA??s*cug_a02}Af2<`e2 zM~qwJ@%KI>c>|)N7~r=aIT*Pyz7rSwm-^tx$1JKP;`mjvxCMN8K2jY{;A)T$WuIo* znLoQ5? z&Usek#)8{ij{l16O2>qbpx^-7N$bqeey9@KO^m~UYWc)|mNV!ueWcX_UbXUm$dI4) z{ghkDv^IB9#s(~Dq+vtjiIRN_5QM-nvCo03l4>fI*jQKj;oz}6wWCB{w+RZ zuMe?}`*9xax^TJ=hX|)*e{WPKoW_f9d-+-843l1Gy!a82y_t|IPXC0wnDlDp?E3*E z2dQHVU@PQTd{xY$6=#v^<=oxH*%@O1I7HA5(8wduv=!7TlJP`y8!(hoq z;cS20Q9me&EUiR1*E&$-&)PBeA2y$MgMhifMHaHIRp7#*NT?HL>I^B6a}@3apzyy* z6KsnQ)PCAnV|jTE$uLaJ{VXN;hJcKHC1cI zieu{R>_+SC)lh#xmMVz{15za)2|rih{jvOjfS+-KN@OEon=JXl;U*TP6$_(n7QTVG zAq-r8JO%>B3Tb&3?LVuTWTEh801Y=eZbkefG!@{5@R>-s?dceLZOJ3n+LHV=C}6Ck z!_7RrmxhCcQCx`5zJrfgemh6jzsn}3#FoGw<;JyAB%21RCZ+JMO3|C)W=O0$+i5u7 zL$*43_F!u$<^52~>{__{6wk1V<|ZL;F!goJ1ubvS=OxNAV(hXzg)M9_g^eCQPHOzn zd9f9Y7TNF0&>lpn^*OyehLX#@Rp+!q!Ynz%8Aldk2NuSW{n%BaSD$vO>iQTC#)D?1 z9OoQBhq%cZNA^PN>FKMBJ$lw^t$d6cN8WDdh~YMfA=K%3<4Dtd2L^&i5r*UF zjI7n;&u(N4X~vxgUNwMtQSKU)^CN%T==>>uXZTSgjgeaNtvhOD{>2?NQt3hfSK#j= zhrf98Hwjz?paVH-+}a$uFly{XNw~i_YP`bvyDE4avMa&7u^$C^seZf?!$g}^ahYzn-{<1ZEU?i!xwUAcMC?iY9-fHRC%chRM z_;~$TIr)%mM9<{zPHTb;gkS7(Xr} z4(Qwd%cJz|@yCy6m5Q2d@n*QZ4Q!M*89&HYl!{u`bgDae+}hS@951j*oYUCg@nERA zJ$Rg^1`muE(+*Cx2aNtTQ4H8>3-*Aews7yE3>c`YMRrvckF&My+xUv{0@ZX;qpaBD zMZ-$!_zRWvTIgE96=i&C!{Op;ytju7$3FmhUN#jq5zWZ)$BR@HSCui!=HQTw7hoXk zcnsL%#c^sv4Hjbov5~>z#n5bfuz1QIET%z(^k5uAuPu0YytvIBFIIi$j2Au7&EMuD z#*5AzFEC?F;bu9U5|mk(5;a5;rdB{RUA(A8%N(l=faod0TL3T zW-9)ywH!j+aRAZeb=EWlLkOL8GBRYNTqj0H$M!`#nucauCKA*NWJ(g89+G6 zF%LZA4j>=6(%21-)i|(tHwaz@x;TJf{Vw&~I52|&%lZ&Gz%Pcos9~F(kZL!^-w$eKDnoBVb)VJapRJCFs^|BfkB61Gt2S5me_{ zQPQh@#`~Cj|;hleb-lfK5vIAzZqjlzPg{}U2NC-7}qcOVZ)Eqt00C&09Ud- zbUE`db1#)y#XoC}=5K{;3P(37#_QmSqtOCf(3PAYSUI_WvhnQnUy_kw3dx01lq*I+ z;`;X*Eg!xM1&VL~C5plW73g!BGv@^I33p_!4!;C1KYgoK5d$R`ngueAwPL?!g&cj< z6RoB@AEA%BD1CHSifg_iklZm}`@-uc3Mq;1H?(3HL=`*j_Wb4dl7cFF3Mr_fbDJPn zFm*p>)7XcA{%@JDXN^`0iTiTcyBl-J;K(WK9{Q8;e|+WmKhC+iqvij=FPa1VA1RLA z+N6ed>p5586E=UjcI&%}c&I1yJM^ykj@`;&Ij*oM|Ho2gx5m>!p!okM_zz7u)^0r# zZlTL%27QBf#zKL--`cfZkL%2^H4hyi0g_GSb4wS*-zUGyvL|wYI8GB-?ndfMU%001 z9kX3cG$6&H{8oqMOtSnA3{2aUeex*Ie1azG@}Q%Mwz@1ojqFN=nLALh90g&@n%`KO zsDRIF%}w^R56`wSBBty#yvWbmNe2ickB;jA*%L_Lh$|!;lj^+V*%)jJ;tKK7 z=*vE4THSP{ZY&A67ISDjfns9r4JT^v){I0+uRkuLoFpTwEF;37_5E1Gk(uQ-^QU9+ zSlM)11rxK3?z667S0u+A9;(MmUcku%waXu(r zKLj?oQGTOYbxqu+BfVAUhC6{(3Dz!4cADD~wv~YxjD&4CNi3#qC&~9?t1LSgVKymZ z=YC|b_qg;Z_}J$H(b+J-pXKpEl&fX*DlduDuAGshYImB$rB?oTNDy$rBaS-x1xJY& z7UVD1XewwusTzGS)D<|TY4t=xRocl_7>{dz*)BMRco4V|b`z44;8#j6iQ6h8_#1mSARX zjXlra!m(#HHj*rH?8LGqKX;w{3VZlR>^=~Ccn1>ZkQR*8>oNBhsiE>@?cvcbnzj(miS6MWhJ#t2`(I-ZpNeuPwuifX z;@ZPzl{*S8p{oQ8!hYBaj*-H^`^GmQINBaQ?SLAEp1#i+g@(BW+d2hrWJg>We+2;_E*`%jplF69&WV2@$KP8v!A3r+*tW2d$`do$FqkU&FtF4jb%Bm zJ)FM9QA)IjJMi(u_HeHjmQMP~ZI&gN0;=}B@k>d;9u6V}ZPahSXrt}%w1+3T_He7h z-(nA+TCq=v8VsVW9{C2S)vN#P=%8Dkk;;N2c3PGJ%KRwGJuHT@a?nb#2>!a#$aP?D{1cyvr)gts$ zYw+GcKaJn3^wUw4v>d)~xne9ucGVCTZFTk2qR$%Yr+?Yc|J6@2mA2>WjrHEp|4#j6 ze9}NaP3Gf?_0ywh#xh^O!EMhUVq&P8Wrj*YKYh1F^wYb0L>2UpkvJb$Kix6x|Db;Q z_bHZsdIbryb*!VG3SJleq${HS6#;EZakK{Adi6p_KV1Uq?MR=_NJBpr0v0lR%r}Nw z`YE21(gyy-;g~$R{>06690n88T{N43CUievAJ?A4j;oekw;5S<-k09IN^q_c;jOjtQ3SPyFb6rJsgRcl6U4Zoy5+t_pTy!Pig_=6I)% zE&Viu&uh$6?dP$07JuTukoX_>Co)W$Znz@lE9)-0V+v;U$MKiB?nE;*j9KVT9Bk+1 zl(3<{FFHRjd{!vBO0hGnV$trz==_HPOfv3ag1*gZNHh+A6+V)WQ3}b$Qg%LGqHI+B zWCMRMP9kqJ53)K4_r3YWR43 z-4!3@Yy1Q)Rdm-Jx5>8q%xHe9B5G6-`M?IrK#pkb~M#pZovzjf~722LRG%b zEZtyfDnFmsnxpLJOg!6;2k|%Fg%|(x{zl%GWBD7Y94x=vi%8mj94_wv+~0U2-_&va zjR|x&p6a+8#oO2nE;C5qbv8!(8Xs`vbv$2V4SYkP?oqzRDF2|KYUpbu78`g8w#-^v*{)UB-v}T1x#5m3wB@qY^ZglHJ9d&`M03$1~Kth!xl*KIMP1(YB8d3;s=x&FuV&#`-0 z-s9tV8l&p+43K%^x_mU$1Nelm-ys24o%nO%XFO3|x%?b~3Rh0XJ8dSm;gw?I*M?=G zf>NX{!i64ySiEOAGjTTma?s52cLt129_ z0R%_e$Me5LF0680KHeF14oC5a?5bcB7PRc+CabMc$Fh(2tg@c}v+U#O>2zKlA2*V}QTw>D{EgYijpRSBeLQ!Cqp#?DoWsWx+sD;t z;-apmxo!O*ri-e{UZ+w}RHq;X`?wq1dN6hLF51UGK{r_IArIyKR{QuQ{f{4F+lc6| z)kv5byB+;iilg5^*o)hQunjw%9-9G*v5UL4y_4s2R8eoA@kGXNcuhU<7 zE}8>Cb7K9~>4)E@zgnQ&iS<`}jjO+wk8$9HQ7I{loa&WRY*Zi%wSs&roJ4e{^|hTVCk<7{r(T@uT<>o5dD>e zggI`9qrVpXMf6v!BJ5NUX7$){pjfZI)X`smTH)%iGNhruYJL*-JZ5W){Lo4ICk;-N zAtEfC_J*)dtiOit{B8PcFv^`+e|g_>^;hEUuKxPUl}4V0? zjoDv~t28>8}_htS&imncZYRUTW*F2a$sQ z`t%dz3Z{m?q5ite)nBLP{8s&Sg8iUT{>j0ZOVK~s55MA{e096ypWO49_$PbdlU_Xx zbXlytrWioC-Gn*TiTsm4eC^2K8OY#d{FAlc{&xT5J1qO_{F8s4@A@Z)-|A|rOF4a3 z3iUdN<*sBo+CMq#L*#;`)n=5VsoqCTS_SV%cBQE{Ek;4hKe_20OH*0?$>sL*fBlnD zJ}rhrp4g}Le~W*z_$#N;{F_bXzy8Vph5pI+s-5wM{>j(*coP1}<*E{~nXR7vKRMWb{?D?H8=HSOW*;|}zcKr`vHXqM$BpGbS^K!L@{QTYjpT3C zK5i_3WA&FKlKJ{nZ@_^Wn{o{@P@S{<=*Ob`c1(dhElX80R24 z`s>YVSAYEtY3Q$d>~#?Rm0^*8vh(|wIVDC(%r4M-IU zNLNGZJ|0+&SHa7psem4Kf!@_^kPa9<+zvhEkzL_EYCre@D2REg+KP#C0 z+JJhI<=3+1Mt%+JX5?2Arb!NSfS5_huZE!A=Y_|J@}Hc$38f&vuIR4i*CE5k`6wvaWm9jWc&c_;O0oKnf_JgmhHQPv}pn#pm9tCpZXuU%W^)qQR9v5w-tHWf>8|A+5uYj=-) zU;B3Qxa&_(*U{^8@I_ojRDm1pao5p$i5d0=Jo1L1EMtqi;6GVK`%h969sQ*tM6#)7 zM{{*qxjE|l+RQWfP##4u^1e3x6!yM0_g0KrH}|!V(}WC!!0jOM;}acaM$y$$0*$@jI1 zF8Qh*J_SdHx~~l?C*x1b0nyyo#+#5~=QO~JGGIpOx^{Fk8tT4w6cfhxwV5z=U%Pv- z``YYE88}-(?`!M15^lYGT|0oSR%gmk_qD@M2jAD`QCse7SA0RE51A{AP&S0QlHo_X zQ&xq@W%xO|ORuRO*iH&TwDu*E=SPJO3J@BOJV2<<2Pm5rw`sdJSDv|s;76M)v#}r$ z0MPzgyLcZp%Kd)p!%|@cL-{&E;#1Q0{@H~$-SL@4LRvLZz4Wywj zpZ=T)i}$wT;qJ_hcEA4)Z5zY~Hw?@Pv)?a8l)KB%-dQkxnd(K>ms1xSeYr`BL7^%i z>tWUhH^dyqf_9s|a>&~#1%3HD)Rg=Emkk?-p}45HdIc)V{r>8u5{0C_a;eJyefRrD zT@HQU$ZCJHmN)9M{k~DC^3m8Ut?h?bmxJ%Gt@Q)dWp&>u#Q5^4%aO|4)#Zre3A*3! zw^$1-vR8JbY-Dx$9xQQ|SM|(VUy1FbSh6KW3X*F53oNM?&|1%myKD=o%kV~2>T=7g z3!pCZ`5h!P5!&Ces8^YZ;t-A}CFvi}$rT9kt%RBsp4_R*Y#GYg1XqH>tWk+D!HV z8F^Ii_0@Bbc+lSZIK6n!tr(i4=iD}LvYH1hRW{~%1D~qkHMh^7V7Yb_-Fp|e6mqQ{ zD42-p!(=vplP7ZqSpkGFc7s1oPba$%lEDa1FvjJf6;2Ii=nm`hio4amUj zTd@qJPQC@3(8<9!xRdKlCv(xLwUf`#umY9^s+0P%lUQ^@0m#?Smjd%5M1{qE;&qje zMP6MNWV6Fg{6G=hJ32j+>84(@uhZ@&wyGE7;_+nuPpXE+0)c>}6A<^|OfuqpJ6`)8 z|NWdC`x`x$AB)GwkGRg^Y1|xoHUGRT;exFY-jw?dCFsxP_@(mgcvB87Av$16j3eVS zOh1yzNG>N+DLv+YD<|+`SxsEYHTufhB16)DnZ&QIIc8*G+GxkAU(lzmcoNMN8zB;% z?gR2=U1#oYm>otw{(* z$Z0{iHo<`6n`0;W+cWti=q6(;W@EF^wIv6w4NUfnM~7ilrALQjHRA*U7Pj>Ohi%ko zw!MpG$i&b|0@%j%Sop=`EN^9&?1oq+8I@ye+INpx+0E6;KBRco5`UBoOKWbYR=&m0 z??VT!VqPzk{N0&h&0*5^w_}DShHWCmsuNa^bHAgNlDLH$)xt{-$-xQgqaB3=E}G6# z5Q8fhQw4f|Bs%urWhN5FHGSM1JC1(z=h={5bDZC3t z!b;U;M%vC19pS+6%sf`5K6s@f^$gwW1^fV|ssu(Xa8X`TsVj!$FNfIA2xbNSf}%cB z3}K?)qO_=>4UzPz$=D5%K9!1pvA|-yhdf%Jiob{TDJMpSu}oN}xpxSob9x49ogf#R zB?Z@hq%-SYhvt}v6EBLOPu&Pr$>>u(g+BG&THV(|Fv&MCKl;@AcMGgfrBT&jDp*K; z>L1gMKGm_Q(WmNQ=CJaLzs$UAVd!bbyt`ZTuB3%`uRSHX)~2#pdCM&NROc7b6!fXj z&8|nLl5jk`c;DpBk(Lr#SwFK6O!`K81yuG=y0gUX=<&ka8r$ogO0D z56Oj?LI>f?Ab;pO#Mx|pn*CAV2-;bB(t{8+l^cxX1|KsGMyC_i$t!YwHPHye`el?L zZOE<z7y^N@RFf z{B2{$8Lg@Ur2>(r%=B2vhd#`QNoY>~kiv{j3f&fIyjn9VyE#^IRJ@XWvuP=;meeW; zBM&M4Xtzy2Qv3sO#rRo0Qq0S%9__RJgS^z&Z-hj>0X>q0GPA z(3IrgUCclG>o4kHD5$z+Tb7q{bEh#@*<930Q-dH3r7(!RT&5r)-V=d71A5g`jN`$U zT9q3~T?)|8DQqn7qV_6gR>Ch*KS%ok%SZAv>_Y6zl%GX=lJa&uwt&r!X(`k)yqMde z*G*+Rk+djRUdH*mkp9#`>QA7DmtI945Rw6!nNgoUv8}$a>QiIzORG=U3-!r?4I=O* zaT(*wL$smCw+#zzOIQ)C_|jme!Iu*=B#2EZEAZtANC#M54YLdy`|){htd40c3ze;n zeT@bbSUsy6yGb@?(Z$wJx9MW5DI1k8wiQMW*2QL$wL?%;3WGojy4VnOiE-Y6apDK) zVpZ`2=|8WLE=HItb+IL7n7-(Bv1r}{gFj1dp7>cE5iR9FHWW3ApOu4z_vTfsrO~@! z6Z(-~OY^n(+388g@FYcz1nWRi|179b^MHaP4r6VkO#H0n$f1>quWD9Y2UhsH<7awp z#p2h(aTp<204XP7dm0E|HF_58D1M=kSYbnGC7T$Gl~FHZ2(6CpxCWD0vtmD%vo{(G z{BF#HB=LI?CMIqDKQLx>&yM_9YCeZj(3W1Qr!DaJn2OiviX*9_yk|G^QKBm*F>i zI4#=WI@WAe7OyZNwX|P^2+0VNr&DKwbF3|${_7;in)O2vdo?K_P?ZY&^qJzM z?*p_YC--fT$CH~nUd5|=8h;&<-gMG9hPR-E6`K zGpmsY?)aaxEaL- zZz@v7MW~1ai;9yazKo%KjyFc-2O+PP9plSF{NP+C_ST`o`_iZN|A8;UTaxjm7@$Vt z%j@VpEWS9KWskdsHf@35bp(9L6?BvSufJ*~Qz+!`bDB{~_sD}UeH6aD2_a4RBAWDb z(8gUtA{%*3cmqXR1kecsIQ;%3G2Ndfc`#M;z>zV6@Nn@dRA zWqhBr;I?Aa#HozYRc)%OsYluRFTo!S_Rg798;ps1tx!!BD3z zYCs>KLwhcvZ|qCID8}DaBGJbW+mPrz%0@<_{jeP}5sMA7WUQ{mm^3e_lj?6wfQs{|4D_5X_R#ztA|_pNmmf z##5g=HIdcKd&kd8n#pBS(md6IUm_wK=nWrXElH9pbm@-R1&F9!S3;(Zt%lWe)ndKu z2GW|j73gU~qZtAB-RQ@Cgg0!+yTH?huPWF19$|@!;z@o_u%FGtc7padb!Q;q z{rMSFj3^`js#+JW+u@z^2ay8qL$8@Gn8xcAfeqO?KM($qUE~2f?jy%!lW%;oH zBRCbg@R9@!ZuSjD@7x^97wbeyg4dP) zhb?;N=WxWjz|lLgSr^ee;fS@99I-xgC17b&^iEkQbo9;>r1qvD=se>K_~ytcjwC6J zFr`uC4Bp?IN}UkYen$HJt--$M0Dv(%F-fEpK|16T?>EF2=X1C_X}Rxec53tj%`uHJ z`%J*jZIA`N;R<8clawZm5iR@wNw*MpB62cBGWbi92h){AJDhPO#j<%<^?5R6aRA`# zRlWLWNhauRz$utKd4pqbkFUgOUo@*o4%pui>E$Hpc+p?ph%14i%TWu_1|tsfLK$&R zjef#+2E>9QN5-!#koVc>ApuuHibbw#0LH}cq?iLV)tKK;F{zTY8G3#l8FNv>il@XX z-ns@QEw4q5s7r`yVwd>f)GvbBm&RJN&RY&s32zAqaHcZygqc_NKZg)!!crd;i`J2` z9zV(RFKWqt04AqOL60%91x^%M+IYi*@72m^>m)tnYlpsg_+5~&3_r~L;DeO`J>yXa z+Q;)`P!Y1}t8v)y+$A?Whep^y>0`=<1Eq&Wkk79cNKkqi3E6JVu}XAJO(kLP)u7Y} zdF*)BW}<9X+^nZGD7|;uw&Mu_O2+&a`u@e)cJ40;l1{jPXq}Z-B&q4m#a{#>4aU*U z@7Owc2hC+fqWAb$v=)9hVl+iD8cDz)9(Kt%b^qeAG3KOf4AM!NH?Kll;9|TtmPekG z>&yGzf{0hOlK^a|BSXHo@bDbnO>OWg#Cr=L5Qt^&P3#0Fo5GLPS7^w1pt@jqlYSVgJ zF2$=tkoD9&@v1%u{+>z5cvYdwi~D2Lh5i0msPduiUxX^ZUDAPgRn$qycvYe5(`G1G zys8l8>He4^#ul$CRQXW%$3m1h_s7Ub+ntk?Ii{HR%6l!cYlpWYWkbj=jvq2c$gbKj zys$$3y;-xvMykcW`2kMh#9%T~kXluzvDEqu8nx3?8p1&;)J0B`?8?CvgLiAr!k|*J zde`jlbZ>UKdo^*nhbOw+$DRG0ed!FLuRcM@pZ%ugQenSognvor_CEWB)~k;tvi;^t z{K@I5jbTY4_M8|$&ZW1Gz}L4->(w_QW$ZU^2)+8NnOZD80T%cMvtIp5&202Wnhx$$ zaJ(v#ORcjWAi=bLj#03KT4a=DDzhc2rnpa`tvJt8-TYZ`9`A3I?e>+7q`IE$GXl`s zYLcrG$uC0%R?7CoNkUQ;tE?r}dfFO^ifJe=MBLWLux5Nu*<{Fit=`X}{qYVZ{OAw^$RGKm;it;jZ!SO?6*P=cG|(G5(tgt-7!68^&SSq} z_F2t0hLc8p{%-X1zK75Q4|_5a#(Wc+{n++O<+(j-NbAk&K^Cyd@jg%4a9|^?H}62e zGT2NtlJ>WIl-TKkl+$y)&^Q4(<&g*Eyq|`(mK8S?mI4VjbH0wodK2b-g}nOl*LXsJ z_2bWx@V0Vm{doEW9*gLzEAxABQR*NA~`=IuhRWN45Og zdNa$fmGlv2#a&J6yZB<$F<__XUM;^~9AxCzlSo5;?V{^7B)@9N(MK!47SfhjbVIy~ zEWf5^7EFHiM7_xJt4miSzb-9d5bZb4_CZc{NC ztP*QsHdU1JYj$tD{FDy zlhd(_6l?aIeoB5VMhfz4%2<@jiaS4<>!vAsn4QE#LVnc%?Kb`E(Fafp@~c8oEx)!KHcm%zu@-js zG4gA7H@p1Wpz?n&`4z%`R%oC66YP_dy4vK|NXkYbzoui2^OmuLS?pVTsWp3zNJ0NP ziaiSC*QX<~*t6npnMm?0%gC<*2MQ>^BAowQ{L7YR3H#(+{LA*qKPGDXWXn2ipPYn0 zIX$mnSW>!Nz^KL7R~xBF_Q|A&_4ob*2#>}-dDY~C+b7FY-Tc`nPuyYbldt}Mo@RI~ zDNK)JJ|xL6YLXx70UE6K$xh65XEpS%nLmciy2ZR5rI z>4nXs5IdeOB)wl18Ydv;b`jV_@-EAleX=!fhw%RXRwTU7 zkJI|Gw-)QimBb)glA0{W{XCC$ht`j`+-daVH;{&YeELx)4e!75fTOohzC&AL(akWh zC$fIL^r3?3$5T-+vVNS}+UUnk&KlIIGENV(KDZ%fF&4Dj^y49SqZIVx?@&|j?_V}- z9ERee;_A+*DEIfPQzYt0`{YuU|9k1jq4v{(;S$vQr=iLh+9#)NgX@C(6Y}1(zJC$i z{u|Uj+3$9X$in-tlnp1cq=f?%qkE7h*Pn#;`*F+?|$46=TRr@NIU-yV{O(peR2$tg*u+#IMmR}{2XZZ?Z zer(V3s~*}yVZ8skJ%9WA-)KuLx*=XgmS4wG3nsrlLcPfH>&-+XzaIR-$geJ#B{|Gd zpk7RAE6{F}U(5TU6y(mira?kY9^h z+vL|Q%0?l-R$z^@{CdDFb}x33V$HS@DSYxVNI}_h4!~m1itBkF$*MHe!Yu?x5oWiehsV6@@sv69@mE9`irfB0Xsd8i z+$`l+339Rt60b`6PksUS5bTPZnQn+zzFG9M`YG&lux|2qJ&A}{5`Sm&|7E_w;!hn777a^~tWX$C^Jy8nsYs4unzY5&gH}?0Wl(;H_iB|-MV>E1(skUwNW7}t)VqbftuFfzTH{q&4V31A z>2LEGD20E0vLjS^qksj(s|r;iM7*j{#ls)4D1Y|Q;|n|>jzCYI1L8>K?FYmW$HNL3 z$E&h9P=1kU6JR?j8$y6_#1N@~eT%iq0_;_@Xs>On7VWe)oPq$m4Jin)`Mp_CJUx^I zSS3gYxo6DYkbeOd$$!!wuc|2aQhfgvZxOJ4vd=L7;OJeSrI(2NulSSGQxBt(pKV_; zd>kuG#~#bKU;9rEvH4GaE&L~!4bk6wCvbqD0Sb86$Lc>xf@#WdVV^t-f3BdmMyvm% z^+qe|K0i^H%U0Np%9#AxSQ&N&YkmG#rLRCP_u_Fx+~VkW?E{ zQ|=U}n2IgHAt9-zQ^jQ@%6EFFw~&(RLCWWNA5{5a$dhXo`B=06uT|$wC%^_H6%=)iiY$7?kMrqTXHz0PEGi#U8*BLUy21AGBq1gaVb{aPh$H2wFp*~ zY5RlT;a7GiEuKC=FGy3cAy@>f$9!}_iM{w@;HwBPzP$6HLMB<;Fg}&~eF^#K56}00 zP$%d6F-@o=<0_OuLqIU}g2Wk5KqRu~mTf5>@STiMLqLDaTu%vf*r>oy3bkY{hYz!{Qvp|GLQQ8B>j+XlLeMors4AA=YrIlEpUec9^Fq+hA z@m=iUv33$;1t6VkVf5+aNJF2FM_VWy?;Z1w)Tg7okL0H@UTd zZ6s0?zDh9iYsKeAer3?!2LwXPSptg1l#Xc{AiwtCj#7|ceGhB-b(yJn5XD8stEr-t zUspG@%dg5RUnsvq*)uKjAu4}MYx(~L`L(m2O@3ulHVXN55KEQi*CMlKFU77?tl4pm zI0gAt4=JcxpQNyyc)T0QuT&$yUSC}R`4uVtLd-o7T8O{!6YgBF1)x`V{=_Jr)#8#Y zw|a>2oF@%Xe5t)KP^affEw?7!Y~$z1Sa8LupKkq(8ZIm_f9?2&jw6FRs%EBt&!%L z{`^%j)AZi;zZ}bo9)XN9meo(#r`jH8*Fl*+5rz^+##TzWH@hn#ew8hJRRu(33Lm{{ zyRB(o8aIh|s|h4{?>hv)IOu&CJss@SYOKNlwW=6^UICCv^dA;OA|4F;9c>S%)1$+ za*Flm27jEMHJW$bu#r@}%e`K5ZSIR={iRv-rKPu`4d_eP@6`Izw_p=GS?uIat}~s? zMWfbEK10K*lLe}i`m&R%=!D~cLP%gqJ2I{l@v2@|`Kp2Os=hY*l6X`5&yN53ltG$K zN^G80V)K+;U&1sb!6M$y4j4L^QYgr<2CEv(GW9LAD?`w9`FF&s8f@B&4vp&N6}rBn zXoNMAUzN5)s*WW%6)8I^yb|8s2okC@0Ebp7Wk<)Rn$OBk2KY;OB>CAU1V4iyiwREE zn;Cf@+LMgDgBWRlTaBN-qmQRHnxdE0LdT}6VHz@!LT^J5qF5q$whh9#ZKU>4t3Qf)dDWjiHpudgH;b~BT9iJuC_CbhbUx!Qg0)!i=v0H4feC0#GO#l- z(EiYi!g#f^tj2s;Ad^rkcfzViwn$VRg?cotEhEQ}W{e!)(^g!-)4FTy$g!fPL5}`N zqg1^2++qPwM?g2wdOr5+fT60Qv8AT5*jo0+dRZE~NHzA7Y|Nr-HNL{8Yt^P~RJv9R zjD&Tq;$%V)6s3udplf|zgLSP@wT_T1Qjjk1ie@gK9Q{C=@ z;zjYN+9Tn8t2Ki<`Kg@$sR9h;%^Txqr)MAZ1WCsl3>FlDj-yvYB^pLXjk09<{FQJc z;bVG!@MA~DcrtEe75|--Ho<_EV1VyB{2bt?!ttk2SH_<@lT2z;{^L*0M!jg_Ppz)P zniBh^jN4=^=AF%Yc|eosBoZfKaLO-bSwqkR?WfLWW)$6<^O0^ZNL-HMf;XqHLPfqO zx6^z2GO5s1LqpKnkE(nbP5v9!3wZmEhMU4{P&B5kmS9GB zU2HnurQ8V*p0WI1`Bv2SFOdXJGz30s2y9PUMWEjTO?vHs7jfBb!4_d^DXHFKc^s!j zaV^IV~!xc)D{1aO{r{{p!so^+d0P=vj+i$|U%8GlZh4virK1~N1VZ(Un z7i|lS7uVtv2jj&RNO-5g%mGD#@ZzKI7%xiVXQ$_^!HY*RTp<%zRmbvwM&m_YqzqoX zEb!vv=K5=S2psVBiHH{)S{4j1?nS+5@M3BOgBNW+FnDqGZF;1)X%Z_Di7J{0`7x%} zMJeFL@2Ckz5ngyOV^ScDLvg{I?8{J5M)SyylXxMcd8|@-70rV_5XK9N=HdK{#FMh% z#Scp8I5L(ZX~zrw3U^R(%9{f%Q=#PP0ELkPNG(y{V`#i+tMKAE$l%}sq?A7h;|GR{ z*@+H6o)E$cPb7ZNQe+%Nq`(g%_+N_{^*x|iOf#&XDzu$ddd>CaDoI;HK!>}uh!&M! zyr88ek#MS>I!DF|H2?326;%tAL>nt=rtf-1RDC2Ml2_3f!o`R(@R19dEa@0;p#zU= z!5uM)G@Byh{)Jdk{pECoj1{%WgegoFctSmf3l(MXq!lVaPTYh2fs_+3RkehQT2je| zF|#R)T}cFtc^QJ0F=n)pu>MPw_;DfS^!%bR=8uaRV=j>N%8KjOL}N_GlhI*JBzx~s z+%Tc@@xAyLYa+(mtSJjfIv=0xWqa?_qVvxhYc@3FQC@P1y64jmqg3X^JCM$-n~CP| z$;Nv>pB=&8n^nqc?_Gx+dTwv}`VD>gvhj0N=i?V}K3@M;0-^$)kADMCBI8~k&H4Dr zlE&V%kxf7@C-?-r1cbm zC57-Lt$PR?Wl+ml6&tZh5a@~iiM;EnM+y}4Kos#02Gl&#D?;OeXOLB_O(5_FjIsXe z-2N`sE{Mmn{o)*1#IaV}YdOG4ER32kZCdP_m4QJvyHLYZKC&vaU9&e5d@!lFQ5Mll z5H^?ktwIEpQ9)GvqCay)IV2N(@u}Q;Tl2nB+&B)xDN-``avD%?7;b-oIkY9WasaQ( z`o}OQU~{!|x;G89O9)N}F*R2XU~;U#8aI4@V8gMXQ4$=)V0?WL#+E}9OmZv|`hJ3; zoq~0g^;e()Q!-%9fEQq z@DL*J(jCRzB@txfZcpN_J*1qn%;#g_zKL<#nzgK|APcuaUS|65!m3T}1TKp62Qo89 zw^r@v8`0aG5B|z}Ri*ljUboa@^m_P44UtI|?dWwAc76)IPL`MGmHxQR=3EoIa6sXy zn%vk8rm>l*Y=y#a(ST}fk!q}wY|LVF{;;Ih?&y5~P0EI_ITsyUDmim_#fI7begNf< zV3}kE?+G$dLY%_&w+se>6l}G_&?Uy%8so$dup(TBAAs%?Zy=j9sRcZ^c<=L>OkXrM zXKhSCB9cA99v}1=u2j(d$o=>i>odmN{6=j@nED0T5o&#aKRG>3Ffd6=3&Av^r5seV z8qfuV-T4iu3FhR!0e%NJGDvw8OsEeg_`c$!Vq1KPpZ#T=?*lmC-#CKww^8sN3#-3v zmXUABSur5KL|v*&_ahN4$Ar9rgQJQpT9RYe`D2~#lkT`1FI0|k|D)zn^o>ApN#5^Y zHD-nFm=HN|FX$Ar_jXy3kU%6zrR?&{Q3^`g3$JOj!a!5;N?q|Ds(6N!kDcCoic39f zGv#x1aPe-Av<7PZ zBp}l`C~}xMVK}ExwjE$r5WJEK)Ws}&=}MINS*kKpbvo;c{R1E@Ab_N$(4W3{OuZR$ zQcZ;$OT(3LKnIA+=!WLh0`(2&y>V{pVVC;=`>%mSqQ3;i#`KpW8nD>1U9r@eB`TcK zpmJ;gYBqQ!xSBdebJXqwXyqs=Qi_@SY|{l6)R0tnqG8xif%DY29i>w+Im`j; zDOH$<6bDbXMi9fJmP8qx0q5F*bH4S0DUOVdY%n;WX&_*!!AcNgj8cLWZ=_ba%f=Jm zBs4;cuAW+Sdo_C;8GZ3bKJ-nr!LyY-Vau`xzz}EM9WfZQ+`1Ew_%mMGL_e z+kA{tGRmgngkEgTiDffjY-l$PUlDd?u(4n*O$L?tp7ODl0{ku8a+8oFBpTF5dINss zM(vJ&l3z*#2|OroYgtV2$;eJ)n@u32RPB&UiNoDZh-Mk}Af8Nsmm>4z5hT?2#r%k7 z745aJqNZpAfV8l-mg}Em?=Irt4Jzi{lZ!`&-M`uht!Gjfx zjxCXrDKDFTj=eXd{qwX57eH6Ngu;sn#R)ub5u1I0{SC+i{rgo%*{rzf*wIV0e`^Yv zbM$3Xe$*syaMtMXJ$QW9bB)D$LEZ0=@OHgcKRZ1=49?$$u?p0iTnym# z9)T5~FSkR=#Ap4OKr5^GGo-}{@?9X`m&VYgXeyRT{GEalxbWN}$gANUp|BY(zUkt_?EASID9xBe~92xZ+NGGCx?^F5& z(?if1A{eWQue1rAl5@Ea^hnYkvyirsOB5Mr-+2auOQ}}LxL-27tkbbJ+h3LEp+44) zbskC$af`PmWCYxRpu3OzE=AWI#`H=gD2yrm-Kaf2OhDz3k#h}wh=Rn&2^~{Y`G5z! z`UpRfP2cYH6@X_%iwVqb4V6nCTdDVmZ_!_ZpZ5?aJN8Jq&-!{Ynxex2Na*;#)U%iw zdtLMH1XOJqOsY02LX1?sT8ojMMHB+z%(~OBHUw%(*>DIXBeC{`B=jpjVIw0{u+VEi0}>yhfmg58DnDLLpET`^(_?GrqpWpAkrS_r+`cskV#p zryG8DdL|hB*^iM55gUV4B>bs+59=0o8RRQ&%1 z{GpwUGG+%CP6fpuv}_`R|Hbf!s5ZgCCH~NWv40CD;{N*hgTaTuA0q4Tg+FR`H2x3? zb_}6?HF?KT$ohXH{ycq|mOr1JuWTZLe{#l#KN~0;1^#??g~XpFNJzO>?N=q99{h#y zXQIZRI>-ZmvK%Oz758XWjX!VTc;4bP^)KL$B|b$_+_wXwi4s*Q%&Q}(rs+t zJc2(tJqrx(^u-8;Xq|ozqIH7a_mn}(*f%r9zGvB`n&?g-+SiQtJ#W^`MsKjIk1Zig zo7nCwfOC?9n|AIC=9pQUCda}`skS^pESrO#km#ifZ$Xjzq$4WPHI&ITl%b*DN5DKa9Q^~N1!MB;?=%qE`|i}*F^tYv?<%q z3N+h?&^mmhk~hj8pn6h&)D7&nJIAs=Dw%-%OC@n>hPcNxarU!W>xO{OX(e4oiRSRB zS3r9nTNO%e(j|ILO``?T5ZfL3kQ>o#1ldeqX>6`|8&DAmQlzc#hm&b42tA;M{e+Jy ze);zBK8B}l6Ubr8Qbk}~=;;5%U*-K$%v+-f^JX;-%2~TQ+^oIiYmN4K6I28=@TBZ# zlA7apCdv4(P0J25H-?^A)D%Pv7wgWE;l+M|O?54Tjb9ntf=^6&9^D``>Mt5}9+vxH zauV|}U2JGHzXHaqc>k5=eK#5y9BpAXd*-AKG_oig0yH=TyEL1<0I|uNmivtS{Q8U% zDXoxldY;jsaqb5Ojgh6X#$=o zYMbniyX3e5jzdox;8+XJOW@cGn(&?C6ma=z3|G0@lA}`Tq9_I6cw&wQj`pTvSzWO! zRg|u_T~A2hkgm3^RbIK;>N7FvYMYxoiPvlB`x)Xq>{v>|QCIr!HvFL1Loe{_p@5A% zuZKchs@FqNC^UW?BRfk9jO&aa6A3@QV?o5$r001Bkqc_-Fk?OoeS{Ot-?k$nNx5K| z0Ik@)kW81iC#)E9a`a;LU8+^#5*IXx0gh>hAOd_&g(34GmPuTRR{gKh_;%3^2P3aw zI}-x9Ee@?NxMB~}p%rzm0p*bDP}kB`D%)1(7W+RWC zNsU;HAuH}=ISn#ZhefqB5fDT866JfKgK@8d;~Vux!uw%)mX=+?hEngb-|g!pN3-Gv zVkD9&AM%ht6GMjR{q{sTafkN4^K>XL!-_Moho2|Be75xCoU02wpl0q3C@)fRJ{x7+W?OD*d@1a5*9s z$2Sw9v;u@uYbK)JQq+t5p5C0#8G`6+$lE>3K+sL)^)Neu8(1vhLk-aG+X~S}dl!0N z|IyzZ3_%rUXb9SF*f<@5aoAxH>mub$nz)pBocr6`}$kJ zk=E04QwNIo^%MAg{l1VO2*pG4&+>exGZReMkm6oH*5|vGO3>|4etCzAW}qPu8lrqT zls6GG9T~r)EP9v~s{H5lcWR%UKzJzO+kXYch>RYhllokDo~Ucv4+I~d{{d7;y%!yY z7?8icXt*|&YsmwejMhWZ>%Wr9RSVr7zxP>;u+;K={-Q4}a%qQm7G=Z9CHdax3anw4 zOAnaE?EO})$u=T|)8ZJUAdyyHz+y)H5A4UCp3m{|%QNFCxbIe=_#e^7_uPQ{4rD)B zgMYCgV!W4^)_U*2b!7= z6V6mh!zqCMB!Q~u&wf(zpizVOPB$`W6hoFFMO$FQ7lqU&+kG{1dyyf%0TkqFAOA=pjS!= zY0>BZ&3-ceB56MvfrNKoQH>+jR*Umr{Ot5pyHw-IehgZO))=HB*-xq$(cjNDP#?yA zQVs4&21oK`Klv8!Lebby4t!#OW8qW-9OFr&7r-$`Q#a%YhHJH-Y&$K0qf@#DjtZvY zb`%$EiKB|reiHYo1P*CGDX#K`_LD;UiLd|{+E0Snvi_^=Cm;L)$sy!R%wdcDGXk0F5ZLGuAk!In>{b%aFvz@u9k>LUPw)EA z*iUNVA`072sw3e||NU1wrX>5x)|c6S;`xC`ay8ik1XI#^$hU|g!*1~6r^bHrB+{^- z>=IWt;=MI^)Y02d7M|DLB!fqh?I*M0rc@C7$-Sr-*?!VxyRn~KI?+JTPlg+nzzu;U z$Fb3|*-zS>05LG(=b)xM`*oX&$zYYJIGZX;`^oIx5`v`tWU9&++D~8>DYTz_>6jL1 zKk2v2B9{>VnX=*Jl8XQQyFB7Ey zOy$QY`|cZm1^ez+OaK{12MPOS9X5=nY5Q*SAd5lN8lNOY{Q8jPL&SRuSw2L(r;z2N zjF%F!z8idC+NV;24Azs8hzV6bRJ^B<YxvX^SmVO} zYZki~TSl>F+lUlC`52_2QI+;#vBUlg#medFZRFP!ymtR@xBuGXr*6RI7PkN5Ut$0K zk);^DGd6Iku>ayu@Y}|)hPM%CK6C6Ilivx9=Q{7;7l*vw=dQ}0MlXNfjvI(pKvHHE4{nGkVOgSxh9Ua#S5C`SJlz~290qaKY#%FqQ)1Cu0qC1HSblbIH{9CIXiBhO`U2QDNu;i zD>1&Y)$nnA|Gi#f^=N(E1b_-29yQBXlzk!a#0|OEF)cJA3q2eiHP%!&H&oc4gVvO9 z2RI~WawGCQlc^H~S(s7$6{IPLx-fGW;7~_1S2O&~z36kIb)FdV>JSsN7Zc;iXo5e0 zAaNf{^dEL^%=?=c;8q}LxL?t5oIDMf26n0oq#>wNoeqIofyc*Abrwb%pOo)LFY`@6 z2Rv+YufwoKXQ7$T1_)+;M^unrb!|3TfF;MceAkQ-$~I_aWikb5iTWmp;S#a3?X4(HMx7t9y9F3$#9bZky4Ek0VX-Ptg|0L;TY= zQXh`e-@#8?T-1c3IUg(vv0wiH0#@q7$G$I^KD+_-BJ0D;-ZA>{u=@J znQRE!ZTt1PpP>}=;VbUfD09fLaXyNRipRF1qTH_^+aOU!?$>v#{NHxJZq(uE{2i?2 zqw#mJmXF5Y!CJl`{tlMQah;ayDGaAHaB*VkbU zvsjvB7V|II|A{qumq4ta} zSw6LdJ5)jJiPxiEWcgHVt&vY#hZ^~`46`J~=z35rru4~;0rKhWK9qudN*ku-Q!`WX z7Zev2TT(?SpIW|emro5;zED1euqRqRU*G?vd~&>NlTR^}jY2+M>Xq{8h*^`}vFj6S za?N^9K|YN{3i4_Frz{6Pg1X}Lyl3Ro56uf8pCY}_n)nlh65eOM3IFmAeBp7HOY}bL z(b+7QTw*-8kotrnL!nuSfjT{hv|Rci+sLJtk8oGrzIuUC z`HNq=o&>irm*o%gTwb7Ey5QY! z;E&UDSo3bn2ZnbKAkADWajSTI&Kke8@+W8mGHwBS!4`=+=_Wc^aezB{+H_JPpp)Az zos?0XENLb>QSnRbd!#^=@k_5#`NL#Nk#6m57ATBgYKdR^64;KJuCbpdp~?|G(}%%m z>rC%>&&>2Rq_fM;(Bz76kIB%qf3nQtz8cK0ZLOd{D2WZ z10_WWTY38W;Y~cG{Jw7*Nnfgn-3;kV#qcldOT$0Y`qHXQaqfz-VMJv}Z4$#Djxjnt zziG^$iOr7U;loI0*7cl_oGd4Xt?XfWR!@SEOjYiP39sF^6{?NRex5n`9Hl$H1-n+knSbx^|rN_3Q z4d_ev_SE{)wW5<*qLX_}C;QQmwUcU=PCk1}b}~wKqT-hpSta!)8Nc+0*JQphekttn zh4D)Z%Axym})qcgw7tX@`dqB^}`s`<>LabW{4%Jd}aPC;+G!Vp}&9{v?vPWm&Q8Q zh&N;TiIVBCqo~;*i-%ogP|&|;{LH#HeXrBdZ%?(~`#&888r(eYKj}X7K9LSMC;{gYvh+kScr(k&T(vIlx zV#Cu0FCM+!;Kjg?^hhUZ5<3xz>QW&25x%(&rGOW=-l6g0S3}|&6c@aSp^EZSV9XMU z7xGfz>BTayE(K~o!ov8a>VdkGql5Sm?xolt#4N`&j(1%czZA}cTGkqWJ4?K4;+Bm2 zNe{3(gNnhGTaL6N=}A&Z~V(=Y#6hf zvXL>yk6jUC%xc;mLFnMTIpKLFelnMm)V@t)OmpOcG2gw7vRQHSKGYbq@rvj$CX&53 z9+yPO-dhF#vc31=joRLuJ%;VQnWFP*=v?jh63 zH)zP($@P{_PGH}~olKFPsQ9Io7D#xL@k@)Sd|~|3-PoHJ#xITi*%kY{2*CxTUt#=G z1n^aEBd~Cm!^H@8TrZ4YD&y?p^?d9pWf0!N_@(q5rzHsS|9t$?PVX{AUcZ{r>mAZE z1Vm;mw4+xlc7EzOxen5dULVHUY|eLoz>OUykj#o3VH(?s3RWn**3#IK`LeNbvN6l~ ze(aN4yQA~{pJof~?pk9(xX8pav&JtigORX;_X(LO1x2@uQA1T)fi9sJ+>CMJPp~5N z!VehnyHFIwFs04;con8E8k@7eOoBut`?EcM>DAkz{o#GOi}5dOe~-Ma?Fbw0;rofX ze1{~>*b&kM(}@wrR(847FK^#mS?(am_oxQ<1-#=hVl7+ zs!Ix7RPjq6$kfUk$1lC{7GqW@YnD)LP4W4;Ai)~Hw9g8V2c_(5)RYfBEG3msP}~m; zLJF)}Nfl-M(v`EMo+aa#KCSYF@k@aicNfMlb!24omLGRNa|`2_;@k%p7z^W!)3e>Q;63ogOW zPS0}&=jURq0=%+k0=y!`Fa3O#CO;G8M-xBpOE{jXhXnHzzjO-fMH4^ni76W0yQ6$WHSmD^5dk!JM5lP;mhrDDU*{oGy`lgg9XS zK;`LS0<~4-8wQ2(OE)2inmRNE3VDU z8iB@?upKDyV${HgS?!JmBz27g`$(C+RPwWDe0{*z` zX#7bw6^rYN9jKzjpAJ*)_;Z`ehr%DkOe~CF%Hbgkh(8o`@~;{{8)GYsUusm0zgYZi zH9P8luH{d)M=khMDb0pIMJXEv{#1kHW&HV)b~Qq;?wO(F+PZWS>+3cC+=o2y z=aFYnHY;xPGL1h!=KgMVnhJ$KkZ*2o$< zl&a?IIJ-}(!K|xlnd9s~z${GL7X(UKaSlUHIcD9Lnso=U=~A}($w)KPF1f%=^DQk2 zO7XB8MY*f-B$@)gy^LPub?;%KlZQknFOiBOmeVDe1Ek^2poFEbq=0AIsSxX_jy;s z1zRBm%|i$%x;Ei2$5BSNBWNC4N=HPz7)QoRyi}rc_LL6MEi&F+gy9G?&J^M5clknNKKjt=A5~7 zNM2r476*TE+~gF~WHAggks`IZ#8EQh@a4Tda($57BUhpgCjMe1=q)q-9-fu{viwv% z_2Dmz!8;u(*_4r;Q4~IPY`x8PJ;j&JN4H?|F~#H+_=V#mTH9|Mg1>?HY%6S`C)gBY z`!N?DMuU>E?TN8r+LAo&T{B+r6anRESte!1%E*2f9KJhBsYcl7qX>@%a5R0wiI^xCFfOXiFN6O|;J=~`Pq`BKrAqo#92t$lGKHBwY@jGO;<0BL!Y-S`5SBtp z0Hg!F@8TY@qf2q@+Z2SoH$g&JmvaJLj-Y)s_A{)5z{4B@v8=el`lGOo<>i;cA&H!s9USp6DE21`NFA<+>unWji#oY!ES z_!EwsPd)^dASkP#T3PO%Ri&-0G;pP^X8fO zlhc!H>;M%sF-OeQFU!Ze|}HV8B}6@Nl;QLzP8l=0A7jFvi( zjE8oE$`{5%`!A1&_S1a*9o$R{q9NcjXdLsxc=AvYb!g_JO3nBEP2-`ZjLzT%;6nIFZ~C&ZKAa<>5vcbox^q2Rn6WQL~hE}~9d zeWIPVCmp2#9L|e1a9l9dy@TR{Eyby#y!ur9J_#K1>eJ7|WnNurlnn zKR97=m+@=bHTy1YsLQ=s8)s|7IQz4zk>Wm=EAB9tL$O?cOg0U zuwCKJN=Gi_aDjb9m#I|@H1LYY6|PDBV!#l zu9Qr6WPFDiqDz_`9T|&|6qhtphqx&OMiPuHt=yM|}_`9z(6&q-3R5 z6Pl_CElq<;$}Hip#6$f8T;*XFrCmy%Sepxc#nBZgaAcH20#wNL;4Tt;{L;6`B9Hfi zHFDlIffhM$6OmNZyo3_m-nhmQ=FPp$K5N%Rnza?a)@Yn5{RutlN!iULHO1eUq(m6{ zK#w!@To`A$uF}&0sVJ2ajZ!$XIo*qMj zmJ;lpdB>}Ge_Sn9Uw0IT(ChcO77L5-vEa$P0X96DPT45%WHDqYs zPLEUL$(bRHCxa$q3|Vn0(>0zvbo}3oCttbT2i<4fza}-UMQ;{x{3ra&_O8n_HI58y z!uGC3_!BOE7#z7mQ;>`iN3wU_{g`HHrC})EjB(`6n%P)ou(bk5-iH7s9J%)&Rvfty z(RLMZv!WqY&!4@kVs`^8drKKuSq0upbl3qBA!}goS~M7=x7xdkjz=lL%HyRqth6;1 zi|LB(siL%ZweKfkMcTVssC=Ql>%ZLIRgBa`tR(E$ThoFlw0C9Tvj@^3+?3OiEFy7z zL6>)J(0){l=)bAGt9LIObhM*vIOvd(o(4l~GIZ22^7DlON*2W;<@5~D(6P2ZLr3BS ztg)=PN>ep-v^f0#X73vRq}-2;K*GCks>YFO4R}A&4L>_QlMIgR$DoC1jX^4sy{q~Z z{rzkM^xL`{hRh0IwxE>NXq`j-S$`{(Z*r_7ij%2ZtAwhQ}hoGkVoN7lhPwhy! zRsU;HCR2BjB5BTpgX{Cb_ajdU1^8j508g;(M_j>_U|K)5gX~A(Wr50*Xh%XWd5(;e zv?CG248A8Rw0Hft*}Fa?_O2htplnv$D-Uas`E-9i_a{-t>#CInL>Ziv*}OUu-t>u#C|$8l z+nT_Bl2gP;uEt2@;L~}?i^Pzjv%lEGI5#|rG~#vb!Zrbv#(QhR!m}^~#{+f$FCc8g@rEEL?JK&1lk$mn*ihI2)c3<*2*mO4dZ*ZY{!Zur> zeJF=Q^k;|5ji|1hT<#t1+?&u?>uHA^8T2q&VSH_`@9z*_`?ii2xwXSvkFp`;mNmY1 z@VT+`?Dh8e+5>UW`J!jtuEh>mB6wF2`QUy?5IRcRtJ*Q8?-{~L>}-``5u(bid%?Xy#$u6 z+ig~tY?ibCdVK9T+%jQ(xEvDRF{89Tye^*g;U&X(AeG6yAmr*89`To2AD-9V=)>tq zLm$po*HbRyK|>#oGQPH(P*rr(1T2aizjij##~w(I-R^?r)f!*+Rw_Y7 z8>*txUK^r(ITRB;T*uXZN4MxC*#+4Xl)ToJG_aM4JVdleC_U|q*%HV3Hi;< z$No>O$+Nd}3i|FUq#%%bp_y*T>?X0?ViSknrxgPs^uE ztBZS$VqBw#Ya;ewz)nxm%h+!d3hz0Wkx$2whJ1=gTc9)EJ7%*OeYEn)Pg`8ngsp^l z65yUL zd=wXJ;aIYfPsdu?<@haZGo8o8zTC-@gA72|C(jK88jFeN^635%w=g#TG_2Zrd6u?fcN z^wiLzsdsB5nv#*utotaM1BLP4PuKI+q~s|Zsb?}7n5dC-7mZN#1g`L=`--H-!$ZN5 z5hre4C!6kmMt8o;<*n-n>DDz__UP92NIHVxTi0cr@1a0U>#V`QVkdqFyw&Im*-`vt zcJY>w+14Go*u~SeYl?uh>t5?7-cn_wuui7Z#$RmWsY;SN?wdwt5C77)LV8gE>_;(& zn9**1nSJwVuMU<#+Q{@SS%y1UB9F9=gIL= zMwMaDzRf5=cOSdsg)|(!;0P!QI!(yS*IQ}A{==8A>Be;qv`SpQPC{}4D`~hF%t{(v zzK&xeiX^B1GKprQqhkhr-gM5?pQ06$lN_Bbb)-KEA3u3F6AM|~<}JhVihWB_l8%|C zJx%qqiYI~Xn1uU#^CY}FNYC{6fJqqI(oDjYNb@ADd0))*SQ=jXFOwf>8DU6)_hUJV z@?^h*PrmNQZ1{^&JRrv!{7+vn&J*gdyYDnPq0tF2>bxI3nmS~ZD!@jD!AHsIIExUs#Seh@vn(!E8fOTKu##nua-4)AT`9bg}K zGalVQ-%fU$$w}jo+d(EK@V+buMsClvj{-sdR?Nd@A$W+|fp=ulz+^x9&p9&cQ3FM% ztEu1s$tT>JdDGs6s>!_Yc#)g;0rYC)_W|^p_Fbho_o3ok5&XjPO0lm|P}+kov=ruBDWcfP zqhT*$vrHxH!!NUKzeQSR14Hr2-<2+5cjQahnm=M`@o;!Cn-y#>|L?Iatq>QmNlXKe z@qdM$zS`mf_Ie04h`7hQv54DFmH6Li`^T1kMqqAt8ep-*LD!$bPi6#R>27UT8y)%sN33an4{#n>>S z#|W|o54{+p)03nXf^x|7FrgRzg1u2@-9~7RIhnaGvOZM{nvl__IwA**`h~zrboT;y zd65sf-M zrjt2nSas4xb>d$mI=LC0kUsVBO;Vp~E%d43Dt|Nb3UyhZDni2&`V=0QiutSPQ_mO# z>aOG{9ab@F$4Y$)tEwZuUGOiwDisJKd82TDf-@?-AAk=0U0lu9r`-{uMd`>dpRXb1 zjG|Xib<6WL;)m!|qPltIuI~yAis_A`gnP5Oe?j<8x)5gYp;8U98I>vk;a$x>aXLDL z(uM-?=>yiapr`7P+%K{=1bv$DNinxg2v!6^nljU4B^P=#T0M;R z*KCee>=a&A4NOA@VuC11NkO=HkhCJw!go@F)T2buXZQ!;is6vlig|gpBYwZe0Q;k6 zZQicdrdO@ae)!`o;vd#!z$yyRj>4_VLCn8qXiM_1Gx3jI)AB%m(1)7J=UtPhFjm=G zbZfFv5N|d_-KH1#>MKs}Rh+DZU$p+u{0vw> zp;aEU5PLEs&!A07x{JmZu+cFMg189TO0co4Je3uT{V_08?&fl`|8?MvCWi?N)`LOhg8L$ zM?z9m9fJUEP{k&oON{e@c1jhiiywg8Gk~pPm^`?6?^~}iefd(wSS5?X-z0dv?r#S} zOKXvoS?dcVysf)3{8PN{$BVH)_7?o?^bCeZA*pI3*axacHh}VE-XEw93^eycmlGmp z75~SbnjKBS4(*RkW-HteC5zS1c&O`rs9+F;?G8G%? zij%40SX6}SGdV%3J}%1Vct@&yQ{-)`&nW!hTsMh$5%PZMR{Zrpem^t`7z|-Y_Y83< zl&<=bhjMpE1_>PDrJOp>djC@5b24^blg8a-Lsi(ycq0Lhn(Ly;QeVM!(e7|bOcoXt zePdDu6{Y+5%M^n4I19 zAde|E_hIMbugU36Cyle?o0T%gc-+La&ttJwlQ^KMBO@MzWV4&U#N$N>c7x9C0^gO2 zsyax3Duu7;ebuD#VM2T3HnR-veFml*LBMe3K!1QPY7zl<p0aJR(6ZvH11crfxK`3R z%1+$U-a;GNY%GK1^bqbR5#L9HKVxuwc?`Ex7+=zm@P6H1o-@CvO@xDzv8@_B8_)-UuDy84_bHVWCBV6W*249X{ zZt!J!j=`6yq@;i_7>X8Y5=SLq%qm<5oonp96{UbLy?1JSscI_jM{&WMc&aF$OOLN5 z@nwX-mx?M+evaxZ;jOg{*MXV-^YP`d!IxMhrK}~SkoYn-K)$iycHvU^Ed_uV8ErYF zAX=pPBt2f`a|8ljav%?UJ+S3t=|EO(f)}TV*=#QVV89l9fIP-%Bf-sn)v-TIvS6xa zfg@uCNl`jaQg}o3+c#ZSp&SHm%5K(?rp3IQ+r_G2d4o6`*RYbZG%}qBd>?>G!ssilO4Ij{t+XyKHi% zu$0sJ{nk=ed-IRTq4Vi&bayv{L%!eeb5!=`^Vrt7?;yA;(D{8lRn3>Z`I<`%cAeN_ z?9H3OFtG1j3!c7%tu;OSz#^w-rDop}Y_8Nk+l4eU?&xB%X>U$+%k%q_H=_;UTS^=rs%eR{sI04z>GY=e*){7kBKkm1dLrTK+g%lM_6rWQFl!1X_G?{&hg`5g)&ql z&<6HG{z)ua2cQqh4N=HH6!7y%p9swdS`p)HZOCBCmvaxfP*7}t^%oxk5)vJ2dBebQ zx^p)T$@98;cOSi;V>3kIP4Fkt8rxMLr)KGObsHXQ{*)c!bg$=r{8fmcGPo5ivZ8l$ zkjg#h;D!6KAJ{tc)#k2XsV=4_q#}L^(chgR7#4V{dQsq1l=Y8eHXVfKVP{ic8uwkH z+m&;m?bjFCs$&jf{g-jmkK}1O7DQ`TiGa7*yZdSe;rnH1gZW;W8lyL{6G>mt{HBOs zW&IUs$drurTe>iIMSefrn6x*W)jvY5{!)tBi||L$LmFyWomK)Y+H!X)PDO$)^+%Wu zZKdKNqqjVte91VRswW=x-p~c1j0WELF^M-U1Zrry9FiHfAx2Z@5^ihvc2`24zE-#EaUz6SlRK8saP@ zBt@k$2+#&aYbd(JIF~h2Ch=P3 zUo6oWZ}S$~PBHZq*(qv$fIm4sTQM$4N(;d=kTUEtD0Qu&MF=~^J}4N*=b#IIhjNoa z`+6~}^}z#Q3067+>=b2$9lUkc2-5tXa7NJlLcP$Ax^<~8seVz#55KXzR{O}0)BF8< z#%!@2^C1WB1)XB@-mV4`C?1Vc=Ps{2AYaj>WcSJ#VVwc?eyMLMQLcz zGqAFHsQf|X)wYYx78R){_6Wc`7IXxc7}0)lJ|*E7S9&hLpEvIs@7Ro z>>rSr0t(0)fbC*t(O-&ah=r=WPVE6v}aHeUKGzmK9= zt92n47*a#>pc4%$ygh|^sw? z1vka+mZsQ?YApPMN6@kW2QRyZ_&o9{s?CBz;TvKqRp20I1wkjvW>yf^&QPjYn^=(F zL~HgmVEji$W!1w)S}&s7vbr>i+L9)nU^=+aHpM(cNWep{SKraG_e0aQ!B6D-1Z(0on1QeS8 zRXPO_G}4O}wP?}8m2+Rs3fUDi^2Mgv4?7AR7>NV%+yix`M(Kqkr*}CwBw*E|8qR?S zFSf+J1<9GP76=>|JBAEKDl~r~^&Oj4ix~8O==WXI(>t91C3aZQdPR$39QfLuqc~vL zHnv_d8<(A4zvh5*R2ll#fpS|(%Ir9@1zm?DXY-X5FL|XS_bXCr1xXK_tqR{X8Mi-< z%W@kBtb4Q`{#!tCKqljW4#WXG4nx%Mi@W%OIBnlJ73rdW-xZrS+9WCM@B4kG*8FS8 zM8oC33njx5Sf{WyGW{J!eX03RPr+T#{O`vCzY5q5H0X+c*ityieU5X}<;rTJ6xzC;RmnC5GSD^uu5hYm$Oor~KfXU*f z@Bx#56w!9&f_SfE9GBHz5<4-m}_BsN$qt-dEP<|8X zz2cQxOUr1udVPG1Sg^=JdS()b`nd-~lJr+ci|J0!>k_v7%r-^LqL;@>!9v^^N-GKl zTb?UeT%^&EuZ>e($Q6s7gP!v5MqwcWNA7+cAlwfdzvcUB5UgmX)kgC5vI_l$NJxT) zbU#CRMA05GiuMfdAfx*gVT%)_{Dd$?iJXqvg@F^9Fj_(PnZl@yP}_`!?B)EZzm6>4 zNxXQOW9VsY=>kc^ifW@O-M7$beDNYnN@@waRNj7xU#ht9vQgiACAm~|%tit1MDwEm zYNlf$n(J`@(Qy4l70KmdO{+zt*Y_JCA%;$WYc(D7&ND?DSSW1znhr)o&uFe5_twExJD1(LxpeITSt?eN-L}U}e!q z&%rq=7Jc-}!CK`!kEQ&KuIKAFMe8GHE0xvjRT*1T8BKjO_J6nw`sn1TN*}E!h~Sm; zG;ZgW^Yw9BAAOx<)kmx3c{qJkat!Jtl=%qhBRu(^t&gbaA^M1N{jb$W$b1-mM8*0y z=_4r{rH`lpR-Iy^!eP`$*ECbZ%7iM0K6ezc<^(?{=}iaZn~UktS*^wHp)nDkK$>**^of1vgB`S=B}l=C%d%7#>0 zPrq|LTTj23zkW$$RTE8EKAZ7;V!n`pe6pM&&+T!X-(m3?EYlnUP~X|b|h91p)Aek**gY?FlD5C6qpt;!}$((CC% zATQ8FrG`v;qt8&)mxn5Hde2nVcRA)}vikNXii#UEl3V<@k_}m`r@wGKo&mL&@-~wZ zt*3v3YQjriZNOe~x_-$RJk)&2=XkJu$vF9v=Heyy<0Z6z>!X7#ezhalL)P13-#haB zew<6rWOKGlm}wh+y|f^M4?Z*hrN4hUeIBL}VmW;PnlrPJRbmHNPIqG(j@g@Kz1Vd= z<}!SHe30!m%9465`AKq70f7OG`jhYudF2|+iypKZbVDB+Y>2BNQk(YNshA+khbE$1 zMfogwFaPN%6u0sU$0jVCcf~{4N?$rXjTUer(eh^b&Cft1>k&RlzS#0`|8BN63=7*2 zXbS8d8*v5xvexHYO|ZHuf>#zD{L|jD#5b{ueHTxZS7^fdV1%ay+w7{TI+ga^>y4*H z_%3uWe+QeIOkl-DgF1lLhMKh4ca8R>J`*h~Q!cr)`496r2(s_Kt(z~4%9V#Y{YBR4 z!}z1B(ID%roXWAusi;p3zuYn#(o}C_HQJkU&wFp+YN}DI!8iDku{6|5#v@~DsAt+# z@9VO^lJ%IXH?)3-8%?z~WxWcF8PqWX?5*0jLoGW5t$(Mncc6Avc!7qvqHFgO%k*DX zMU;lxmsKm7jbycUqr8QER`g~%8?u+rir7pq46Vn&4YbtV+qQkGFPK-O3?kZeLO?%W zx6W0TZXWxR_iud3`+ooj=%$J%GTrnDsVK+@mger|thy-;lR>GQ_Wf0hlfx6kEKA<4 zVS>cNyeF`Sb<_`g9*Ipw_bVPCANG!X*oER@#*+7{z0h@@eMD`v(ddWKm z`+~9Fy}K1(@BRV5Xp`l9T^lI7!c)a;y?Yh@%v@x%DU$9GI8C&lz zc}ZItHbEF7g zMRZ>5yG@?Y#JPo_cH#o-UrzdN1GA?_^VdJZ>)j7v^d|Fa4>>qumW2VBuXnG(F^!L? zE4JS4w2Y9UON@5w-QCr4H+J5zHO9+gc#37YyE?BePDgyxdjelRKppbYpA6u9ZPs?F z`&`^81rf5;-3!l>69@mD%pl8`x=sIlR{kVf?yiL46Fc8wN0;lisGc;Xbb`jurx}hG zs!GioVOD6zuvNiGDOg!tBG$K2>dTOe;aChZT;xo7u;f&UY9W$;J_H{$Oc0+BDTL&C z#gZOitH9T8#-HxT)@oKUk35CY#&f6rw6Kt`XOgSSwmuF+Zvz6KLih>w%1g) zy>+UZ&>-F+6%%KRodo*S*4rV@On~pSz|Yt)g_-1t=T<>oQgU#P=`us+w9V) z?s8$|W_8CuNhlY#SzEGkov<{fpbG2E9tDD7j37GcnYc)83YFB}u976k1UIJ6x-ns+ zdSTE;bx}Jqak!>L@wl>v#?XvzcVzHc zJoNkpIEUhIT@&{fBtM6#x={3s?}=$K8#}yX(C^m$chszDJ-ZUNda(U>2ODGkpkFj&F$+3CGq>-kJ%78M0OKjt?muW6*L*m|}TM|yvEAt^0<|2J8c{~DCvKZvQW zuzqc%=jX!*Os^lT;4W(FgD3P&T=wmgEPU_;zE*lY`$nXTx&aUC**E^6w7aj-vs$y4 zucxwFUzPFaUyA zPc2oD-rg-8(Zmv{o0nP4#MpY}=Af_5za$l}Y8vMLZngohD<;<%NF2GZK_`i|Y*=WF zuWwoAgUBjns(Xa5(5n5=fhR@r^O$bIa{l*@+(8(O?8d@Z%Olt2^?GR$QVGG5D}X53!$!$ye>bhbS}jm! zNrF_JkfSIJE7+UOuQ~?p znf?jdi?3hbBRAiD&MuX1*Bx($w3I_%YtFi!p9d0=Z97R!^xw)zn=XU5Z8D{PMNdH#5g*ee)!# zdz1{w@xChKh@CYV4ADsU?^i9O?&zD)>l>8g?4f`TJ-|wyT4Z=Cuj&lEaeM54z(>Eo7e}%qbvM}@w zY!OFH-z+*#jXBV9VwwYe^Vhc)eY1;BW6(DhG1@YHv$BSI?!|sNB)#qi1KR^^7szVBjz+8Z`UT^NjT1L@dKt6xVNFN19BN{f4A*=^rXt_bv;lyrCX1F2d+%g ztYo{m9DY5zFC{J2{_cx6XjN79QN14h11b!X`?5Tf+`dYxJinugoZk0T_07lJiU}37 z+S_ow@%8AQ_+@DKNjYUVs4$hSM_+(HIlU{i-C!;< zi)^DDzRiHHDC^OQD3hpwX{dl$)}w2|6)qx~Nkg^G_itAw2sDdn|Miqr3d`7f^xY%0 z72#6deEO>rccc=V>(MXn;<(jlq_QG>t4sVcZs%3y{|X7kdbIxwK~Q2ndV@TVuSXYV z$Je8=eptv%#u)pc3b%|ylCf!_;9_26@3a0#B*Q#QjYq6d_mk%tPC|%a1KBzw;T5sBv-*y zT`2lNSbT#d_4?@q1fzd&ZW@+ya;XzK%~ z>{aDohVsXq@&5iT5zG*Z_>zneV?s*yO zt1|vv9Heiq+={!%{|PCj{}Uu7681oq@XC3a5=#H4rB;3Oggg(gZ{q9GhfCkY*P~hF z^6aDM{}f-3j@|!hry4qjtT6P=oeM4c=6X7fLEj9)Xv_Vd^sqZ?xg3(};>hW(iJ=@r z6wH}Rmoa_g-iAED{|RbGOmi-~_;BbO!+!ZHrVtcA@)`Ux`{iE&rDraDp4l%S!k?Vp zR$9*-MDF-d+5|`b_ux3o{cO+2k={>3-@m}@DE&uOk-wmr{=0BF!!tvC=VDKGMIJg5 z?zO$)7A4h7SIx%c98072oS#7z;yN1(`E^`kE{1}9*W@oo&A>HG0CzQAowotsJeBmnUPTe`^Wm$CDRNQ9l zDCTZ^p>(%>3)!VD1Fpby%I7*6Un#GUUeMj`a4i%sA*%v?iz)PaU zk4dBA&a51Zzb<a@{Hh^cu~3deQy0)M-bU$|TX2p!&0kw^ zZ$a|h@0E@TTo9`%t!&@#V`DG&cQoDu)kEL!tB(U;-ZrIomXBlen@YIs^mf;JXB2Xb zK8kg-sYU1>8=y$I-nk7&dVhxxXXs5UzEhPj6qVqgfXl`{PUxLa5Pbu~L-ikhrr!CT zW-4s)%TOWGm5J@_Fh1S)7LrDt*+9`eGD9nxOZ#a>)0i|o$^>I-x+>lARJz|W{7XeM z^fTNAMN>XgDH@WONO(VP=hdbHI{)VgvsDqjP3&m&g`Yy2R;8ab9z7bpC z#29s%F1g#ov;x$Q*aKjwogIV5t2$F53qM!Ug!7hQfbicf)y^UsxJm8khX>xKjDHG8 zd}BX^+i?kxR>(FGP(E$-&(op?xc=cgJcdA4JRTj5ZL<7P^bZwXk8cj0yA13L;2$Nb zimdDFV$75Ep+2e!ivhdq-{tQ%>xAk~aC5qS5;36v}5o zQVX)GpnYM4x@(>>59&ADqN94!X_$Es(^10ib{9rvrlZ>F;rabfyd|o(CN+Q z8HQr2hjW%iOyUx7oSxq<4bMXTHS~?ehE8Wraf!bVjmYfdOJmUHDd6r5j2zD9t0>^^ zDo1Wx8lel4j?=H1gL?6A!%gJXLHpcXd7HA55Ct|+c^+;qS8zij^jNcRQCk?D3US4q zx{8#T+oP123sVTSJrO;QCP7&#X-Ibfoes_>Zo3v`?CM#8$wA;EyiP8qB0V#s9x_C#-bs{W`-l-<`0|wRe-tQxs_FM)Ky%EH|yg zeYlDK>;T=NO5L87T|JBNu+Dj#9J$A1$mmSb^9xWl86EHq%&d#Ml2ljY4_!TLI1Z}u z(NoTq@TDu^Q#Ym@C^H05lk)CI! z1AELxA~R!ROkYR7pGHI~MJeA;p=ePnSp#_{&SROFi;J>GkR!&z)U)}x;_`f)LIX-h!L=!Uou0*RPoMT~&wwCp&hbO^#tSsZvI_CqxCf=j5yr!d zPA@pBpvpVQ-E^H!xB4?PPr z@?8z{yVtz3M^8qFBX>;1Fxqyfqv^mo07OUA`zCpIr+fByXpyxI17hZqr2hDYS}f;V z^*I|8uf%9xH6R@m!MMaf%dN zO}xw3`6@nS(*CMbAwmdrEiqz2a$8-h)~xD^RMq`CRaf2XIA)cW+JaYF8!Eksa&<`& z9);m`72X2lCB5V^NCYprlfR^RjjFm-$iV5Tv1ba-W_GZznP9!OB|z~p~Ax8(Kp?bn?lhCnYo+VKM$-C zysxXyua25YFMnRE+^>#WAYcD9B%$ed-XwZZLDC#Z2eO*KoHdBkRx4QrAP-Yko8H$A zVl0kXgJ{;A%c>znf&BeEM9;JB^QJA=Jho&_%H9mPrSa?qcu1~WU~&mm-3>hDmnnLL z`rIUi$NC!e?@rw{xGEE`|1%g2*pX}wh*wI+I2KFv5gh1vrPlf!!rnjPGxh$4tQ72O zeVr@&tkA4NC)WUfPeeJcl^L$TB!oWDTmP^=nx^69U&U z14<{9I&cNcFc#%(n*<&sYFq{HVo$n|HwYQO>o-t#pt7iM8D&&|m`c?%eO<{?P}Fl1 z6pWRy0S;KWO*Mb9DKC3_O<9gpSmxQasaysLA=%JQvQsUu_D{joRP?1=WnT)&>K=_h zjzK+_#~YRmx!77gk-q=QM1MEDbVP%^g;n)u2oM#xhUHcVc3~|%=vlVy`*U7iG-A=d zbLnyRjjCjkY*R-2ZfjHTQ{}Mn77lp(R^?@dn7mpQ!f4elofrEIn63JKYmJ{TtC@oN zF`3h+hSggyu&rg=mRy79Eg{f{Rd3cGs1F+8ejcK~gJ)K6xOO~@3re=*GwC@ov}1Q@ zeFknufwHbgWGPi1)-G>ZCHkDvRuko0gI|^7D=!?nKV&r*Cgdq;8%OSs_*e3P|3)72 z^wCvpG;X_yjmG0>P{x1-gW&IPTSw!rIG3}%BpkER_=QG%G&XjHHGNI-A?*q~iB7?X zk)xFMgI;KjoG?P%OO(YkF_6CqldNLQ$cPIU%3Wb2aRET_?J^=nQt)ENT!m*TP`hm* zVbXY%ndXT!d8SD;F_)vsjFb`V_komw_;C6}9MdfN`s?~&4sHHN{(Ujdr`v53^XW5j z;2XPK&8OE7W$fr*z-ss`?S=dJcd ziVUsoi<@cGFGzZ4sd`N>)S>?gx*TF({HQWY4of3|Z|%ym&8J^Qy6E=BTb|SN=_XzD zd^%CLw^LCY5IRKt>zl~155uXnFZP^+!T|WTAf<5rdtO&#FH{vz_$no&K^pDu)ro@P za_D@e??lDUs(dV!B*M>!#$r_0>2Nk1YY)%8rj;QMWxZHIrFV- zZwc&$HqOSi2D-72e_giOJ(xD3^}woj{)@+sgQxwt6%bv@sbPV zOTPFYe~Fs^J}<^jIscXC;pe}wc!FZujl(bFkYj%SyGWvN==>MbfvkS{h{@`-`ASv+ z$itM?rk8bt7>i@pAex=RWp#-2-^DU}f7ARIPCH_{W6Tul>s&>jDIA^o?*hT%hd%%1 z4MNTvj`sXlmcy7+Oyx58hs=MARD(Dy^Iu-xqdotfuL@zbYM0JS^j3Z3=D$2|hd%%1 zf|Bj{NX&l=MV~X;YNCAW;h+B&veEeFS!^^OM}sogoWD=7j>cVauI9fuW~1>7$MezH zod1qfl*s14bQ;_I7dhecU!p9YiGln@H2=j63R>f%nQH!v3pD@zkT{VPyqy2?EFGr# zZz=fkZ0w|<^^+#}W&HTWM~WXmzn$@8GIEbRw?v*r0iQrAoZiL?f~Jqxwfr)U^IL5G zRMd6rWM0>gg{+^f#xz#1pZtL{%z)qas+aUcE&3CXEb97673aq^CB{+;ro>C;mI5Es zsmS=8!yRn9LM|nZwtSMNlE8`d!bdeeR!r6SNb-f;Hxcs{B>zS;Vcs-;LG3ubO;yc( z@~p18emG|B^UpfG&eBjSxqMRjZ9D_`I0kPK_}G=dWC(xB6Z$28;z8z1(u|jslP`Hz zykwJDK56`W8K8jAf4vJ+&>eUSL-Fup&?l9lJsQ}k{WkZ73N7yiTHxV@o;)cx*M)h zW|Su>$e zud^zB1qZMxhgRqi_V&v@G~eEU8E)Ahel#o2E-z%wmj>?aiB|LSne6RYH2Dej%(77z zS1k65VEGy!rnXjIu+_O1RoZy3%yvTHT4s1|NlPe~v;1EYrj=+3s3jEkuo6`V2?6?a zg(LSHT181j{YO|qv3w8ChjL;C3+%tx@fiC(3?cDPH{{W$zlnZm;_dQc0seCmX?gO)0XXoi8CS@jgrqDL>5 z-3)788*3XQEQ&9D)z^oMgXUlq~cN)z+ z!IBx999Ww9WNS0;EC;YJWmPxA721EM-X3-l#PaPFCiI2yUy^O1H>;K2vg6Rz+I!gdV78^? zyRs{PK+U#=0a{J@7O2H&@@--+o{7_t2^@hYN8pvnXWl!KCfniy_+s2tqDg`ZL6aXH z$Ffvbnv5JDVdRbxXpo)T?)(JJ5uIVZXSZiRd2{zq@6ci!CM?>R8?9hA@7>ganyWeR)f@)_(TVe@r;5gs!lqh zPW(M^IRlGBTagV4D^|6uXGuo>11a4DH8S92jU07%GGA;^;pN!vBmHk&sVj%nC&y~x z6_X90nnK}-U6DL#$Pb%4)ZC6_bUX9ImbcfWt)I+qJDc6u-;>;N1mi#=Xy3usdZlC| zBqK_?7vW*O-Bj-o8jE$xwJ`ohuf-QGn&WQI#3D!4s@3kx;$x3GLVhF=^gsxZ!V7u zf5X26T_td*9>+XIT=^L(y_lm6f6%ol-7`}E7=8!2pUWMu?Xlx^uS{Tg!~m8CAz!=T z3bPkC2iyt2ksmh}5f?q(nrGwJtK9#V-)$# zboMNAdKO82>2%Ec%rR?ex@Yy;1G#InYdA{^a|-u4bM}=V^nJiJv|@Te_ez1ZJw<6* zbXYOaZckA|c9;}swx_5jJ4_5T*i%%Q9pYoJdx{d-VTC}IJw+9=5|OMt-r3o+(&>N!P~oh9>g4pbiG3tV<_LE=iZ-g1-5XJ0g_LRJ!T66iPy&vNDz zRtU77gcsl_A#lbdycI`@fm0?SD>zCD)SiU=;izJua&FxhokX+~HtDT-ASL*yh zeP~#}j~sxO;vJA~A%3ViGNer>kDxR!s*+u!y}kqU3bSiD=E4BOo|Hk(B7yo$TRg;O z0eC#Wcs%=YI_53_Fcmuba0i@pR{}a6Ql+~S_PR}HeKa2Ql;gpkqVm~p8cEoQZ~+@$(7`JN z&SrxS4if@R*-(VT#6W#EK;bYcP=^g$IHX}=t=+KTcsQTBJo-q4p~15%WMF^`n(Y1% zkfpyx?)DFoj1$4&Y#=!YqS8+a;DgB+WN07>yvqi0A6PO{e@El7yxZ*SU-%~@bx3!2 zLmZKZsx7_ire^ZrX$KbtW!5 zz5O*`Z$qXzbvkh5pRV{i-QeqmoUdEFu4-W;%I+T$hOb?UuU*c0Fj1mASN{cmK(X>Q z+pNzmuC?Li6boN(%+h@QQX|dR!ypAAxMx*KZ+jS-UjPP{e7#~U?gC$5b%x^WS~_7d zZs%2W0wsKuV0xABgadsM`^YuhQzv5y&HCB?c`_=IS zH!}AkP0K#P+>4`>JKsXsHw6*4%R<=X7((MQ-XRE^e<)ZIw&Wqpcx>>skqv0wUgF)^ zs}*6>1dLC{vPK%bZf!5*`*sY?$Bgz;cHXu$m2AP=;hT!m&KMME9%cadK+?_|Byewz z4BSlMN(*oBzaN#j>4Vc_;=GMMriKZ}!{jCzmboZa6DHN6ynT`As=#9bUPk|mJPh1V zLjDcR%ZR(PAa2oK7O0^mFqvW4dP&^2{a^5y@ouy4f9{{4iCcGnNA7T3md|7D;R1B~ zV|f0h+ryxtVcUXi1}1-Zv~LCxx;VvW@$UgJL;4)Rh2&Rj$w;7Y0oR*+C9twaQ`3+fN!KzdjMzK?FE4U`jPkv2EY%2zmwkK{QcKt@b@hf6o0?%X8ipFE<3&Xn!o!Y&m1nF zxHlSqzc*f0LT^+;srdVz7t6%o6Ob$x{_cI7=I{C^Y5qO{AqctEQzi8e3F7aJ(YOoz z{W(&Kod8ejgxydvyo$zCLc!nT2Zr!>9$kv_cbvatvv0@wTk799fAjT_IDhAYM346R z#@}P_*1Q$x?>K*(^Ra(5f43Q)#_KeHzf?o>_b>=S$n9BG z(%S|g^QM2viU)8P*}ol%zia7)#kieU(Fv4L_?Mi}PxH6%FG-?HasQH1@%N8M)4#;` z@9-~~RSN%-S$~Uv$*iO1U-F-BAEoDQ+olo5zeG0;WebCkS&+Hg-oGS*e3i-T$oQ8W z1$k@UrkDLY{Yz#YcK?!Dk^D={_F(uQ(6)~B!|Pvi49Ncw{7bahBXsGg{v{A~kbg-S z`KrFI{7cs6DxRuyi-FfwueIRyAAQ2&wdr3{7j{pE*P9+ukG<+9Nn0P>NUZv4 zJY<@~Bmz>uCPlhJVQiNEVBId)O74uRA4ZzCI~O zW%*oH(kfI^)4$}7dvF){`ZuH$_U&m9p8)f_a67M}IX57ouy4<~M)S4sFL{G5#qHb2 zIA3eSRTTg2u=Z^UQDW6EqJIg8C}VLq^1||A@-H#R|Ie)*1Uti*Vioe~B8Gnz$Wv4fw`8!FKrWJA@nZLUZQI&82$x6lFC*EHs{_com zvGDhSi#31G-Mf!un|& TOk~A-CBmAIIqYtAhA@M?c&J{?6LR>WdP#(Fu3qcAl_3 zB^3PKzFP=?H=;{%{*LqaA^MlZ`8&LS_fgQlasCeV?=}^1@Ptb#;W&TGP1Z5&r;PJ= znf$wtLjV7}=VSkB{$6vZ0*Lp#cZb67s@6GSd+=g$FRX2A8q7SjWF`&Y@kmPRH!e(wi6JXKChV6vw10 zLJ8#Tt2tW|GlAQ- z7rh%-?yic^RPGU5EQ6xm=%%8~W1%i5chUB%^<3_VPlN*sX%b1~27$ka+eN&iBt1yX zP=a^lQcxUYgXQc77#f^jJs>PaUtMN9Ak5bT0(zVr5YP?ffPlUU@?`@8I;i4Mf{nKod4P9Fc$~OP%-zFrs>>fF`zrrwx|ra%Uas zB-)8hvNr>o%qz9S$)4ctm7=Z4fF|>#!^uXuy;8?7G8;pruK$JqMX|dQz5fNH5sakE z6wl;5T%_)A)elF#IsDBnfV-U7N?!`z?DS?x6D{}%UHVDt(n%tu2^J_qLYhF>+~s5^ z-%-iBM^t&Me!s;(lW(uQ;wFVnW1Yb7o+7@3? zjD=y8+rP;|il$HY?+YZ3wZW0oTLUzUzaYTLwoZ({i~HbS*uVQK{=Va*qruxa{ZigRC|3S9{JYOWvZ(x>?nyq-O7r*Jt(w0lKnOx^vr$0E36T=>&qwYT8u3kb zbi-X_|NchtcN?8>7jEYX+fzahk8jfcT+QFt^7tl==o0aFM)de5i1ipl{6PMoe@Ohm zwt~OY9l0%qYDN6OZ;Vm(JsijR8-rWt<`(`A8z-!_udpy?WP-gvp9NRRUd0I< zt%V}sXabi!ALnn?UP|wRV%t;(nfz_rR3d2WJ4ssyhd42LHM0DbR$lLaMbJ2gjQx;A?s+;HrI&0pL|yS^)e& zi|_z$1~{n=%P0fz4cDmWUU9bMv8indXIm-&zXj(ofcNW;dkd21UZns$Fr`!gUIzaY z*uROt&&U+^Z~Xf5dMN&0p2+Rrxa{FdDoWIHc#A44p=I!5c{$>{NIDd`$*uT%!0ShWzsdjP&ECTPjbC3<7scOQ_E)3N=RAVTPVZfszboppAn%d=PrA8O z75sr@rSm^&3TL2L`P=Y6X@+D``5XQx2TsuZJ$ISr?+K8B5Zi1NkYjZI8OXiqf3o9T z+(q{9<%+-C=!Cm)J5Si25=#H?hMK>{{>w&mDeiw#CjKVBzN6uPqPNujqy0~2m%{&K z_R;r0nfpZr(bOX>YjY@13L{}bI*BG}68{ZAr^T$#Z(_z#=^iH(4dy#EQk^xx@! zGW)RmpUjTne_}R8SoE5OyU0lQ8mD{a*Q>ti3?- zR-Fb0epjt$!S6qg50Bqw0F*i~jB@|)3)OS4{GY_JX{Qj)4pjKv59cs{KhpvC!vFhx zh2J~ImkPhj68}Wnzb_N^Z~Xf5Iw}5MzLW8HC0urTAJ_an3R&iCIrU_)Wkmmz;`3A; z+>bgao&Ec5cmu`C--iFm2S^r`zu|u}th(m!PIEPXpVUd^`CL`hDpXX9{rgPZMfPu` zl=g2(Pujn6JFlZTCn2HmKbcck^SAInd4n#+?cZ_!K6L*R3sgn%KjBbixPH(#ERU@J zi8&sd{@+F>jg2H`JVvGEFn;&|A{Go(;tE2pBx$glmE8-55fLzh&@7=j_Q8` zNk{NM2`6vW)s_FrT^8g$ zGJkh#qpIK!Br6?%H-$4$8Th*+lEuQ`2g+;yp8J93?+K8Bw11<39Ha9cLHxa=A?_mk z_jJYIZFIt2xSc0#PYI>}ca;$SZbX;j{2k}-IDcD}@DcF;w$aCN{!UMkQ?)pMV*__7 zwqwTm+t&Xi&fiC82kw7|zt=QTz*W1l!QWNNS@`=v35yYJy8+YmKdBANDEI$vuAX~E zMag4RlL%+0DgM3%=P-aTIt};2|N9KZ-vR74D8&aZ%f{a<{z-db|HiK`ud(9q~<4*AGCp z`a*dANZ9V`S(1_e01R@eD~Hr?yVmVl=-T@WKAxU^oZGX6A7)jA_1^E{SP4jVUo?zc2_H)^ahdZ8*`T`J!Il!`toPeD)aO~ze-?h>RFM(>XyF+uA? zaXYV=1qn!a7VWTHB~MU&mZ(p*mRlU$URPMxG2IT$?=bQwT(CJxA;ZEjnO>laavVbR5N(yuK zB_76jCx~={kS9+1Uy%rPGQYC3WPZ;6hJ$tlTo)G9U5SiPsFU+1Rme&#DGHoDDKRT4 zr>H`p`J~EpkPtX+QcXHY44gQrAsr+IYEDX{gNlJlxrIX$a*8UYH!nhX6L;!@LD!O< z*>>MLMJ-69?Um_X)EjG7GHyX_Y?NXl4M^g&7d_B`edqZa?0wAesEc*j-!h$!yNVor z@CkX?meSbXO1v_SZ>@ClT~07Txl^||9?p)qwetCpt(DIkTPw}|bsodL1f3u~Xo)$E zG9X@^{aIjwh;H(hY7|)yQ?+zg`{ymqqlao9>hJ&E_Hkp__Mw}G8QGv7ZyK!njHb|( zwT}*LQza_a_{$0GR%=TUb}*m)$8{QLcQo#x{nu<5I30h0;4u=6ae7t%U+SFy4f_8woBn?o?AfgUFAC}Zg}VPQIz0V< zIU<*!|2rNYMSUBMJ;E3e^lAA1h+NW1OArFD6l3>(gErBf@~8MJd~+I(^=PqBw}HSE z_HpnM9=8PYrFXaz0@NOI{N>S`)eFs@yurUx2P=8X5bHEtX3b2F=S;p0jsf`&dN8bP z<#t%J_aZKdrFTl%hV78kNupiEw#e&(QU_MD3x-^$Gr2a#{cg`<4A@d0rhI);zN+Vb zkxC1qTnrjssyKoLRo?j3579_uVvSQ;^LrTTQEAU_0_?Y%bR&2rf_~4n~<9??o zKA!w3c$FaxDw(Ubls*xI(BGqtSMuiK#E1Cl5<&GvVi@%&0q*GZ-39Fw6 zlYXWMfK2gj#Q}ghb4(<8E491PKNa^AFk?Vi4@ryYBPLnp;|}oIF~rNG1uxsmnC8rV zC0LPKq8T%Z+35SkIx%4-Q)W|3opmxojLvX!kAtJ!-y2K8Gd+prz-0}|+^A;2so8UoB8 zFtIj~z#NvB^(c_x`5}9&e-8)d+}etc7X4xX^T&P*FuzBqVSw2*CNF};kpc5#WO_vN z1~4c6EaBU8#=u(`Q;m;B0<&>pR zoLDL-B>jnI*M#dz*q75R_GQwRab7-V=2OhRY_D%s(=at|oR?WV{j!G7KKl0MI4}Q~ z*AE8$w|M!gN{Wu2{=wko$F^8_`9V63ftR0#*^%+`wPb(fyu9vvNnLGmlGbTR zUY4gJ`3=;)C|?mu#UXjzx5)C{!l+FC7khu@ko?dn1g!^$jPk0d}Jck>VFZxPD zawTq5ZcUd6lJ_3EQJMQ+@CW@9jLI2~+`q7LDi&Sq38OMTC9p~v4tY>h%L4o5%AhRg zyLR?ua$r5O{ug}f*pcn5!PO9{Vz^L8hlVfI$C5pek%Oc7B;%f0A8LIYi zxjJ(H!~t`)$i?&x^EhKQo@9%GE}gYNlCLr#Z}2ZdGST_{kf4zAvr5X2+?Q}i=9pOQ z$D#fGaQj>6{(c1gEgnVng_SY)xw7%MxKInxgFqdkzsJ&*Um-cRmOdA$!h$S@2H|rd zhlarW%-ra}-Ly=g*XM)fO=6}2C$ulG^RLl97zKu)XOc4qnUNdgBKkI);i~>@<6rY1 zk@wI@@(#8s_lNPLqrIpjbXDe7(^c4%uhq!|tN8+f67PphyseovnZ#?m{zt7#$yfE| z&Hnw|q}=9DP!IY$Y^8%X8Hjw_XBI^6O{ZZH*$mJz2$n{M$Q{V!$e;Vc28m8Japd%# z-B2R(!u1T1^Y-B0g5>%Ks+ViK3~3trF6~h&L@v8MdDL;7kB8&ex9z}Qawlks&wV$W z%=q|LTy}b|K!!zGen5_qLwq!)CXW356d(U?@bM7N$1V4(D%gaA` zuTZ&`PFRfFc^#cV2~#;LpRh)way5?1Npy)&`OiZ~W$u5$AM{T^W%yrIfU*|Lt^X6K zjAglX#vu9%jzjz}uE50@?1iD@>0#LKW${GiUwHHRPCle%DR$zZB+-<|l zVi8V5(r)7A-F#1cA;4U~!AbzLg{ROH(r653ImAFL-j5=vW!u#pn9(?% zP>n+YGk3ah@kSy1G5)*rC&tUR?ZMvPB1%(`yd0(}L{P%RiL>@|v0~PXgUGy`rsS)1 z><0g0BoiaRj}V}6xEQa5?8to?cN|l^>=FEAE90>K78el(aUpVEX0;ttSDt7A=8giG zMS~D77t}pzY!Gf23YaC&+VHZO0kbcEu7DZc`7uM%1kCbz44muZj~twB{n10m_kZR5 zNb<6+yn}g}77H0Khl)BvSACx(+|A@=AzjVOT)Lc>+r-4nT)wI=Z}uPHyj=bl1xOdJ zHh8(iQVTCPqth_FY`S1v_?zJ6DmW0}^>DG|r>!fAm#I4@H>isXM01(KwFS&W)-UdE^H_2;Y$_2;Y$_2;Y$_2;Y$ z_2;Y$kNW&6&dZo8|CjkG&dZoE#Ce&qLX&?XFR$6AIH&d^gO{s(V&UZjAIHYawf%yZ zH;@SudJlH%D;7w8np#A>oUM5I7Mz2Zdu+wMh=1{&;^jc6qrl57{>AS#%VnRR-=aQ# zeR8Vd3hqZ0M2LU!`F0J*W$?e4iR4is znc`ne*O2VKMMH97`Z>lGWXYFjEcKE(JJKXN`k=m#J7 z+o1S(@@U4#+i}_HouT3DX%Jr|&sC)o&5TdK6it3+<+*{&bEWuqA{{ksx{0p718)}Bv(RfNI<6lhCs4U}O z&?Q3Uc>D_%E{-p?$Kzj61B_%(j_>D-!9VA~5%*``ms^OB(;ZoV_V|9jaQ5X;`5x}| zc`e@~=)WA_&lhI>;6EYX|JZ)M+*OK>7QJr(^T%&mfcZT-4Fk-k^Ti@q92qb_My5v& z%t`M_c)IyrV$WF$nCsyjz}$X0?nV5Il?s?&YEoKYF1x?-7h540 z&&Fk^cb(?tbC7Ayf_>kK#>>N3s9I=_S|}Ya-?OGnynH*7#lp+YUevt2tE=Ya=Brxx1IhdsN7~oaW_be1Bgfx#tl1&BJYekHn=vNKkpu*MUohVrlMhZt!z^id@z&lB5j^9EmTI zgeM}tJ01}+`iDe}tEYE8%iZzxEjJzyBkPdkVcZdMd*95E?R}5)ePAjchRfU#MBS;s z%w-{MwPs&NF(YEE3cMute!ZhwN7g7>rtZ$-v0B>4wYVk3zWm>9ADL7Q_Pc{r(~uq+ zgi@!lYO`-2Em*vitI#ilZVH%TmgW9=9@)Z`5c*vbY9IdK?~)k%>n-wU8|C%($AiQDg_5Wi zXKd|V6*3~Q{l0y500iv!McC!&pCDrKGHS|r869GYm+=#Ywy?y@(BCSN{TyG?vgzkv z`lpL%8QA=1Zu5K=msyiE;xC_iOvroCL*e_k5VP64Q-eCLs_*cfbqaOXmSX!~pP=*s zGX%5j_yHbwcu+S@<)%?3^^rcyj51q;!1N58n=}zVpKvAUe&H zJad$W=%MFoi0(04Wpt1#Upkeqz<}tt-oRae=vM6&MDKviM8&*~+j+(OIu;49;t>67 zfrjWt9HO_cmNy!QxZDSlQF6iBW2;xEtVA2ujf06kf?3=Yt) zkoi@dpZVz9L^lmn!!mw$DSo!J4=X<#H*tPOgUFz-D2iwayTzXTF#H_ynGzkJ$|8Sy z9%}3?1DGlqvBXdJ-TOMq6-s7Bz(eehW0=X|CE!_1jzPTk9npPiu+uf*bK zMC0&=%oS(z-v(a(o=wB&(XJ!1{%H(GwJ?H13jxYGRau|MA@>Tu#Yd?bGmLRO!g7*{v zB^DU`{70^ZpLft{7=AV#I1;A`e*TmUmYko{Jrb;5eUxBy_^Fbgd*U4YJZ}>2El8gF zj^gLd6^|M}AO8vG=NkC+WxuQV`NKYppZ9)DS$+{&7Nr@2JabH(m>-Rw=T26YFaVVh zfuBF0a!C9<6Un0*p~27D_iKLcaHi(xnWubAnhH9t>(IE0@k(xo^*8wTh&KeI-}Je=eF9Oq{MNNMfQhK7_HmgZM+elGI{ zvZH5zj`K5*rxEApe;+^Rys7|b;Yfp@KN@P`=XdEe27X=$6D6}hd&pMF`8jcf1go1K zA{c#H@$>OG2R}diBJM3n9yn3)^NW8RC~@YjEWxU!%kx6YYMP*qTsQ(h&KsQ9JFjzI zd&BkDEuf8p;y;&Xp)0@F2^smWhWXuV=DRBQ=y|pCDrXO8Pv_OHkvqm9W!s&OrUU1= zJ?kA!@0;Y=?b+X<#l~r1=d>kB#rTB*0RGRfFn(@Z>=NuG90ws(>1Tek=q<*PrknA zNq`K}QJM^VYqwRg!N)mT77wfF$bAFnj#m4JTu4^V{^=;idR#%>u)c%D z3{KN{Oaf{GB;JP3lC`M#yooqpjHtpR{4e9^SI3#l9CPR6Q>BHDK822!ONSkoU1c;r z!{;A`zsB->l*)L1Hu8NYaz7ex2smR%-*%bKTy`bO*h7ITY$3i)67?T)a+rmyT@!wA zC+r-JMgrlwN*41vcc<=kRf%uW^JsT5#es|}wS zl8Z`}w78O#%JX<2lM3%;OezouuQ4W$<8cRDY4?98#c>%fK^!9fw5l&55BBt$BoG{p z2vj7AjIN|*BBZg@pMb}R9z>z8nPAS>g7L$;Nb?c&oa0!(nGz%iYVah7w6Jx?or*l4 zdV_n-1QG?>#t8p&w!)CTW(58k{pl!&*=N#F;sn-XE{K{oe19p=jH@y9muwV{3uvtr z(EGTg`U~nWp;Y~(*nb<+GjW`b12LfW#v!w}DtWwsVw?Sf+BM0eL)cPhC{f6^=*Y`M zD(V$9K!xiSgCahWSHFB>_PMP3D+Ss|N))a?P#=xd9|l+DRlOHgEwxYJLJU$_=d&J> zPKDt09b-RO&3c}@`Ioe0M%pl|K7U0OZY6zjywc}e?giyt+jgg)a_L!>KOoV!>TV0` z71F86dWqWCpggp1{(zc+%KEVmV*u zXBF=~dNJ|dQvAi~O-HUpfi6L|Q2>0Nf;fdyVsO7kF< z=!8vG!oigA7tVWw@6^0k#CfkTT_WBiP&*8~H(l`F_~)vZb6gEx`$wXlODn1=a%7}H zSz=^tL#P*lA7-Jsn&ae~)I~5(#==t}n(GxLCf(wv12`Fi_h#Ag-Xr*P*a491MK}wj zvu`sQ$kv%2d1$~_Qg+~*FBO;oKBjMi`66*@NuUPfy|W4fNtEAkz_%zO@P*~Q*HCq) zyvE|3o&$B8nzC zaL74Wwv!%McHoneDFg7WkpdFHC;3e9Uab9OWdz_$)AD!`m1Fjc0-TdPB7#;(uOQAt zuNaLV8$s}1F*vLYh#-0O%YK&Ko>zYn{zIWh7&U?G?3sqar*P}r3-h}gr_txVtPVX(6_pU~+IsS~i9Tan# z;=Sn>-fPWy??sQQh%TrI|967-=xwHb50}Zlw;x8jqA{HJ$mV7XZ>D|EFxDYyD%F_kP?3-kXP% zV!iwUov;Jyhu2gNCEQJLx5}4uv*x`8bg9@kkS-DL5vY}k_x7PkWZ&aoFP?$N?1p`B z4$P1EJ%18@sXO!!Z{MQ_X!{;j{o%Cl(epy>dt}tRm_uJW4rz2{)8U>t5YX3xppT8W zE>8lcb@%`L^PR(rY7MuMqY1D(>Ot zidS&7TO0X)b^K6>SA?4RqP-zVg{9+}C;~HDFf5IdKD^oD%&=y@{#G;JYp}b~M7OX1 zBdNtU;1XuhMuj3zs5NfmtAQh7s&3Zhj2~up7D4kFG6yQyq?Ad zCNE~>v#kvRG$|^nj2wBS6v>=H)MqCs!^{`qsX;0(RftDWU-*RK%o@+&fy%7WU)bU- zX1=<(gNx%UDUL3m2}%R%JY-Y6c_TcnY%uYaW!58pkwY7EAm1XNOKI>`zRd}nB3a0?%*s-}>~L=@T5 zE1tjwCJ$!fvn{ioiSH34qdqYjzjlJ)eS#VJ!akE%zwBq(=d$V-%oi?ExZXhdh}0Xn zk*}|Rw_rX}L4ga%$k!45LDhGxy`Y$BIAP(VXK@Q(Uw`d1jsEA&A;7n@j|KR) z(5VT0#%?0h!nfxU0eqj}K=8sjH%YZW?nYv#Ot2Ga9#4`B=K#K1BXBR`zdo#hZ`Ec7 zd>I8Vq)bH4J&W9)KHZ&;CWC**Klg8Sd%g=c@2w~s?^VSX7+TLghz&5*m3{qk6z@$w zi+In6zc{_Q$g?QUt;jT|m?!&!Vp0_E&8%$N_vq^YFnPW=Qq@Cm)PsK;ZpwgtF9Pqa z#g-ph&n*M*U4o>g4a|753i}Ql<+r#yH&oi*J$2bM3;(vBj^(G-b|by2Hu;6$DqHoTMhGLrZDjR16eOa zYpEG%d`wIA(F0>!Vm-A!XvYs9qQzQ9%C~WWjg+y_R0!|AhSbblK?m@lRz|)M-g^W$ zMA2OgQ~201xNqmpXzI1_>i9_QZj#`E@DEy!MKqV`3fl@zTZs~2${lT z>?NQ2`{Q|RWJ|$;n8jX$L(O}ypjs@wf?74}70kqED-Ao|BfdhPcoaXjVla$+VV@b) zBii%IFD3BFI?tzqk?#q&HH3IMNZ|X$1imx^zSJP#i>()|3`czBIq@wUrgVDM-XX;I z$K@8{+d-!>5MSbOL42P+sGgqQODg_XuO@mLrHHR5&Ov*vzE*L4405^?sa;MAm{D&ME60{_+nB{1!KSKo@eztTMxxN`nd{KoBjOe}HxUT)mJm%{d8 z_X=LZire?1(-idcPZNYX;~?0Z$%mHd$fmwc&|Qw8SmxY zt$1&Fed4{!_>0rKMDyMw$TX*zX&IoHDE7UTcdB|AhI)v_zIR|ynR)M4BrP59Wp~oN z*WoA4d&zgJe4nKXoJa*W?R&Rghr7UgKOv>u{|qS#-n$LA^O~A|DH00%-t^9z_k?|K zB3(KH_Pw8BevI4qVqVWBD@WYE7o+7OZr>Bklg&Jg_->lGeJ_$#d?c}AX;*aP_C4Bq z=0nw)UMX?=o{g0|ntjiosZ?{bi$jR-?^$G-Q_TDeK{3I@%i)*odwrcBl^an~;`Ek0ghhsb++N*$tR{$^hN~eDwhA%?2VxcE z1IzlZ+HWBD>&`_L(6Wf!ulqjE$(7GdD#muprLn&PZ?TvyXfp_}tju(T%uMss5*XX} zHqYF~Q7yY_CTcy+KatYM@E*QiBF!V&SKyJ@#*v9uk{0L|!vicnN_Dv)RX&lCKr-9K z`w~`rO%z5TA%^8A+8e${%tdcKq=$GV7+H?c#0KsBCWE8UHcai@hJVbOvwUg>PEx4xm z^BA?ibPnpwl+z@flbpg=N7$ud+Df8CMZJ#NK&Y@(ucQ6JVJls|1-Ui)c6K*BFoE|t zgmj2FGVOqF2!ltB4Qh5>kE@owN?V))tDX3Oxy`&eMb+pkrm@J?z7e!kGwf}aI+YT~D{ zWw?T|W%!L71b*Iu1HmENI!F$=2uDuuj|xBcwrBX+uovzvNWQ$c!q4F|8Ge>x%WxF_ zoCra54gpX^C!d4e5!ucqN$=tp4Yi!FP9MfnEdh_cs8f(jZ7oxn|nvxNxI}(1{R;kl`mNC}+th&P5sC2H{Dk z!rd<11qdCsNkQnjI^l1)ohR%>2?rt}Ahc5(4WUlDRP1X`mk2`J;G7^dT||j-vnVlc zM2VTtqr||WUIr~sV}DEI5oE@tklQSaBE!PO(1Qp=^I;X0UWLw&N=h)^Gys0?9iHX( zEK#9iS|d~p!jSWaxID{U&6m2QS0~$3w#cXHsV!oE~Vzz)cSiBflb3cz4 z!)~&~i$N@9?kZ~TCvo9RKYj5|3UHp`$Q_9Ri4I+k+~smWy&VT`&+o3hWscleK%z>& zN?sx7!mg=H6@N~~IgP6q(I95)K*Kc@+uutqpCm1p&ECa1wDcX&f%F`{1#h-3dP*f2 z?G-_4uq!=Nu_vO%M$M!}7uMi{p({Mjl{ znX=CTRLV?D7Z&=@!GlBxoF+RU?R$`7Yly!q0Hvv!DJUX*5>zywUKQm;cRSi(B)T0v z#YS3+J;&kiVOv5t_H@$|6F7Wc95r>+4%dodXIVPjJX3yaQL{{b%Wxp&r+KGT{cNe9 zaFg}3_J+Xj#<3C@Rjny7s!9@A1gBmlD+!5?JW9p-6&=_}O7yEy_-ojjCbC6S!q6+9 zo1G>^R4EYNw-w2v^eE1qR>?%}QCkd&1TKO&vN~xI#N2hT?2Em6tlPSpuc25qq>>$Z zj{`YbEDBK7?#mTbv~2_K?CDvRPC;s#HMJ1u$#fc?I5Q|n%@ufD+a7{A4_u|5JGhmE zjx{Zb-Kw50x3J{l90t$DU2$(g^4FIs;;i-#4@fBYuwYD&xQQhO-VDLb4lTN1ZwA>r zJK+~ivYhXY?#kZzW65!_cQ(OaoZcqLupqCukYf%|Ygz!Hepj$L$^x78aU9gf&Tgs_ zCZH1hvlwiK-M?}Gz*zgLD6km{juej&D!cjul#fajhff2)~MO8yUO2s^kMZ{lODd{g!ncqr`|Mq?3X&nQ{7Xu|Mj&kYPfwTW93sKxB5 z;gf~VJC}HqZE+rtF_@WQhT;-g(TsROM*Yj}&fGLpo%UNW8z@R;?0yLX(0Tp_cqo%G zk&KXWOQFC==iiWQGfHwiMtTT;c+hJYy?AGUe1E z`AM57iO|j_I^TaIo+vu&JlR?A(t^AMf2QW5<|`P7#QxlmgQfV6lp-^c+E$eUNW6g! zuX18YO+pEh`Y72qsXo`4%Sf0>Z6y>8s^R*Z&ZuF(4%6S{E*#q7F6ZolOKBI*#nj_ue>0>L zb1(apz0zh2b{e;sw!cKN#?UiDET*^DxA1EpIyL##*n(|ZOb2%n{Mr!*f@G$rNRp|A zBd537>5^X;ozD2R{zbUAAh|(j#jhC?OSfPj1HanZOGjhV2-!;?z%LqYIp24kmA$md z@5hx(&u`HWe{yS-Pr+FHw5`Xoj$%_EJC> z*-HswFLOshSkqo=+CGu?GwiU5YA@vs%fANcNnTx7L+rRk8e#`R9HPhts>nB-hB6c* zvjnjNA+(~lxC;>5WwCh5IH~A_h0J!c8)U zVE>JH471|&BBmT;)Hh|9B9D-sTo9;Hd87LkX72>f{^uNe*zxGqg)m*f2;_tS6O4B}z)M(p1lNz(2+E!0kKApq`M++Bhl7R-c%n_~+ zN*ikAS0LYJ4|G!<2sULl)QG90_P(@8Hq?l{kK!Xp0P=r_4#I@F5eF0x-Qb_n^}5A= z6@tla^ty*}Y(KxCD!0h*St&neP_?ZdJ6dJI6=7`Ujmp^(+sJ^-nW+P*I10Iw;4v&7td4ZvE^jLsJgC> z1xgpwX?Q4AM%67B2q=962Lfs8pCrlTffEVvx|}Ydv^CCQRDGpA?kz}uw4;L3*-w|w zs4749g5MbU)W*L0BQ~~>eRT_d(O?n(J5||NAN`T+t4r}0r}sAGT@>pQWS#@pJtqLT zqS#jpI;i^Si28`dzWO1y^km4DTzDQ;iIq(D1;$L*`c ztu)^VTCNJ3D%$tDxP2Ahn`xsU<-}Sc_SLHR)D6C^M)=x>>|d25qOAIeO`UcEWjCI! zo_?i6BG9xNgo6VV%J#!KP0Q7eRX+DRS)-~9%8Yt?rj?dVI$aA2P3U5!o>G0I@$JrPvhp7UU@C_ZHNUs4Ge+ z?5por)DSD|tIyLVg4n}uU#$fLaNNEcX8sFEB8cG?4f|^LLB!zJP?|}nG1ym!Hxp3W z83%$)<|axqIR!^fZ-Ii+)d>tr?>Qa!79=-oq@eWb5l70t8fL%#qvvq@>TvurI^EVt z*;kvbBl~K9{Ke^gSHtOcWS#?8EgboKs>T29SZ>yvrEcMi|1DBfeQZQ6`0KI7fAu}> z?267$u<8ZAtA1ru&ZLwNd}P}eF-c9MkhSK{X z4k5wus;IYDK;HYIfVAk}7eZTE2X_HVd%UTjw6;#T3b*q*I*}5diG*16Kk-j$Tk9Ew z{)G_fnDkVnO9Z7maUR)owO8aXX?JH^q;}ykaPw~WXcN9ue>i^0vf3Bx{r|L2Y2S%q zN9+nNBAL^tw6Dj3aG}b?BEMmu`9FJa0v}b8wT&l)K!89;C5qq@FgnrTh$0F`Bp1SX z8#@vNC9cFhZwzkv;V{1AN2;z#=GzIX*_e)f5V$R9VnVtZ>s-E zB*yLRx%hFe)b{R*)a0g~ z`=S1gbhR*hD2^AEK+;~twJB{~)jB*8ADSSmH0I1PVzY#3taYSs;|XJC8B=OodldB! zXu67$Y8YqN{bb&^$a!B;(MY5bm-dwic-Ox<^0V|~XC*tJ@IV2mTestE!JivG0OYL7 zc8^`z2Dp`NCuXgk{WJ~TxGjrl8nJx1yg<=o_r3})*l_BVb?LTOJ$AjSY)nDU;sBJv zL%nnW_jOC1lI6>da@J^#+8?EN71czITI*F;jmQ!;YBmB;m%YVipK8yNFz@-1+LMid z-TooyY5yGUa0QFE-f%?4dLv6>;>sZ2_AzrJ_#6j4U& zh}@jj5F3qDmVdEWPOH{nW%<=3{m4JKN>qc`vH#bW{65u-C6Z6QE%gQA6>5R#L5Dh6 zpzwEJ-CMZ*zra?Ej?T6&&sf{;=u@5NaOBZJ_|5x-RT3~die-@F8(Pz z!-09>!H647-_zN_irHiKbxXZxHh!<>pX%C4^`XNWz>MBB-qoc>`ZM3Vp3JKrc0uDmx;Q*v>J+vY z3wsfYNYKdZ86RkaSOAR~(;U#a5el^m`w*flDSiDL5*~?!0F7V& zm^R?B{U-K-PS3%5*>EH?fJgfcm#4)m6^a|gWYNH0hW2&vY8RaM^ zf)X~Qai@tqIn?RgbkdB$j7<=T+r!ycVJOUKZ=z=pCwm~&nnDE}-F*_`t9;jLXm^19 zun%}qR-?5-0FW-vBVG%SB$qA1tt9SJP_pk?3JTG=aB*IEmJJtKy=<*L-zZ+By0|RO zR^J17rurJcs?mn}Hn3Cse7P!(cr%{fh!}n0N%-2J*5YoT?`JE_f{bz1B`M?dg0`OK zzzX(u&-}+;4RJ6f8oCStt`~I7KL#(_S9Wvpr@FG6XtB=P&-vkW5V7jlqfn4Q6E>c< zrlMZB$Sp)(#XMmt1}4_1)yIL0(~(rws>cwDryT-DOi_oc@V#mJD2AujmdXhS9>f?| zMK;=Ov_5qd#JDf@$-BKJ%muZgEw)~&KH2zXU+U4)@HMt}j`3*cVuaoEnJ7uGde|PZ z-t60Dj=V6NwT5Ujo?eI;eUY0QR0%Pt(uQ3VOLIsk2{^Nn8eC^%yaIQ2WnoG7wz6#Y zRF*p1;ccSoGW$krMnl`-S9w-$ezm{Tqi1ddX0*4%clkaBEvE9ZVYKM1+{`@CLW_}f zhwf!tZKN=F3(?|=BW$!d2G2l?r~42W=YOU-Xz`D$_tCTXpRLzC1$$iN{7?MC3KPtG z_jCM-A6L_#*a=^>&=26)f<5?;eUr?u|>Lz5}SmG9v2~!-i3`pag%txiZT1)D>k_R(f@B3ARf7&!*_qDz#UoO z5){)0h-0IvXw`83CsNwye?o^=fyX1dlGMD-NSK3!0EqLxa{*#1U*Y^uz9K;U-2#Z# z$xE7?uB55cm2$IqvQh(HMfUpK53jt6bXH>ish8eSt=H_0Uyhvq7vAZ34H_A1Tdf+K z!>L#VOSA^nV*m_Qd+XWH;_NqR`2KEjDmKzxcs8B57?H@(aeh3G2P`HM=ZCX<$aa5l z@Y`V1>0rbns|B@lC_Nnn;f{tHEw^G59LDX^Y`5YlKs$E;PKcn+&?8@azmVk=kptif#^F;$0RR$Z3pT1c3*z_`-wV&TL1KSbfp6fQ z_x|-F*rlVt=Rq`PpDi(>>^u>tVqEFO-!F6z5?byN`WNddF4nEs2;n+?-f5P`n!<=h zaWJB-sKub^DoWKuB|>{|kf@0FJ31=51!+V@r3lci7;k@&?K)9Agz$utTep12@hQF+ z&IB#1uJKlCL5XL%s%neR@zz&1eBXCdN1Me#j6^q19kZ_dj$g+r5wnRp=3o@3h7Ewe z{zrh(R+<8g4LR-Iv6rZBl~;B1M6_@*zKwv?u8AlK+hes`ecx);qA(8`5LF%Ns%iv+ z?gE49ARkkW2(+XcHF8#&qZ=FF`6})}GOH3%`1LB0$IBSM208k$X_lTM`q+2KXsaQ} zqM>b5b0-bfovj`PL{GM_$zLw(j@IQZ7&&l}p=P|Hx zEg!wG(X$KP>tx*A&Vr4z5U}vWvESPG!T5%-@y8=<*w_!xfQ?znh#O3Qu$2QF7hb$? zt0Y|)<6hv$xclvwxcmF9&BX2 zSp|J?nA#G zJ(#rxR<*(alin?`l|YmB{H6!*IC$h8SWwJPN}d5J4XOAX<8EB^pgo#D)ouG6pT)EC zj?nukJ1UjW5y4}suZZV#Y$^*B(33@}PNFf0vIzol5oOexsJ>MYWnCwUwFg$g$~qg+ zdESnLS&l(+agq>U#=|S(DrscBFf%IV9vbAsb57NZnzRu(x$5>`?%&=fDPXzt^4>p5K=`)6h=x zXFdMNp?d%a6x^A*(c_Oa*2VzK34ClASUUd5sfiY_JRbpz?uVb)VB`dZv`_~?Gvt_M zB!By{0L$-ofrnsv?(eAyJr}b_Gp`-~U-3u2fZZYVM?Q*Q*kpp4{Ks~JqGN#r2hE^A zvJzjk(6QjyDoksqFvo-Wru>olyB+E7Lc+cAN8Xsw1eo*!!&rZ06dP&cV#x;mk-si= zp=4-+3nlM`Dy#y(hGIe|K+okLp#)xk`qN{{Gnb7h__FUsa zNy{IZ!B>BQKXNwQrD$gQK8N3Cf28Qs-7jt*g5%=Hr%?eMU&-TaM4SDQSXJ5Vk1WC@ zoGf#U?}njsJ5CR1`&63!k?hg5=m%)wDw_R~&Hl)k^W^|!8>u`08~u^pesQ3|rEBW> zBZn=GfsF(C*f4B#c5PqyTWUh2HBeV30v7OiWVMYScCR9AJkEiQuizQ}$VFQbH<;dL zn*$q9%KvZsBa5*Ig#O42@GA&%-ZnvyyiX|Q3Gqh`#3wB@*oBbu!M*}UKduBYYQi7c z_9sUoZ$Tn^Y5k!snBj^3v0EYC!&RBos%o-O&mW*;iWC0Xo>GE2q$o*Jk zuRrpMO^5{$dCX`BM1JQM`6)!VWVHebEq~;Xi(C+C`6EB!tG~n_IUWvEXa9DyKN8;G zshOJq52@P5@MN<;5`Hd>KWuN;K6y@>{gI13x1`uk4|UCXvp>?olN_cgpZ|1(;(EQB z{gJMhzu6!8zuq4?FYJJZmWw_9$lcX3!14z^HViBse`L$e7O-6UgF`p3*2WZ1EF`cT z;sDEY@C;yi`?rW2OrQCk11vZ8|8xD34eYn>jLjkRM|Qxkpvh9x@khRWKmCzAunR;B zjda1}Sg@=B(&GyNkeb+U{Vq#}=vW_l6{OI}e(SFy3PX~Qecx|gfz(ZUB0bJaBmL^P z1VqYy>*I=D5ZTUjIL>m4x@$go_wKj8a2;X+M1F&m)};0m(4JM)5s-|M(eq4b?YDj& z3oPpFx4w_B2qHIQanql;-@0=ol^baKcEB$^kmCg#kD3w=U1z z06Ac|xC%d>;bNVO&g-6F(7fNedB3$f@1W!`ImE)=Z~g4&PM2=JSI_nM(VH;{aS0#2 z2(f|v)?a>UA;jkqumD51cWuBhaSrj;CGiq zgZ{@)@hgaNz-NyCu`($B#|8MLg{HWWF%M->aOk>s0Ee2`Z+!=gsvzUZ5KANbt!IAW zx*h+6{nqCqd0fos^*k!fXy0Uh(o3%Aao2?|Xj})qSUmrXBC5hJcpKb%_giNzLo9&C znHM>r@me?GF;2n~Cbafjm&|fOqqX1qV!k41{L}Yae+SoS^M311v7HB|8|wqc=Ka=O zG?s@kM)Q7anQAR<-f!*u`-&S|`O2I3Tep_QKe2aNHSf3n|H*#q(Q6$jFl&~_pZMa8 z7_>Nzj}41n2E9mgZ4;_EvRb})iK8G(_Xr>De|K=2C$g6-oP3*V6mL*f* zu=DGPz8Cwg?_Tes#Jz-jEv@^-5I-8~0>n3<3XAU#P)r37pPGph zc-J?!o{Lxjh<6Qh0Aj#R*v3gXlnJf%jYD5>0iw0OF_*6h5dY-;)*ImTea;3RT@cbjuQ{-B<+B1C^+kvqOh0Gd+ku5VoRfD0n0`dtt?5{j`n4?0C2@eIn~UEetO zEyMzd95l!Qk*(c?^AX*W6%$(P8#_#QL8P_5ara{ui2T#{TR#XlX!H8UMt6e7J-?@U zePb-1Y+m13UD~|g`u67i)_LmKeYwWjdxUD8(+ryTTh~9p=l}iv)?ZaSpdoX*#~<0{ zff!(!%EyL*rQ?sxoNock$UKMcWslmJV(ue^W*<1fatxl~k8J%O;^O>A{8;;~|2X)+ z;*UH9+d$;}NBjz!y!U;_ANlcha{eQ}XrZ56FnK>%RsgAH8URug{>Y8*I#Rd;QrH`R zWZQX7fJrYf^ekD#xj&E1Z2(I)=#Q-UmkTBR{ah${O$0NFu)Vzn0&_4h1UKdK*=Rfk*U*M0_;Vz{=vNwL4{gI-UW`ATtxcU6Y zxEHI*n4->cBhBYO`fhJN|FPbsP0i;&#;rm1;p6|4=Rdyb^zc95ThAZ)bub1tuH|FH zu+iCXy*XsT##snh_~F>8HhwVfAqe}~fsOs}4A^MCj<`7gah3xc7yAC&{>UBJ$|2`J z;#Uyl&^H`^6NA(m1hs?#Bnz41rZ&Taxi zHsX)G6lvqmX?tO3tUofh+y#*>d$}NT6BJ`{{s#)GioEjQD1+A@dBT&31rYf*QrhP~ zx(QEo65hgu_W6&73nK0FANh(P@=y0iwwc}Rk8I*(x@Lc5Lb&<-$Jm|t>SW0}fU?;i z+3b(RA(w(EoBfgQB{U74sslk*?(MGN(E!6a_~vXIsBU_N|eTeQOMDi-_77sMPU-XHIA1W-OproiMbBnL~FHz9N)d zU>zm?KfI4-A;|E$!ewxma@Tb=e#IZ@=ZV|=H_;!$MZElXtMBlMeADp#*SzZ{VC)KH z@6fFymHcJVn41hY=lfpyR!=hhzkQ|~e*}^?`G4E!H$IK@)Q#``*;@j}H-6tM5((cB z50Uu!Ae&ym*u;J420rXSPS!oVTvujYpYaoHQ;s{n8v*O8noMjl_1ym|_qpYww7C7u zcial!?Q_X?PK|RP9~ReeyTS`pU65H6uEslF(S_?Ywp1ga zt~E?8ZA-f;3UeB8Z?aN=#a=;HdLBt)U$zj5oV^zmVm4=YaTV+37bg@(4n+&*D{qN2 z+1)R0Z1>g&W?5uiIurMDn}2(b=?y=UOx1w!R0QPujdBzmmM(`l%YGc_W8s)&zfluu zkNXe(#=Nn~ETrdR8!4>rTx~Ni++V2TIQdbo!h7PKdtZt}-_|$v>(#ft?W5tViM*<| z*EaepUI86fK!K{m!!z-wzM|Wfrhwxx7eO?rB$Y!QNIJyPaBCcL3}MG#H%2>*IT`O@ zS6N>Ee3oLf(S*A1-MFq|)s#e>t8iv|6}k-UibCfh;NGX=$Q!mP`V8PcyZz-pOUNja zA{vE2+}ny!#Zl2q7lwJKj#ZIY*%f)btJm}K`3m+au39| ztCFvRt(_|pRcqhIyZXv>YAv+9oQ^==jopuT>MfTzBqrY7xcJzW}p$kyj@SDyxg9(_`O-g?RVxv9^FFEesg>DJ2U zk;<#e!^6BQqP`-nTKPCWHmC^FR~oeGm*!gIR%<>G_{goq&)QI{8Qk|K2S4_x(o^7x?!v7ys_s87*5L4FsoU zP6y}40>5#7f#%B^y%m3M+T=IB(ne?(X%}i2jJW8+dAL?*FYeQcvp?tsY+Moii(i;6 z&Oe{-;@`o7fAK{NjR(h8VOl$dIUdZ1k8$wtJHU7DWjehr{L7LlDeOYRaD?d5aPTiK z>wXtt4Yu0;ChXL)&#c^=^M;g5lskj+koriZZW8(CvWO8T$u-hf6d{?8?xJ2hD zz9RCi!E@v14Hfh$PKBKrIxcC%U#umnv9ChmO^mu;0k|+1WH1BY>rI$8XW~~p(FpSW z-vxQ|5`5)LR8N_U;5O(=An&~hSdjN`fV_>}r?UX+jk%a?7GB6j5)HR%>^uvV>x|Yx z&D(n!h@ib~?gg2J;TpW7OF%#`0@l4cR{vSpH0rhTs?&p>N1Euyn0;Xrs8>i^9fGui!IGb_XP^%?Ev{J%*!Iy!Mueo=6ye$2Yf3UFL5>A3-21f zkCQ`;7=1s57ozXRL9c!Ez1{u!DhP4Csy;mYYJ5fSE$X%J;!RMc_bIXCJ%IWglPs85G#U@{&JB-sYw`-HQ}lY1tJe$h8SPU$ z5W`UQ{knR5+16v@ptsOPN^Y$se)g)8wU$7b%r;bOn?${mQ=K|{4k-%iEsQjddYO9P z>T6@DcQ5PfBVP5^MZH%{f%MgVGW%3Z7l)nu-g)zq)h^$KpsP&^Gp1HPYHM@fATI%~ zSCNN@dEZtXHU6iV*V5mgh3t5qd8)J?>b>{c7}R?kAHAs8v%SfSdLMnzLcNzGV4BT9`?z%C?g1pk&{lhOYG|04B1;@?X>__w16|3V!W$FosD@Q#~*?zo{Y z{+*0i#J}k-{&f>>L3EX{4-?w>_v-rim#_W`{0k#T-RZmH{$~8!w9~g4|2E^_x_xak z{#6c5v?3Un0jI)+zt??mGydI#&Xm1~tbcFDziL$4jDPh8N2C8e{A)P)=Bcad0u2LJxc#=i(y=;^a!8w8z-kQRF1#lKey{@q!LxWKAj&H%ibMSf$0u^NHG0KBy$`Da{SpJR7F2)I75HhBwv1+Je{&imM=~6*!!irinhpA#=(FyD;`_=|m@1h3;O(g(vC1Wp>u7Qm zNxV%GOU$c^Xey?_*Pk%?2zTyqd9_tueTG-?(`E&X@oBn|pQRh;rulO|_m%rt?mlpV zqujoVL_DF7NiT2J4voZ?YD5_|S~qeLUr)mb^_Uvm13SLuHecafd|s?)f5ewd<;{|9 zzKd$iZ9lXnh4yn{*gbI@@o_a{4%+6s0KSgnYDTx$XQL(NzlnhB9Pw7?8{hhkJ$h0N z1ym3)w%Qlw9y3p`_(WypH)>UG^O)NrPj2N_Zo`_+EfBmiDmnU%-?2jCJKgunZe$B5 zqvY&Q+EcbPiu@z#ilu;2f_coUlFWj4s9YnxaJU+xKu}n5kZX2IQNxtKK}o-ek_Na+ zf-lL^^Tx}dq;!a%&U)T>pF2hkRu&;WRy;F#g(N@SO@8X-cJkYpoL9`~#@ShFC4_NS zS~UI37VYo~wwaGL9>yw`0Zw-;{MiWprc`{QW3FZ*_`ao0e$F1hZ@{OKc4q`zo~-B8 z_c&8*ydaJBs<%IT73!?-&A=(q%Dhve zT4kVjG(V?i)W^DU4q}1Qdz6rF^zWhf(Rz%zG0+FKq$EGj@Wb9T*xPzPEY#c`g?ul_ z9LEcY^*nfZi)s3t2+pEE>VnLS=&;O@sL=<=+K5L$RLfaddhI~t*GMt*BG&<J83&>&8+RJ;Qvc_3mf{7u9P$0`mH--V)RLIssoV+i9jdFKCe@>GSE5d&sh!m2hna(@;Mty44W+w)4+-dd?fs6bASR^<1YLgR_hwSrs3Y99_06u_P0$O&r$_i4S&J z`}`7{wf(?J1N2l{oL7!tg z+=I^Us@mih_(hro^PiKYvm1)iy?lH-vcF!jp91!+vi=k0(n9C97grk`H^C7;rBY~l zJi=ASzNI#JWSDJHQWP|NJ0e5Vxkfv^YK|P(&qnRiRBz9CKH?zTTPM<{W_G}X675k? z7edIW5X8eBNNR~$aPAHmkLm`~KnLc~7q76oO?t*Z+EF&_8UKko$QsR_aigz1o5DuV zn2TRu#i97i*OMznmbqE%CNGS(m2Q%ihFw#Wz2k?dmFyi^Z}z5ll&&Y=_}y>(g7&e{ z>L!b@L@)d__#h7-?NK~1e{Yw$pn)!Ph{ZzJy<`D-`lBoT#%~mI5qi!)@{@i?WAU>; z=@;oSH<7c*!HUa(aam@ua2=?+EVGE8jP%0_q~|Px2+c1O++OoNQJ_I>Dg~~+$X4L9 z7eaygPz`%c9j!sz>s7CLccRm44wmG1xyf5O$qSgg1CsmEZFqXk8N1mk1~Q8ST;8~V z*^ZQ6a}y@TD?YhpKfBxX=gdc^`FUi2f6{#Wo$5P3&vzQ{er&e`s_*QH)sOCaIKMQ; zs{ica^q(i7|2%>HXAjkXk~z)d4$DK(eWF*xMKc)?RD$_H^6scMhrs}rV*)80q8r1r zR99t3Hb=MI9SwIyBO_cH8cn)lrHKz2vNXbnyex)$7JOzp>m*{6%&11?=JT zSe2!R&o`F4ojjZ3&eF+00oJvg`M!z^K{e0#CVV!2rJM#9kt}t}X(S=%KJu-957qIr zhj$C`P;^%`+zAnc+GL20R73)$c<>wk9{%8fMCh<|jADFf9me&a8)94oFBUv;Iqzk2 z{v@y+Ie%Cnc0CQPZUN43m2XgR&Z5z0dUc>j7kNwZ;htTh2C9InfVOR@0+^x)hozw1 zS_(8~-bk`pAlry4#%yb-MDbMdsx7<^fHqkmu78N5iUj8TvV4oL3Lgp)4k7|=G1JmS zUY|=~j0eioAX^o_$@kMbamcjtC8^Sbv-D`So}=~fP39^-CB@&NhMkWHfRTPEfvN#Jrw1ysB|JqGHHvFW~xNFj&-u!O3em}QeZpO zuvpjZZd|O=DwsEuu=LLji1kj82@{0Vewiz3iEr{$M@)4at-zLsL=>;4Tn*b22|Wjb zl+bh%X5%dE*^L$@v<7vd5o4Q$&yma7D&%=v1#gp3v#W85fI(R8)g*)tBEFT~3>xGD zLyF#XnWUGE%T3HPmLsL+n_AQJ12fy=Q`DHGqmRf<(=lQJYbAIXoF%b`pivsqyKH=3 zA_p~0%GO8?Z2U1ue>-CjQ<%}=skOKlT$oUDSOk*&>@eUyXApvWX;DMYP zUm2H16U!8Zzx_={I&Y7~++8sF=PL!GXL1a$Fw5q1h;hFL3c)j==f+_!#=RF|yv;DX zuU6=GJh=@fU#o~cqlm39b%;$DVu^sXir5qnG2JHi`6@-MyCRmMh@Gp5-Qf}&D#X$q zVgo$Hb`P~UyW1l6IZZg2yaokBF;f+>zAmv=LhN{l*me+*(HWrp%Qmrr7O}e(vD*}} z_pcC{ZN&i3l3D0%%IqA6*z+D@7u&>ktdwF#fc}zCRg_8<<&j-&KEp*FwhXyb5)e6(cSDY5+B4T#{ z*_{JO4t4uo#Q|iIi$fn(nu0N`Itw`THc3`*44ERyE^(7x=_Gp)$s%XzW#iIv$8d+6Hi_q5% z>=`w7!M}TS@&U;(a9^YZ2(9kIcqWW1MQJbF261$0(uF)79E+V?0%}-cyIwwR zV)AkFeh}XKjhitGb*Er_ZFV7(1czsti66GbCwRj6jqCsP;Lt4f-TY>aYU(!eHI;HT z+OiR8`kGBZkIYzxq#UvmUIqHh$t-}7eHAHq;t27rt=1?tXXB_V{Almcj_&1nHFLch zul|CDU@yk2rk)fIKL|Es2cuxRfx+nfb&CGfy`x_(gV7cA%8q2g(^y^%MRiDSWFTof z+VqWdf+u~Zl`V^E!3miy@-Pg&EYeNKDsP(E`}hwbex_H=kkl}y%kqrx0>*YDeXJ(K z)RBa1fcqoOleTSV#V=(g-(Sq(WGLM-hofNx%AR(g33K=qRgYuU0*qBJDniY|a+Gh8 z-tzE1c>}E}$4!3TU_1F!O#TRx`{Ci$jg=Ul!N1#LGnGD7nAYLFKxwCdb-2PAoQPt` z66;Oky_h2#3V13FhmPoT^H6~vK=eTHsmv*ObxRvPXF;HyYmJ!yfSGdJMt{}o-8 zrTzTy0p=UE0hk2*kG}GWNH&Ep>Hi%72c$KA#SBc~glK<`U#r$SpH} z0$+lRr)F-&TX>z3()WsBf{!dieUnw3RZ?)Y?HMdAO{V06 zIGj!TQVnHXa}oaw%q4`p(51p=EF-}lR2a#Flq8L$gk$K?*W_g#-1|*y`KTWo)bb~& zFwj`|o8Rjf3aT-$hJk3ap*&O&6UMFQquH+5toib)VceL>+FWunURVc~ zLKR24lpgFi7Kr(*4;a;q&-bPeWwCgvD-*=GnhIuSrf;T-HkWiE&j*6%wtDuA%t90* zk`YaR<-4{Ckxm?dX9NB znuslISXg(!Z@`$xdT}Ky9a zU|blmigN)*RA5mEWN*-uR!Q|nQ$A0ba|qmm!uXC%A_xXxCdc#ze?=Any6WgpTw#78 z`cmx$gj~c-KSmVoU9_AD;g$H~P(boKyP?9W+Hoy4KONsygNk7z2I!VgkG*TAlw~IAYmmDYZDM5#-WWznc zkj=V4Hf@Z~ygy)JkrZ3=cB`n1zY_O1LuTRaV5Ia2$OY4G=Ol_Omzkx|f>feBnt65j zLqvi;OHWirA$%+|2UAyNyIahrz(5PGSmy3K9oa84CxOfGGfaq8-Y4nCLP-k~U_YiO zZNQk@Y!3=Zr3nQ!$!PjnI;$NI7}sPLqauLppkT}6m@sGFD1a`QIw)bDT~$fer;Pky zpl^cteHcYPf%QpQ$a?8G{Ml>uq&4Ppd<3I`?ArWnRUu&t%go!5C1qLUE1yn=kysfH z5KY*3=bbE`w2Nm^)GEpx3pTBT?*kfG=~%$I8ym2Il^zSQvxu|?L1hc22kJ&x$(Q9c z)|p#=KvDJ=cQ#j1NTYfx>H^JglklkkLqIw}T7Qw*4r$m>?U3)&GOPJp@kz-cXa*J` zoH)=}2?;3g49k9pBq+g1LcH|w&Vc%CrV4iKL>CN}#FQM0)XS{YNW|1IvOxAc^9&?M zf|>XT{!0C%;zfM~?VkAynGm+{Uxu2$ApYw{{MU{6FGFT9HbgEX{2QToDg^xdq~xr@ z!C^-L{w;-`GlBmiz<($Q7X09kKROV&4=Z^1fR#Vr1OMT1P}0E4{=j}U_#XlO!<4Zj z8!0l-I48TOP^vkb)MU5c_z>PFaR$v>s76Q}_XI8l;12-$O-GSr(%TR3E+)MLNn2sg zk;4?WRg>O031e9xN&GPa6-B}F!DyEtHAWt>7v^bU~?8-3@@9eo86W*Q=eCt=b< z3l*fHp>L&yp2070*6kQz!cj6MUrE= zM(ohDY;Kl$+X4#jR?oPrT;`sQ-xpaJ_fD5Z& zPYC9o+Q4D_H~Q{+;Q&X`z<<7)*W# zQ`uHwK6T=pggDR~TqpB$#dY$>P*Le&mpHDIGv4Jy{Yp&K*Rsu6MRCJv(sDjzJ88a& zN84$F9n$B|YrtR93(J123Hq6&;Psc}8(YwW2P~Hf+akD2xYgEJ?fONu;OU82nRfAL z@r@*(i(j+)62~`^yubKH`s1}0$~+1=D1)p)u@HhdBK;uz_tK#^par!m?fH&qGn`dv zi}4L|O1hXD`CFDl;sqrmkoc4&ZX$^#=7)V~mGF+R$79bu1jJyPbMPLM*nD3k@B84r zZn;KSqm=8zcl0a~rSIrDnbi~sT_x-0dR!&bAqS7E z_VM3DoxYMmpvQ%8%2&enny)99A%gfw;IXK#=O?k&p?OxlVN(=Vz5R}g(z9CDWw*y_ zw}lLiP9l;rfXCo(;L0--UItky<>#(I{+r0-chIVgQx1(8(w&9B!WT)WSfJaxj=KhA zEsshKJuPZQoZ@7Z%gK=rCq6evG=`A_-N#5h)8U$9|7v?!#yK9A zFgz?}E24^Pu+OuFW=Q{Nm)9<;l#b385^}SWlxP}qA$F8&n)?@Y3&ls z7g2qDSxWcy#fzc{%W9GwjO#VwByL0p-_m3otI-KY)8G1}Etc;+c$~+(qkx!s_NMJo zUzV~l3te&%?Q*QRaIK{bv@uSPL&FjVGanXb|8G} zeh^$x`Gd|p6GY$-T8HdVM&@!42H(hK+J%zKTK=e9`h%(#y41<#Hc+YXwp47tWk0%$ zMoLXsF3zF@FfKUSed&6QfFDke!4G#py)pQKR=fB9BQ-hoC;E@3Wj-ghXqG^p7&x5! z6`|ME%pNGH8iI0|@$naGEX%5e53mFq6JUVl-q`y$Cfd3ugS@h+jAI zI_8APm5bn|s~cGnRb!!-tcX=nj4(K6E8hg}xr`ESp{E#>$-8SZb1{^$5iA#5O#Q$E zVr7M#z1TwBLe5AA-S6dsO9!PA0AjFz3LS^R^ChtJQJR64gzu^k!tYEyei;3w*0NZp%_0d%cDKkn$?hW_#J%Pi}KA{ z-C+43sTgO5D&M6jmXz;2WPtJ|sq&q8o|Nxe{;2Yu1YfKc+TfJ$D3r;rXP@=9%2)b^ zRX)xTdCP~k;-I{sj%iRju244f`%Ng-H&{g^MgLR|=NJ&hxXk*#gx}>*J~b4AYwIM- zguEu_F(J6G#N?(xUSe{s;+UM*imtRHZlvwfiaw)^nzo`dS2ZZ*L4_WT1TZ zF*Y>eb4YV6zU@WnMy_nIbn$IyKf81Xs?xoW@}P8EhfC>>W+9agZG)X?p;mvhdxS4f za!dCR!cw~9U$;tkj5Gpn9jR|a&wPebJua?66#W4NINo<{=r(?z#MgBi9x`I@BRlzaUqWXOsJ#nUQQ`M&aGi?{OcBme{W8F5g}ra>qW(eiUdM6D<(C8Cv3 zvKH#{H(NyS_i{xv7GV)lhu16-wPveOr)+GT`+kZt$s)LtZp6f}@3K8DN)D~qP7m8O zaj-`YkEZ8i7A?r;2wE9#{Aq%!}E!=S-7VqtnD* zWqo9xZ(t;ntb$zVMh=XXTxMoIx{<||fm>xUY9LstM!x7tDs;2~^e|pyxu;LTXPw>` zTeSh1KyVlTj9S)^dXcH^=n~&Ws;%54>J+2C=a6T3f62h=PIGqqCez1cW&R9;jb{Gq z(uOkk_*k62sT^5J+x;ke!C>;~uqGJQxM5;cukuG3)r~M5Eo3-GbyZK-sJbC6iVVGC z8P#hjmq>@*^h~gJZu$BnEam&?WvhH2KwVP4NV5E} z0!B}2c%IHN6F&$e26~{kY zR#7ep!sb}Eh_UC0TY-LB@5(s*H3UI}8ND~Wt*>o}w|WMXB?5iSX76N=L?Wa2#rQ|B zak-83v8Adg925K4CH67Zn+<(DY)a@~%miAYBUZU=Yh0J@dQ>zgB6pL)ReP}QzV*6n z=?-9O<*%)Q;_0$|0LejmIzRd2|Kn*LTg&#)|zK zOx?pvJD@U;e*#_MuU#@~0~QsLk;ob92WQpssjvEh^Pf-0sUP%!eCn!xI;)=p)K9AV zNtPcvW;tl2W45!}g#?!Xyt8!8a&6*~ucB8*T)d??=y0Lj4%08?G7jY+S?GFb#bppV zUcVHZ!K|;!1;XNkr;&)d#B$m)NT)4*C~Wtph?MTkoQPknYJ7SoN5j_87p*_NW|o6M zM#DKvd}a5DEhz_uJt_|WfF-l&fffvw7J7TW^gzdP7_VCB9e-yFJ=$rZ&F*dsJsn|b zXit9LYN0vO1huh>+H;E97yMDwUJz=pIn*BVP&><}7Jkm6_9Y7eKZT-po}zY-OYH)o zw$Py#@K9@MQ+wE^HdRsk1Z6{Cl&Yu=bg6X^Y9Bk)_GG&yeG_3RX}(1*;s;|>6sekk z2yGXCR59lrOQHSXkb2!i>S~)*3yYNa;mI4iPk^jN=Hj1sV(S+=Dz!QqEBOAl1fK9d zkS!M090jNnL3H>Wi;i?X;UDnTN;6+xDNi`PfU7p5GkRZ zBz|P>ejIt%mTRjsD&|7`ZOYyzc+&>+&eL6!rwB*~)bllaeNPzVilnfc)Y(L!sFc1Ub>NOU6!F35xx zbv{Dv8pCvtG%9pn9|n4`rf4_Id0j3OBOGj%C*hOdxFw6O&Q6pLtg8nKK@T=31?j=w zN*Td}Z7u8TFvH`=u10x1e(Y^8#`>{;S)}~f*RTnS^<#GgRr;|fGM9>0%meHEO84D| z-TRdWeAwFpzV4f3%^W%eHnm(AMWao?CelFFQ_5>S1J7J<9p7)RH|VggL8Ytj+lA{q z-`cRfin(D@KZ-R3aQVU;{SA-Mt)#?!ufYF;#RZ=QlD;M2E(#c53Wt#ln_-*Re5~@r zsyIo;GO%Arp|mXLot}ebXEka!mhberdW{KRay7hd1TRINgvMl^1Xc?tG#{^v&=0!Is5NAWB*-e?!W7#_TQnQB4}8| z0)eGf_*DZX^N{6)Zsp?Dwb%TJukV7)0@(mgX=Ip3E|yvK0x*ZcROw(M#@=|~>Y4(z zDIRVs3|7pWmSUX3#T!6D1pq~P#tPCgF%gnszAU+6?sjv@@n}uP50qg_=5Yb}9S5eI(v9p|s0M;R4QPc#YDX@iXgNOWq*BFJ& zGhbiD@e?YMVhfmQ^=iKHWQr`M$hJQ+!OGW)6WPxel*%JG67DCk^d?60@R&$Wkg9T; z5Dr#D6cqO+upkA+_)RU1Fn?VFmrANCeX{&OplOsYW}H*eBd0{u|A{HSNE-9p0Rno` zLaL$=>mNIDAXI>?WIV_vF6PwsG zHI`GgDU4z4Mq0RbW|o^nkwa{((`?d= z#T7E^^5HnkUT0?6Q6p$DK+ldB!RUkJp-AS2?6-p3`m=~v;B4}0v-ml5FFc22p`1ga zW@h7@L&EiLT!6O2cbl}gjyYUY#~eyL=HM>UZHF6N8Zn0z_GsH<3pn+^mm1KP_M%bz z8>QfYYNRL_jfU0e7Mhx;wZUbJ{qzKnG+&VAu^C)OaA4&?kZA7+r+g=8xY1& z71pxA9q9fj`OH#i$g@028rERPEbmzNQ;PujQ^VZ_^rg-K1(cy0A6ZET@d}e?@t{V$ zSe0lkPQM55e23@CdwYH{qp|f#m=$VKT7;EtXaWC${z9!$8XRwpl6U|1KCV~tu1%65 zFh;)t?jGY1n40MJ1c$)#inxPVjYHrr`lXzll@mEO2$(_|g!mSgqVu!NbEshS~wjzJE9_aMNdAS&@29-xk8oi9mIkETomSu?$b$1z;)36}oSn zCUigJkD~jm&>i5=z28H3uub>d2RUq69>oF282>6#S1D5WxTG!+Qj;B00S~E`HmQd# zQo6B6H6+D(zAS}gxZq$b8Z<5pAatkcV+6X~JpCIM%fk1x(2EcYeL^(-5lm->+4jxym9I@VahGZNkQ81<&sWL1mkoMFRyeBzoc zzK%CYeYGbCsW*ZROSEZ+iCUnn{)b?EGnwR^l~tn$a5OZ*js2yP5Mp;kY&AeFLUGgp z^)iWmiyQrQC;B&C)BshTdoqSmg2~sUIs??86Z1R7gw`=YEky_e)Mri=b6ANVYk>Ou z{Yul;xhJbAXbe!_cobrpG(Q7cahFqZ4;m{%3I@|Zare2`&3FM0Clxr zh`|p+8R7M^?zuKbC>Dkk5y1nmr9dstKfUafgp#&;*^Ma)B}q7&O>mAU0yKv;ECnfS zx(fWpV)LhI92G*~SrSY1s{<^jQOQ7W_Ojleuxi;?uEw5F$meJbr>Oyxv6QH(m{g-O zhQp?U>e7)~=zAIy_V+pzl)alj$~sC=N-;y&M@yDiOmztsTZ;HDz89M$7eRs- zps7X=Tnr`)M$C(*Pk9T*i71j!iWd42`9$tx|2q)^6_CBiJQ(GQwBZ0+$4hWbDnYjN zfK0i%(H4cS>Yy8gQuL}b(dsz#9Z^aELg<8;!PHE>swMvCWg>2(zX}Ak6n|B2ipZ{$ zBVkfUj6Qt5IJ)g+_V6kCZ>x2jFu+Ji?8IdTh}WluZ>n>Mp3+aXsD~TK+vx`SwZ-P3 zhhx+vyz!5vuWK^VMU`ZBvTEkE%!yLwS<=_|4XEJ^Z2AmCX)CC#LQ7dP^;uekQ}5XP z29w9Arv7@>8x`QmQq|&vTveTmuu|3GDwJWqd5@?na$+?7=Qr4(vfTv&4}A!_k!N(^ zbvl!AO&-@;n>RnyHeq4Q91y>mwkI_ny%-=S{T|A&trGfcQ4qM8Z2|e-)DrWsvRMxf zNyIAWq?%1Bfp$1Dt$$+87b)#(YBr_$+u3nNKCpK^*uP~UmJ$%NfPCa1gIfnc&9%g zFZi`xwM4+T0oXi(Smae=#QD+*3EP1!j_xJdm~p~Hz*xyWYfEX@85E59A|~A_>_VvT zbM(!qX^Z+wP5ZaoQ*Tcvn79cb7(Al3J%ML?b;x-L<86ld)4$`^H@E)Lrdb0m)yPl4 z_|#u@zVrx1#88Cv#xBSxzv_$(e-#dXqtlLKQyrTQ^MeC6_=7`x@Pqm9J(QnnL?D%R z6b6^s2CAHQ9x1AKjMHwBM6(2`-rKTl;3LGzFCtD!Jmv}!Cx>uL%?b1Ymx zaOXf!mf=@-lx9AN9#e_#O1wZbeAvRqnxa`WsZG=csYqtyx zAI-RRJm0%RrKtPESUy0Jw9wlqYM5K^q{Y#Lmb(v9Y4N*K583y#D6Gp3AIVe#{$zxCsz9N(bQXKQ&3_25n3{^UBlW^#M0io(g7C5d^N}x&+s-shl32F$Q!lA~ zH=ZK3?|>Yq@tm+fYu}Gf?aRcoUHd-W&#HZ+|K-?Dk*a1gy3iJ~?}8B6cH#1;Y8kwX zHb*+2tn)PDJxAG6oxICxsiZDz3zI-HKx$aQi{FK4L9Ay~aqpXj8a~C;dC?CQl0nsU z!eOuIv2p{(Sy`y_J-2JdpwxT-LgV|ga4TGniJrMEoa8Hi0*b?te!hyc(INPa{ux+l zon>gLWjk8&5XuxB0lA}1Nt?1AZG9CdAd0ca-)B^o@3x~5#sJ+t@5?$b6Y2XSM+{bd zuPn-kTPZGqIw)&bBi==mB(L>i`M4B>y?w6HEKQw`a}U`0GGHu4Rzq}&=ux* z;L{6F0;?K`qPePg65Q&>0pfdo3h(PM>?6Z%$#60lE(Hwvpk#oxzE@?AI#Q4UH(ot1 z&qy)f2BZUdJaC*N>*$t3g&bMIz@2hAD{;+>Q z4x5Ra&fK|vXS5D2tFufqzo zP%MpZZxqXFbg%SDqpP26H@e$8NTd6{dVxmwX}qu+-BS!yoE3FTXg4WNdy-q$_bu$4 zNHB1i>dw-UwaL%m39?Ht+Z-=${tLm$nVZ@s)F$7_c=(5vPcBcb!4oVVo#*k$8zFFU zI6~-et51>|-xb_hJUw%VQ<<%?4BOcRw5r#nV5Exfnum?d#({!b=pHwZHBKJKf^y^` zX#qGf!lQWfPO<~*ChYS?gTs>&rXWKtbibSaIK>%wtZ9z{=x|gB8(z`o1%Eo@x3a#^ zVlxVC=VK+0`|u?4*m;~IkJGTLqc(X0=DdvCh8N>KPW@5@m!ATamG=l@Og)SS@og(i_ zMUhLSCrd)^Wru+YOyVpB0ZU$;@Jb6UaTE7*%236`Cp(ExlEf?hWz{L>9WH_OhmycD zm%tt*Q$o;5U^}XIz+mp*ASSd10%@k-C2*e*Sm6?Q*CEiF1m186Omzr!GE;P88& z)xIbI}@Oo2dHaO`aZBiIv$R7!XUf*l(TtY{-5%`h*& z%@LQ?fYfT;(HEk`OU&mE#qM0+o6z0>oc=X0#eXUA0JmAfl@Vn&lw>4?vIB38V?S-Gq#$_dx zUZ&UPrlnbLz={ti3wl^7z}aseR)(lYo0Ut{qs_`_^=PwF!gWGbdU2!}IVF}}!kkjv zoGR33yU_QkN4wDXt4HJnZ&5J)0eKvPoa8a29+8tg-XTm6L80+jtMx`EYEF>mVkyI^ zURk+~b0`{6g|3;5!dIcs*c*OTWV?ZiQ9#C(+RD&HUzVhWN)CQ+;s8Cy9 zIcNJ3FWFcA3PTVrnCnTFj^v+%aD_oh8$;XqM zVEV@^=xA6PP2UNZKY8(&-PAjwq$61BjnBJT@%Z^;d{zgDYP`ie(H*<%~PmUgOj;U&WE=w~Zm=fbfpQfjM9lN0GJBgMG`9Fmf;gqZxpZ`d-37 zYXpWM0P;G>pM;<=akj-H-nGTMfyNml!6$4CH5aGUMAJ{SBH&9Qz7!zh>K<0&PS!UZ zP+Ej84V&*WWKJW`zzLA)cO#mbtd6Gth*^m6D6|uJf0ewimh9K@{dvUILkL#)XVJyk8Brm%TzYlSsMhBuK*y!fqHb*A{hyd838t?- zPI$%iZSR3bAS)%`XqgpFAHhkHVETNlsS!2?<5?XHkJAOuK&1&B-N}3#fQD2snOU|V zBWGOqQ7?wCQRdRdS8*p40dwh2E0IINF?3BIu5yc}@0kuO>4rDG>^DE~4Q=)S`_0FA zL;7XoQrnbvk=7#}-#_`gCEd62T^6_D<=ghlrzH{3(ZpP|ALosL$uOr8AchoQ1-JVk zdZgAW*qA!ezm*gCFUsnP-B<%MpkXvUI*7pnQHhF_3~wh9qt@8Mcg5ED0cYjsg7T4y z5r{D%n%+sMBOQQaNwy{N-;Ndj^C0oOqy>3NwbAsgGMB-4kY&z}QEM^&DWJTu_Z36P z;%JR5Xo4dq1p~MO$yUobqx+~kkmGtTNn?F^0aPO0Sy@XD1UJ;~ttzi*`oH*%(mmUL zGePpC?=F}=&DQgk@)8Y&^sBuuI)C1jG0RdDZpHsf{Qo!pKZO5J;{Oc%Ux)v1;QxF0 z@8Wg1&3!jG(+g*7AvqKEVXnpEocR*F+ za`~!i9G3ZVegZBzj4Kj<`7K(N9NE@^4uW<#tN<^=JF$FCuT4HWOF=CJ@56c}iQjdk zQ(DC41Y)?opp?IW$EtC%2UE#MmDKO#UT2QgB(q-(e1a zUo%GxOvqm`m=bLcjbVz=Fx+o1nF45S$l@}bPw_17uAeBls;#!*b}-l5xj(ZudHs=& z#pNN$Jj_?F^$L!2so{sEHE!DC_L)ZtZp&8kw|R@rUwh{G9p?4GUq6dKvvUknJZLf8 z>aSwTuBzt=$Py{#T8n57ZK63aHH2HgDasQFv-rPdL&H2$5 z+)2#wWi&q!+<_K<<`-9cnd-+VSWmQ9Od)PV7T0(icc?ISslyo0dI+a0#$GzYstS4x zWAH#Hgirjd7^9h0{zz$ku-TRtR}X|Iwian+*i5~Cl~-CYrlx#vOY39ixE(_(sPHJ8siR|<`i4=`|0EHnE0A%0(0lV7t#16N&W0Eh9v@;2<|#XqyM#7u{;VuNqfu zLFJKl+^TWo6jYvIeqpkVKYwSJ@dy^+BXEc^o{Ke1mJSwTb(D0vi800HX43Fxy07-K zxHgAz;ApPJa4Z~Uar@atL2*xX!-W8Q(qXc=QJRe!892qY<9dp_?K>7X#VKxo7T|l* zsZAb*GFrv`94m~(|bYRcC<5v0iQR!H}vY;M=guiUO|co63~6$v0as5WLY1j&*}22r@A!+tVnH1JFAa z3P+&MX)|~Mjqk0wvx7Nz){%2(m_=(_(iZDBia;tMGMXV$-99(51c!xVPs|D}x(Y+Z z!4T8h3=z{6_<@*S;g1s2YzRaPl^`9SA*u~OTVjK-rXY;B8Rp+cD(`XGd>!+gG7V}? zCBdVC37}$G1Er>4edW_YtgISq=39&f`~D1Lt$7s2om>V-e*w7hA%85m?_ zU#Rj#?)zK0%c)wq+ZVL-l}|(pZ^=f3*6zbe>n@kpw<;ZIr8=}0Vo=JGgn4UC&rkZC z+kMQ*J)>$pvcESEYYp`2WKP6D6kJw{V*r<)xCO;35zIK|oZEfWO04-PO7N9G4?Gp`6E9yuk-i%W0xudK!{p{o*Q0Rjk!mBZ$WT7KZyHv!1LM7S;NA+>mofoXm zPSeme?V13`P4=dMu?+{*@o?WDru4Ph(@`WprsOx9zEXO2%HejR{;b^?@CS{nEtduB zG(wrnowSP$Eu4EIjrQZWcStao;b0zh+F@xv3ep!vMZwQe1wRN+mPI@V=C#m7q(l)= ztP}BU&uZ+zfRqI@@48m(GAw6nhzf%32Ot;TlVx5aImyB?PQ~7XERfR=oCgc0zr+lc z=uVTICObJT0d+g4f7v-5$(*pWi61 zaVcFc>xkUmr3aPlxD|?p)48HXtxLrtR#W1#Q>Z%=Lj&u{|8<%I$$l%8<&&)+0 zSef&brGY#yMB{oImWQEYu0<-cXhV5C&jhVQ7Fa}~ui+=`;}l0R*PbKc451mFs&-Ui z#=T1*@hP>gJl$`!(r=wkdq)q2=lQ+M3_aCsS@TF81iBY*%9V=Jln$55Js#F zbHmjl%!m%P%$XrK5T30l9q3XzK-3a+C>`OU^c%*9Lg`dD!P zYcqrGTcB;>mZJVR&W9)Est8>XJ7$ci8pC zyPsComnGvHt1tP+7v^U-s0uTZWW5yz*J<#)e|84(DvXZS!h9GW@?#+rE%c2euOvL%lCQ-GQRH>eRXgE9t_`2b&eN(eY-gpy94|RJ73LDqMow!w zOHLEqoZ3rH-#Ixw2I+5JRDGGW zT-6tDRPh_vW;pdl)aiR=2AVPEDfOisTQ${#RGA`H8L2_2j&-Vx+*`swMN(zRG}CfV zDk-DZR3k_D(#Lht6K6QJhMD;)x_fHPIEWGworKwFG(pEuuu6B&Voa)qes?4~0ME8W z+aWBvd!kq*Dz)ae=cU%1tSD{bkD{~@^4CJ!97>xpezPgnAS{#$6(#P7p!DO`nq_GD zSZh*eHc)FmkqGO}2u|zq8}sezGJ+LGyI=%-^ipMHRhN$QPt%6l*P70rn2T=ao-tBw zSlw3P8TXE3QzsA^n#i593XwAkb`QeMOQ^~xBTw@VWTTp|0hR z62%oLt`_3)pm;_PvJIm~TNH01ETYK2QX!j3^U0LGHQ{`$MH&jbXulnej(Myo?LV7T-RTrHc5oXA|5x*j?CaMHib!htTstj zi&Um&&c<&(*UAq*;UK=*?SJa!CnA2ykb0H8!SUh6bjM z4gHb|y8>_tFO53G^e<8MhHWi3Q!)lB(B?6h@Zc0I>ferwxBc0_u$A3_Z!zn;bfdLy z9C8}(Kn|w16_{`3a!{+I_y~0en;6Z5U*dTcU#pHnG?TMQIto)Fd}!{N0rjD$sB1Jd zc-P)3@sy#?-SxAnF7uJmmetR(tR5Tlq~zEdI=naVRlX4x>*p}Twtmh)z!>`=d-G1@ z?Xmtp(#87E#1E{0J%5yZiXj861#{%{3&x4Id}bpo@)>-Y?2wr>pS;<|KZ4djoYubs zO^?|{GTg*|OT>o?a48g%r!rpKQ8K>B&3FrlYat$UjVw^@Zt`T@&(3(=rO5b`@L7mO z^DoL!=3hhv#a!am0PKnGhWM-j)E{d=F@AAnT^)nRep9c(Q@+%NGWcs?@P)O?;3p(t zFtC|71vAc=Gh-~BwZL~c+Ca;`SNek;#lF+7vg~`eZQo2y`&PVji_0p!31o8?vz}=O zVA?Wci2Xysvi=u;RMv+})=5s*FCsfT>+9{TTOn&+ToqnK3k}Z6oS4AhX_*r<@T;H$YILL% z*YA@ZE;!pHzqnxlzg)tGC@(^8tCU* z#1~cLIAqN6tXx%!40Ab#5NLx9rD*C|8X0K_$jMTd{S2P3D#A+%@ zCbqJXKT2UALn&IQv!k%@zjPJ$9>R!~VV;epecYB#d9sI4vR1MWpaNI&y**f%h@9=g z!Ynjlyo}}-9K<90`Z?SXE$3{IPGX)!SYxh11Wj;}#~Rr`p#G>@!%_Dz?Hq0R?CG6R zb0%MF)trm5neJ8Yd%Ne?kLS*Kc0cvMEF9?cA~@a9872I zM_0&qG(jbbCslFL3NQXb3J!O}J1Ud`W6$!5>>kGt+ztINlIjyaR5BR=6Pb}|f-$No zp$ljdOQ4@OADl<4ksQZB>>%`c2O(5QdMf9mk+a&chiByPN3M3Q>0@zVuDTF8u8f9X zgRTN}c%oRFaQe2Kzk!?pV+zb{H@8)wkKArhxqZ(cl^e^Xg*Kz$Dz{HIxw$Px7;iJo zORU`TkelGCJ_umF5!Obbjo5*bvJ!5>FRn}vK&zCnp8czygCoxRZ$(6OEEt-T)^@Jx z%US5Fc)A6XVTlb^(oE>nyF_+X73CYtz&`vzL}KD0%P{9*hLSF@1E?PC^W27|OTpx) z+l%B5bIY;?{A!{0psUK_^OPm}xmA|?F2GO~(lLId{J21?>?XZM>9Jr1gMTzFkfOyb zx;sYn7yf`9tk1at($GR39ZB`Uvu$H*KeHrt z&G}wQaUl(aKUk5>QY0UDNxmS$@8*!)4l*{$$u`M@V@UFB29W##M8M_RcEaVEF3F`x zhAklu$$lP^$Jrzop6BKA2}SaYEn*^*70KQFQ3XFyxIEM$`NKw+%TEzTtPFFsmn2RP z#5zaC;t7i0i!Qw^VX>t{Z>xvioi@EAV(7InezY4QvrRQm)l!Xx9z6635JvN$8Ofw0 z-4Fybr>(vB;xxVure`A>7)6V8G+t<-VfOuCxmP=x|-5(J`NWUs@cHe8fZY zpAN~$r$TardI6H_@Injmq<4$t4UA$EP@exLou693?^vtRvXKLG!5K(H0+4iM}Rq^D4%02|Vq z;w450=u^BiXGx9RitZo6z*3+QIToM=C zBGaT!Wm}CokHUKLXLZs+7RNWiW)sHIlDYAsB5tgkWA%TZzH`iXY3A?mioBCS*Z#f;=L{$aRcRe1s}h6hHddrkKrK zXCO|0iSv>h=U6A#CpREYCE}bZamw8|Z4f8S4S_iCN9vz>z&qG@6+Cnn7(#2;-$-tO zD0XZ2ZL(b%3P2K-p)aC=Ba|lxFSO7skhaRPWSFWFK%3a-VO6*{-j)gnvM-VVu{?xu zmqT{vdLe7&1F~P^g%;v9Zx-3n7FizZD{UUF!n{z?MVmL&rmKbKAeCzKo&Xiq=1ofGbi@z<#*D^~O_<{>@VLh7w13`KFX3+F{rDx)>S0DRrROIB?i4D3vkxua zP86?LH-2`;iFE5=z^0>RF&}=INZsT(?F?up6(YzBo>2;EOrl9BHQ%Vj@tcw|>?av7 zL=X_o;&oyJ8D`N?)y(<07|9r(f-KUJg^USLL^eDZ(8}iWmdqx9EVAkBW^;hbW{Q)| zqabZ(bBUeJ&a;rs(rEZH0BI|mTi7IWQffI4*x~g_$mS@N<}_q85Y72`s~oQ>8Yn;> zLgBa39$OTyu_+j!V9f{6fiNeDjGlRuMc@X9KoSXzXM-LDem{W%#qTISKb4uVKcCSO zorQK4!;L#L3-3l#Q?>^?6mq0p!POXx&AZ+bE96GG639Jf%62pjMCxkQTIA#tR`1L# z0v$Bt<}f$uevoQ>b`b?VfqQ(v811vm4N{8E%xp9LZB7#4tg z-i{kqKr|IA_S@~)j8O*mLI1K+%Ok5~y_HqlF ze_%lda9dohBC###Crxb|@~e*&`APJ`C@io6yJK8o9S`gQsE}8k4vyobakkswY%X`$ z?R3UV6>dg!uh zLRWm9#(v&twTrR-n;=mJ5vYEidLy8Y-Md4D+tp5gN*m#0zz=|6;|mzK9?SaSZuX9> zVN|z{MDO4UgZRdcm`~29UP+!hmtJot&p_tECns53ldMOuURBQ@@kZ6tOFhAs{j@=5 z!`bOF8@7iQTIPP{ATc_WMkUCY<>(=NJJcJ5l{5Y*>YyXRd89vztD4_OeVCpWs{{~u z1PNO8HdJ9%T&OBW*u%KAax5d1hMI?o2GGkt->z7zPN0~o_fAxUCsa64ar`zU4no5~ z$f|f_SU7f&cG@JIL@pr8AQ&&=k-x{0J`~sz8mUP3XaN zk=}y{l(q1e7k(0Y4=8$#{898q3ca%&dOOy+^s4bJ6%A>P9uUCaT!dEocrK06!Gz<8 z`3C0<;C5;}tTKPVv07l!cj`5;twY#Jao+=Q(4t4lTu6VNx0Ld#MV+@WVo{DRE9E_); zBm`Rs6M>UwBr@bsWOmO((7cP&@P7wf9{eEA`ro}r$GxdqRcW%~v-sAKR8^W@(e)bd z4J=P%^PP9w%EZ~YS%l1{JaEcsQ|c9IpdBJ)Rn#Gb|S>9^yA>b-jH z7a2HTB^1l#dd9kcz`Gb{rqpKY_!tp&)PPtG??wF9dumOUw%5B@`>AE}Pe}f`W%AER zPPR-=Ry-W?FZD(;(Yj#aW)j3XA!v1LS+~UhH4r_C@0_8_TJ?WX+Ix}WFWLPDrA@?1 zNqfAK_Ib^u{S-2%oAwCZI{3Y{Wr-+9iNW^`S z+1YQ_yl+CjZ*~YqYBC;256u-1=|3QSZ!770k=|7B=qKd=*%I&>K_y@zn$en8OXiQ`?U2!5(&sfw zc%&oYeVB{}otrt8Xdq;}i2v_PzW+eZgOTs!`0}ZZ@+JA!As**n07U zcTqD5TQBzFMN<)5FMh7LSCn{Fe~HPR7l0t2V*?n$A#^2|cUgOO(=ys9?I(>wRheP}Ar zvo|o!d{bjfl(*HY`AxJ2Zy-)X+u2(BPD5*KExpmu_BKtY*p$8Xm5F>EqSF4BnLm#2 z25bJ3a~4s?rH+iRQZg=TCgTmrY@d(6Pkns~nGQz2^WjCdNxuJ=`ntYf8}#*W$mB2i z=AUiS*ICHiCVd@-j0Yo69b0Q{lIQgZcRA5axXX|k3Y;QeF}T@5F}OlSc-R)d7dhEM%Du&{@KO`zhK;hEuoIl+ z#B6-)<%)NxtUNX*xSYT<%gS;Be;#ujkX-y`J@7e}pV;Vr6$Y69ReI;m#7C-7CP?(%RwwB*&E(jnOHl7{F}UypMFcNz?zs7Q`76y z57ZbLNRWd)f?kWih|2sWX)jT8_~oc*j(65ZNSrc2k6Z;3|5TmG^&mo6TXR7$ zKECtj@_!vOR}GvH+YuS9ZU4^OwG{u14#Ew|Ers9FRman4dLX$vT#l|x8y)16zPDnL z41ru}>}FQV<62Rl%!n)|CH7W)8vnASg_?rZrfP}=M@>IQ?8Sc6lYC1aj=c8h&Ry3vfGE%{v|8Dm(VA83;!?q7SGa9JV5Vm&>Qid33RZKLnW%#;*Fii z9D6IC(AmM=(H_0<0~EH}oB$0Ha4O%6h0(5a*>eN_!Jhkr+j9(bwtDWFR(lS+TrGPp z#U5Z-a%k!>1T{Wtp%^;IyWv~#h<`>0VZ(AuVX?aEv}UwjxjJkTdY5ncq&HgqHw0Y5 zgt1bd@oA)goTw?pdj+F!E$Z|smO?9ozjC0psKI9uj5 zLiB23_c$^;^Nn|*jQ-@3UDpD&Or%$7$%;gLDzdmDvVk?&oP8i|AF7@|Fw*9oTS?7m zIY#aIx>dYwWb1gFo!ks>vy+?QZFaJax7}AHs?r+X=4RazzaH6ux6S;Lcw5g*;B9k} z;xC!=9EI)TZC5K{7dI1jZ)8sOr(2hIHgX+|yvMg4X`6WfdAEkQq5X8f2yiyR+nn@f zc$<^n3~zJNZM@BQj*@vRcw0)|hIr%I6mh|`6!9=epPRVTu0C%>W>=qW6l6)pHQs;p z{h2x~5Yx8!6UcWkLT=Y~tWCZ&ZB41uT7}!tRvQg!0=GHo&EPgCy&2r*q}y;?O>ZUN z*5J03%pb=eKt_Mbs;4Pn7jC;o>2RNBI(!o{JL7ZT^6iIg2P5CVwjFDW>`RH$ysg1) zXe#x$CEVt|Xa={rFPgz^?h6}k8-=q5h|}AGfPOg}1F5{Uz7^l|p9J0EB~e zOB1-w)#A>`jPEHFexXN}WI}r#xGf&v*6ZMe8*DS&Hl-D7?5>~Cwq|%6+DO?p!Q0$4 z__)O2;10TJa0i(Nk4UAtcv~)7B}+gdV)?$@VR18l5>K2(F`oPs#pvQ~`!EK}kvY}g z?n|EcBj3R&>z=m5Z8KU|R+uQP9~|gQ5KOL_RBJ_Fo8xVef}5U#w>3#m!P~5K7jJv< zOp&icR3zM#C^LT?pM#A4lDa1-V;655tL(u!&FsMy$ZXq#eaZK10OIelHYZCgBoYzL#V+u91ZdFM`V-6X)?Tj-ZshFW-A z%JAk`)hxWNd9sDKHBYwiHY-`ME`_%}ahj#Pg7>S1Dyvs^nTAF(e;mIR8T}f|+K#vVW01nzPJa8B!rMmv=XhJ&inflo zwXtlgc-wx<66t6iZ;KUDEB1xA9jMZ5;*ODoLabklm!dL%$-F;NbABz6AaDF9mr&bCLEap^?Xgp22;e0vO~KnnmO6ObbmVEKru)Tf zekHtZBoLdC4kQM2rUl-{94+uRm6(dRJ>Ns1gBEz3TRHKz*~sND`QTyp9KVX@TntEO zGYl7nfye(CZ= zm#j1eZ$r1U81c5(k>_CgukCo-uvYN4YrF+Z@c&z?jfC5zeX3Q`Qr%=8X&E!xZBhth zW5r?t+}eX_0YwXzsD=@6a~fsFKR=n>bKXPjo)7VCbyPYXsdqdw+y01y z2Nh_7zg2u4|M8?$1wJtNwoWNQ8(in9t!P85fZHfpZi(;Bhp2I%$NL1_P9@-WGz$7l4u62c?Qh$riYvq}RrGGQI9Z0`Df-=t zd>=***FYRtzBjfNY@0s4HTkv%u%W&5w^{&fPI@zd%}H+tusP{A!1m^eO2(}KY$ zHzKFMq~pC5vWu@>qU^%y&FsSS$V_~V>I=tjNoEgC;I)dc;&= zMtS~U>t`-n)++uBQDP8K?7)A!Q<7)g-6s89j?4$r&xerbVD$5bwqj|mIslHU{IBTe z;-lNBpMOL~f62dMlxjamKQCAM*{hj;-iXXfKXn=KSa|_I>!AsJ#`z!b4@RQhao#xc zw_(KJ(wYTT5vyuFX<=q512`2kYo2UjX3di=%*;wo!OU(v%2Lplm|1G(kK^Yfqrc>y zxfJIq8TN1=qr~}N&BWvd^lOc z9s4c9DF4&=v{67-M(r0bV~!ShnMzE>%cdW0E%lqlr@56AFB^_r{*vda*mM62@oA%+ z__R??Ax|*bbJ~G%Wh~bKFxhc zSP(d%ici~onDmj`F2ZHZtK!qD@!DU~bq+h{?|7!V{lf8S|6zw52W}KVw(k!4ksnna zM10!1HUngB3>b8U4y3kOz@YoCNr&_Y&%iJD)gc9V?JpTJiygv4Eu}*$_Tg+~AhR>y zIQUJPgO7c9NUD4ejNkkU_}B<>Pg~$)c5*X(%ua5GkJ-sKKITEY_JNPNS+~SL>PkQ) z>$~N|#|komkNt=gf61;0rF%ep>`7!!na^91?=<9a_3yy){T1*rw6|4!%t>#Ck2&eh z@G&Rd#>XDbRWfb`A4|#G5WgAu{3TD%phyphk6nPwt>HK64n9_Z90w!PUl|`mQ(MKy zob+b+n3LWNA9K=ee2f>DQ-xc@$5JwX96tgX{UrnMq&%ze=eA9G*W_}E7|O2)0>W2w3S9sh6S^p|{b2ZekKIUfpBt9OQU<+=i7!Qb#l_7KL zddbDdTzTSP`-76_uYixCeXZhSP0~~Fu_oy$_?VUM;$y$x#^!HyB9~<$X<6~_r+B!bg#d}9A*(_cO-LAl-Pva95n&0MlrMBZ^ZHrfu_Ni7ii6=wLR6Ln# zQ`2~|MF3e^#FIIVGUE^6Jr*F^}Y`^2Yc%bOPXus@)70@6lx}@m9e^xCZ{?iY}#X8ci3-BMDU08^8oL-01 z6-Q!qjZ^mSQVBR}u%XgA{?Z2_f>N<_Wl_F|usTt=PEiIS(A_rb(CJZ)X2HP zDe~#9EsE^eX&33AQly_#@Ydotak7!xsYNtqDN|B{bk&{{$Im;>XNJ^3WoFd;&ZPDY&Z|xqx zKBdUjPLVfT7Fq2S`E5#(vz;PyTNZiHDdKgCV8ER6565*^}k`QD;G}0YIWv zKZuc}#?A3q>Dy;d{vGR6r25Rj4;V0V#V8b8PI6ZNG&|psM?03{APqUG{tF!4%!3_A z#`NQo-3D~W@y^C(jGGD`7@PnDTJ<7#{JyJ5Vc?F#Q=ErR+cJ38X{OPF*ey1(7w|sP*uLV*n0{ok z+XqKFEz5_#O%q~UTw>Qa#2T^4O$<(zA=Y1M=J72kc`ZtEHP;VVA!*g0x)sJ8QGJXG z(GE;G*W(F;ZUt^J#Z;8Uk9E53&RvaiHP-~5x>C;b|8d6bC>%kJnZuoZR%DgIVy@ydy)%!23Uic?ky~M7!}DT;@VU!$br)f2 zvfEjCj+}Y_!(_-AEzVRe&fyP4fg^5rVeP9`Z$&4nE}ntEZ8LK|o{{P^|MfkNCFVuC zWf*-yBMi;%VCI#gNl_0i2PwSIrD-hd@xO^o|HdCh;Xa|T&7t5+q44Qci^4RU!Y5G8 zYV1p-CA>^8E>l+$(6c) z!=#P4^5jP2-I$)P*KYMByDd7LqKE$h)}upkf)RcrQ?t`520%<_LG%#QwC6-jGx?*$ zbUhfw9iUDl_F#EwH{uOEOGgad*_e#ifJIhXhhNnlxRL`Jbpif|E~O!Ek)xbq<5}~M zK~SLBK~X%!r8r*1w#T9PW(viDHpQ*qH6{}mket7#b!&Dk>nBu#Di`8DQ;eTC`J)<< zCsqFJR5>A~%El>HBko3(yo3uM@J@4wO!3p!km)H8>rDMT88W=wEX(xEAFIq>_@ze( zMWB4GSz-i6O9jqNzUir1Sw1(9mik6;KUE;9P(Vm9^8 z3#?;~SKy3noPKZK{8zazB~_gvRMK#+o?L3A>;BD?{uSq}7&5+tpk`i(j|+^o=B&BjCviwW zbiO{>&4&^pcnU@t^i0nl6z#slejZTVeMfn36d1|^xGl=4v0sHze1Oq;hk18n2M{dF z4JKC@xKnP&q(|dla`MBy^3W!&dac{=E73^k6B@p=91~6km|@9CQTmjlLF60bd;AT^ zB5153cc5mTDYb`lt)|mXvFRoFweE|@IApu8JhWN6KaJ-hlfQTH>Mm&&r_eEK=Uv!8 zFOdbjynkNR%=}7ppr$T2h}|cD#Xm%k`FkJD5;-VQzLwh-D6_%eyCV||RW*y8YOcXO zoT?tsLT6j`&`I$3{_rZho;6lI>;1joVqy>0lLb%6;>M|k6TDMPSZeu`Q_DJ+o4H6V zf7Rc+iZ45|woh2wB7g5&nNrM@BlWRxwJer&i$wfeP*C->c=aA$`FoeJf~)pj!Aq>b zo7+DR|K#L9NJYi@aJ5s8Ku8SE=sKfza*qBA^6V*=IJ7*qHYbVk{@(A51+CLa zt0OkW`saBst)WCN>7NG($d+uXOcLk%dq0Un<=Ot;kFoGMEZj*7^VkQL7S^oNHeKx% zda-g5+#2)q<+$}DhS^`^r4X0mp4aiEL1Qx1?i1&vdCQ=r-Y>4-`Q>`-BI&r#>VB+q zdM#s1;Fx0YJfGhan{2#BWe0WF*hBjQO`ob8--e7Id6PyJ=+iol<3B@i|N97Ne-gi= z_-kHtjI337YJK+ia?#ix*Z>&eNv;uo82E`+y#y7hLG(EmqV`NO8P7=dnH|5Dl|%d) z>{7uP@dnF!@Mv!RY$pbn$$KYeog#(_mk(p-<<&I&VgA>eOf;jBQwv#Gex=La4LTS7255)*RK2&tMYHrq3BQELas>cxKeJYh`x^(Xu?sb7Y22KGN`AzRIZpr zD(^T{N>ZqtW>Z=ArE0=lRtD4b5{#G__p!uhG5zYs-4Aok_5w#dHrG6?60zyOSt70<+r?koRsE@{i#XXDHT@ zF8LSGY^^%(kUu4b{82Xfml7h9u(3P-5*9tyRVVAr>c%ECzZfGlM_DuL{DtXs{NFQAK8+5pb=xej;#+*42xiVxBA7In$(O*0R?QKD z?3lJnQkYD)nVkOxFWkfRkJS15;UeMUFmv+P0=J9@3u(f_duEMAb{q!+$@W)da~0Vr ze=OMu*-VG*itAn3+=XY6&GD&Zj{{krrHzZ|aPu*E;T@Auh`lxRwF3F$aEU4vGl1t& z2BKiGq9_$=yg{a?+yyC@M-XK4O$E{$BFai~M1X9^9UO=FR%+CVsq z7fHaXtGOuzy%U1GqzfdK&?z3Dc~7NWjB$w`Q%spRfgRkxf=WZNV?qysAxOFid7)R; z<3(KX1OJ96n615!3#S!eSgYnCu9i1=s>R9~qV-nIM@U1x(W4|W>qX4TsG?d`aTeRG zI_w+NtyMGbUQz9bzilhiOL5>c-}+oNb`2aFD)NAYvN9LoZ??smGjKzg`O^w#+Mt2? z{V1V)o}&B;e-!0sg>nyv^2+O6%GG#AE}uC>Q4Sfq;zLLbYKq}nZx+>I?oe|RiF8#& zKKP4B=pvU$rVu&JA#zm;k)bw`#5QRji6oHI9y6q~;;RnQ;#a$L6Ih=5E)|=+5K6Hf%+c2TeFmF3<5L;ylskr2X6L8 z9=ZLH)1BCZp%JkMv|7P?WV~LYc-h1s)!QW2!_BRq+W-jdlA*C17;sAVI4h0?O8SeB@J6Ct6jz z((18xrFW!MTIf`|=66!*Lr$fyAVI5UoU&DEe^n_jj>n=jr)H$OF5XuFTOKQUc629R zm|PwX!j8^eo@reu^#iL#_whoh#ecS2@Vp{+nnP@$5aXpH7O@XLkrvRTS!!J#=4d#} zsrj*#ny+$dzEIV?NhLrwJFa0fcw(AWbERD~rXMJ>v}NZAa0$<3=*jAuC*_>->N7gh zdau>yQD0y=dFL{E+Kw|m+QGo7jVbGUZ73J^A!92*lAfAjhKxHaIW~<8OXa?#U{SAK z=GSp9PzUyz|H~G|_$}yhumXW!R7CXS4wK>*d6pX&!A%}m&_tecA6!#m?z&xs$$iE|OoZ88 z@l?SdCCs&=UY9#O&Bij-7UmQ@3r{)ZX)W5=uP3gv?5WS%SbkZ6D};6CMy$8sEk~cC ze#EkRp=PxGWjF>H;W;iHuFtjVYaBYqrqJnP(|M{v=p-&hqsLeTZW02wuo3SncjIDJ z`HTEfjksPazs{+=7E4&W5ij9c8lkJo3C*!Xd6jn_T990ypfxxFR-I1lA?Qo)L5NQ) z7j{nInxq`wNo3g&ZIoKTDm*)Ki;gnyR}2GMR7Bh@WLJJTD~G1k^(%s0fUlu2kZC2r z4IsCGHY_r=G_9gN{#t{-%=srmi=zFCnit{@m4ly~Gq9?Syf&Y;0zn!$an{P)+nayF zjpvCjP>C@L{foWBWz|JP*ipHt|DBYJuuk- z`=QrH?UlzsFbpi+c;Ecyvksi^Z1FM(6Q$304c*_-lxhBFj zjYde7$khmR{;T+(RhqZKxdIVLL+#RqrKd3di&Od{Oc!m4Ep(8A2K$C zqW4nfiT0?C4b?pqZs^q&ef*N4K zg1C^*cbuiisBI=L>qRy3XIKIHW1bEmWw3H>EOSF=(%4+<1vBECK3fBB0r&w0j znPgxsIp?Nk5Q~!{_M+;|xzd~LvL)F6oT~5Mp+}8MQjHqyJ=L>e*aNo@>qGvP?86%A zPoq8*HJbFH#ih+7dK?*1J?uZa@;tjMLxvQyX4)WJ`BJpK$ei%svi(JH--7weVYRRc zr(LFvx_qhKSv+_cF=v!UF!5=Uja7db$6 z|C3e+v0Z5OO0yOpMaAX5(mG<}P-OF#EgjP0d9Z$RQzkS|8p9cq+3LYqkvR-`5~rhs zr^8gRd4U`>PtDZVA_=H9XpCG#4Gqw=coo@RKGJy8uT+-f>&tabR=N@{_1|GX&uotCvt+J zLf3w0}&tTR`TcVfv$}nYLZ0>|Wtmbr8?%^fus#v#vV8LqE81SxD zPX;&2WHestIs;GQS>)p1oYEXQ#Fe6($TBIQ}(vZ7pt7m=RSRkXLRV*FF&|LN! zw}pDu1f$tZs6;gZ<1QKy-KgU0VC|Nypb_YR>p@AYmHE0nKUYFkcwr16ar)-P>E0Qt z4s376&Q0T8>9`HH5rg7x0%qR6jUn8-Y|uz!IAH0Pb;POS8&)cH-oqcIARQqst@?A! zZ%UFcT;fXd5mMjjB?`}n*u8@^_6!$kYz`Sq&D%H!KpGq-9STJncik&` zmEez(MjBeIRi8kwL#D#Vuzs|i%n+VM8lSw=nAG4Z$DbxhAAOW8>d`r>x8)~COOrUbNKTb6|BtoCV+`K5Ul+yi&sid926}t$SuS20-mO-E84U2O{J5W z#?nL~^l(m!9@bM8Z9P13DOHf#sMK(5yykFW7z*@?A%W1})UYDO)KXx_kz!p-{FrMhQ(;sVrI9{+w_6F7ljgJGk zdt46}ZlD^E2zfAdJ)gfGuX%O(kf8rtZ{%tWGT@h=F7GJ&uWT9{8h|%ms#RhyF3-F0 z{B$Ireobvdws%oYpp&UbR%9gFGfhvf&>}w_;hi}!3p%edcEO8lGMF3rJSY+B1g|5M zKc;Nvr{1cs&@^q=vW)VM_&3u#>pJ@2-i3R$%Flh0mW_yFrp-*spSjql$DhJVJ64!D zewS!mX1Q?YUD(B9tW!Pt0-@4iEb~k%z__4)iFeNVpt!57b9(t5WwXAjxD`4?0dc)@ z1N3Y=pU1DnN;lB8K2Y4VVkEGiKyjdJL&ac~Q<2tUW!VX@rR^}Y4H-B9Hi`X0lt*+4 zEOV_?lOE`brRp43TNg;@$Vy9=<;LgX0m&w*70+qF&!pctJ1+0}WGPYg6HF%IvoX8? z9oM!WGiX`IG~-d`wyMNnmA}S+ScH6~=p_#Zi%<(xUFCr(ljms2bC~srYyoTuczkvZ zcrLJ>(JLOCSv=;UEAu8FJJ9Hypme%9B|5{7LVx0`$Ve>5glA%o?JAbbJM+K6bRdc< z?-o%;1D!xhJsVkNXn-Eg?1a8Xzg~!}c*-p=NAFgQl1}Mo^I|pm(xn~KDnp)Jo8P*C zM})}L-DVL4IH@Z;jDoG>MowDd91)kW2$>y)3KIEElG-z=Ym=m%OTZXR{7QGo$711S0Dd|0UA}RgdEi_+c z0HxnLg}%gs4Z`uoA5w*O&LyQg+(M5!g*rHe7TJZaR)rpwLf5&4e&ewBw#o`Y%&-gf zRE7FVp)1@%I~+=nI)%b^p&v04G-FwC>_@tPP6I}TH*zCQ4h#i9*G%zumBlkM4`tX> zu6NcSks0wtV(|m8f`=9u!{9){8`op7gzjFrFWGHS5>AN`&NI3umqdp^d zkdUsM^9<^tjWE)pbDqa<`T1gWN-d+)+_*GhB5_n?PrHf^Fu9E=ndltZ(;1jqats%vF;b8CN167=V!))Tt5gdnCUek3h&PnKBxy=Fyie z#W+3TE^3+=^Qtv^%7)nE^iizV*g-~CSUvf$D7-K#e&YiC^jc}Yy-K-*2_w~eUoBh0 z=v^CcBS7sF&>bRqhz7jZF2L@(@k7Yiq8I;n15&1(6`7WuR(^Ex_`C|h*jO^W$9=^A z#_k?FdNHyf=n~REYn1LyTI(3Q2;SO;(fF5M`$?7_nVy%MR(=f5G|Ph%7s$)SGTIZ$ z%MKP|0*_~!9X1kg7V{_2*w7;}#f6Sk7Vwb_?j6cY?5!aYZ?dZb9ruP}_{XTPjc03B zdO9C+Yt1}7XJ}QW9;^t}GQo`(o{min;uNDO?40g`9{;9FWOyTgAYd$2Ga+=X`dah? z{)T19ALRPp>%+=6n7?~VRw9XqExfc;_9~*4XuJ92N?EPa!Jm(=K}WqmM~HNEiL^&A zYSm9VMBYTjwu3+3CX%6us0AZt;iKUB>p;DJNV5WvWgY3nH(iX?%2ZksBdhTy64U?-Lz9*U5e^7$ zf>wu&H3ANvd2|*L1a9{qd!HVCXP13xC^^JJV*QzFsl%1Xo6} zoa2MQ9I#p&2R_W{9k{C4%rzJZ%{oR14N`=D=MtJKgqApjGE)fE542i*xe)p}{&qIO zkZ^W5R*`MKdikCtJmSqjSSwLHXml|jaFeQ#1Q%_HKW8!Y!8_}Bu=n1Df&8IW5qg%D z)ANUBVE+zr$asyuczLFXU=0~mA~;_-bAzmAfE!x`r`wz@ucHWlj`sm)tbQ1VOaAbx z2$ya!mBVU;#y99;&={g$2Sl}^s9oSawNIoBg+qPrelzz6)WBz|ht4|&@qV$0R4AcziXrL6l5z5A^z}K=JM=^S4C(7F z5vQ-OgWR=hnrYP!_d}KKzV3@>r25SFm#e-W1WYavHONq%P#{L6m?u~O!*1(uBT z_Am%Kq_1PK7B#HY(snwWU|ss&&Y0CQS7ysxo|d^XTINEue=~|{O*18KWdA|)Y=+WV zpo8;g2DHTeQ}@(v$qS<}I>wODs}BRe5*=*B1L2z1Nym7*KV?{suy5V}FD&T+cs z*8Z+;{07gW8*eOCx^WiFOqD^RurNbZhaZVykD4!3HIH{|{uHIPYB#3Jms0cU^R1f4 zS~YW(u|fujkK^qkOAvg%P>u7US~QG-LIc6UI1GK?sXl6`zlY`zuicv4X+!j0^*4;h z{)S?7!DdtwUBF?)0ldVmcZ4N)@4}(^BWt%pzkq^B2Pw0%CD-j7=DD^|=@}5N6gL(oM%wrsx1T6iXM zci>sN>F_10n+#VsD%uMKoH)Z#kylU$T*#Hgm~aWRK)--}wM`8HPYtvuvtKNb-BBOg zsC#5PK=*&+ja(#JgbC+WPQzx-Ul{MzAk&|bLteo9{IiAk;O!REfK+-KIj4|0gvA*- zn*+?&Er_-u!Pjf0R$YQ>%`vr#>k85^-oxIA8AkmK)wHJ9->90uxlN>!P2ni19Er-c zY7GVPH{7f@K*Ba`*Wy{El4jN4P1-<&gpmOC=D+K(EgZ-%W$c$8igiC-FZ?OwSp*Gj*kuzrvfJ#X5EBNK^I-An3-m`*2tO)f?BoiFw--InUDHe%uKGq2WY!E zrwY&;Lx?6e$2k6j#xN|Mu&D`x1qj5{ja7`v^qDWlWe*_)+RM>&(B3v(1d${Q$PM)v zBD4oPv|k22TM%Px+PmMhXeV;Cx-yw&b>m0i(X&1+A5P!=e25*c6F*}b*heT$SCszf zQhErrY1NlFlm?_w+SS);Wek+i#zYoFOfKcOK*P}-b6br#wWvSGGetb6?yCOYQa?5= zjhx?0{g<Q9yk#t@{epvPYbGX38CP2aK(k|kL$LdNQFd7sPUjm77e;KIhI^0o z!VStZfXBlxFYxLWj?k)fLmu?nMyyRV_pe<1mEpX@T15jU6seIiTBadSR-r8CFBWR& z{KZ1;QHV|o9$Q!{+9YPPUg$1 z(zO+^X^=sKl0gO@xA0MRTTll(oD&@$Gx7L$^_Yc6evFG$eBu|0VzoiNf*pUc+R#P8PF8K@`PR_3hP8+ZLee&IOk;?b#YP>{QH%B48H@!x_u3d5 zTVu|s?Vv()HZj_48BYCxMLmY|#x!c@R!G#+&Y|ZaG~_aT+l&c-f>z5~V_ncF%?m?T zVMB5*qs9ryQA%2rj}=fQ2zKw7(D6%=v@`ASu|H$oC;qC)+%0i{pLtSLB~5 zm&V>{OV!BQDCFOE$e*1;erq4Av9~Dlb3qkhg#Z)hR9cTl6}M`VE2DQ;Atiw=DCad| zAER@aD!iP(Q2d7=L=l-PqUf%8`JO*Y@LvlrUpTxx2l}=shTFV+wUDBq`|vT{2KFqY zbg)~!*3{6$7_uaC{+U8#(PU}xJeSBHQ4zl7c8HXv5ZO^|HMmL%w z3>rDlB0uW?J%#}K^J3Od?v;KvvI5c1zv2mMbp#08`nel4&C^+P4cTAHQ*^}Jf!D)f zNU!JP*Tw7MI0Z0FuO~HB#2U}^co>p{*c|W7yF|h>D)AFABpgF@Fl5JdG-M4l0@>Mv zjn3F8&hm2DfM1{4?FuJaX9n7$RS)LqR2>mja=>st8Bb{DW0UO3cWsfh>pZkuy2fXY z`e#ab3th#;7a}W0>(E|k)kEEy$D_U`(K;@EDox<`m+wpD&in?nXi5npcdURFM5=5@ zl4@h$;y{PIiY!F$bO2O*S0!Fp0X-7Sb7un*Cathyc`gZptL1Ju&k}HjMtyRV=teo( zX0`hdXq;C47}$jz(E1+u+g8AL&arf3!pkY`Z5ht9U%VY&YS4(oQKg$2V9Yx_ReEv( zJD&aCO8jN&rihTs%9x1oa94zz!IWa}F0h1lBqrLOb0!j?bdRH9R_DCAKzT;@qgFTt ztlW^m!H@wsRziOOjAV(*3_$!(-XJ4Hi7H%{u>?Nz(ss`LRRhDJRd;up=?9AO8xSei za{d~MS>LzpIe#G<+D7TsJ_h~$rlG<<37N*(5CGkShK|p+w&Ns# zpns+IS~eP}dGA?PhIJ8~_R^lA*kS339@d&Z96PL-zSL6g^olEGZ|m#xL&nNbVSU*D zmUs4(6c!A4X|J#J;=eWEuPN8}+GwBGZQ}#2*JBt|QdSq}S`aF%G0x9JWIKT( zyQN{HB%vb_evlV`1t2jFqO?sFM8f`;Di+ln!X=!G7?9!0u{GQmlMlE{bx@5Ok zH^9jBxV->TQ9Ti$#m$&O1?J3GkeYrwq65UGF$Q6F!Ttc%Lq}XgE^1AyJKx%w$J*Ck zhf3(3PX&Ln$E@|48!z^tNr@OcbpHqV)OHgh#qdSLA4E*CoEOf74kJkHnmzD0Ff%hq z?At7HHWJEquXb(sXaB|VxgIl)8UQ!pZ+m?9#xqiV=IR&K_*A<;DM4b3g^$7?xkHLJ zM`sHQA zzY#;6n-}y3mg$A{h|ugAEDYu520dCq&=bnd*8R2RCrG?gs>M{A_XJ8;9HSZUCQc4; z!>DZ5#;IQh$1c&w*AxvXoL_mbU2dJH~DlT`3%zo~wI)U`^S! z0Pu%*_G@UstgpPY7U0)j${6puD;Wu5WI)v>*hPb_!W!HPXbdsxaP0a};gaB;jY(8+ z^RWo|Dbh>$JmnC6Nid|eN^QjkfL>h4YIi(N zw3ltKKY*?y4V$=C>d6P|-A&Cx+r(y@-WQa;{vIGY0x@PP|`q51P5>jKd+pXS(O2(B>&>roD zV~{Nt*L)ra31VI#TYq^{2Kph2txx!)*y?^3*}BYO>oKrmvlX)0`sDd0Y(0X?;@5+` zJ{E&&R?Sq5=^Ch~j2q~0FnxMj&-f4-EDBhVCTl(RWMvJlfvBKCbH;TLl zxQvYjajn|@`sbT7T*emQ8SMMa0Zke6kg>Np`HdBVdQ$zwa(2)YHfm6)G?me4XN+gX zcl4?>A5at;n$kXNId|a$h}MT7-m5T14F9aWus5(eShx~HKMO-Yl!ei=MEBHN^#-G% zw1U#21*})LAQq@e<7+Ll4U@(Sdsqj@u0`N~7Q#M+9pGKM=e=NbXc}h;3Ra6wNW(lK z&lrONEn1!iV<(CmHp+9b5}uxuhjI1WAmCvS_k5$!Qft|aRcDt|yD%a%E8EQ6&QDx7 zqcUoJI65ke6|3i~vU05=P;#V0^Qb>7lZfDqWo^7(y6K4<9Q}FybavBBM}KtuZR<~G zJWDq{@mv%A8TB~2=`<7(cJRGk&t4nDg)7VnFM&w$6AfssclPJ_H)!l_8eelSB~d(~ zPhLg7#j$ijuy7rs9B@EcdZ6Cpl)OqM~ZSQrXmuj*jGF*x8jC? z5h~6LV0rM2yjKPXT~CAaRbvKq1|smzWJsJq!p4aui9ZIBzlu7ItYc9UHsn2|pyo8z48PC% zw*D`wC=BOdJi&s!g7;eW-#|xd_nGfK6A3elF$tTTOyv-eP9x14Td1wc^RqE%Nr zRM#m=XwhvJ&G>eF#bK~SH;^JpsfF@7Rw)bR+wg=Q z-G=w}Lb)DXm|3KS(>M}n16SbP5Z)9r`or)H#$jE@EaU=oV<>u@x%%(e3%WQ@Jgch9 z0o88Jirx&RpGDIFjEO>kH=dgt=8$2SS!vNfY51^_^a?29WvFYO?hP!3|Ie7<+>j^K zHQPSv0-jB{upV7X?tSJB-^nC#0c80wScb0Ncdf|sB)fq|)>e@vPia)L+}gvHSLo*ZYt!I8QemO=lup}i~^Giq-y*I$f`9VaLwct-8(e9+{O7u2XED|lm z6C_H$Y>9qGi7t3bdM}Y-EamwkW9aIR<5FXy{mrTM371-T+5b}Ce2x>WeBfc)aPfjm z5i3a7*Z80e-wGOAkU1*dvv8Jn zsZ1H=hjP2_hzHxJJ(FJEzOKJ4sCM`ShvanA$U02;~3CWKTia!YO+R*d+@7AwKx zdr5lAsbQQkrv$6+qX-ovDj~Uq=k-HI{s?nT`XZ63MR|x}e_t88%~^G1bIB1b?wnhm z2UXxgw$5G1dS|>0(1j>nB{(QEZp-UcgI!rACyXm1%?;*9Z>Xrk(}df>c`&AmVE|-n z%%69_zeac>X8b`$&d!sm+F3nz$_O~>nkFM)+HdR;&;w5x0r%j&Jp#n0y!1FjBv&Wn zpMW14m6{c|z~AG_u)TTnvoI=y^Gd63&b7M8ZZej`Vrsa+&xLuR=*6gbS8R;cYp3f}%Cvfq^ma0|pAa!joRFc#vxr z8VQToJP0uUGBnXD0g%C4wE=%~%-WKO$Ab*$>BcXvQ$SSHl#;6V-LZ zEFn6?qQRJ+#HjQ}{s}e^aDjJ{H&j>~<9;2W29ydW>w}(^LC*#%SF&3tY|Cg+9@I-+ zwW(V#_4d7g~`B_ z?$eCYT#WDDdgL{+hMKWq%9&vUbGTBD;ts7DB(H{e^h5{BCKFrnxytw`={Z=Mcg7nG z(zcAC3IK>-0#6HC|H*J_{SAm+w6y+RsB~IXc`(%AxGg(g z4LXe0Emz2BJ>g2?uI9i$D=+C0MP?^bwd%jRWNsvxm%zZ|^5!q@8;+;M19+ngYj3%4 z{CE?Wd8?YK@yqC-$FiQepG`=v|+r`QkaCY`X^X>$(PNpOIn6FG44SMkPHk;Q3|n zOKU%;F#uHX#^HDuHm+))jYU#>3_#dhIMmg794pZ$#XyA%uWFx*;JHfHgOPA<(e( zi2!gg4RUMXp@hP4MsSuP0J-ujnP2)qc97UW{(yHF;d01BtG+zj_Uy7D728XG`8cHy z=D`P98rUlY5NsSX%{TSA)4u065ln~!pmA}e-ME0nJ~TWH7`TF4?f&v zab+5}Pd?5176TTG{D%rx=;L?MLIQ~GNz6;p$wfJ};eai7RE}W;wCMN4<%3x=SvuXq!P7Fb|43&+zFth!f zlI=yD&<+ea(F55sy|Wpcgv}c7oEdmP*1f4&GbC%}(*kT}i|*JuM7;wX%HSLzv1jCg zd43#CHQHcG_}Qkd7OEY}=!ll;V>jps_ZRmVkticTVMQ(Ul?@-rUyO?0S@YL zmeTtb%=(HS>cXn!R$K%7C7>7%ff~rQfhi>B<~QJ%K+C^AbMY|7M6J{5^JYv(K5lGtL(T4DI7yD89LwZA#{721&%S245m>dLDi-e0%Um z3GW6G-gZZL+m3XFcMqPC%V+kuUwx{j$9iwWVOf~pVBfNiMp2}^t&Y!nVHN>CEc}iTotG>Xe!6Idr5}{V#{l z-=^^S1r$p7yfd{*uvScFxy~7AWS+rwMHQX{`0mZbZ?JH6IMz8+g+gJ1g<(=sH=iq& zjR>AR=v8uS7$N-Z;E$5q;h?Wo?{xTi0X*1BcZtow#R}i(5|CHYEd>S0=L(eoA%A`Z+1UejEa^@GX`D%t z@W>Z@)(nni7H9D0Gkp{H$ledNzrW*m@xIhU?Mt~pehk$x8kW_S^}@*dKr`M88ym_D zQ!|oSt2f|woYeFDW3y5v>#()@eN zPY)P%VJu7NXHCfWab691YB4#RPe_2-)Khr$P8CXvd6E@R5tlo%D4FyZM7IyaA=~kz zImB`P%EeSWX|?LOxau6J=#Phs+CX~G;tBHYI9z(U6N*?;?0K(pjd|P=8fdj^$~pKD zEaiJufveGk?7WK3q2luFf+^T1pO9T3phS^DP@)0+PI|{$^9lYFHa-j1ewtvX-N5TG}BKLTp*BwDR=r4m8OV3YGLV~zn?gp2BwE!ft z%K`r)m_8vB7>l%y5pnQ}bk73m*<=kqR$;4A>8qn*e4-0iXkKTakT57j4!spK9-q6q3oWQnFW_BQY157W1#s=X?tgp&DhK%JnP;u(~ z4(?vRwFgt4nVZNS7=?VmDL;0l&ZzJ4WlSKpg<#@T`*N@*uv*HAGF&fQG`z_Oz*2#Y z1XNgt%N=&n*~j_oXnmOxY6sAudppNh-e!LJF}-zbac?>7^;Hz}HMGr2D_23dYG_}5 zn5~tEswW>bv+^R*%$tVFg6*Ms%3(+7Wa@d92(IhN;K53;#xeRLfBo-xXa5dEffGIx z~Lb18ZeqbcPDip})kmGx* z*umwp8|jVyz(&Qgeg>;xyzW9_{1x)AT6!&7iX(`SOSQM+P}guCk7uO%%vbIfu$jYF z@UMD~KOFT@4u2Vx$U2cxAuA#JU$RG|vdQov2>lo_L*u+@O# z^4whS?E6p~p(op|r{%x_G+lEM3nd!^uoQ?Q9h&bOBpU;Oj0)f4_5fKlFZ)nL!pZF} z$O3(I;sw%2hfy-Bk2Z)%cqX9gqYYhMk=%x75y@e{Z_-DJlW~e6Mz1>j(DGRXdDL;S z50alD+tGf$5T57Z*|Oq*_Mk`R!hdt-Q_72(j|a_6o5ex$ECsIEdQSBQY*DFV0;c=x zT*XfOT|^i2WA|o*g6CCz6prG6RSe@`bYOPshQOU)Hh>YD2kh@*6l0#98swhVmS8k8 zk*K@1rkDL9jOm_WCSZK6MJHEr`%d|#hzn9g zcnlqnIyN>0fQV2?@d}iT{EVY`!Iyk{AZ!l38!0yIfZF2dG~ zV6$;RFkyrQmgl2h#u z=tf3yK}7*;Vm!i%mPXGQPynlkG2>rV-c7^Ih3Gach-_qFaU!2?%|6SA#%>OIEfThM-im>+wt&d7K+MHEnt)k{{=d&>ZcD zLM2ME+km*BFIhG1L%?$GG?KWAn1tGoi9gBOcyni(H`bVv5hoUTqf3xLZuxwokd+g= zH0*QgfJvZJv%W@_eao;FA;b~Rtxr9 z<6~B)wvbOu*NA8R#{KV_c2njf!=-g7CxPm<{ zP6xWB(386n(wG>}gvG=VwudZY=WaO>y?M*A`2Q%@Ifh$wM{ozB2yzd38sZGzMw}rk z9>eTYkn7koUOf17&mb&funuoMf9Rui2A0Q$wL>NxCQaOz44Ap6pxtnMc^n0ujBz2ZHs;XIK}G;zKj|pb z6Q-;R^@J(Qub$}FH3VhqeQ<#IbxUS9CWDM2z;V{7RvnO6{3yMFN!MG3^A!y9G~GU^ zZ%BKr1HFAWbM5%{TCAsKTOyV<21mtd)qimLxYXg}s}8~u_*kHxeDTS|4w{_D7ckVp zdlnX>&fg0g-vGVI0ZE)jXP>_}F7;%*aP%q!ZmB}S=!C3X^E^3W!%Rs-Fw|GrI*Dby z{sDP>*yVAN;s>${Iy`!99@k;vVX4fNPIYBzgZt?N^~iEMfDM#Lq{ajFrY|_v)&98j|FC)F6FJQ;lzFlq%%-~6NlOvhKSZnYZV1_bs1 zIVCo6t5teyQ{_bkp|Q0H7x(n*sTZ!-v7MMMEpTAy`rz}o|N@@uOh1qvG`1B4S-jl>;XBozHaQb@_d57 zi9-T+Nt??MB7-Icu(-D;LJ5w1Ko&Gke6qlwn(+}&B4R#`hlX(%X#=JVPafhTfxE&K zB4~vFMuP>y=Xoi7y5~Ovv~UBK?QaV0GcV8Q`Hu*~gu$^r{Ls|AP{UKAHeDPOtJtaUW_A>4mi!Ngw*0#l1xbpU>B`jf? zbA-RO1DAE?@bzlkTI2N`q5F+Xw~s^jZAA?RbB0Z~FR#TMJEs9+vX#o%Y+Ha>)>~m% z^_2avC=YkS%E#snJQUz`?1GfPv2cgpE8fW>?hgKsho~H%W{uLo;iz@!;UKjJ!_^!< zr$gHQ$LZL5n=22$%mbr}DIB`m4lM0|@WK$uqIW)~>vou~+cJn7xHhbWB<)F+PR9vA z)q4Uz2k|^~uGPm}^fKH90s9HehYHbQ_L1maI$OIC^zUl+xd~*+$WlB=!N_Z zlaB}&F4JSfp%rT#3pwmjM9?T*vUc#2YbDB!+GXo?69uMj8>{OqbpCss*@uPexlrOI zQ>erR7`B!Y@9@}4JSW3e;@;}X7yo{@+Ws4t@N$P_obqqzd$e}0rkpp1Z&Gru9!Mp_ z-ea^g{4J4tT_8hjm-QzCdNO`LpzWAD57<9O=p%aew ztU?SHW3R2zdlNo0(yGsOE1IARVRHXD&8}#fdh*3*vLdBe03X0Oqn{CE6pVJVl9`ZA z4|rl(bQ(NZ>}Y`9O)n6-8Hn3gacT!z#B|(eoN#=MQ~I(!{aiL~Q+*6J?snKHvDx?u z#}EdL54i;m#D6o@_s(Sv_;E44%Lqf_zUHBt6Tm3njwt{oV0?>+Z=Q@6*OC_$S%;(y z2d94!MpDkgd_)liBkdeUUQXr+kOMdLrt`!NJ)~6JQ2+E6@Z#vDdI{mJNoS$oPOpJfUmP$z765HUDO-$n+2KtXuIG-nm;?9X25rb2LmT z)nm$1NbJ-K_F4fV%_R!>Dsn)#Sfi- z%#c#teEcg+KbtY=CqpXBAv{E-xSenQEb@iS?@>>XN~wDC#otC@Q#!*gEni1MlsyB+Du*52zBhtl1U|D&9p%Cfy9eE?Ro~}U{+a3=u(tarVG^u8 zrJj5Ve4@41T!f?i&@ddIlJ)#ev}W_&(q}lOtDVxnvr0pm@=HXSmg5J(MPJ{d_Ms5O z%Wujm(QaZe{L70k11d!RxHA*wg^H^Glj}8-DNg6)ZiD7m|qHjf9~~ z%VHJjqAjsgp>YUXpvH{}6<_c3&gzU##ofP{(QYFg9+rU(6c!h*ScADNIzBIXu}_bd z`Dj|Wq8o(kE~qkC%RLJ5Ytb|g0+9NeX803jMpPt z2|;Vr>5THOpl2?>zI~cDb47V(AX<^Vt4zhymO1gXWj=|g#c5<=6;B)X8D&l4X~Xa* zz?z1iIrAu1OoaKyFvQb_C7$+{Bf$PYdOGYs1McGIVyQ=%i3=sj!e{8Y*l?k`1E;YA zQ}A2K_c+sQR%bAdZ7VWfW-Q0|d;%|q)Tc;OMtP4*D;|(np#_Ss>+GHNF+S@8+!iP< zC_f{hzSgP8RES&CuUCx$;^bpFzYpA`M8G^t6O-0?6h*yV?|@0|2BfJ(sbn z<8X_Jcjg3;wBxby!BOkfh6%MLK@YBrsrSw)LK$tJnZp1BaV$XyXI2?Ml^H)_7fz35 z;+%9u?-MmOmRjmE)!gt0XOyhHk)tFfvhA`T?csm#527dC``CKY2MN%VCy-$2N&OVu z#Xb+c5a7|Zs(Tb6^Y1v=fuY(Ml=lx+=hf|i76Yp61S0TP+-+3pe-t?CN0OKU=0 zED6XaF$)hn=F6TR?t`8gIc%ZsKJ_#dJ|+(%#8oPyGT*3N3{@V5dw3K+R^ytHN|~T| z=M;X9o5SAW>1&n5%exJg{*rOAG^kRpwyyu1erQj>8LpEZz5miu;-faiLzndY?Y1$5 zwgDsEnScH@%=~|^na`@=WJ{&JnYq74>yL%L;EVwVm?n*La8x$$!_8*nUCNe(*s%gR`25f0t59AsP;IU2dBxy$fdWMRnOAmnN2faaz4 zclOUMxAM#Md`viTo#ZjfRgt*XzUO))ITC#(qwUV z9V|E}gyW=Qq1c30GK|1ds4R-xgL^WdQMbu>XfSh=6=u(j zvk6v6caY`Wk!25*Q}**^idHg^Gw%cQ<7F$ub?c?~-`N>%b$I$h-h)p?S-+y%!}YM! zZ(TdU6=OQ{16WDWl}Fwsxy;;_q-W_%O}sLqtW-Ceq58Iw*AW;Ay>=O%Hg zIlq|e;flQp^KaB(%`&T3Vg&(Y=!k{pH&x0YiUwU->}+~n6e--$4gHDEk=NO431nY; zuscsy+MT`JF!UFaD}0l5(kMvc+Rkn0;CGhBH+V8D18eudL!h*>=xTgZS?egR?0Gc@ z##)Cq$IZz2d&w!7`Y8ZbLn~*>Ftjr^fKNCv($trEnx$vgQ}Izg$U52mJS*%gP^(%z zv74Nde+Lp8kA&PYO3Un$EthO!&&uiZ!gYQB{}v??ED7F3CO* z+^g_QIll-l72}*C4a^gdxXHu`n^M0~>GfRWKs=rtkH3pK#Vj}c!416I-^KC1f{B5< z(8KETvwU*$&|4957Z~a8&EN>ftWpQ5Ou^VvHJ1x|St8XxfZs|_j zH`ywa{q#lSFES24oTlK`MKqi?F{r0Yx~x;&FUTQfy1E;+E)Ao)1RhH*!ZhvmUVhJD z9TluiMrIX~B_pfd>VWi{Ele(|!K?PawnV)&8u`1Bt&+h$rb%3(THj^EsDlOcK?+mFW^3(N$>O zse&fDdUbx0-Blrbr=>c4Nkk_Jn$k7`qFXdw(-tIkBLW-GfTE(K5Fy`CE2g(?QD^-r zD&{TrxtC0xYO^XT7qXp(7f?=7dGD8{R_z92a+Z%?DI#t`1^|vhIr;HaZd(htAVGu> zJ;I#wmko2o-;stOUt|nc?zIYCFMC5ZZ;2Cn)sUYhvqXPE;UCf>uFdNp_NVSDm=o33 zq(4V0uUz{?;Je+-p`)8FkPi2=pu>G_vp0P@>r0*H_0i>}&%D%vtPCaEHpMzm?hh47#8C7N(WD6igsqtGjsR+}NTaVCj+D|-K z$T*ZbaH!2ZV5&Kl+N0?c17P=1O=cj&*J%0_-e%iH(>>*DD)0G`I`*$H;GU8Pb+MrE zKJ_aFU~r|6!Tv4=&hulSYBAF?dAN^^`Z<_?SZ89OSR0c^h#K6>KC7I~8smp%jfXkk-k=P?WEb zZtx+^Y5}RjgS74v8OWOUl=9GL`uR%gK!mX3M5?<~jpHg{}&#h~gPvo%% zGSb_yNp)rI;Ywm(5OX!;66*B2MNgSsYbsO5b6c6z!`WnK@vD;h9)a)htib@>Di1^H zAE-T=e$uD(X=?U(b}nh<+187tR%0|hinTp?ZA(IHJfb}?VO$HNuCJ=Nci)V3WCL2BUOX|&RuA7^UrInTZw9)NRuMjM3fV9iG9?Jt@6IbGC1Sp0 zG1g2Xrf)}sD#|_&*UjX8%$Ae&;re>g9I7mmDaB-Ztl)Tkdw(#eLFZRyJh#vOoEAH~ zxXK>N0^VM283AxGP6oCzpcO;C(|EP@^3$w%$qEU!7E5&w$(Z$EIni?;rJ9>!xX-kh zkDgA0&d4^ri&X2eXDg^NZvT#t+7b_a9oxQM@)hWTtN17<9@>;WWv2jc?(1Tl4?Xwo zcF|OSy}+#%STa_H#`IJR8nYKU|89`hBf-whDjj14^v0R~Jfklec(s!z>V3<(x;UP5 zWl!&bK_fYvOh`1R@?qXKAqcZU45hXG8#VZ17{$PS}^d~onG(xZ++dlzVZ53iTn97VLC5{ z<`;ShpL*nDH}~gfz5YB{eircaRQ=hNpDq{wZTRrnq24Our~gqtppNN4wEfo6mQ}K=4g`VTofJ>z<4d^U5)?c7ADoZA4t?B#KAkhxU`d zirKiWHOR;9YrC!@mR$Dqm_0CJPp#lp>C`y?*Yr`p^XP1O6}R7XeCBNg8>jdD(HoN zLNw9zLb4jAF8nm;h2H+e?S(EIX@r(oV6k*PFk-zDa{q~2!pvDZRGN9ec|6J^IrCZh zVeP|q|4~iwvGne_s?b9RhqXWyru~BPc$$t4UdzwY1Rd(I`D+G;Xzb(9~CTWZwFC4ab zxF>kf2GVPYVCwv7j$A20Qi7J;y(XXPC2b|TqIQ51+fWr8;JKTt- zy90^enA~w215O=Jnzs>W>G>|FeA_y9CuW%s$}c|o?X_yuqJfDyi$vkYLTTMFecg*Fuk<~_#b3z>0uJR~a4EOm4@OE!z+Udsx|mewIWgzM4<#FRNF zy{}YX0zQwyO}3)~eC{?phw3{F7EnFR#Ru+3yD9ho#e*5-C+8aOd&7u$YFM|CG5a!B zTE_Q|*`Jt@v;^ADehA~)MTpugl8E+^L~PX-5S^WLCz89`!7_4((s=~ z7SZ$uA=OazXM;a9t^Pn8{+xro5#cSopz(ae3vc{$jvA}XS6~pX&nO;4B8gu@wH>Uf z^BQQtobjDHZzPGhy`+MT0x`0J!gPYstFOo&Ad#a;aGbkM=4vJwX6_+290&C0T*H8y z=)zWp;e^029N`*jva^-%we8DU&C^x8 z=vw8|UcfsFxkuB4a%+=WjoB3M4YXCpd}CzHM_dt&r5N(k_q}5Q3ed=IP+e1;)Hhu~SMAH&E%kbd7wLzDiAkEu| zv!6jtlmU$h#{Mc95z)}sWT^A1^kNs^EC%@0($S&bHN~ge6`|h7s9pS$@G0EjMuTD? zMh+Q;RV2G{s&W9Mv6Dj0HOIv5+>q6j8$yv`^MHt!Zpv+kVzzOPdCUNLgdcv{#Qc1M zVC7mRW@1a69ykBkk$tZ)Z2%&(1&EXkaQjLdDtCI{}7aT(|o1w+x!xHdnKmHT-nUG zBsTcYJm1A$wmhgjQg$3UqwK^-eT@<@6KM|DT_85XUHjrJ1}F6NMKr>N1!}3uRdX=W z=_iO?S*XaKCs_=m>LD*9{g1e_D{qZ~yZtKFE-7<{mKhP0$-GAyS*;fv*WaGw+PG#0 zKfhFs`g~yN-5W(#;d(jR%Ajtej(=*|FIk4ukuEp8X7oN2^H`3mzom?y7}T)!lwtX(jGui!BUortFO@@?UeY0@*^^_p~*Nky{PNJDT(AeYMsq$U`FoMzW;5Dr)= zY@wV?fUy8;H4b@Ur7C;!A4Y@!sntFbAY15bQAep#M}jNf(UfTLJ6%L5lu@>Tvy_k7;vcPa|RrPoC%}=I>i88$-}jXznrdsb!?+X ziqXwwjBdu4%LvDdTG^tzc&q$O>DMd70J8pAWiJA5aaTf0^CseKevm;EorCngM{5Z_^O|v8e ziZWLAS0-9zneem)@``vn;^d4KVHnyIrbW}I`V4(*b->Wcq{$U=_N?Z(n!O;S#=*ta zUWkK!$=rAMdZ|3SG2+GukbjM9JV4K z;!Q3&L0GY}(ETPM2ke>^h4vfe4M(x4)6hSsqT%AU{Dt_-KKYE5(i_&C z)V5~$Nx3yAo|IoZqG5QxH8-2zFua3#bTp4n=F!6wTs&ut#{|nBT3FF=NJYaah#v*z4IBtEtU9i-3(yC zfY?d-xQvMQxQfTUUk!ucM}`OdkO%yb2mFu+{E!FykO%ybN0?sE0Fc|-1c3jmgTV2B zbrf*^uTDVC9vS@JwIs(Mlwg&6Jm=EFXks^x_i^ousJcvDY%n(DVN6vtElwG(s-M0})fubt`3g;8Gww1e zF`K{ar^aT8S~w?Cr5%4T?gatMo?<2N7iU0%!Y0H-+cTtcj-t@+Qk~n0@xoZl z(YcXx!I|RM^19?8oXnsUL6DM~-)Z{9Ya3Ug8d2KDH7=-z{-}gBEL1`ssDwOF33;Fr z@<1izflA0DoIM#Y1F40=q>`HIKuTQZ0YOS8=RnEs^=mmR*sv;9t9NxTR>D2Ig`3*dj>o;V1u zeaLT=ywm9Aawjc;GZ{mUrHdbg0&Xl)1w5Ds9qBdlFbX(wxhSAq9LR5)#M?^)1*{{j zcE9UDqk!2w<~lOby=e*$yfi1G)K~6F$VPh^a}bWW$vK4ci7x7e*r9q^JnBn@(X?K9 zXR2Mf%xi0Duwtw{3{IusL}*pom|?}@w*pp7BCV__jOjgh&!}my z=*H2Y$xMw?oy&=FZMv^vi%`a;&X5heSp4{?9o%}*Ex$$fcxdvXLb=EGeB4W#yZrDc zI!!i?82$*V_C4BL-?+D;`i49Vs@H)|G(EvbwPOoZmo0Hoy~t8jrA4Y|xw<^|KTQn8 zy@F~iaZO>Hn)CF08NPW-o#VdLA))wZe@|V=b`MkYprGc55K=Tf#jp7xYV!=%SzgWA z0aA08OO*T|NHrlVS;UQaRd(lY0509VGyA^`v6nKZF9sPX6W`f`h)f!1f+>0{cqCs@ z4&2a1IWS)yh66V%2X6H7*uMoHZ@uYqV3ff_FUi}Jxb+113EUuWk^HO282|pt} zJdLHr>c$BFa0+8Y9UtnPM-OI+vdJ`yb4;5m>`(CFbT*Z>=PIC_Ix}j$@!yK{cY7() zy9G$U3}VqVL7ZI;yM*5!^Y-zOe!9Q*IGKBxF5;+j8Nf23-b2qG`F&kKZ)x9~TF8Jwckc5vMP8Y0vT!`EWr(oe5=Q^i%B&VffKc zrV9-p$|m%ZoyIUS5T;a%0=VU(|3tK5JCMQgUMsB9`|d})a!=lT-(P?t-bhtiPQ{yr zu~P0oxH_+@$619K{JCtFVeq+ReufktM+aes&ZR>OYjKRcNju(-@UcWz3u*7w+@<;=QG@$T(_QBLgUFO}^#D%itw#Jf zyO#FL4^K0G_vZ;q{@Zx$Y(V|L)_+vy>!1RWD7#bV=32?USc4Op@}FwLb?bI9Y&w)R z0t#9AoXwYRsJ8v9?ebY39Uj#^e)Sq$=B#jh8*}>}s=CW|*Jkp$JdEm=qn^=pH-Aa0 zyeUxKEel=Mozc&zuC(YYaQ>85@JVOasw|_2aC@WWgaO5v$ho$YUeb1XzNzr)pu&EO zq{1G4h0CeLYnRu174A)iZhS?SXK0GrzsmBAu^U{4=^70)zvb#b-55_k3NxHphHh!H z|E6M){j5;w{z@K(?zo~cz(>PsfyRzET)J=S>kmko)!MgeOJLu9Pb*c>52NS|pHBCi zOz3-CFK4#g&);GaoTG2h&#LgLf;6urcxa46d)`#ptS9=WZg!hO@q(1Dlp_}LaNovQ zYHWMrEoqbjEAiP5mM@$gpUN?g4Z5>11R|d`L{1GWksSjfLlDK{2|O`&X%vKcyx!9z z^6@AW%}%iSaynNT1}Jl(u!G=>a7T(U9OI1R4mG#GPZ!Rf{m7| zxRNterWFZcr5UKwV~dKdz1{RzyD8awdML2RNTt#A0{}B*59PN<_WpvJnB(Wm!Qg5z zVCGIkWP5Dp05MNXoeqM4y?$q_Gz!0dv}~!9d5`Z+YAhL<;4~3fuU@NIvCtMe-`c(v zD!!z7B^ujuS)G-(&fQT>GI%G26=E#x_Qs>%tTN^+$!sD=EA$O25 zr3pHsCMXuIGGCKJ3m1lQuFEOA)?-DPXB?W+`!c+M z$Lpzr0%23(EO{7>oe9urTFzga3b!@}75;;?R@j>gsfWd9W7b4~G9bO#+;c0Qq34?l zp6;j=oe)&;6F}Gy@GBVGqJna-g7;3MjnP+~E*re>;{#2nRB0D9@*UVKfd=wt2yDUH z*sK9kSZ&t#udB_P$9KPwcP&3+54E5W7^pgg*KJ9wSy9WqK7B(uD}duUD~?g?ydax3|l|a%H8+N z%$W8GH3_iRoC|l3UCnTd4LVCU2rb$1X=%CG1~3gvBf_)T%EheG0_E&UkrRBX%bGLNqVzN(OGB2uLim(?$gB!=B{G1!2rL7T z(MXT_4mI^%8Prz-{Al`EzrH5w@ami3)wdh0@Uv&gc`ws|f0LZoTRfQIq4=l@`wh^BraY>|?^M7($t$qnuf*;Po!RTG?YON=aaq zX9E~}8h*jUf4&N|%`!ep732uCw~2M&LSrXUpv9B>8V`_}3cK{g+9;Wye45OkU&(kq zEjR1#gg`3qp&L;LgR#^>irP+)+NNl2gcamnIesMyRW|h5Hi^hf%Q;Y2y81=*tck2J z*$olFL^kN-Y_5&OUXGOBV+kY1(N5!AIvo&p9r}bg5ibl#9V1nSq)I8w5~X@~I(=f> znk7_-liJo?lkxA8_ys?hvyhmwJ9YLRaNHS*Pm#Sv_U%uUFR8rk z?SzSP7n4ibGNI>uEk?}{RSqwV!phWNe#=S|5_F#5*O$f33MLaU=pzB6V11$jJddU) z5s}q8&4ZBm?c^!u!K~P)Vxe8UbZd~M!5vYy)uH8FemcmW;&aB0)M%i?aVt*z! z2v1MT7M?d4;c^!$^3!*sv))^T0S@wMsI%L9A@yspfd+r>%oF~E0{*NAg>1P<;drfv zf?5v;US3fO`ryogtqRvK5xi4*M=FG_0fd`pd%E+tmp!vNkrXELAGvBWKjxtwL&RzB zC7ox#2r(u+yT^31(n_4zh8APv{8aGvAA(o+hnWBVwN!;p<=dYT38QOJ|;D z!i6kpnz|<%iTk03*iH(coIhPJz{J(;bQm^G-es9Av}{za+M4k1fh?4{MsZcw1BD*y zH7V778Iy1i^^gv2oTu&=Gk0DjW+&AC*!fN3A<(9IP%*(%YtA!>#PXdQ=7VObq2R8N zcCq7l0OomlV2AJo`V>A!)BP#WZw~bId>2n8|h-w5~Cm)a?a`qp)l>hafN)L`%?-2iDdx3Z);5!pf zv5?5Q10us&$)OH^8~7v8)KKQiLl^PaSdn7VnH)NsX`Ium`a2Q0EY*)ma11#$=o;F{ zKULZ~61@_c0`EuO&~_p8q8EY@)uE($8*%3JG!!Nlj682XOVRptWvks+l_$`{I_|<~nMOs?%TW0uv8E*`JZ#*C1S5I2;+yB4g2R7<)I`ZpqVO}lLr)Nri%_wxL!3(iDa@??9n4)nz0PTGTm5`${AWN+Z!9h6B~| zpC$j5%YVJ)zn*f;d5Tn!>N5TfYBM*v+DqhPvHTZt$_!dE6Bs*H5K4Lo*?HF(sUqu1 zf{Z%ll$lf{Gg^oP#BWxtgWgn^RSSN-;(1ld52<+j6sb5BRNU6DxQ$=&yyuJvGgk zP1nLeqxuV<4N$v*w5t9=K5DoP>A0d`J*Pp~aLBikd8M|I}vUx{$ zySr7|vF4n(O~8vY0JN%>xXpS0#F|3+l{}#QdU;5jvGe9A<*R(kpJXua3FT(eO8H?& zctWw}SAk?@?|yVu2`goMgcQ4qedI{D-FM?BLv(vc}T$8^wt} z&+)a5Ipw5BJcUaUr|_O_8d0xiV6RmWlNy-s+`}u);e2zqjNfVxab8)lt=YNg>sBAwQ45WP7vo=T-cSAHUsoSB}8A;`yfVwtsG;AEb_^yk1f@|I9{&WKqu3+9&1si-#-62?dWt>FVBW?y@OPLp1QEf9s_1#{fTj zFN+|u6wMm(_+zBCp3lr!SAZz_Ysd5`{t(I^Ot~r{jE>8_ydBxf)RR`h?C;}j&2P&4_#>lCo_5Rp z-JiOP(j@vUlq^wKZ8EjBO?l7pM#hXzHQa`g7lPEG*QsSX+aPsXfYb&mj;2S+7*Ta6 zw*^vLo+xP3ly{+vgQ9nBv0-T-c6We;2Q6cr<}MEn^tFgxlD*OeVclJlv0&tcrMBhG**~cQGDlhDe0>zQUsg?4o0b=+0Yjr~1Lc@n z#z`%yE|&q!Z5j6~jkP|Fqp8lbL7fE&85liynARn>lPS^?XZ(0{MSHQ9O0Lw39(ezZ z{wDgIvK}$mYis}1ly?kC<^KwEXiRYX=Vayowx1QZJ(rv1Msau24J-Z@Cix_z}2RImkYQX^9A6_0N?;0;7kB! zk7t59)R`u8QuapmG!PwTDsLogW$`^I*UP6YMLAHy7Ir0tE?)kVN}`em;aZCa4wbnK z@PfvVkUja1_y8#|Y!m@klcth3&R51?903 zM@6Uw0*-&K{`1Z#zOz4AcnC0)L=~&K(_mFi7M)ZPTg@MPx+hP!Kk?>Mi9Ro_12U1o z^g#Gpu?PR)jiOs_&=q^o1$3M8W=LNCMbrBQ7I2v$qte^)h{v?gNWrurKPuA-q0VL6 z72RBWK;v&UVd{q~gcTu(X3cS4@3AD>yppCW5^e|ETP>rX<5zkoRb`XXnHqqG8O)r_ zzg_5Ei1Tr4dY|Ks(pya+bxf)}{B_ zLw=9m;@_h8csND~>7dd}{YoF9s^6j4)4z|_g1W1JD@ZGWSDCFH(Z7m`s(;^ouNYq` zYKTU$2+$0CTvDmp=-(myZpupuIQ|*^YtK7Z|L#6`7yaw~ztX?s1+vjUS_|rTu!rqj zbDm7p?~Vx~j(iu1Xu3caLQy&fjHd}Ms(zFH>8al(q)@-!hPat%p(~Ef2mPuzFkChF zVe}X(iMHh25AbDdQ(i}YsT3c>XOQa7r@HH~%}se*-ZR39rjH3K zKEZGUHNAj}jV(Kk6nyEqLxnyGZFTwbZlncYw9l$vcipA1<$O8}k1bo_V>EqyP(`j! z+i(D9{|;L$8ZX7p4mh$zOPv^$y4RosVjnyRM=qqxq0MUh-KDqu6?@fAh;6AaU%%_} zWu~cA^yT7O;maDn$iHZMdQkm=e)Tb`H+(sPl<3QL<;w-AjmwwU4s5}fV0{M)D;P(3 zj9CLCqv;!hI+wUmpHL3%<0}mm`@K zd2E>rAEW7Kf-2tjt2he4sxKprzH|>b@^>xud{F92gARy2`H%muFTVYITY0gz_OhnD z6{L;*Gu6ntU&VN}f46_9q+KbJhi2?aGnGWs_pmX=Zz?^6-%2%OfiUT&yr}}3e|(QP zcOPIlA)~4JvXmzS4EvgV=XsqUPLhZ)4MT(x|CGHhQ^x?Gq_y{Zynf!KB68c-lI9`e zWGYcwKY;1c^ix6E&%wc9c8_0nf?F0@E%;Umx(AW5wDimV?iht_cpVKe9d12h*`(8Eo07@@sKToO3GU{xzX8ZkJDFYFK-qRrbrc2YRjJ5x!H_E&cIEQMS&fSHE(F55lIIE zeg3G(mG@4Q!ki`4#o1L;m~?TJl!o8ENE>9AU-Owzr*CO+VXfcHQWXKSB;AMc@Y z%0^A1 ^tL@g5H-v<8WZM?y$nJy!$?MNcK+V6ZL!0dL=;$k=qxI|9m=k0Z*W|Mh z^Bl><&+Wo9?vi&nN6R_&V^A%YGpR9iFRyV-kK28$o8em{UVd_F1I&swYMG=WPWhu8 zkYwMZwwltb;e9lH3#DBG_J#nm(ZtUJ_3S9SOXE$I|T2JfoBKXMIpf#;>G|N)*4m#msDII~Q(Mo%rT!->O#c3nyq1 zsX=X>hqUE-dh0Km?4*r;|G1Mpk6Enp%y7J2+-ANPdG5zM6-MbwU!I%C`0{KIe$n(x zpl+B_0+yLvmFKO?MV{Rm|C3M<-M>t>Nm{DMTMkYF`2ZXb~PD~eEZRYdkh$1+D(1t!*n5nf17Q%@4nRbyqM>860Ihk={H z@~*!ljtlHAdBCDqzPC>xx(5bHsd+)E!6N1o;!<4z%qMA)(e%Ihs)DQ6pRWd)dm3?} z9n+}72(FM6f=hp;f;$oUx!vKN`=})oxqSicp6S?W(L*OBE$xTh1n(pw!CH~eU-+fW zy=%EIpHnaK<#P#@N7Ea@&`@sw4f)93ScyL0aPC3n^En9xF`c_W=s=Kp`VHNcLk^NwaLghxJa4uMZlC#sMFMb5UFgrG=sq6$dr01 z+8j+g9$SynpDhAut~JbrttAE)9YgvqW$U=j%GOWduFKZ)y@U2}o8as*wnZ!0rT;p9 zAp|o%6unoz9?T16N_3ge*KOii4<+>XVep8i_vE;7y!HA5_~CN=S<=d`Pp_w4Zf?num>3UX znW-gZYFYLLl~FzVI)dt>>C1iYCGH3Y4p)(G%G)T$pMT_!I6v(nNCl%m?oV%pQ%id! zM{mH%8_(S3br`)3S~7ts0IjCH>)jfn>4yVeEu?ZI&@H!nyjo8RDn8nzyvm0Pmsb~o zmS7c3`v}Is;?-2+S=U&@{948{%`0dgvP#}rMe}S>-6+4h+o>db6J@uEcF6VKQ+H@e zR>P)>8gboSp*>YXq9^+mdF{v;kzXGVZJi4zbUd_1#zWeFzRx>V$?q5YTK)6|zE(GZ zMKt}cFPH3XXmtSv5`E5G1T@j=0VEW}XPT0qoPRCe?BQ6O#k^0 zl60E~+XtLIgKm>9bE3?%PNv*3G}&nSQ)t3 zbE6D2xKdLBlhbsos>KFJ)naSLSBp~%eUtMbOz`aeuXUtuqxyCE9n-k0{PyFWGQV_@ zFTc$reEGEpJML3z?^&f1aCGH&t8^(k{_(n!?w@(`tC4T~gY>PKI5Ym){rBW|NBvIn zdrVojH#NKR>rD;}*~;Sr`MuLu*t!>aTr|?-t}c%t#ISO)@2rUwcwC>z<#h{Ek74ZnOUDZod3JfeHU}`339I-QR$rG7r)I zoAtmwc%gE8j$=Gn16kjhK1$5c4(W6F7foLf;CqTs*QGN(Gc<%0_}>4S;yV?zU3}LU zv@k<0I#w3a<95ReV9N?^uL=#`Ww%(6&ciN8(-#NTE;5zDrgo&VGmK>ukp9#w82^d; zz6l|j=ySXf(URVSz20-~CqBJ*TqyMB`}95>RD7CW@g$}~(w6PKOGrWQivK9RGa=ih zcXQ|8qj$}3)B8tNizk9=SD4D6cW+YvgF$Y?>EfXLB^n%VNOB{1m7bCRTDo| zdE`KqE05uweor3aI*9)1n)KT=vS_+Ef}NHvXUbrif!xm%#cnF88F?8y#0ji(|zji=b}PxQG!ipbXAU=NjV zKT=d=Z+G!<<*3=3gQSuhpyyI z=B4P_`Sg3TlCuS&F&fv!nj(e&Yz$>>doiEGBN3h*toF;pK*Qr(>suinoaeyJmR z;?+mZFX9D}O)qb2qtIT%gmHs`Ky$w}nw<3}=OX!4a}BDI(n#UPyYOsLST4@ewR4w*NasAIBZ{Slxj-J;4i#)V0b%eaGyk?LT$JdSqgn&ai%sYQ^0Zew7zY+{a3 z$AL=??lQW)4ueL?NPz3t|4f;Yf1Aq;*14+CwEED^hqs$ zw4RrE26$)rwx9D`^4;{RE8pZgU%tc775T2q-G$n_Mf*ARp zPBqc=rPOQW+l4B;;m7w(w^Y721OER+zC~VW=ITVB!(LJOu4P21fnzf?;c`0a=Gu#z z^4jxJoAASHRm7r_*I>wXFXr+0eQe8QikRv$3pF+94hKbgUw<#AKfeu6k_Ws*@8G=C zmP66wg{|0cIV|28M#G800yNd-R8e@tiG7q4hZ|6^eUt|Z#(%}cML98Dz;y#0I}$jc zjdi+AVPBBgb4*!}1^U^aNi#hPATHFj5gzdNG~5K*KU`>8OB#JSXoS~^?ksZN>!G?M zzP$@R6-auH%-}c3ln2u7raX{_xR4;IZmkk@EfS{3TAzdW!n#Z??UtrqF5~iv&ZXB2 zu3H3Gab{knoxC3fr@1XP&AHNSx8Ss1AD?LxT zhs2M~wYio(oGA!Fdx;GBww$?I;kslAO6?Ws$pO%T1`&vE@PS_F0lfmCa`dRzs$_ei z(it;No3}t(x>nfNKY;Y859wqdQjrJg5Fo`<1BwyF5xqndtkqAzyLLp8Q-OCQlQV%i zX|=!{7{Huu%7a~7ALfFD%dq7bCSdji=5Z7RrnAGCet}h>u;Kx%%Y9fc83?d%q6aGh zEGspHONW;`XPQ;Rx@zb%l>$-8?lqzw$ulNqUp)tYP9qM(8@>@iA0~pF9JnY zGX70MK@59GEeSgXkIOe@VBTS8xholepnb^t>M3S@wYPN4e=~z>S+joqZIN^$;L1Rs zD{ZJWd$<{^>)PreSQ7RNgS^Z9yb;4UB>D7IwcKC5qug5q6J5*w$6vPl;~#L^Y5e1k zeafCz9_jk!{!Mv3_^C8nFDaWkfbq{&P|^|CpnsR5B>l6`lDF{>R^yymdj| z?S2h0YB2nN?^@-5&(+HR9K_)AfA|kR|DPA)cHLsyh5u(i4r7RbEBmHfYiZ_vd13Vb zUw$*+8?SO{=4+o74qxAtx0Hltx_pIlm{o#Tth5eh9eJ%;Vs0 zi0Q}+E(e5Smx#N*_lem2lXu#qr9TU8{T&pIAY-!0iU&(8hm*`CW*&(Fwy@j%`wNtZt7>)jt>Vvx7XI1XfGUIMbDkoS3$(Yu$SH_>Ol^d+?yHs!tS zbtGpmSFW6f%9Nbyjwc7MKw1V$ma_1`@|m(5FHAj#C)=(G#*-HV)T!UGy*c}52>$>H z_sMr?%!12O@{rgSC{Ay-20&zV-`!j`!o+^^X5(GxXX3?bv=vH zD22qf)pKYBEG@0huepeygmP@G-Rfq~Guf-7=^C;_{$5}U?zfw9%Vv2P+$Vs$DmprJ zpuv^CF3-dI?rkW)ZZBH%o2`fvz_BX-pB>%5#jlp@iMo)u2b*hLli|F0FCr^jQzI^K zkEu!5`#%f{_nXQ<;ND3p{sYOuC-AtI7ii|myl`7%Cl{e$otHDvg}BD!7QeWLkhD=k z?{V)tuLc$I8n&3%h59!GggUB)lKj{*^^HWZJxbv0uz z4j)({C1fk2Ji;kvk#|kCZFQ?(d|h6%)h$~QwW|xusw1^!iSK`@IX-bsq)ly4Zt1$3 z^Kgna$DfPi$@Wp(BD6_)ar5G&)y)ux9g0Pq>jgD#Zjede9fF&CxX+iKk(aJ5?Tp`e znJo3}wM`I&L*_FGR@v3v5XBvI3g^^tJUobU`XXZmF2@kj8C@Jb z(KAYWOz@1-j;l=*ru|f!A;#0%#W~l1BTYI}iBV}TFU~xzbHlJG-0;#7Md9fh+HJq} zVy)I7+;AOd!jX76s@ho73rYFP>FX@|DtfDj#c_mWW1Hj6MR9I~W?qrs+>bjqsU^){ z8)wR0Ku)%ocK=7a;9>AZNtb{c{{0?B#>v0GP!Oj<6&ztwk&J|yum|j4I-n?AuZwH; z2+@<8a7F255jg^Q9$2oc-TJ%SymGw9&2aq>CMLuvt-nQB3gKh6#0-R;5B11NIH@B{ z?ir=mW!Kq5*3NswFdb8HYpuurNu*%?oAgdH9;b+F3fg=P`&h*&mrQ&z5qJK`_S@f^v6Dx%GO? z$yE*1NCE;BVSkDsQU>jXpgkg=1%~8^LM{@IG!OVydl#iaUjvGSLD5;YIB5O_2yRq~ z7WVHVwSNOZXa(Idf{5}!5Rc2laHto2iKb=EoZs;Fz40yjKhnIV|Jx$c5-VFQ3{MqY zfb|r$AS^_G4%=DgblpgqGmjB-Fjo4;*0bf-{mVHRrth1g-QWm)^tzhk!w0reBZ(0d z=Mu-9hnaY+{=^Ite~|vfS;L_@Is?}*E^Qj1JuzmxMV~@EG(+up878H=%n3$I2Pw6( zlw+vPFvC%eq`z89TB#lVm9%<#1J^glvuN&oe^L>ouf2MLa|E~}Z#gaAVW}wV*MYo; z8P>tj>41gv+Zh%Coq8_f1z2?lE4MLxv)SvY8DAu>DQa`IEXMy#1};_#V7^7c?Bf!Y z0xQ+s>GQd-Zm(!oUEiV(#+Lses}76c+lQD%xai!|;y9X`XtB>lIzLy7%piEB_{eh< zg8t0@sFLn1n)v?b+OBfm_7o%~+c9gw$HY9Dofx1Vc=Icw>5QK>Vd z>HB50OFYy!b=mOe*5s|AkR0)Oe({Me*EUS92g8 z6k<3N@t#vm>oY9vXeB2pKU%Yt_znc5{H^<0?_1XKe{?_V1oG{CKkF#o?Q}nDwSfD- zy`R;Khem%(3`X5~Gq*iNRmM){>;5Hdcki*b zrvBW9AhzMec)IME{jb^LIaRiP{BE&SZV#($;e=uQ8i#J&&?sL*%dE1s<1F`M%_%Ya z7W4jHt8BG6&DC|Ag_Vxk9iw77u>~3%+^IibJb}W=hXslBQnF-ZFNn)YwRqWj?yc@& zh5LRJ3r#a9thhQaZg0x&R%JgfS*k*h%lmlA=Bl#C7m*52m)iznsj6nDH_aAA#5phe z8ZE0$%F2nY{tUCQxrWOp?1j$7ggBAgmWZ9CE_~cQ6feDBlG>%dhhR+HU03l=WS$7} zsNGx4)-sFXO+5LmR4cb9^;$)cA3=)q59Zwz0TnNqYlRj$KWxd@TvpjE!Fc@Tar;>% z$-Y^0*;BM=$u|R2osVtqlI?)<+an^Cr4Mc(drkYQoaa}N5Onnx0nX9dF4i3?u?NCb zb3t9tVd`GB8}}0lL)(L8l}(hr0TCBJE7YJhr*ca;pK|pOff=_~$L*7Vofxi<}dKTT}tLJH@oj3$8mEI!8PiwqUB_Fp5u&9JuCGT3H z)y~$h@|C_=@_ADmtWQ2F^=IzVvM+cVBMR-R%h`Sjd_5p;-y-nhp_MV_9yJA3W$%yAkCm*kLLX+4$lv5+796K( zE_+Fpy&zt)*ebzqeSc!`vSEIU5X8+KJ*|+dEAwNamCnt--Ng%Y50iXdUH9hB;PaHULs*2M9rD;va|!$bg)yYia3 z3t1xS(;bg>YB>AswOwoVXK67?R@v6@bQ@WqX(&l*;X9l`OYP-n7aVO%7Qrxx9LlRq|CV zb=(nI;CTR~c(|?^yn!69pGqPQG!4sE^4`EyZrga-ig04R5*8w=e_Ne-(o+`4`4JZ> z5NHFlM<9LYpXaDJWDDu>H^sON5tSkjgHhYrQb2b%xPu*T!bc3<>NI5b%R>rCmb-=B+ zp0H!Z7IJy!CqVDgu2J}0uWubc65%Q7+`l?yOQHM3(mHdeM{dd6J^=jGvJ1z|>x0E>mxosciq9!HAvdy))>Q;|{f&6e%;L zp_DPC0wDRQSjNmJ^Z}enj94XU!QQg(GL}9mT~93Zy;bs&$dfT$bC*Adq~{qPiRHX% z@(RFNCV&4(sR9hw%@l}bv&Axv?`-wZL}s#9(;$Wn%GC@D*b@-h?EqG!_yjFu6pG2Zqx`9}Cd$sM;J5G3NExndaOC7;Jbjq%XN(#M6uaNQQs zD-N`T>pt^2`+>{Z#A*^EDA~jg*NMlyRrX^zABX;rL^_5Nx_RDE+Vw` zT=LW59La;WzJ3F3eQ9&{aHJ&G+|%RiZbpfJw32C~K&M9HJ^rC;!3AjOjk|?0Mur<= zBXsyr`!@4end@m|UMtPo9Arq$7X3ki?&E((Zbtf|tRi}P4TLjwHp!|~XGxlRJ8-v0 zw_-KydUt%pdGkyqUS`D07wCdRIS*($M;=BRn?}ikLk6z!8&*8cZ*K}dkhDnE+4xb5 z88P-O(lk{u1U>W+f+U`kzdlBCo+Muv>zM}B1JPZn#!E+qx z+0HO1d~#ucLKA64VYrKeveTVo_KOJ5YPfXC4CC-a+lQg_gY+NH9}bam^q`18j+SIv z#A4p2&2X5vP0C7Rs0KxnX`#%`BaCGn8`P;2b@#vDT2GaEjccfx*65WRYr}Oc3Al3_ z+5_-k=r#rJSyLGU$(&{^i{7zPoj-)9U&4+VvjijI++*Tp^TPET$sM!zh^1~Y&3`d5 z?sZ<}bTdINWSuJtQ={n?x4TB0CdTtPeY_-4A zJAM4A42OwVnskjWCm3^{H5}~c8eML6wtXWSTId^5vbsjJzfvo|pEYXQFVZwcSd59G zwjkpHWOV0lF*E#)mod6gcUJ}%<}NTH+HgQ((#o{bIn>0m5c2!Z6Y^U`H;SP>ncF(J z6}corThLz#Go2luxRmMyMktJyJ|Q^P9AuTK;j^GE>9#YU4H-@|w&AsDp1((;VO!=z z^2f{G9KRb9ozRU`c-ouVIa9V(=PZ;bBpih1Q9~-6+9}W6@QfFPi6yf|Q(Wud3zA#& zM2h?Sw9R*k3fEn{YoD0PJt0>1s!%eaoCz312jzv5)}nJ1B2B+~r6_Fjc~7R^>mj_B zi!i63DhWZlMDtsrU&tc4Gsp2o=t}ISz09xDwF$c7_Wg#g6_#CCRkG1aHHfrO0g1v- zpB!;|m?#w@`~Hn0``v^xV^yj~BKujiBvF0VqhcjG!;Bdt>NSGUGMNjf_R43(+OxS! z{*S-e99D1Kr8&fN?r+^3!gZUKrdGoZ(-4FgvCvyGQJC?*NG++EGTjA3(S#4Z_OOeT zr_bY4mAz4~11OA_m`V*dOtavfunN}1L!TMe>}Fz62y04S7uFmfu;$niu;y9{sDN5D zi`JTi200&YFjQCNJlL94TD}=-g=g$5>*z);OoP^zo-v0*jkbutWUTC8W;Nt?p)_@B zvvWAx`QB=X-0W#qLq5fXZ@_Lb>2!t?J*AZuN0gYHBFhF0 zJ{vo;*sL~X)S=&I0h1d*%C>|P;v#8H{hs+UD`X~wyG9cK^a7cuJz&J8OJ%XSUxb8$ zfg0(dMW5bU8(w`?D3F-+t_k>qPrt#v8LJOJ-@Rm$5rM*ZtbIS9lAjrl|v z`5i_cr)azqpm8Uan~_ZqL1VYpXza=_S0#I`a?bfslv2!>>77L>ldiSrt*3<2eH&Fh ziULMepD?g+Q_7kCZ%|x)~GV&_*?wX=KMHmoI?UkdeUHey+t(d^#me-`T z*5sp-C(Eur^I7`mxf43Xk~0MgCsYJ2)@1@iZj~&va*UCeZ#f15N%pu|%%mA@X?Nv! zG%K6CJpOHdn~d!3=^&k;n@gM~?ZLvy1BENZA5V)4w-F67z1X^;XpF@8HM~*iEV(Jc zCGWWe|LHdf&dpJBAc~+0Ok9>TE&nyL6}G`Va|d;aL~1%|sl0eu6Kh!|-^aY^jZb9u z%3Tx)g_7aqpw#vC5 zjpZo?s~zVuY1%7<0KgLuNBYlORVcD>?FwZNaL!2R2$}lXQLDavxUQV8CT{{8 z!uA`n?cd6w8eCDn;5ME(sExErHf7mn0GdZj;uMx*Pb-SP3U}%HSI_3bjIi5L72%2% ziLx9XYbux$S`{pOR>aFThNs7{;naki%zy}pm9v1JQ&sj|c-q6L9~b=`q{}?GQ?LkP zH{1-isz3*`M0vGvq7(^oWSx3kPV^K`6p~K@E3s(L+}BJ`g8{G{v}#zxV>ckRe{n3dAf9tcVP=2mszBxyLiHHdXHDaDvAfw|$EpR^KJl<349)ix`oTaZl5 znOe(_p^{<)l@LrER(Q|sjpU9b_7Yw#Vg~Ew)+TBgt2Jy3NEvb#q&xygV)h%cl7&{8 zQxzWY1tRP5Uqm)tA+;0pLfx$CUMn0wA4npyb9WNi<+R&62yG#AKZa9?(6DrUbRgd7wQa^D#c z@|FCy@g9NWN5;PtrvaQLXzFKzBj#X;Ug+PeCLpt)6%zSY4V>#KK`O ze*20BpQ{CWcUc4;3V7o_t2o|fpfjF4LBLk?UxKR=SYB$78tT6c*IiFSN{$RliYA<^ zCBw;)k}s9_>hnU}N&Lv{A#d0Uv`Ut;XA~=0W)Q+55Tq2OjP|PRMdr?9-Io)H`7T7A z`la$NRfq!wVk5d9r~gpH8Cs!QeKc!ZrkT@PY33`mnZKZgsssb0(N74E%_<=W7vKa9 zOg@<==gP`mpS0g_ZW|WJOdLBF{7c9>J%E?3#<2qr1(&B6c|7&*Zx+FyGEqgwCYElO z;6Dx&7oX=TFVz#rgA^-?o1XKWO?I57&L#(u+3nn>zlC=ThbUzeYb>s~?j}}mHnGZE zZ(^DCLfti&P<-!4?Y>rh+i=|mI2Y4#l3pBTpAnJyWc^}ZW7~?@`eT5n8!;y0B3l%? z<2@NNW{t?DwXShj=my@Yi-dzfmmYYsJPe2Ppu^nAV(7X6GQ35?LrL?N`%jjLpU!08 zPY2%_!dvgjRtzJgYYO#~St}H@tg@zYT`gH!;Iov%einI={aH_p+H9Q2Yh7KWq3W~M z`kNh%RM~57%c}2KQ-L#Oew9rpmRqRCy`5FoH&U}_9EX;8Xkd{rsOG?`vPHG~aRWjT zJC|*(vVr+s8@WO?nha^h91Uj~)BhK@&n*(D^gR1X-f^XDx78dMv)j;Z7AnAY@v?!D zn%!dTt0rN#f!nJwruRy4_f`^bl4Rea`Bip)Rp?DE!WM5|1hlC3h!~v?8E`kv2OTa} zA4uOZP~1=1x>|&X!Cb3kkyxmKh1^*&kZ3$DvGdlCEA4#az{w&q?mjuMV3^y4D^^$3 zTUO1r0D}gLHN2sP{K-?T(CBSBIW;3H*~wMW{kc}PSr&?4dscN|#51Y`6Xjua;KM4> zf#9n4^ZD)Rz(J%H(Co!67K)KzOQFenY2n$%LaAN?#*{((e;Wd}JJg;^O7m-=rcmur}J%;RV z9&$@TM!*qK8s}B7NQ@$X&>|DWhzMKkym!D4CbWA>Z5hUG7%wmrgUt&l=*kP%sy*8z zR?T&GcUx5Ei4i-3(;Ms;*wLQixs}*~5qm`{O6OR@>`2fw6^d|Z54Lk z9E!&}P)pyob2k)fYG7#Y#-doKd}-|YFT$@7O;dQ|^uWW~ zKQNmNp>fT6!&(N+qG=hbXBl%Wb)FNrse)snh|NW34`q&jBf1Qdnu^4uc33b-S_rz? zljHbZr1Bp4JGVpFZvzQiQcT52N`yO*z+?<9{zCcFv_^G?f^=9h&yWife(JA=<>$w} zLDFhoz_-y9am~pE^JQpprwG!GHz9&doQfd36Bo$4y**W6@k$OvlyjEKo}cY1k&oM3 zO;|bMPrTbiq)R7GXUp7qm{{3X>LVuHC=dFh@^{ah;PLdm_|jSVoTqahcnUS;ZazEA z`W3#=d*#PV=G~0B=zC&rTbxmv=hZdVZlNlwpvjv{phz;>Sc6$~4`uA6Uv5p}0Vy>N zq9&);Hs^S)3%B+?S-@gK#mMhXyx;K8MhiOh7r)KQl0~)oR`XnTTx&NmM!7x@%bPvc zvMXhSI212=Q_shczir>}OPxDbl+3Md50bbgWp&^~!SHEYXsL=XEa@#Rs@%ua7;wRt z@jb(Mx|+hXB@)Bg#*?$k-oa2`V_>5Vx822EGs9$>yS3N0Ud?t^oPQ$DhbV)4d$F{U z&k#2=rJzr|0;!u;k*)Ch#Kk5ho$uyHwK1JWF*@Jsxu%WCWJZ=p#>QFT5%pmC#IDMB z+B_LzNyIE~!ZRVT4%aN=0y698y=0B1PU^Xm8@4*Xj%F-kNVr!g%C@meRvLfj@Jl%- z=I&nG3C<$Ut(;2F>$)znjx&0b@|eG8L#YWl(XuV$p1^haVh?D{9$cg#Q=Jz@n+F%7 z{t|m(&0{*M>S2hufnRQ~`VACD(`B&1(Ee4wz#=Rm&D)4`VI$Pyd(gJmm+EJk%Ii6+ z6SJ%3xKnv8&Mdb)N+8lW0+g8O;?dQcnmF?taJf^Z$K`qhoql_iTTkY8hAb>Mi8~$B z5dJQ+yeY`A5$-6&=z{z&OIt;v3TF;(p$ubtZ9^9jSn5vHpoShphIt!FmGf6&Z%z%* zkxNEZCl(X=E^dwxNw}v~uqT?f18mPR^2bQL*VogD7fniZV$;K_6J-=bCr*0Z49F4- z#_BE4pnU#$^W(`UyFH7vML{<3bQR>I$3#J9$nv+*kD3dr?3+X_s_2chy`Ue(Mn7V9 z#bVKqP|TheleRa7d<+NjPwZSM`q5aonA1J$efN*z!@;S9zG)0=k}X~5oK!2W-n zy$O7j)fN8@gh-HtK?Oxco!V%lxS+TYBbtFA6G$}fD5$tpaYslH6(x{>j1QyYZryNc zrLMGU>$nC%gCdJdU23anU7l%N5I5B7yx-rs&obEl-_QSj`FzMc^UU*{?VfY)x#!+{ z?mH4QFW|LLGq9Rs;XBPzTLgAc2=thP6XNo$xyA;k}9_ z;DL@Yj*e4BhvehN4!a4FjU6`SijM3S#3*$i`XXyHz~W!?lnE$*JKjqo+54ysOgX*G zKzwX=&@JgD|0?l!;1)~YxK;02N6aYn7J3>MTU;qE_3M@rV)5>`>}KE-H&90^vRh`Z z?&-%5FNBn^@b*)c4A?b%wtWNk@d zz^~saqor?iPmv$5&z7_OvBMw#r=HzX1$!m5JK~2#dj=SALyAG#@z^!^HOw+h0guoKf+%jO4#PHf$9(~Ma>mdbsOP6UC@^d~mb%`QT1 zr>MX@hljfoMK{KC1G;J(5Mc*2LfpWJxHlwji^8s>(x|-OVjh9WJ-mudM5M?H>TubR zw*``%+8*hylKGoi>FhmE7*Om`qYfbQ|MKN!K__r*de)~mfhg<+E?*G#0zcPzAA9d+ zFXMD_g#)V;x4AM$<1nQ+kyI+To7|5qiJH?+R`{5I92iRN33ru*(ZWKr{aRkb^|Qg= zwpPztCfs=c*!N4f8ZwVsj~}|7`s(cy?W&Yh&zTrZv%Y$F2?G^t_%K-b5K1^hXMP97k;Kv}g;*>rrK><(+MtHlbyXyBF5$=Z#Ghi?vud3c3u<6+WY9S>XZST)$mW)QszRT@;a zgkgeKE5|Us?IofLdQ<<%7q!naky&exm9`@{C|G&BQP4N0;9#R*QbfVjA_@)*Dfsv= zjsja;%n)4G(D7!|1t}@us;c92}D5B z0gj|gBpWA`wV+MqE{I54TOAAKDXvW@M+PMQtl;;55LpzQ+noq%g+SG>{K=h zWk(e1vX}If15RfTugaLAIahqs-DROZjq%{ECX&x3 z8P^!&O(cgzA~|wc+!%XuZH@85qb8EB=14KQU0Z#dBr@&LwD&WEiFU3d;@S@E)uZ`0 zXlyT>d%Nk4>2jpL*S%osJ5L9?6iA_VR@E4^DbEzk`57i%Y5nl++Zhw*ZoZPnMfS+>$QUJWd%m7xy$KpAaNr3_opmg9d~|iFqz1 z#Eht%U*Skn4@Xtj=9Jy=*7@`QrVYr6d3mE^EYomGbQHQ5I@4{>SEXv6vc*1$+09y# z{q04gkkgmDnCd@wv$6k`q~MycmL^N(5?CVN(SmXN#!a{n*W69|EB>UcOvttiFwn$B z;76v}%>N#umTsb^Vu+fi19_ZhrH3kQ=T)3Xi$K-*O68jRgWRaWiIS0jV;`_uRQ;&C zO6Esu-C?%0!7=gEO~%9?F%yF!G?m*VV&Ym}4E6AQ0TC0Mz=V3qp-2T_e?yxroGIff#rG@ z5ULX7%0K9n9hz(Wy(#EJPo%OAI~Xnw5DNMHx5EIg8C-Q;W9d*^-yh~v-}XNk4gF{^ z{r(F7Z~6lKihFyy%>c@s>YrOeL7TbQ@My~x&H8jP)+-k@kf;|1PQN_1X-2%*^`%L& zs**b8Uq~L14>KG0-r`oRe}n4mxwuh1>Hai#D)(hf|IU%D?%6w()ogc_1l@YYpZKbM&MYxyc2Eo+09&UO=z_9Upg zU|7>DG3Q}yB-J*vClmMgaG9puMb^208$01tGATMml{rb#GEA{jz=zKypGkR)aejVV z2~YFd*s1ye<$A_3QV8vs(!F!^Y>3t_A5@GsYHOhN|-XRH)ZvMm9d~MEfE55ZoWzy`{ z!g_*+$}~J=ud^5u$a2+Qs*EeQ(E!P1*s+MAU;$ZT_Jfp2Ag5a^S8Kv$ zG+74ZycM;{mR57P6S}v!a=b!E=UEZA-LxT27=kYz%<$bs|yesaP|KAug zy4|I+|A)Dy1dG{wg1OxsLw?C19~(j5u?X_t_Xr?g@PB}edOHTRgzhNG`8PLMP>o`g zK7~cX%y5Tt=NRQyL&VI42&D&_keM?=lt15RhN<)COJAMc2a8vgBS!i11ulphOWi~5 z7?xMSeJa1Ycsq4fCjmMT-2F~Ch-@bZ8CP8r;K>MsF>hFo-A`!K}PBrDB0tBIYJ%z7f`ZYM9D3?hmPGBn};GV`%hL?w&~LwuGIKwe^Do)-sD6YqBDdt_c|CPfHJPRqV8i zrAyQ^mJ0A2fVDjP1#5PiB+$S;wv`6n7q|N^u8q;ayB!)da8`gYUqWEQPI)4e+PIya z`fW7d^}T=M!RjhjYu0h3yNqWYXLEz8Mw>3ubfetkxO8{?C4i%fV>Vx86~M(UZ=u{aH9A-0s7SUHPhQM5E|#D2WBrQq)+{sZ4+(3lRTGv((`ow#p5#` zXRQp}RF|cz$JMr+md;G7H7ItwyxO1iw(C}&)cXQ&KQvUA`R{HkUZ#)7qbIe#bHqTW zK4X$~gI`E#AbWT}#-2S31MYdjtps^VNCwA9)%uW zs%q%BgIU|t9bu)C)sqKO*aO2IlJ?%E9*>jt*2sENZOg7{uY7BN`a542ST)AWy|6^Y zdsO8xR@4My-nz`cTwyxDsGnOZiA{MtnzgKz)Y6d(3KM>!IXdv+hw&B4yFnd6r5&LD z5qC@z7jgg?2Bn_s=pW2k-Qb+nZ(?WF4`HNoDhlDmv`iyj>WG zdhb^cuL7gaZ5|Xd^5(wAh*GNazc)sE#5^r8NxcXtA&jdz#1tE3rW;Hym^jyzfc z0}FO=5~b&j0oKrEqA1Oh6fDrcf1sFj3(MOdf#oM0+pPE4N?p0H^&p9M)wUT*ffJV_ zS6!;|gz9M)mZV?lYWe<|-pK07G6@Vf+ElYDG5X7zuckF$-S(r}tI)Nnx8hj?{Msi} zr!R|;OxV)K_VaWyu7Y>ec6A_mh$OPof)VY%ND8=L(ce9Ow>1rqr)x@&qg!<~v~>{5sIf#a(UPyU2B8@)=ZZaHz?4za7`I<(jq$8&jIuj5 zNFnfXOYQ;@Xq1p1`m+DTy)12NTQ*KD&s1{FyNR}o&Hr?Et)kt=Tf15G;Pl{ghZbi% zDI#DKca`jFF@)Cr9lr_RP0coIiGkGPI zo9%c5=~uiMh8R}`K>VE-Iu7$Y1FcAVg7qztf5hLUup756CSB&O_hz*Mxoty_sT1I? z7D5Jt8bX>yh-3ItD4>6y0)^awgeuT-lCUEQ@t1`pCH>p(i2UJ1`9t>CENj9aw&fP` zc%fM?>rRu;GQI~021W8&wPV<%k8%YWpUpRUd z0LomkP%j#98;RS+SCPDHP1einnN#+D@YgKR2$)st+ov;^mHA6%M_&AAh=JR!9Jgh$ z>_38(RPG0mInjUgtGM+SaBW;wQ7qWhJM2*8$kYji--|k!puaq)k|A(&P>FEpw<+9& zO6^k5fogz%23__Tg67r!XAc{VO2VAdM*_2=Az;NJ_I8bhz_xOfpb9@F9ZfKEif2t% z15Y@__S=lcyoz10%mL7UzRf^uP|yL{r-ftxA;5>qe@%eO-$kHT z6$&bjwAw%WUIR;k*9V~r;4c9#mHUnSlYlzM`&+$ij(4oPO6Kcn@WR+CGI~yK(V8(6 zX4uM-Uy_wDF63p2!M~9j`Zd}6u6lUek#yAOca~nrrxeiNV-F)n%NhRlWf^_$$^62c zUPz2ry3lcf5MSCM)cwcZRT3fYdxg_~J`m!;w?+*;U2U9VWT{vqsg)yCjb8mdI;-V> zT95pk@qK8FYDt9Zj0ja@fU4_SrTWrN;nxV(77J`8hQ_~XgLG63=|}e#L~j>CDhVLv z4?&EHzp(g@{cKuRG3nbiFtQJW9WDAu{AI1W2{tGEE!FlRI<}&YXR6dRN&k+|G}`qw zq8#H@91Xo-zYnbdz~D@v6|Moxh0YrAitWuky~gj{NNW9sRv`MD-(qUfb;+nXAGQo+ zUU6~@WhWKQmwlV+U)x-2Q&99y{!P?vBra6>nC%?0XXcsMXNEYv(ly{q<<0=G)5LjT z4K=ZhYe)a@9Lh4olbOUyKKN&+O!GN#u-8lzfwk2Nxg?ix_&bG2h25)|1P*Aph{ope z14KitvD*rXF?{oO#_$LH9x{Bs82**8vnH^IR2bn)k7G3n6xijb+lIf%V%Tn|uHwq& zTmzt+KXb6DtDN*cl*#KXeMnF7FO}2A_rNGW1HTi(R_+SBFXalF|8Tj?s`7; zd4rq-iE31pXt>P(Hg7yDH@)pyGS8c1@aH*cqFfz=$W7njoL7);j@J7URc9U-&3!&X zXR;OZ)k&$uW1HRGX-&}@-_UmTS<4az)<4hsbU7*9v{$P2Ocqg*vEvSjm!a=?>lx}` z3wfz#o&MBTZ+Guu*!Jn2rMK{D!a8DC6PAkJ^r9dh+}||UxUZKG)NrhSg@aV?x=3q2 z-Zs{nSGmq?*917zol|czLFJ!QDHW0&-3V>9tgSrpISSOdk;wtIu`9X6kc1S9Irh^Lwa1H%WayxDf(KTK|erkh)$<1j!?E zxiG$56kje5FBP26ODYmScz{-ioBgWCXr)FQVwp9^#IkotkXQPSiRG?HEPED-<;iUV zfgBOgvw*;{A#2N%Ap7;pdedyn{STkf7~SUC>(4ec+c-2U^x>r61Xzybj?i2O=+I`5 z3ej}k;Mm^Pp_va?pg93FWM%wIV>Ih=25|F8yK!?`j3y5#k~0yS-HXtCwspWwdw^ys zl|VGKt-eXA%H~apjBC;TZe6=okE@#&f8DO_S;d@5_XM!Jb!}Qrk~h(=xGRwN>Md9x zA1p_IggP~l*WZ!%ygr;1&NC^fmLcDw=X1~;2IoX_dQ39bf; zQ@OVzoM#l_92?^N;M&0ch0})DHwv#}fnFJl1%%^3+8i7Y&N7Le7~^;oIH}xw5ss1~ z9G`3%@O4>$<7vDXT-0VRZv{o2F14<=tPrl|8Ea;Gr+F|9eEXp{#fM)Gr-3z5U7O7I06R<7@6y-?oeh z_^50s;~54P}98Phe|j#k%*cmplUWqa6DE z9Qsf6;n449V()kx*M4a_B4TfXJQ&N&G+7-G z6FbyowP!@^d;Mc#mvU{gI(|VBvDfizK-{#`3|pPIs$S9VN`F?Y`I}&0VBE@KT&@o% zo|Fv+hea6AEy8$sh|#~=Y5t!Ohj*2@Zu`Dp%a$SE6#?Igc8%%XLft2};F9ih9B3xE z73P{doy6CPif_l&WygG8mmRU5K~@mlKF2!jz)*LYqbO*?Alv(@hP_6MCEneHr<7Od+pHLQ0Fxf9YL!+Ktw-D>ntDKM|o+rrS&`XBc;9E1E<}J9{%8#)B8SJkU`B-5Cpv_}~Jdmbx$3+DFb@NyZw{UG@*xM1L)iIOYcwgJl(2K2|ChG8}cM&Zt7iJjC zi-pLsyq95^7-2ZN2t##z+Zr>*o-;p)cq@04 zu5%pb{bJ0Hx3eP5?`#(H_AJ-N+c99qO+fm8rkHmy8&ehyl->Me-!VMrz~KbP;dTzs zDt$N(_cRXAi||Y>!gFkh=ku)L>Dn>vUG=yiKmE2v(+AN>vBQcjS9bSJHmk`B)19^Q zPTjP`)&fx`3@tuoX3OGLJ{6d1SND92r zxI(7m-j_W7Ga{77qF5gLJ1IcT$%#-NhjAr&$N^;sEZ`%M#~a@NPkAhl<#9aVQn_4A z!EYU2DEO-H|5tg$^E*3=Ip%kw>7BmZFgYyXfF#;N(>v4FN)BH$w$r~i&%DO1CCfyiY=DopeGOB?lERd+dt|4tJ05+VJgu0&qG$PvQzb=Q)RaCm*5yXE5+6g&6W!ra6}tg{v0|s?thg?%E$TFQUFDK=iHy z4|9XW-1X_f%CShx4NkGKa~`XXLGoMvuB(e?BpZhtUTllVw8Z;;apd1=&C@Sr9vwTuNrGr;PiQo^)NA} zk_b}@_+8iW`aomPfEj0&5424Z(e}7mpDqMGGk581#ddV9qrC4Ml>lDvpHDS`aJ&w_ z$6f#Tz9ae?c_rt&4+9&=R04(X2=>W7aUXFD*W69|d(Si7%=ciR!xWd6qORc$|A`%J z7mnM|t~cNxy`_w}#lhGHu6Jp<~_*I;iI6aH--W%&J8ftvZS4a&4!@paeF z?CWU#SRTXC+66tS+_e#{i6UCR*fgMZ?&Z=i;@)8lE`+iclz%+WQ2JZPSPpeqR$pQc zsWrwj$FN)7-k%*)eUTk9Sb zV$sUhIu~Iw+9_kTIpc3E7X}txY+^bt20hmpxGe%by9o3tA?WYt1Pt6y1kdK(7a4jr zQ}_!-IQ?p`-<&sEE-ftSvp(I{(X-~NdBB?TZ#4N5`qO%r1wOfgw=?0k@x&Zk;Fc}@ z6vcp~0gj{>;&^8!Xj8erL?nGt7IV9rYvcCxkfebcoNykxgVuSGF*rSjrgl%|?v9|{ zSp;oX2yI{p?Iofg<Zc^!R+2G&(SES3^;zvJ)F&qfXXRqCq+!hkxu`Kp0w z7w_}ER^R&(C@Gd5;nnRkF2jj5?&lF+!#>mXJKn{KCNrUGNYVAM@Vc|ED#`oUn!&;U zSJ&;*FL6UhrfwG&`@QTx$?8or(YB>MQ*A$%PwkZ}=lQO_`tf#tO#Eee+mfDyBem(a zC8enXTeqH1*SEdUGo5&Nd5ETM2}aydwV|%Yu{&GG4k>I~3#FK@4W_-(W&&3e#uYlttT%g*c<^-@E~;qV<7HIVP2L2lS2Xx6LRzANOD z_)|c5=V`eerJ`8v^s>geyb3UlbIU0GN7pHQ6%OC33k~1Q7+)QYo65Z&;adcb5Z}BI z-wwiO&fPY4`UkK3x{!F{lh3FxuDI9L?h<-^X;Dodk!cw^pBci`5jfsAZYo1E_ zXU{zTy-8n2aS8@!c8O(RfA|FPXQOI)#v4oQ^{4q+=C`_6ixJ~WzIK}+;~ea>38 zkPo+|L9;{Dlc~$5Xk4XH`dl~|XSHF-mxV*T`Lk!N?%S=QuW?gydaMvD(PIK)qdvvU+y;;6b+wxuQU&Pvmo?-jXa^aJJQOjUk@a@KbeQp_i z>iaMM>v#QE2kYVmN|ES@*`q2alKR~RRYvlR?<)S{B;VO$teexlr62phF}E}YQKx(n zzvJR{5=ktQQqiL(5&ESFoi0`PhrZ)q!crfo)tpM1qjncZ?On1dC(L`HDV3WT33EkB ztS9$#Z7MJVYJ(7MlIC}`T%}3Vf2;g^w_+I|CYxwK0vI;MJVOh3*pBae{DyLtpWTOf zS6dM&&*4f6@UW3Uef3ung_}u`8U6v*s1{>y4$U}HnAa)su{2?)+C1{__>{qAEMODcDoF2Vy>tgNFPT7`xI5QKOJG4JI|MCTuib z9;okuFB(#*>`S#80i#Fj?ag_c6H$lpq0b@uY{;C- zs)A(_JR0h}4OMggt3XI3f482*I&`vqG(>jJWK+dCGGr%~g9_!yX%;l|ofdiM=kiDuujHD$_^(R@?1lWfQlD(a9>V6m z=I`+wx7|oc^JEg>YcyOc*A|Q5U0~#`(gus-b{hKPf5n697V!PabX!;VOmp|OEnN+n zdt15LsHl)~{8pGrr)=G^9RduZC)HCr!*E?0kcX4RJwKI8WYy<~x;c{@=w=#o4s^5PV#(!3V*w|uf=Zhcl=#m- z5(_3<@g}{c!iq15X%vbi+85JnY1wA21tXk6LdFY9jZ?pBC$afLS4rAq4RfF%_)IOK zw9V5Dk5legtk6GdsZp?wv8lm3YY0bCGZCZQ2AjZ+PiBs<^8d^>77B|}N{QH0R=EHj zfW2$ZGmV&v1qK>%4s23RrzF&fTmKVl#0;)YBeuB6S*4anC^cV;qozDW>Cn^q00(yP zMPYeXskb^yH9*?mS|glb6$)hh8)@=hA4kTg(oYaDx&N4vF)|`!KoJ?={1}iif4cLL z=jkOS8ODCzlL&7WiyTAj7T@g_?Qr=WjDqi$bB*m8F}^<&c-0TFRLXtz31PD|w|c~(@8q>g4ii<4om&6; z3hQ~dJXsx35NXu%tBgS8CHK=(O|e{fhgO78%VQFQ>4K(!ECDr_`=YJ~6iYhu^ zwdoGjlt3#r*7(%5=YwaOfcJ~Zz1+yX3OB$jXyr+e7}!K zLFdSRbVuDd61mE$#XsBDk%( zn~Hzc_e;476dKA6LkHVBID1q< zDx<^%=ONRw{+6tDqT!VIo;qClbscFG=+_nBnhw!akA7oU1fe&$t0W4!#UyLtqtHwI z!FE$z!?F7iT;cAo@k`}eX3^$~f15^mt1ZJp_S^r*wRCobMp`jC5fvx77Nbr?sH-E? z+Xkpv>dw9$c5kzKN4aNTGDf8)>dz$op-*MbOf$o+_FJ&Sm+u+}>b~5B|GRg;E&z!Q z$}9cb-Sq@r-^DebD#eiYpI2!M&VQPquI$agp{4nG!tf;=39NU zU^_Qk&j>4Ot8G{AGZ@2NJ8^A7dFuQ?C^umA(x!>yth8a@+{Ye<(Lr55W^B^P3*F-CNZA$l!BG_45HF(IPQ&I{P-L|PK#1}Daf0MYIa(OBg6l2fL;PcylFD4lY0 z%L6Qx`&)#lq6pDvUj`ht2Z*{(me6Vu+V75byu5Uk2~GP<&u}Pe9g0~oin)g3%?QPP zfDN7Y6(NdW1t^Fd675&Phty-fYNn7^JrC+Y%Cp>6GQXONHX73YlCH?t>w*r6 zoPTL4>xOJuaL>xl?&u&04lk>tvZ?q~H6@GViQTNVZO*7hKhtP5Nqi)`nu%v@1=oB=DeWasIGLA7l1h}s2 zI9!Gf??`&-<^Jtc%DM%c?VgByLE ze6s1|1yW0Aa=Su9Dp$rzWxmtezxzD4hl{y3C$d>FmvIn7c|_ZVx!nKw0?^qHnUi1! z+AX~o?8J0LH+cW3&x|jt=Md_8j(2v0*jb;d2OC;wJD_`gW-P_QFQI^AzTQJGD1(~! z-Xmg}oS1VrisKOL^*TMtUmapQj6N&Oq?9+aXa2 z(SdMMLzX?=yvg$wO$f^d>C62AXGb}-zyC}4P7LvRr8mQPezfiA4Z2}(er2ngz?Cn1 z%UYvBp-`D6xHVbM7$nhdnQpPkQdTwxi_HU%G0|wqTg}p5sJ`Mvqk1Ze5GYA42va#$ zy$9NVYgerOGq^VG-@>tHQuL3R5()fntSFUZx;lh57Xjtx!78O}37-SfSprq+KTMXW zA$zOEYZ}IJOssb{vy@YZsA8~f6%OkKMfKOS6^GOD`lo5nCF>u!cH1&!G5(e8R5Ke3 z%LhgKITkowVUnlf0=-}9-X9*luiyPc|Hu1Qmzdysor$@0EbDvBbGx=EPBeL4>FVSA zO`GVC<`~5&&2$QWk#ifQ-w2N2FzZB!4UYV`+2Eq_p$%@&4Q%ienk2Bn2TnIvnjeJ? zW=^WgFBcWr(w&Yb%OqRUh=$LmZi*p+pZ5R$6RQeU+dZ1#ecVm(YR=~7+Ro879l@G*lyjnza?F2D_ZOcdg(T#21i{us%O-I#sT)^fc(N+w z@HxE-LGU>PnB6Fy)5?b~PtV+1six#sJ0IQp(W*C{D^`@(upZ|)JX|XGV@%wPJUvcT zI(zIev9aqZQK~FqPipOT&~$tABqCD(?_1M-c3jwWr*ea)+wFv?>ApP89Ds(6$2yMu zch8JqPkm2jUUby#i4pAG9P9-V>|PG`nh6o?>qFR|HScDySNcb66^VaOJWwj9L3BWO z4`7=Os&AU_VtK-g9nPkMnzc-t9TYpoUTmFFSZ7NzC0sd%1w%zjpZ3}-C-RF+z2AI< zGM;D;r%=eXO&Us+MV<)1llI=zFSQzi2jet`;?|5t9HV1@*B^hOucg`1RH(D!oC_l}|O|B0Z- zwGqY_L9Yp+zj_MLR~Pbjq6-65`mP6Tcn}tvt>e2!uo~$t4n7yz=XI3}_33?4&z7Bj ziMgNJH{_i5zT?O}>3`dSeMz(1>PW9=UpstC!;i=LiQ`DH!oI{Dwd5A+`Vx555}wrc z9f(M|raLviV8{AceJsPCz0Tg206D-zk$tV58J?Io%#?(-6o#w8K)ckd-qEc#mi5be zNo0w6OGPG=Ae@D{!hhsJWx>TZ#RY(wHqX$tseQN~wDjbKWPJ^ri4(TKB+B{9iD-62 z>F4JfZ1xY@(W|N+WfGWL?}Nt_%_cG?oNVc-bk028evGNY9BeC4g(GMXYVt*@@X&{` zDqP960o&f zE@=}u=_nR{B+9;sfb}l|_VvF4$~=Hsnn}7eQxG!;)f03k>GPR8s`p-(CGGn!%O@+* z_yZma+AneFX2j_J2>4WvwhCQKlBtRCKRxgo9!>;C6Y6Q~gt<_pg zm9Vp-g4b252v?d!O)D(t)EO(Y&m$A{r-@nOMvi%L#Rt=>a5IU+il|J_#$+v3@+(M0 z$N=b?+*s_tX1>xNY+92ddC|A|hH|$nS4k)0P(o z{IwN{@WOP#S1*1D@EL5WFLiKJz{xKyk6%3#+>V>Qn-keFjNyRxO}5_ zpUB~g*Q6E$$^rWOKCE{2b#tOMu2=CftRaT4(0jnO9}%=I>cx1XdM&cB@x-=V5l4K8 z3UDK-?dID0;NLxg#-+sQrpA72D_GFNK;_6aL13Pmu5lhl5w@*9nsH*(-)sgZ zENvUW0pwbM1Pb>xV5u(y2@da5|A?LGty~-bLmeg|Pzy!~y81*2mgAa0kUP{6>>VSx z)(|X<5R5NEP!l3}b)2!HdDs>Xbs3@!Pn5b!jOqmDXCoEVya6RzwCm>6HzQ&Sgm@X= zA(dMm3o#2u$IKV+m=G%=N^ev5gB#+%DH7ti05hzqXHb8--2e4G^r!4^n{OSUl8@a_ zvi?*(WZa*!y$WwU?RYJ`HP!k(RNF2=Tc}MyM~Wk~$JK$NVEEnq^`y%JRsG^#>A=*O z{7n!_e%<$lLiMBOOPV)J^QwYXdFlL(*sIP)G95(C6km{d3LH7~(Xw>V`oz0ATP~hK z%g^PfBBc$mB<36h;Qu?HQh8KlXkXIMsodo;_1B5|y`etzBp8-2Vn8WU9f7W@txRGa z_gsG?e?#%He)ZL(l2cgKg*UUBxprKPGp#H=2pdv05jkZ`>itz3 zNNeLvKm5CN`v!<}>ah0hShqX4=5EqI_89d}=TuJ2FVg9j7qkZ^~~Q+!BfJ%mIDbfhBSIFV;7RfHGMaB zvD;B62bLyRLw@r*sNa=1g9QjN_0Iz(e-=oUp;bMP;&`!) z#$-S|>L)O{cu}n>lBe4*PgPFllNuiviYq1DVTL<&q>Wx`nC{gQ654rI->1nKv0{Re zp9aG3pR{q%q)DNj)|d~QHpbd{wVE3UwDWGXC6OGF9^VNFH@s82RM2BsF3las0v7h?s&q*!gsE?nam9Osp9;p(ZtU#uGp~mn zEabx413a0kK7jyGYw6^SjVBE3w+C_Zj23p0aVMld$IUz^{iA>H;yY!FpB6E$pGoC5 zjVFt~air5Y{6-LyNsh=LP(=PWuLtDM53sO0giQqpb!+ZPni;3E+CSnpIy6gn znT67!4H;)0noM}34vo3@RteVo(IV#&Y459|daf1-wjH>M;^$51nCM~jw0{>Hj_Ex2 zisEBVNO*zuAJh9yc?os6pZ=EoUaSsBz)t>CI-JLyXWh_DjUfY04cXG_LA_W{QfTPa zFJ1DqX|DIL{A1v8PH(k;H+iQ9Z>?lw5v6*8@35g%ZcvP4OV_HjkN=vf-0(xf_F2je zeu+6%K(8+}CKXCe#-Yp7T1I#Fk{HZ%b*6<_rSRjxz`3^^ zDg8)fZs#`W_A_*KF}mGBm%oD^hR`GI7*;!b_AerGEtZtJsFhkh}PJq|`KZ@5xX1sbq=Cx4LRPLbo{RdFM{Pxzx;geiln*%f6 z?to%5c1bFw(1zb_W~{OQbJm!L#VB%+h}>z+ZC|yWDO&FM4658S@T63XHh{7AUy@P=_@QE&~tVDn~)mf40#v zu~LZIF0JaGn6r*a_4=Ao0~2i@b479f0B_IN9o{wE1@D6687o4>gxn+H;sK zyjK4~4LWjfE5j6-v!Owhm(-P|MbzxKj53KmHNqtJnYE4e@PC1WR1TLFNNmeBvBbVz z9k{=20}>Xy?j?6X{}Dt4sD2y#BP5YPev6{mNa#=Vy^=K2`{ zHuOCgW>UG6MS#nH2&v0|xT_?ijKyOk0+!P+r@cd1k609Y1i2kVtqD#^)%Dv@qc2s# z=u`P~1u>d`PWs+j3!;zqO7mcX#^+uz8f!!&Bm~7@7p)4d;tH;CoJXS4soYsm99YHX zP}e{h+!V-YCrGsK5)vMPz(m0i$Fq?XYBl^}rpRT<6|3CD{d6XXNfypjO!9gVkibQ% z`Z8jY{SkhncW4xoys~dx(O)46aY=I+mpn}c$6(g3(W{th9G144LC=JAi6y8|*|{>5 z_#fR>67Aiu)*9*z6bAVZ8DsL#zbYbKAIWM-EtEWX=(oZ7r9l_=Kl;bn97aR`SReI| ztdCUx$ofe2j~*R8`y@i5ywbhZ@aFDz6G7&Y`ns!Rej;u-=r`YLUc!-J^F$MQAw{C< zw$fhDJ{G~&^f>yw|G_4qQKl_sr<qYLOz_NbZl)eul*8~4A2#LOFI-IZ)(8%O+D6ad@k?`X>J zX)8&h(CLJ~#}S43$rS?2c;RykowN$}KC?z@)`V@>+wn<1KUCOE@dvo8WY^rpoRJot zl^$>J-4MUm-6Xn9L8#I1cN0C?4V&R6CUL;7-gSK zK9!wO*}aa;-6DQSeY>Pb^B`}NwhblC18O>(H*@Dbj3aK=vWycs7lEPS)iM-vaUJ@k zDgSyqxl{1S#nEFI%#w9_(N-YJs9;E11! zke>(ft~8th*9pI5BV?krexHAu5zQYWBMMjU*7CD0H*(Uym*vlj2-Wgnu0q9W(Dx58 zZM<0Rij=UTkw`t28wyE$N3M4-kFBVUYwjlfZALk-_!#klmO5e29bh(ss7B>Rzom{( zVcW(zj;Rh?d5mpe!?u5fZQmkn143*M)H!UU!3O!gh8FS%a;ZLs>DB`~!Xv}Sw9VKs zq%bDie?v>NP6V`$1qvm|pQqgu$NlR&4 zpW3dHwmv;H({Tyt{WX6#)j52RP<5bP%Fl1G9PEv}#Vwr|S<4|Ll{*#efxc|zS_w_L z$RQ_~9UqVZ{jI}H(p&Qy9^-=%kY(j_JJka;$#Beo+Ncd5n-vR4GvinWnofomdkky) z+4?=oJGq|@#0@t6rZee|%w+nxpVOJik9+U>JsdY}yGH?N+DZYhMA}u(4(_1rW6|n} zB!od`Z%5`7DJXnn2Q`Q_mHUa57~kRUmgi%IxP)s{h|L{8c^yV=u5P*iaEFUM4@X#C zfCKmg`NsTT9;wK7(VU8_Duy%a<#{t`&nK zQR?NKk&FD)&09+vrNcgrY~XLM1KrY5HB$=4Be47cm|5`|QMG7U%2}W3SFhwtJ`{d$asvLpIg3-ut1r-b;%0v0jN*y47`O zP2ke9dj6-s`?8)Tc=ef)8yGRwdj^p5D%30Zf)&5^wZ-iqYRt}B2&($*0wG9wFZlIwfPch;yvLAADyQDUiS79%u}VM2HFuMK!$Ibi*q)SLYAYwx z#oSwcj9H&H$pY=587%E64Q}cOGG**1E?%$ZlDU5kURJH@LEvIgL&5IwHiC=h+7tr0 zfo2Wk2BhxAD?xL;Fx*iwYnf1~j+XYfW8mJJ^sFV4wLg_W3tUP*O)^}k#<*4hu(E%W?V_q~&HHLN>b2Sp2c^(d1q>yGzP7mUj2On{z_6$ZDTHL82e#>|jcPZA ziT$J&>9&i9b(_{JUD)Y(O^>l~K3nk?y;6Q8MJep}PpeelZ%6lFxxZpwuq3q#uJrVM zJncW<8r>Dt_@7(v+LlW~nUi@&vtBK|{MnP%b4-dW%KP!W^@X-4hv=D>a%zca4UT^@ z4*-8h;IkCMpFQ=LYhi^2__ID$T}s!U{gnQ)=2g4;SKj$;*r2z#KKKDQ-kGBmf@|30 zJET@WB4yXSH#{4w)rnklH|c*<6E>)D(V*nxCkrcM-sy(@$ubI&qePvfm5=RKS3Ppz zw1cSl!gYgaX~>d0)cOUCCspW3dFAX>>=9YdlAiT#&y~q$WJPr~BL_}-UDeaF$1>`} z1u=8q*$-F*o+WG=Y|PGE3>vJizn|%E=ZY%^y&bm5*~dNl?_tid_3Sftg}P?kRWhFi z9D5QNQhMDS@qXj&CNRbIdmF62F|5zrd-&F=5v<_>tggBMhzR!gUuaMiW4w%&q;i|b zP`+~_1#CcU~?%l6=wX%vCmj>a?Zva%qo# z;l9*ILjr3dVmxc1S<770#ky(wb2PJIsL<5}2MylII5%do>P?MIs~CPAr2&>HAcUsxj52)9n?V_?i&0%G4hVAB6%HB7D4d1kYg2`%FfH~e^< zFWyN5iSkZp@DdH#8>?uQ2G+c_E$QFjy_49XP9C%O7_Ysj(e$eGySU&Y?V;#Gy*FmV zm`r=m>CiDIQ`d7C&bq9iJ81;d@>GM@O994W(3$%Sl*wBmbrL5@f8Whbr{s^vz#a{A zt>WwKfCkwQkKhsOjoFhTW>0u3W_Bm84bW4=jM@Cn2+^k1ua-N*dOE;v_HfuQZ1#5* zU+J_n^6b`%hxNm#yfuE?xp)KXf)ni@n8hvk3w^%Ayc6>l^IaLU>>VaCi=?uLGyTWe zG3O{)oB&IwbaM}%Y%h~rT;+c;L;gN7Z#Ug?9lYMIU~63LlG(nR(yPzkmA{DISUN;9acO=p`h|60_tJJz3; zt!GATpiD*?`sdDRAX9QfzG=V+-ALgoX(~Ii&~R$U7;k>3#4)CO!?M~jULAkB&$s?)OijDlLSpU}fH^dK zojup+y+nKP*0NTbRKBUOd`zZc8TpQeGIGYL4P&|sh;&1<-*%lj@SUQy$3L+S_@NGFAB;SJ*`|jSw&H(n!A z5rf>YP|M}}$2ruJ|LKN$>lpR!5$f|J)YSp%t~L_-N+T_XEi%PBr5QdD@Oka!VoA21 z9^76&PR^$7->m1UD<9VKeki<=gJTzSyv#~jev|nMfB)Mefui`SY!l0EH~1GQKFVBf zV#b8pRqRjVufEYajr;8TjjoflK9QIGvu|*}Udpe~3!Rj>BgoJYtK;;mNyL=b8mV$o zgGX$rZ}TCwy*v#~qSke;$f}7O{_YRTgHGRCrnq3&h=Vyg)F|(XfNbfDj0sb@5wQ@z zMqphjemTs5O;zK-{?$Y@(=Q7G{z@tJ{qv#m#plc_81%9<~ldIxTwrZ?=uw=5Kl{Tk#U{ zh+lC*cwb98OZTv*ni0$SOGgQ&*5+yrMX&#>c%DA~^{9_0TM_s1SG;MSZba0jy~wgSuQcIA z^b|ROA`yN6=a7}9T%~fq4_R4g#vZgq#uNeHAcZmN_amUJ^NywGU=V8Yq%P8f*I7K- za}zpK8^Z^&+m3(t{Z?l6#8G(M$VyA<8KN@t5n8DXR5^!ZC;|}-^v{MA2rGAlox4wDpl6w`yzK-G zKbQR}Hqb-3Mx&DcKX*3{q7io5YjZ~W)PEW0lV+SBT042_FC*g|H&8~nLFs&TI@Kmq zKbEQQ~anj-l}67$5pLyJnKbl@XXxzA(3+d4J? zd}jps>;Nz_*)YT!>$suTSnI8KcYNWKidb%U9w1{Ja-x_?z{2UQHw=Ol?yFt>^RF?* zk*6s)S~kSAyyoyi%YcZM_a1g)&aa2RwkN+vXx&?m4f>#8C}rCs>(?@~C1B2VqFG(a zH&D!(5wDLqsJMffA^dB2$-8!)8pU;RmaOp;D=Z(RMp=epuvSDJvh&}2M(}CH6|Z^an58woe9(IG z5B4yY|Aq1e{r7YN3u#=NO47r+eFjWgK9hn%g{FxZw8pDvfeXn!;=tT09d<3Y7nrET z&x9l2A$|_{!XCGUYcuHb2-pA4AEvz?j5Jxc!BXF8do%25$R1a=QN5ZG{ZfS`O6E`x zum|B2DbmbX@}fQJYRaap^+r^tGOhiv25&?aSE>liO5q_vxup6`s~qmo@HlbR*I<&r zyE|a-@F8NQHl3Y4&zeT#N^Uf9qnqGxJ&x14PA11VM813>L^k4% zOGTW{C1E>HiGFOzjO>>#b!6&*fz*)Bk(E5$EnQm1AfR((m3;|HYo;^g!347H@g%=? zn>LA9K($boq`&)9 zeRkeBZZT3$S9d3T@=6NNSrU&xzoy@mPf}j%M2N|ZtmNu6yP^wgon$_5@>u7O<7x?ZMC-5-Y;5~uTjf#9kncQT+1eDSvum1r|=N8>}P|mW&fZC*5V(p;*$H- zvOlV2ZDrC`_tBcR7GRdUO41Q;`5Wh025??4P4^l}YH{oLWiURpL&eu7Q_TRmoz0$F zb;kd4r>`>~AJ_XRI?$m5)|duRzhvvpzE64Y*-Rf11#8c~Ajg?ZRUg+g(LNmgb4CNk`x#Vhokir12)tfaBZ2ZvNm>GFfy833( zkZ4yNfx768boH&aAE;%feJc!@lqTcN>ZCF~6U?O#LXK3KRZqYxSjHEBH0{S<%yL(F zs?l5_?3;EqVOQ@Kd8$=AAxGA8cMyWB7It&myHP?=@VaQXGM(Yp+*Pgx*4-L2IsG!q zJ9LmR)z*85VAyWAhkBpsgk%{~d55IanG`mn^-=+;B20l?nr1R(;Re6TGjLU9xjiuG zOVYn3Z>20WzN*3(rgBHbygd@}_R%5|D?6a;jQ=WY-A=fgCDZiQOL&P?j`~iaL=KnU zmb;1U2k(WdBcV!j_pQ7osW)USb)5G4)@c$YmSoiv z=v}au?u%)*dPrQ)r~63dE{Wm42~@{*qu{&!eOPFNcgzq9#8nTRwtv0%W@C1?d_r;k zGE@~mV;Mi!A#~6or0EcNSzW?EMP4d1TfTzJ>0HWLh(-~g>YOc8l*-g+X3KfhWoGtd z?ck6?Ykz(Sl)AY|>xWE}oyuWlR0^+8)Uj_p_<`xYUG-wR`lb%RO=H)~Oa71SX6!1* zgM2`j?Egf0j)u&$PG^JHYHY|)d6P9f%4+ljSZKCisMsOQhU&*-nD>S-uN2Hb#xNDp z>oF^iLQ#~nhbgAlOl4RpS-IMx{v;fUTW#cvhA_CU*SsA*2uIx|bxUA+j#htOU; zV!wH9_GIvguI-UwJmOueFdmS4kPip)D)np)naWo>+w`+KCYhPHV4l8Rl@{!5#mjW$ z>0NHDHB7JhjpH=5(3TvQXiU^J_avn~bevWjMTaLakwraMm2%y9b;d;ILXh@5Z0%_z z(i{l5dhfbQG0^C3I6aj(uf0+jze;@EVa?Ii=)JZ+l{l{5Hg#yknqHsS?JaE)uO(x# zkQSm8q>e&-HPJo=inYK-RMux3N;zM2RbuW0?y@baqA@_!prEH-TOJyE)+)H{-QYP{ zmNaS=YF$sNP^$Od&~L^&gd2;%k@9}rxE}R?qj`hJ<`i03unZ;7@TK~I#2l^usqs=rWR^)8ds=jISi2` z^Ji>mraltA7RJ%1(u`xqPLXkZf47Vy&Dg*hNA9lJIF9DpjN{YoU8GQwQp1o)gK<=8 z;{Rc}fyIXdi*;+(rwV=>yuGM|iIvrRClb`Q+i01OCVulqB)vx@*|f!s4cRB<`t7)e z=Y&oEyG_QB_WCa`w3VYo)|Lm`E3?hINNZk5FzBw$3KxVXcCAb-?OkENd9%&9GP9hG zUEw|g7#mw}o3mc`UM6@lXgT*nW`SU;JeJrX_?GwR)X`t~AID<;dFV!`679!u+Yw*i zHggyPIcvx?mUgTv40z?>vTj`oY`KY!mb{|>IHe*&0E$x@h}3B%+Hy-pAhZi{%IG`8 z{^{+7c14_W2Uj%pupOJoeY}9d#S-IUyi;f+Ovx6C5nOj=QMG-L{W}WgI=`_#pDyGva9F9mdh=zcP-Vg|T3ac+?={=sZJp z){uIu%3h6(O}gF@m0FcPj_(rkON9aF9E6b7?R62u_{UIbqFBOxQn@8aIDfN65%cZE zdathttMW$oqZqkG4Q3HPoErrV2h-Egy{K&*3%5fqSFmsnSNYQcXY?f6t)`hr%C9t<7 zut~hAyp|D_9XrGy^rcmwR7jP3NyYo#i78nNekYw>MM+wejGTe#4%Gq1y-92fjOJ11 zyL_Nj)j$4Mv7Q##eg)P>k*mING?T?20&<*{8fHd?QuLxlb(L%%LU|JbcD`rmVH=nn)vmOdVYu?bQm%`e$t;nL<{*g#jn$~Y`9Kg+f~ zRm>$8^1rm$VU0YFn(sb)YRA|<-*z}q?i#N0<9U_-sKHH!*JTFQWyW#5#yB$QuhmuG zBL1fBr;a5as5Q*TDg57)B+)2#V?(sEGeMQPjqmgEeo@(;cADO&PZ4%;A>O4@v*QTy(QH3 zzFg%e2nE3oCn`GKR(!LgGr3;=87%GVqIikZO`Oyr_s||Ek5?>bz3xP@Gi#Z8(qDAE zlvC}_F;`KR+>OsEiFLL+@MYGgYpf4FZiu(OK09(ANtycWk!6^TN3E=p2;XnyC=>rW zjzJ3azx#68?S`0_s)&~nzca4rYWCp@E&XO&Gm@RRiyZTlTUx-7f5*_?Vx1@}T=oj~ z+h~loj-ML56)JeaX6w^1nm>FY<($s&`4(#?B*l+PP4r@ZjuEje+6^W(eOo4RaIcAb z(ioDEU*l?Zi+os=JD5m`bT^tL+fry60)N%CRd;KvP0MNDZJo&-O!vYWD5 zJ7~ZTx=GsRf^DpAu22&p8DfSbVTMwmaL#w^co`nv}FqgqMZ&_(yD z>qdI*kM@NBF_{OxUTmN3UmJ(h9dcFD;u6Edg=QIRYT-A2)_tw#r>4^W&94rv+N=Gc_c$s0lOW=wSvcMxdsS z_>JbMr8($MHO#cF9!pcmdY)+slcNyA2!xIRUMKqJ{X43a+-I5SnHkfwcSO&Oh@OK& zdREX#bG~iaD&$i8i$=h?90^1Q-!-!<)rC)z1<0B==rc}%IqwyCEa%u{ zO&qMqmMq~JykWH0K3|46^wlxh`7hf|uV;0AVab?mUAIF0ck4)vCm!xdXS%ac@a4zl zH44kQt(%QCt5~M>@>|%-jK5Oh7}$v`wH2voKKs%C7|fbS|Ff3I5|_{BXYIg|L$L&< z;jUk@w!~k(l@h3r{zvv7*n2fDDABGZTA>29_vVXouT6YTZImfYV?;f_Qc9sE0XI_# zjLj|dc6KLQF^<@Y#c=*)t-41>c3Wen`C`Tlh`R!(mloIb@@ z@A}>4x*w=q9^8N2p5s28l1s65J_w+L+X&+vmE~E>HJy?i|65!tNK$gu@}-G+e*-ZM zGr9Qy{UC9w+toXCyN(CG%a_`)hEhcGOiBN}lQpzS%u5^LhZ3>8IQ_Vd^D8)0)5di7 z_LoH;2;65)`^%<3b61LULypOSVvOBzg1 z==3mM=&Y~#IWd2N1{eXcX|FbL{B7j5h^POfnni&s_^scmhF@CMsf9T^BoQK(JsWpo!N9d)Hlbb#-@j ztzRTo5NRUCj;knGZ;S;{nu__oKWA=95O;sy*YEq+kC*1oo#)P+GH1@5IWu#Hz!*_w zuiY3|_8e(bnkNL6eJ9f=lXsM>>~dGxt#M@qhS_dWWev?LJ3p)}H>m6ZSJ_rq*}<-| zfpKM%3^QU#2W0kWR#`Hv?AZ>E%#p6L9_U-hT(hH*`J?J|WL`pnRQRIfNR3a_6RC_p4PM#cClIKm%+s8mJ15{((OZ2I%#zESi!HdHSS6<2t=?BbFCz0YiOb#n;+Gs$}xaSl*1C?02CM#f4x<*gjUovR1VYAL2-;#zHeN4jVdqV zm%Z(m&hal7MXhy8##*a75w_L=LzI${8Y~_`9 zBE2`&P&k{yLJ(Ed9vq0I2x#7sj>hX_QT>24xk>+G2Z8gS+;f&0(RIA>(f=pga-dD@ zFQH$h>JN@7cp5YtJVaZwMwS-Ue4H-doUqg7b9jx;h=p-wnJ;b=AD*q)~n|?%obUe?^j}XIIJo4Hk5kbXtq;nt>SI1d(;+YhSUvC_C6iq_nN;Xys8GK3KZsy$4<~GC+^9JC7 z)71~js+~i$oRBq~?v(S%#@+fD!YWrA5c)(Ax(5*G+fxgynY-*@1IRONoC&FLy(zsy5^XsU4bVeeW`c^t?6?x}gBdP!2+tz% z9T_avGiD?*k4u*fT!DcZUGqh7VERi38Yu4!()vs;=X8 z_VNy1%9us9MaC1;o$tqxbTYi7A$WUph3 z10qshf6l4|+~oW5+eeC!05MwAABfOuXK4K#qqP-e99pLcttV+R7YUMBewU#)Jv`oE zacCFK{S$Ss3tjG+HTlWfkrxW16u(vBb0}=m@HZnWly5F)v-jgh`-6Yh4z}Z9?v!G6 z9|%!*g3?Xjb7aSF|F`e?eTF)Q?SUPL2nW1D-sv;PC55asmqo=RKFM06O&WDqL#iM~ zs^MCYvJKRlOd3z6S;cBr|NU3np;@i>q>9wi5{R^yN@}zAOr^=Yk!N|PlvQr*_SwA1 zZr2Y&G3DiUCHbf&o$T}a1g}h|qzV($R`3${l2>>OyMTI$K6Sms*eHpo)vd>vn8qC< zwEX$Peo6Y{7e;+zE6OWXpNt8wg>cuX&s_rxJ`~u1)zNMdaqM0z*flP=kDBqXd>)V$ zO1mjQ$s~yq;~B$d$gc{ogk5yD*+VwXpRkHj_1)u2*Mm*t369f2=Lv=qbpSC{)H{9E zHda@Ok*2QFRQYHJRe5M!<)-N&f#W1M@8caKT-=wFRhP|N=}^8cOX87rcuzzU;9&1l15=4id` z*G$xD<1MW*tDPC~O}WNszjb68T{%sbQM_mKjhfgpnoF9hlm6%|M$XJ0ZNEXwfpo7_ z{Y2W;%d4U=AD|!a=2IH74Hc6?%T6Z)8*B<0fw{dimFTF&sGAh#0o?E>m=$Vjl+kQ!Z*!qp zv$?6gMsS+Th2qh3vv`FB%+!9(BRwi`mS?$DsreqRHCvv zc$XuhNzoT4?*^`0uuD_*cZ9e$JkJ(sy1k!mkeq7&i-m&-y43-xf|CU zaQn=u#_hXkO~>us$bj1&^CE6P^vgz8zRv4fs+bRD-Ue-&`v(7m!CT4+r?&nBcYM!{ zcg%4ZBxWRVMfo>!+%t&6Al}yhMdRT=VhpAMreQL?wOUpf*7shr0YGcPLu8($Q+Fr* zpyPc%8IpVaP_Ix9k5NgpQx<={+5`tVIh7QGvn5vI4&j69PPQtzpIsvH%W=wi{A^k=MCx}i?}V1TQ@1* za9T*!dq&j)?x!mCe5fjQR4w;wtmM9qs&~=C&FFcnI;1B<3VMFYHhQjttWwKD#kCN_a2)<`i}8>l4MF4^^shE(s=75k|ddj4f{z3KcEOoHTUvBsZtTe@1l6B{(Wk8 z3^;4c^F*q4K=Vm4!{JnrSvNL{G$#GuH?Ai%=nE!(-V&y96)f^3N|%;;4NR;qg6~a* z(*rP4a<+>7nJMHAF_=UI=Kd>T&Auib&PwY&y2FG6UB{mPm76HqylPUqPifb2RS6l_ z%&`_Sk({3;!w-PF1OVAy<^Wz|0JCj<(79iNj7_?+H;~+XCp!| zhRcqx2sXK?&zW(Ly=N+(Y4T8wK8l~iZ?b{V}cK7T3)bm4q{$_o&*qKt)--mO@m@m38duYLVmM*`z&lLrDI-$oi32Y+ zt5``}Q!&tYsW?^NH3E8IGoYPApigcxpr!1@Oq-qvwQD+a`1Gd;E8axJ z{N?_06Ad1bs;eof^Qa1JQ~^v!&@#=TGKKS824hq`8L9gH03P(cv;)MvY08ikx^P@n zbnywNIUE8i_{o5Lm zX8nY;3G3tyFyD?p#m8<8H~k6rcZ{^#;Zr)OulZrQH=g4#U!@sEFyYs+!=tF@2Z(}v zcFZTdM*o=&k(>H=gsV)|HyWF)3#Gxz;d8pf0c=JD*g!@rM@By$tc-l{gJIWjBE6@= zo1y(3w*61e9Gzj$BEQ-BD4D%d9_3!W)D$xThJ$EJ;4g2O<(%C#kDfqqs|hP~_*zxq z9aInOj@1APQ+_l~zN`nB((U^iCpSi%{Q1(DlXsFfP97p$mm*@f+xAiR=wn11g&C>F< zokTbgq$9sPXGenCdg&f1_6@KEm>Ns zH|a0i*>(}g-tW>RL-~RbWiM}6$5o7izCzcgRR_CNk|t{8h%0Jp_bnzR|Fho7FqGKP zarLXh_q2H|3vY&`s?)=5aBk`XLjJIX!B(qZg{D>iaaAtX19-IhkKn+?DTu{XF<_tWSo_ML=u(E!|EbdJheQ;cz`WWxa%25$3J z3j*wXCBDC_H8ScrAOp%wl0pzCWUC>#q)5^P9l5fZRHZuQ&>`L{$`)~KG-QLI9rvPL z!nvO<%i^7iy%|09A_GVTi}mtI@RIb;T4LVt$+{H%HH=o6U}&VAdZXg=i`f zv!++Y%$h)&uabU;bwrqP+Gf0581nfcDg;H4khy?L{kTI6uYcMb*Rl6(wWCEL{mLd2 z8D0Z;-JLMdEL~jfsF8vI4hVkoy&?G1MiY=**V0IY;rvD_)JqrnH3zO|inEe#BX9Tw ztNf<8@&{c-RQ}Q>q4YiBQpv{q<<>6kZD;?qInU-hsrp;u;x{?rp!ii$@r#1uHrZ;b zwoXkw>E5$f!{~{sX9~Pc)!!4>Qy$fGWK_=qVLiKHdNl&ow5YW1-Ws>`tsU0oMSgWR^E+s6z6#5lc*r5(2D6RWI`6%g@@#8mX=Zp+ z849ickW5g?@`Q^Qr4{^X8Hq~oD>@9d)r}4UUtY7NsVc9c&!UURX#eG$T%A2g^?CDR zE~L3dc+~vVN{1Xhsh(0|3R!(@WFguVX8els8o$+qwZZO&6%%#tW;er9dy-598a@$J z{AbJ#QuR+n*-OYq-IVat1b#vm-dz_->B|?JdGi^Ey%9(n=%b4ywvSL+a?kxwya$HP zY2R*aYEYL*)!ibF;1KL#=bAYf_k=lBf`h?{I%7a4ZwR^rc(<=N*}DoLfw1lZ*yfI= zaUm2SjwWrLI9YsZ^@PG*QeVSFWb6CkgsKi37JE_pd zL3Jx{;dV|}65o!I-CbT=^Y!?0Z(I+;^)vJ10$O$r`oEmBfqCCr%{4~C&G#HPXL67o zJnmNq18=&1YoqRS$3uAf=J>elUq;%f+fj&_oiW}SLkuw$ndv$RKy8;3+FVC37}BM8 z{9AUht#UGNisvv#%;X*5z#bTbZK7&6NJn7%Hv^j!g1!4|1KV(|x=7870>?<~jtosm zD6aVEX&Rdi5%AGb1n_x!YChxLGQWL{NA=5w$Nk?K`46a#9r>3U9^XcIJbz(K{^g{N z{G9?kHt`vF2n>GJ2TkQVJXY5j9{L45Y=(~?Bfz6(=VV)6f0Kg&<5Mn$ zP~`-0E!Ndpi4*6Xl{m!eoXDuX)Sm{d^@$xF6o*Y#)gRN|ZPpxgbjIX@#~J?o%X%7C z&Z4oK`SK1a5dvFd1P+W4c;SLjTTXSUr1jE;)3tw6{~C8qFEm!`g`}vpBz;SJ3`^wu zVcZIKuxXV0F9uLeudJ%Fw08ml&IaV0dEC@7Q0u?g;M+kh?>IHrAqA(tJU>MAeV0l$ z_LbYF&!c7{Y7Tg(mF^gqu8B(jJt}=qP#OWP{l)~N4mrhe-TNz(yGC|PS8!y8HW9Vyg%)2#+)CFyourP6x~Gfv zL*3;a%WHlnaNwhQV7oJ3G5LGcEI;ru~b)IYZ<$k}Rcqlg`;i~X4 zW|ExwiYpu)RXA1^zCaZkZ3-w@^S5$Cc6|A8kCqmhq2wB?X@B>fd z-x9z2pQg$LKhLUJ9#=D)!j7Pi#!*dQV}jxCcUmRh$k|(Uz>SjgqT>}FuC|DV@UIvl zxtVm1^0T53C$KK=x=*>CvhYV9udYE~IasadaZ1$M(Jy$ywO*dAC+=fp_Li#Gg?j;? ze;1)OP-s2m8u>eT>h6aRK3l+#4*R4&o1)KXO=p`CUe%w!ZoNFvTFf9sp1<)Vx1QlN zA*{vNF1W~Q{oH_CD2ud!+x#qB^_$Xp;ZK%I1`c;eBzJxt_LMe3UT+H7TY;0-O=yoE zWTAM(@|&&thM2ap2ME>st$EPO)e*xkR++=aN*DKtP=W}We{4G9_7D50Ft-SYh2NuyBWYh1-V8@+w(ifw z%|@f)9@3o*{KkKjluzO^SEIG8Aus&Mz%S$_Y$fkvlh z^S3fGV`_B&+)Rx<{m2IW?vJvCQ{d{TK7{ws)wk(P<3-Y+vBri<=Jt{>jyzWlM=~C% zj6Be%-fPt_`^>6;`OB#K5mtTwsQO2!GOYg2u=+z>^&CUg;Lcu>3weZQgd6<(o@%_P z8_6$T!7%t~V?89D3{2PiDyWZvJ_U5w36CU$eY*l~1HZpuHFU6;zN6~TGrt0^P z+v#r(Bh0)km%<=ke}-cMpuYRm$Xoq|8ETVN*)rXX$KJ{?O85vZv>)5S21)U~^ecfl zJO*)91Y*9c7!WtaAg*^Hu5=)Fbs%W$gEjz~VeC}|QNvh{EzjiL$D{_tM!8Z0DHZgA zA|t(b#P`vF2>G52!1>P-##a{5EN#VHfE#Z#eA&Lkxny`a-y`IzI+VAr0#^%~EQ>d{ zJj6OITaXE?^LgnrziI~^(CR=>7FJa`rQTLe z0ygtf#!f4l3KKe&V_=)|c=#PD55=-fOuLRxGznhKlXwm(I+MQEAuonlSwW4QFkkA| z43vV|%$_rER7b+jr1ARH8k2tNFX%qKRS+4ZdVg*iM8b$)pINYTrK#c=&jj`L= zZMBl6%K=^7CPkdAOxLw4^)BRXSy|%o3zB~7B`uV%G^(AESIVZMp#6jRFSu>Rb z9cWt1!bOFbyCUV?C=-WBY5JGy^G>4QYu(;R)w$fT9VT#YcB!BDcGP0iPofKJBadq{ zEh9@H)ZCJVZDj^o9B-!mN3tQE48C_-I98qDQs|QYE|o+pWUV%#{XMeFyz;dVOconw zP}9Naf6U6brc6}RDN#*Z#yTl(e1;NZ8dTz6af#=n67RV0fW0s*F}3HMhHlJ+qU9j` zwZw91e!+9KR3eerf?jGC@q4aYmw7+?JAI;V987!pCQE{rDco?qdNPxH=>>V{{f0Gs z=zwkge8C-L?9Qk_E-fZS8dA|w7tk~3yPnzgNz?ze1I=`0ssE%N6}|r|iW1je2JT*s z>v%Qlc!_K5;tkd^#}kdFX+j@$FcD>1uG16Ja*5i$WLSKI#I#=2AO%!_0@8PhY5R#s zVFiilKZTLii8|{5?zO4#hBCi|Nu|A$6Av!;-VOp? zG5EJE5`%+=xnhM^k;%T@e@M%ZM;;$M^dw47e9|f zTOE(?YOP-FO0@Eh!$iN51rn!mN)wN7Nn!r?d1V4>crK=L67TgQVq;b15Zl-IAfsN6$AuY0>{7kIRuIyRWj-D9xx{)C1?U-kOX^wrg?qCxh@lQqcR!_guL z!YX@-_t1(tnY76C*S*VxId2KC_`q*vgyuLMu7_s(%&z6Ph9iykMO>3W_`Ag!fO13Al^25zqqyKz$7pv%r0SDEK!{M(sXkleN?J`bS=8 zq0zbhhu5I~t50DIlCJ(Z&pwjmQr^(5PbgkH-v2)x3_FIu$3OpQL8T+bGqiPy|=MDuo}} zmaLV?}|l`se${EQodmd};>u12L-qj!>;|2p#}} z;04AD7s>SN|TiT4V;jj5r%er=7(k5h9)im?%{gah2R!5J7f6+c!{ ziYA_Jo?WJ(?eK5<(;tc$@grVin&TAs0gd;{hwNl-E(oXUzX3VkY30c$#xl1rX=}3= z-g1^{&2QXC^=tUoAz{toKjq+mk6{4(-DCJi8+;wlcks82is3&*+Ti#7Z}2h5aXE*J zym7p}p=s~O3U7_Jzbz-$eYxj1748LUPDf6tfYC!8W*0fkrkY>adgDBxbJcT%*=fx% zYae3v00TMZiz2Tn#Ax2lFj%`j>T;NBgOaA(oG>F&C?cR?4SGtn?Cl0U)4jGpUd?aJ z2GuG!YwdDC`3YWm(3()4_PgV(2^VQLo+*4^RPZrsBij)DLxkw8kueKTCv7a;@`h~5 zx9S<_v;0TdjMH;YBVH?x)akGro0!KbxZZdL*PE2C;pVmGG0N4x8q)}Qz_?+H{>8kG z2ao&Vn*mtqFx67p9xxGJ9OE!OB*t_!*ti8?k?G}iXol(R5do&hgQ;7=i-H&bTE1Qb ztpCshk$iUs2q4dJAYYJ}JLar0kgJIw0jWyusiATf8G`4s5Q<@+k$ zL(X&yF?(fsOZgr~<^PxR{mK22d?zZ5h4Pnx=oV4o5Z&7ljph5@VKJgpN%Ixwoj_Fb zjWM0&zs_cv|0DVSgZx7Ru-RcM`92yZ(%7RMrpL#aP6C_e@_kS6++y)6n3zk z$~rsI8DA|q&61N6vvTSN7R!Y`^#;_3mYJ@-hD4&~AJmwb)xTBUz%-BSx`7ou_?W$k ziD~oPkGbi(fxXDio4YzceP_C@y|kXYx?}XxX71{u;KjwH_Kxq>R5aEGCBc08n7h6$b913IS3pc+*5Fo~=5k=A$;QsD zuqjplH5E39Gv3^q)u4{uJlD^*WW~n09SMKAa9}Trz~|<5ZJe8|8ZVjy>T*P#qb*mo zxg<0fDT(C{MW1~eQ>1O+RWFL79Pkb(x)3^C1GOU6I4~dwi+_aM(p zviR@g0_neKiMpm_@RWHAONuf5vlf;pGzX6gJv4YTY%>fcX5r20kAw|uWo=-|Y-o-oiG8;dBLJQ}j8!D>&a)h!6P`muaw@$3$mofLHD2Ubzabgr$A$famYwHlw#@Xx z{(}?+D?N8}er>6qcvT34LFsw4)E$N zyg=wEA;hx}*a%L!cS4DOW(*{xSN@dWb$8b}q-9N$bBd~A$0|*=tD8%8k z>vc3JLU@bwv>q0FTJwgeT{lMUdQ(N*t|Lh!no0lbMF`=>G+ykQS4VTV>?s7|eXwga zQdEH>#(5u>r@ePlvqxLrGv z;DV#Jn2c2YUJgFmdPR8vKk5HbP{$z-iVEltd@0zf!FlgKO7~eg5nnmKbhlqt66qDW z^Y?OZTG-sh_*BK*zu^8pPAmO^eiE$Wo~}aeP&bAp{b%M&AG))*4T@Po3>a%%XyE+9d@4bP2A0@G)?x_z zx8DWPU_w(x*W2+7ofAyd{f!dB@1|0RUmZ^^j&TgG;GLs7Fc*5;KU{r2lXt<%#^i@T zGNjTmujd+T_{I5b3KcVXGs-(mSFPhnZehld1M-jI)%A6w{BNaqsyKv&?^kRFkPzS^D|)k1A! z``zRDs#$(LwYaAHe|t}+%iAtr#ne+ToD6$2quR01;SW32*f--WlbXTw4FA|SBsX?P znTF?~)@aYclt3bTI!<6s-8V37=fN(OL`_x7ftD#;2Y2!Nzd6s0(*~`nYGK852T;58 zMtUUtBh$#zs(Q}W6xSw!x$>wqh4ATX`Sv;$;JmN7^F+RyprB(yA?Fp z-S4Y?IdwbvDF35x*CG06QUT~b3G`;7e_d2rFGF{s6Q*QC1%{#jASE^a`}gcXbuufs z0-G&bjD&>M_TQNe=j$%-!GWR@U@l163b%HBbO8z{3=MtT^>MOe3%l}*1ko6@pv+Rq zhKa20gf=KMaYn$Zu9XN2X1W_7%^pi49R4dTzAo(V?4|x@FJWN7K{skAgY){bAKbmx zKcc$*f-Ql~RUFjUQvQ-Ae*hj2T&DQ%!~dP&KjEP8B;9Y;I{ZdR4*N%2e8(K+Si&HX z^!H686$8_3p{^>{9}c2%g0U6X1!M~6!hy!!Nj#ziCsYb=QKAirsU-Nasc>IbPJne5 z?*Qu64A7Flw*d6QKmn@2TKN^6lT@qV6e%G%ZIlq4Zzb7+^WtiQvw(NttT1EU5p+gN zI3s`~UwWl{>HFXD1AF)Jj+Eh&JJ7D);o#(w6g$d|!jGxWAdgi-kjs=1v)Yjjh(3I&PstgpEOBEBa!in1HD7#=+*HV8i*QG&1xT))R_8B|u618JZ=PvdC zJWC2!#~W7qH!Iy|-o?!1#+EeFbC>#8+0Tob3YS^qYw6hz?Iz9_g^J@5PLGlluuajG^`}-SB+JwB+@4_ebd{2QD^dkTThrsuTm47== zfab=4)=|9!R3kuIpT`6F2oHB=o0Et9%&2{yLidabMs1{m#xh^3f66I#x}WheZ}l+M zKA+kfPX*nQbf5RD_Eh8@)tBZT8UKRAwJv#FiT_89-*sal1gai+T3q8C$&?=cnE#RD zRIDerG`7$diyG5Z|0IpdF^d@astEayN{kE?|MjF8P+ol3y3r8`Mj!_}zi4uh&UC+v zOip@EvXh<%VZ1REa2oYZYSX~0)2LG@?$|Po`qUra3ni=@8L9gBz#^#lxfiTrwlPfV zqnN+{?qf9`;^kF`HKOnN-U7gjU}sr8^^_=`!p&%{t-1P)Ezt?kC4MI|u!jukoqT@sTEr0*Ux!H+no|w+LwzuH~EeET$H3h2B|M+Wz z$e2n0K#!(C5Eb3vc9uLKzs;M*y&K9ZH0!PJS|F}$>gzeh&PwZ()<@8UN+|qj#;xD4tqHqZZy^z z{X-0gYa<-aZ-zss5Qit8LlW$ONm_H{RIl<(x>c$cV>adH-K~OD$J$hwEUHaqYwF94 zwu+l!_^@Vfjz4B@FxgHA%^K0<=)oTs5R-(M-OHJu*96U22`dLeMymdKux;GUX6KxK znlO+bHX_EU`Z*%jwd>K~4$}v$2<83OqfOp#dn=UpK4b)vzM}ruJZ@<(6xq%Ua4boA zSqfDRB+T7v9vsyy_f`^Dk`n_F_NSG4%WQ-r0LBu3b;dltnzMawh`uYm=Me1$8X&rF zglJ=5L-cDzl`rV|HlDe#f9hYaT^skWBhV{Qap`iC@gC}Cj=Xh{*Svofgv3Bt zJ@YrdO8P@*|8M)(OAT@Vx|<#a?4Ony>}L#ZlkEuxJMLdM0nZo-?4v{2Z$JC%wwF+D zr+@8@NbVW*uTyKSNzaAXCj&oKuP)TMd+_Ta-&jj%kl!87r0TEd*MK7*zyZ)b4|E}( zk?=<*RcPX$oCJW|{bU3UegxQsJ+Gw}*tWk61iAj|FT=qM)#I*40)d?kJ@uIYfxR8+ zK&}Rmld*$cD%p6xdEPGB%RFqewQ^RquXI|_(R172p*Vx|}IUzS?4ZAEgJ5)GKO{npTV))woX z|DXEf(Cec9IMNVp)*l}MHJax(&~nXliY8c@!Z9G|c$G~?&^&ki&wuKVb>Bz*aRF+F zD^&iM{gGMKNDL-FiUz)Q6k?O#&Oe#E6xFFW#O}*69CeUIIZ;$s-+K(qqF7er) z`7Qp2^DXS^_gw(g%T@w4Q}C7sdyYBRyqY|vVOHbDV9%Phhs~m%BPI)9Z1L-{NbTV7 zaHpfecE(kP?X!>%wnZ_vOG73ihKr93#qeR5O8Uour{vyDc$*b!b`KQx`v3(kM<|{b z$!WZM`Uh9-IDO7l1y0Qct0QikQCvEZAHz4r1Rrc0zg5U5GgakX?V6O0_pyM$DU9*q zj%VO^K!jhL06!Yw2WLu@K^by8)!%9SsF>Ftl zN;d5MtHrYIFCW=>;qQM46I964KAB6vC{=$%2*Jxc3br-wD@K%j2_vL0`D}Ol?5Oy& z8iBTfVa;Ji>r>IdP)i21zClJn;jU2Fa$E=vs%`XTus=jck_iV;4R_gz&UynkGG<0% zs1XZ$O*vP{xLDZVVRX{RG=9BqjH-(X4Zq*V_)UDm`bEP~Z21ar zKA}jxWr-Q@YKofC{zK~@>H*w$%1{^b?pkAtIs9C+UjV4ax(eHmq0<$Um|a_C{w|z& z2_}NH?w^fAc^B?3-k%pD?OOMAuyd_j;!?>5oy#BMy{eiP`R%&r>TsCOr!0qpRQ>p{ zJTXlv(V&n>LZPy}B?>o<6AEjEJScQ=D2%WwE)A=2D13Zqh{B65m2B9nISSVv6ru1o z;|=$ihULAybA`hDG@MWvFBB5fwA<9Np*#992(fplS8xa4LdN(5Rmaq@4u``j5e~fr z92$u|Kv+}9g=Y>h6f`eghwCdVz<^NyywJ)9q}yEE?H~i*vFq_3V%H@AaO~>Cd&sV)LtLZ#w?Fz@`X{s} zPGIW)z9xa~%z5K?W#11j<&Mr*aLohqknHS!SAcv!SHEWprpxLIt&WmU$&1FT=i!Fz zjm+?De*j7YR$*{x*8@D><5DB%3)lL`iYiA=GDM#Ntkj{8h#e6Uvh<(Z4&W17S`^@d z@$oM9sS9p`{h@Fejb6_=I7<_yE4SHIu#Nr=EDv@xueG&a7*gpK_WxanEX@yB*ytp6 zyR}l|(s~i&@V*|rT?4#yu%*Dd+NIRz3LK%Mc+fH0I4T-8QGP;eZ;!pGztmeG+?V>V zaZWKXcu)~lP!7DqO~8H%;W-u2RrT9AxT{Mg8wq@7_DmjM`dLZYGl@D|H72eUmkg%r zo5DJXyWzMBr}vILLT5e{2y?BIr8{HRZ3<+%}g`S(WJklFeqN! zSJhj96|uyjc(tlC6fbe9WW%@raY-STAFtmo^84a}9z?_j4*K-V_jaP@q+k1J#37kL zcNyUQ>?u4{P_ZW*Aumvve0~jY5?}GyT9Og2>sgjJqK-lnzy4w=J`(MP>MBO>qGhV?$ zE0I^!N1Q!8tlDvQe-UJy?c`F)hNKxmEqBdm{^=jNUu^HxEE1k}6jROQW-Z{uV8x_Lz@@^3n8&uYHGr~@OEu3lHNd|;XCLz0>mk4|Aq+|Hn`& zaQA;`LjfiaW{xb6%CgK5+>o1fQSCay*m41g)7OQM!!Pu;`r_jM5k=EtRDJMqs19%` zwf})p``0ga?axE)|FCJ4h9UrSETb|zF}}K?t zT-w7gR^xav^sy22X~1kyz^1BW)0t^qWn4wqi*UBVTjppfXP6W%+FLyY9=froLD%iN zh|b})BC0^gCPoL(%HTOJc#aRAwr8F1v{PN**9OlUdFoVy>d{bEX-H^AJn2{2NruV7 z;Vu}%i9oaNR%}B2*g}(qmv{uCd+tMKS=d^ey8-0yY< zD^;rpsMCr5PQ`>d1}5()o;s=zN8!n&$xZqlUDX^ZcbAx`9o!s#Kn!B%P6lEZ2V&nC z!~g^Fcm!gDc9;EZJTC>J#AptIJISf1Cw`_lMC%1GXJi#=8p`3L47X%y|}N}r~O1D9(Q z-4ao>2v8wK;{&jM8^B20A99zSuf{*Hil1nfc#(UFVd;cjbV}qtGI-YEW=EQ|gjS4+Zp&*WQ zPBY(<=FB#p{b{dwJbQYs8hlM$&1KN!#Q|F_07@L@gc4NTz&81Ma~amLxe7w%^W_NyXL+R{924zX#8w}`x?>d%dl z`65E*R$wT=kdj7wqO+%@^Bw(WVznY*T=OXbd8Qw`JHvE;kb}`oJ*)oOo&rP%9 z_UbG|G8|cJ(O#T>Mk#Kyp6OwoYclWYoS1e=e1vGQ{UNy&T}YNroJ^wIynL;R5cliu zyWzL&AJbiKG1A+TnAMSMTUEuRX>V2yrRFoO=Dd-P6<@@xm}jiG9NdHheulkcR_sQa zT-G!H3^{Oip%}nU`b@zXnUKuMhWb-E!yhP0Ike9Gz70_a2VVu>eP9L*{{uBa$OXW4 zV7_K*5_0?r(gtRrfoVM5kT>Fa z=dvM(I3%Eyn3Z2Q7_F!l3Rs<(m30=|wiD0JY0E*VCVpvbv#@`D6H}v#O#h_D%}KKI z#<%-{DS!XsDC0CTYA(;ss@jX}j!|}+?3V6E5b7v|TIQ<%xbuSfLpXXRNl2x&$}wk7C2pHDq;7c8rj?z!QRw~ZPqItfy;4?7Zyj8eZknsF%IgLfRqde; zmtWgT`AW9sNBuxflOhqPjF=J;^K?GMc#as$cz_HeW=FD(7|Zs^rbnQ#dLTr}aG#fO z*S}x&=btbr{QW*`(Vr9RtUo_8&szR>uSVjPom9(jiCTWfUa@oyBW=>va8KNy893%gorZGFgExJq1E5@|1^?)5% zFz@dvrfG{0@AUkOcn`be9;A(F&)gjwNzG?o0sU13EdX>0U1<<_5#Ewr?EZ!Sj#R>l zU@)3(ZeY;KVQ@~2!C=Ec7PZ6Ry**+K?jda$90~?baM;aNg+1?7kjF_0264uHojT;8 zJx^M1Ag!nS?FQ0%bUAMzt%q>dTm@&Y;AnX@GNLdZv`D9}#-p>qa93~w!%G>=Q7Mb` z05&{o01Foa)nPcK8HQVS4=}v(F38Oklw)T|m)wL4YkLR__iemHPL}jn?U$~(3@pbv zodUdK4GzYGj}B@THs?Jc2dbaVF;w@6QEhFgu8NrX56}yn^SBVz&+puRbN*_5RgGHi zl@!rm$J+r((O=FX1@0q2xspx&&&5-Tv4#pJx1`o#(cAUZk_PqZv?mn*?8J_nla7w5_rEX2nYDY4K)7rSrL#`Du&|5ghI>Xwi;*n@33Ceo2-yI^GG1R1^b_>N754+usb{S_c5Ya}_~ z6U8Ywk}BB^+Chy8(}Vr`FS_ahR6{EAODE|GPr+rV?@s|{TDcIl%@HL4W`?8g7x}C1 z6cO=n{fe)AA2}S;`RAg-ws^75x zMEcOy)--3rJRO%Alx!+|>RAD|38j?cKS8_M76~u!7zl%$pI#14q{T&MGo8bS>IYt4 zEg9CMll~sJZC}TRRF_HIH$DiXHumG1Mg+ne_o69SO>S3?_+ zT8u0-oQ0V|;8z-J6zX)5fB!u~|2K6Qx}qZ&-n0)F9ZX)6^8G6!_Hn8npvE)XvaKD7 zU>WjDDn_C1rD7~bvK_U~dlzPe9kl%`Ado4n*^#V3$=uP|`g2FKqQfqFGiB(aAF|cV zic;p;8{#r&P(}l;8%&~(w%z`9$a89=O6wZduWqLWge7YTyIG1>S-)%4Zh#s=YXxmI z`Ict=s{@r}25o36JokUJbJfH^ktMzcTc%!T23@PSs}=xDsJ8JwH>2vG%QHLZL8-R( zn&T!~xlFXqVy2aNb}>^d42u4FWwbrBuGTL5ja2&;sXC64ww!PU^Q}}aa%~f?JQA94 zY2sz`EhTi4EtO9q&#XWat%Dg@$S<3Jjm-?|&{X)wGm?;K^7WciPCvb5-Lr4dk|?K< zP{x=FLmVsD6voQwA=2a~{k}KH%nK%8-r(H2LAls#GS^yC!}y6dasPt(SWEdzc_q1$ zF~rMB8DY4ci0$me<*}Ddx<+0W2wW&)hvA#iIHs?|f#wNFLc%Un5o*(5wuj0EQrXIi zjLmORW`x+Fk13dPuo@=EWtQ%?U8&s0KJb`8R3Fu zi`aYjE-`xtkT&+Nx-npHno0X06m`EmS#%|dStm0`&u13XekGZ$+b?47-k#$T1-D?U z`L}Cz4#lB8wNpPgtL_Ykz`b$%P5=l&`y&KeHA7(j&H(~rKtO|DiS@wrJa37;rR&tR z>3Oj~ZhXzrT?W#7CZM}6e1dbsd2M;c%7Nc5)>_XGi077=)uB&Q;jT}$!A?p{yOQ72 zO`J6SX@@I52Ld#g=ucXIhVp-<5~De8o9R%k)3!w3sno%ZVpHb1PXaR0%3z3B^b^^9h73O*TA64Pb zN*&6VNb+zVjN^T0NIqPZCL&ZC_XhJ9wMs20X_dvFZ2idz-A5b;Zl%+}rnM}oL7d~j z$}J+tdFFY@7-fb%U+rMCOrf?vzJ^F2$5v61o@apBIa|x1VSf}&75;atD>gqY? zp=@xp!WstlwzQ8MN18dbTIfglr-oya`J)I+&`d=hYeC%dhQ45wpQMN5_MxyPReuHS z=N+EE)G_YEQ%MW3-zgKT$ap>*D8}^ce0Z*Zfj*4*5L;kwuonix9GWQes3zgieGddLfw)!-<>ZMud)PPHkhS&_NTr}_Fch(x@yeK&UnI@W=Ly$HtldwBnXV_+Jq5{oTl_wAVm1Nii-vkG5$gkvOFy^4Y z-;x&clF+G_Ox_dEo4jn%1BhsVY{*E}zYOyni8FSHNgPJnNNl)nd-4+cKeicAWe@wi z?Y*fub+|j{I z&PDa%%-{ktAmK|g0^{@NT1d?lrAY_ER6^*Y^0gF!Rk1 zh8hnhqLniC$H~qz#Y=nA8(e6l&eddu`mT($G3Uc;{)4_-{=Kvl=Rx=Y=N&o-s=f=y7bd~~| zkvYf6gaa;u3%*HF9WN2MXfE5yR%Dm_#{JVCQ9Tp0hz6|KKcw-!tLKo{Hkj$9+YZiW zCBX3>mz%n5*ii!`=|7K0zpPaQFYyQL9yPFzsPxUPIzf$VcL0Jmo%Q(q+Whp~hWy$z z=93=T?vdjjx$cqY9MuzhMHt&f&i3VUwx2;VF>6kav8jiRVjv}dD}7k&tL;!f{wj{R z*-#wXLmI!zbmPI(@7bLZQb#>znL294gC5;Z4u!|4W`b5#iqXFEw3Bumf*~0 z^S5%^LqS;4PCCNazHTs)MVfaTGu?*M>Pd5X1jgI@oN|;Y$68*AzxW$<8d8`jq!H*#ZYZzFBg9&~je zpg1jKWB)gdH(EjR?Vng=vR^YNdmHOb<4X&9!#|S0egDWlAgb}t&5#a1dmDM%rH#fv z=N}1&zZK6|k&{(K5(K#qkrj&Dn${+w9a{KD&eI3_7+Ak^uojPt%REMz|0HfUUUnP> z)g{kqOv((}(o~p56jZkkhbrA2;P|F86>H<}7N+gD_!>vW2(YM+Ge z-$O>EvzzACrkkcMp0HMtlc-{}^z2wB2YEGwWJ7FU72cV`RRQ`m=foT}=UGv6X7e5@ zg9X_^bDnw?BRQL?(D2Lr9$5KKuLaf9e&t?UyI?7?m)5g|4@mXxk8srqLl5{~6P zBq1jx;qECR*3OmcMj*v>*1Fsd{%h<#QxcEDnE)H^fIaxM@#`@?Xm0p5z<|9F0eiDm z4D5E&CjQ+4rh8>)Xw=FgMw#PROUS?JoK}CJ+1c^gi*np$)j^VA?Qfx_`N_?a9WV9Q z=3`0oU$^yd(7d58hq8=l!F*Kft?~X1(b5AdxBSsW|1@o8lQg3)w1MLqx{c^)D(rgS z_D?{o{oK?gHNsBmAj#YS%=k3um-gtO)m#hQ^8H3`l*(^cYa-nyRg0(` zSBY@vVJMa{m${Ug;O=q_e}!no0Rt7N#_{!kVq$ zQ`RFc4_nX6I}ChN^-o2#%%Nq%?l+CJv9pV#K~1Jc$h2WRFZYI465g9z28!&)Jv_qR9}HQr^ygt}oxCBK%Hc$3MCciZ&u&Wo$pWOGl>ZFAZ!O4rQGt{vRe zn7^QZE7bEmHb70WjS$#u-c>9M&1-EVcJ1IFbsa>*TtC0I|4*tcF>7eDcKFXh@^`hv z(S6EI_oyBGOOX6MRV!KDE9c^N{KWpqd6qQ_cFxDD(7g@*3s*?+MpafHm3_&c>CBaF zSgYwq_pJGHGp7qGy3O}*)fYJ*OT(_JKt$O~)bq_DtZ@^ssHyN_@WpA+gOjxoRv#fx`?vO)Z4rq4W z_pVK`S~!}tb>Fuyq5IAOBx{b%|Ebpz_`P2sf#u$${Pq80YM8~GA)T4r)VI}y!%=@? zj%$aSxo&8i+kj{+N@oV*Heoc*VeJH^mMLAt=r237Z}x=UC|FXSL*blslAn6F4Sve` z^-pY2R4M!0d`$;+koLcP+?1HQ{nwCBZ{GJG#)X5$4CrI9z5z-J%*+7jxNytHmlcol(#;ln?@GGU-cr_qUpE*!!Lz%;CLXr&~jYqRAC%e4D`BAhzLaP|I%IWuAODhIFuIMiDrP;-{ z9#lK&I-C?7TJ~_*pl^R~9J&vd*^r#{PakhRjy~VqX~_j+m0mjCb5315BTeshLwOGf zZFasdbs|fZrpoZ0u(%T}Rk9YGMPI9Ab}@$}QJNOP`g?c_DKAu*jq=q~w#(wTy8!a^8P z_RnAEgX2Om-|+Z$h89>)Edl*~_`1dB$nWnp>~FbKWQ9fiD>i6b_NiR`?KUocMgst3 zfj$m^bZP;_ALL3@ro8^?qqS$fs=L}0kaxPW*EK8K{lpf~c2L<&!8nJ?Xufx-G!-5O z=G+2YS+iiPmri=`Qr!XkthPuc((f5Ax`3`U^QRnY*w59l>;2Xg-1F2_xIUvcD61^> zJ{rP?)ae_E@K%;ce{6Afmx>iGykb+~YF5nXx>AStA%Lm+(=^e82-M(;btag9d?@U? zHDti)+`omAdLS8rh`xNjvx4LY2dSI1^(|BQd+H3yIu)cu7DzD&0B{69PTwf9gqx8? z$rD}4fipvLgf4QmXF${RjgUm#^YkW&as<)fjvl{!58I!tqm$QwB|as{X*h^oD!Lbo z#d1l!7Ry&(0y6*Ua!ToM#)}s5^Sb?2Lj=O=Oi_~wc`Ks$d0+K$r*=;O{;2D0blLv_ z%47|B)fw!6N+dFc zZ%>b4-$sL_>f`ucPXaGA`%evF|8VYajPK${%lLWC<9l7%H;V6d8SuA>?{(S_QGBny zaHd6kubsB-tT;yO#dEe{o8hDT zmbz-0H;HV2MMZw0wMvb<`b%9bI(5bM4=Vrr7?G4iWbX)(&3FJnWJ^tm z$Y00^y5eCVvONy{8lRJePhO1Ap@xq_jDpYB7@xZhpZ(Q$oD4q(*)ZlGe+d~g&f#-* zgij*I=fSgo10U~IyOOhR9J99ZE?!`26hjr!WOeiO6VR_n*^k)sg|0Jsme!4%Q&Bg5 zc17K!Co1aFk60+dwOoKeyjk5jgx?UHnC8}~5wrvmwJ?E4%Ei0)&>GYDXw$5GPq_F?;0u%ux$52?x zES5P%;56Z2h4h%e8!7^YtE-wyKetKrm47yr4vtYe5wL7Uk5Fpg45eoq1C)*fB|IYa z?DOL$+v|lNGc3h8!Ac;&31;@W1U9*(M8QqMIH4^}pl$5C7L(@lx=t$f&eS=FvAAXZLfQ{ZbDnB#(fqyJ+5Vwu1MNvunQ!$hh`T+QEJ$ zKQY9ggR*q-W;6x|``8QYx4en>g`)lpWog5$)scPFk4ljE#$(eS25w;upQ4j1Sbq@i z4(qW;8RKQk{q<^r(Gm6l(=Vv8@upwf!Y!v(+wklEQ#`#aCk8V4^U!js`eyO;MuQDf z^bnIY$GG;6;0_Q>2I}yshN!a-Jj;>3{rHw!Hbn9CCR*SA-^SBxd0*UDXlQ@rk;X{r zsn@QurXTFane%uH7xFPm8R@I&zPfX^fj`y3{}eSw zvwuA$f?r_prN|ry7XvdCy$K=w4*v~4GRQ84aQ+te52j1Mx-YItIK=8E=`j0)!)#cL z*)2eCexKGM%`p4m%YZW%pK0nQx-afxs`?lYEwe9|6(ls`;`$3-vpWF zO6VHiLlzzyBD(ktr-T%Y!@q*fL;pwjX z!TQ@4SJ8xa_)OZQadS!YFRbN~AJxB2Hcmf>1$#OMb-1T`k4jD)5dZEC z=#_4IMGzCSDsKF_cCh@7y_DWsJ6H}!OaJ0Lj+Sncf3eS~sGT+rNVcllc^m$# z%JQc9>6!&4L;7$KG%@ui{D5-!;P8U`p(d|+H7PIgSU2o#uPZ0L%Zhu>sc((^)-RqW9^vsg_wKDm{9Bz$X*gC^4YqYA!fd3+DEGv+)RJ06T|9Y?{#7 z>yYE`)HQ0wNwi`Ut@vi+4z3l0CVa!2QcQK+VBVs-#f^Cu;E$gs&}^^Jx}Pxe4k%&M zV@^^*W6!`*mfLO+Ti??;4f?EY>9mDawCj9W@+zDuspxYS92v@_M>B{Xa_a~`@dmFg z?83*B#9J7w*J8-%nj!}J-WTG%DH@;csh`pYF4$Y6pMzKh@y} zBj2ToqVR)W`k>f-a7VKbXnpQZT1fEsOL|@Wq)_N(WkINif|<4T&(_ykI$7Ai2W-yb z*-Oub{fl9*{G6Z~ZV{)K;>*I7Snw1!n}2F}7BAd?R5DVXSq9|QU(cj;+RHnMT}6Sf zLv9*5UQ4kkam*+d^=5s9cEC(3>7_77(jRD=8b)4if8|xaj``QyTy`Y6W!?fzI);R# zf86IfoLzxk)Ehaz=2~-k-P?7A)%ikj6_mT)r8IN?)>LLJeRQ*IM@?u;#NSL|!#z^F zoNpkwepBK1taZtJO9ON$KxG8UwQ=sC7Lecm><8{yST!3+1h4;wkN0sHdel~$NUxN< za7@(efEzq-EScuU5j7xh(`o-k>fl&s$sn&{FXVP{)iLJlNqO_B5e^qoN?}UCr(nL< zCM%KV^?38BG3gJWrp6;(K`Wt8GX?M6Ep1&;N7=C8Gh|Up3{=4a3bM2q zl95QSFga)cJ7o|%9c!KJibVL!?Eym`Ae(CZ*FN2r6P*N)d667eF()L&l*{W(Ip@=A z!HgQJk;*W}rF)NQlT2JAajBXABbq(dXPLsqxbf6px~Q`oIc;F~u?TOf;S9lYrS+>7rirGsknBiW2Z6?rLdB9O#F z`t}t1w(fVS;G-M)NLvH$w5Jn35pumbCFSSXd^-HN`qi0yV3U9`V^59d0NfXV(9+3* z9J9B6dwzf4L1r{4e)rvzWwKsN;V6Fh<4m^#N79(f^sBZ@Sm*Ncx~wsS_J`X;gZ8M8 z&4TYUAv9>$d=we96HocoKphx_4!`Zp?{5LKaDIQHp%%~Y+x!^M?{n<8IWb~`zyp)w zk_fT;!9(&@6(W|75nFY#%$sJ*9{-o~fwc!4b2T5>Fln3lfN%iIkr9?Ztuicsz9hu* z0W!=sOZrEGrKUpLpAWnbH)tiT0-R-F{x|c1z*0)nyMK?>>fd{}w!5qr=M@h6A-15E z(KoIF*FS)3(D-*dWRFB^s4s9&T!<`lfrbwQ^Q`)$johd5O*tbJ>q@q?EB01~_lRli z9!Jz3E$@!k5XI3~hP9M>6aMAI)MpW8Zgfao`!5oSS;N@{GcUUweZgq=V{)QAg46C0 zW9w-ChTHAyRug0nwboZX+^X%|Pg*w>{(KpI##>uX-K^!iYgraOCinl%vFOZmm_yG< zQVl0lp~cGEFr>E(l518nehFW&sB6s;)23ikOj}M>p+{@Bw3=8Dn1Ola-Zzb1b!;^; ztD6dh`)Atwd^Vw7$;SC@64P&pD$K1LfI-!NYgnVbvBPFSvKgFo+W|@J$HcVDgmYj& zy8V0Unk^kBmNpfR5S?`#bcV*G;m=_-TB`n;n8uGku-1Xbi(HBpJJO|+4TpeFIx~O? z5;fR?=Oye(lD*e7x3-`5+pT52mn{jP!R=(4%K$icVwzp<*-eG-O*Y`YLg3flCgAg9 z;HO8xfB3hd0r2NsD%qfoJ^*J!&Hx7Qcg*lC)BZzea@JvOil0F5r7h){i)~A9QWk65 z%IX-rqI~sQq*t>$pASuFvpdaNE|(D_LlcKjVrnslX1Vuj!`}^qyqUKO?Z3omKOCWb zWrTK4fHoZ?eTxQ=!ULd|tpnGM+St@cf66<+s?oN^px0DJ)f^F3^TPY42l8AMUr;3I z^ZlvDI?wBV@kZ1g?)T5yGeExW_vXBt`V^+uB-w#1PtlKL7FJf6Sr~RUsaW-~q?u4? zS`5zhcERR)9ml_bI*+DKe-Vf1gUxg&gZGpFkj0XB-OWbTa8L^He-K8uv2J-+d44UStg(&b$@#LxHOwgy)w?qaQ7Gup3h51o*j9z(?il% zmgJe_DS^O7EbGA&!N`BJDm=jrn_6eZz{~c)Wp&HyC$0} z|89gmd6N-#nOF`XEQ%}v`BeQm5n(6t9un3iBO_*$hw9ne*v)%jtIwrkl7Ih*m-BcERyT zUs|O$a*cEFdukj-0C!UN+`Dn(Od(Bf(k~oNrz@k4e|Xs1csps;k*L)~luZ%Ts<_m;`@Y5nH^hy6pRY6T_xs)e z+Ryg;{Zk%~aOXYm_sltS=FFKhXU?2?pLpG|xqq@PIU5Z+UY04l?{&a1GA1crXpO%Q z=l(+nSDLlGYu4IJtXYqWNRB$?)~wu^yzcoNbs9+FgJG2ogY*$t3EX&)xq$I-V{XV5RetY?Gdw}e zjkqEkaagUGP7`j#&F=~XtZdK~xj}b<0kJ`+<{Z(_4Z6bdd4tZH*qcEY*btCcbCzq+ z+00G6C;Egzcm2i2R!!<5$5yS+bscndJckmoCu!@N8^(&QI^=wGF6UXOXq>BP$6Q5| ztfJho`}WyfMQx<5qVZOd&H?n-=o_kA!?=D@N+tuZo+El4y^~80uA#-`tWU9tQKOl! z$tJz}^tlDdMGeMDQyaL`2#^&2*&0LXIZut^2s@BadLDK#(L08dLefmhXtEyz3kMVL z@?mqw9_C zd7%EfDrjcCK;cOmUYnjNdlbbHJ%2g1py| z69qw-qPEm(2938TLboT4QR+!{d*8jw4NO`pf0cGPgVvE zm~S;}s%A}A)=kLc%sYp3Z|3`|BQ`i>9lhw8ECChmm<~?qX`*V3?Sqwg59oMedL(B@ zM-*x`aAHqOcOwsn%@IXG9x*r<6?c>^l$G+iakbad1c!tyr&)K{ zCrZgxT%B+A(38Y>O7tSJUWwj1!o%l%3YveUXTnn`c7=tQvQRvm^MS=a?MH%BCmMkp z#Rs?vCDiZkiMFf6>QeMRzTSrZd=1S^#pO0ajWgbSTHW`4)P1M(95TjW(l*SNjS5(u zF7jA?5bxB>3%TQs?vozXhcJmcEpGkTg+2tw_;PiEFgx2m+wx}dm#Y=YusBoOi=$`4 zANhfNkiGML6ifeC;c>Rm-npUuL0Wqv_5i2SkS@~r#nCSQT!MDxSu{VBwwtNAcCZ=b zlEFF1nX9j^Y!of+LZ*Z?4}t9W#6tv zwhW4F9zxmIL7A8brCZk`Z>^M-jBRacO%u#b(ZpKls2C7c^k5bzo*U|6-CJr!vNaj<$hN zqk7hz*_|%)ggmy>ql-u?2&|4_nsnWVVXc zOJ#335AMs3;8UDv@vp<0z--aoE5hP2b*EH@fy5+7e2nRIp+Dwtiny`;u3yt>>7z?K z>U6>7zI?Sb|Et~HSF=Wm-%D;A(aSzgrdw9=Rr(>-$OOuLK5Ipo5v48F-J1Jm`uq({ z3X+AK7PTAQ(^JZsfr(pZXSGIe!Q6Wy>jhT$?ZxQ?+dRtW>>VUS*6Ubz&rRi)Z!)(s zKey%mLaSx!AH5M+U~4!c;`3M?`hxTbr(h0{3J5-r)n*j(+6!;;N1j9m*NiPkRwn|*=zXrYP=s^+xJ zD0lprB%EZT^)-ERAD?WT6*URoRtpVaPaS2 zPaT<(%1@lw2D6T%c)l8kyhQ7?Ts=LOg!TN5dKgM9Z;mI0Jo-Q$XxLLIV$)vSe?MW9 z9ri@2$sE7Sa_Y?HKBl=1C%`bCc0H3r7*o3j9k8fgF|`BRu=mxFwe>{PcJZ?lUSBLs z_?RhBwybg$Oh3r&*xdDC-12o{v)b@2c!Ayt4!r|GuRc9ujaTwGBuTXXjJ|Wy^6kfS z((*FWCM|cP2`x`|x})ge97RJmHBysj<9x1!o^9h4k=LjLsGI-gGa^E~Hf&0H?1FcV zH&VRF99}_uzs>}EbuEt-yEq(;J7(wSI%F@Ugb~=<><8Y>Xc` zh10v?bl#nkJuCgiFGjLQTc^YyIZARzSJX)G@~uge|3L_njd8FFV++CxUE^AoNuSuT z$t}yf>yI%kk$zn7T`tuIqi^$8 ze(Yd2XDup0J}V4))?ZV{@R5qCw-JxzYwN*zk^Fc87|01#X7Os_jQ!t({d`!%He#E@H@f<3f&$S24d&+?tf7y(Nug7t_7ZVG8+Ttg{ zw&kFL*t|RRrsYvFZ$a#W-`jWATb#paq(wQs0}Fo1wojU`*FFxJFZ9D0LG@h=z(ni( z2$|pU93pdYh|E8KYQi8eb)GM68Hw;>b085+J&!0CiKpKh@i~8XWi2>Ycbi(7YL=gnfV)(r+YI`x)#3^9)H`QIy)~==k=|VAiD>c2_#wb zxe19etu+6M6JVls5a~UXhoaP`bu@;)`2^Px`sN%p&9$ioyiri1Htm?#rY9c` z+VqQI);G4z+VZGLkJwzvz7W5lk(0=54d4z(KDh2#jN&uYD!&7%i&Fz67=v!v}b8FYMW8K#z8sOHzICR5QnD5~0)@J2OIIlQt`vZc`>Swdax=mt>F zcQqUuAR?R70jG3r)j$5OlXKg@bGX6UjW0t-xxoRDsQr97HSAT5mI$@*ULgd9MKIzBy!jE62p@7W-#VREpl>;_73|C%>TG@=2PZ*ng{7 zf*+AovC(`}>;2vaH>m-VlJc_-rXjXeWv0RKF`XWK-RnX`%uxkj709c1IuQdb4hFr(F*nZKG2O6|E?$& zBUhon=msrS#+QaZRVAGjw;l<>gPSYV`04XaQ8@c7Yy7Aj(NCp;&e6&>{>ue9nmya>!o?YHMX4~kRb3wcSQ1ovP2XVE2sEcl%3VFF>Ga~n| z@*viS5MLi0LR?@9{^Bf-rUgURY6qSj{dbeY&SI_Dm%OC+TIAoc!mnH&t=2w9=E_oZ zl73mS?Fe7H%Xju+mJYhbj6^}&8(%5=kJqPPWGEsTe%Xnk=s*2K(I($S26O-i$E zHr(Bl>_^^6_VxSZC@t%BNWZZnv>e5H7PE;H@GX%ZpY$&GmIKjbqtQydWyB;qP_Pz*RE=+$&8kFuL!n$n(fF8Lv@ z`gb2x?gB4&+E?GlT0e|Lz*_k=z(W3vCnB~fhD{`#rUz`ZUnHEG<*^bBiH-RN0}rk; zp&eW4@Y{`e=Rf>YYkt1460st_TD&P~mC7UirWh+phvpG{V}XXe`Zr%7u=GX9R>Q;Y zizL<$;t+|fygHl%Q9M@(i1joB0Xm z;_N-9a_v7URJmT~%P7!m6-L%LZnK9Eu*ujTp!j6oVmtgMuev6gjv^Jz*`rS_#~vLI z?a{j9ksBIEK(#C?P<`fb9)p}8`i983 zx(-sK?=Qb$&upzz9J&QAeuK~+^zM!JXk=>V6(jHAQW>5%E@2|$keO_=HU9%EulA5@ zO0yRQRdESSR2AEWOj%U#whU`HsaI4upKwZ>$nW+&`9* z4_9yfEuZ>D@nkhx^PJwCX41%o`E@vvMK&>vSV$@6c>cc){9WYbMUcNR@~fOOUXDE0 zWAmPde3^0a%pnBv{joO3IM5gP{EWbO!cw3QUT&GTJkqhB%~Nc1gxp=um+pFu7yxU`0h?#Q$|GRA zg+JDqXu1&R35kuum`uxE_Iv5YZeSmQb(ea7@XA28!m*!sUlEhwL zohW+6zc}|;Tl7l(@vmU~omlRlVA(p{vY@S{tu%RcG7S8`!4M4gOIpsZgzsN!K3XO< zog5x^X8E)OG#WWJobQh#UZVZ6Qh$#18cz&+4i0l5X9?|{V*KU~1y}mV**7{F%>0{N zQbc<@IS!0bgB}WQZg65D?>sQR@`sk!4QU(0?%sCT)|t(p)!<0B%||ZVV7Mc(;T$%MsT)9XT;NW3@JQ+(sfL zqRw^J7p=nO%ILR$ssGaX)f?!(l((YZTS=@gS;LZk zEs+(`1GQyyHjPQNzLy(>cRI8&w(aRsRqXTM?3w}%W}jfaunnYuxTAN1xSd%Yy(Zcj zKL5)s9@t9%u%40WhmhZukO^4tP>3oHR25KWsOz;Qs@!_*SW*o4M|e*LotJ3s05Sia zxIvjCBr_!o50N7jX?8oFaNty1++W{^^||hwQ@ajzu%7*;p?hiw(ktn2T_#hI>#`(3 z!x`q)n}RNz;vi~jNP066ADzw9lI`y~kRu2MH!1c;^nTe}Jd!c$hRoBPkwTRbABoyOUUp#Pac zY(ChWh*Of$kR%YypvFmvdhdwhQrMr!`Z~D{x$I;_%Hs;TmHtz`WziUeEHCC}iMq=A zmII1An@>eZNR*p=xQ>_?DKT^l3OSQ<*JUio@&>Rque=0xk74lRs4a1A>{@KmH5kcFy1&y zvWhSd44)PLRS-j~5oD7FmA6Em)0%wT0E~t|{>d1p6XSBx3hr182f9n-)ZlurfV16e zNHLDqsg1(KIrOduZH22ON9OpQQ}h-4YS#ua5LeJ|asQ5+Wf)g{DtJL7n=jXmR^{*jt0&z}j#HAcc0FOz+BU7;&f;+G>?8p6eHyO&B?c=3&LtdG;R?>D* zZEWmo6so3BZDu4U?utjpgEVF^WElaTRP^sbQ7bjv>l3GUPgeDmRUIL%){crQd2_5) z;>w>JlphXg<`g!BN;ao>RCM0j0E@rnD)?i51qb91P#u+2aPKd|5Qi!oV$htcpfSIK zZ*Kg&lhdY7k-_(9&V!u4Nx=i!Ue>8FdB18ns3SD2F77{zl+O8={|Ish=lqYGcpQRW z`a#YWS{|8RHnxDZX`9fPJ`1k|ZGD_S-1xsijGw9K8L$G9#NKvTeox#^a~;Z-mF?2%Rar2vt_vNrU9a1b@mg5)Z3GVKsA-39ElX8$kX>kf{ui zXHadT)r&xW^M@SBM@bvVaRxG*z$;oS4Y?Vk;MAZn@sPkpcBiu@Bcn%)NDXyPX|!C8bHOdxqT%2)5v~nV$OB+ zKjxk=b9FCWGNKql5dT%QNY5ySIOWZ!8&R-OrAMQuq{IT|?=>9>N2ipq!FJwZbjfx< zw=S95XneLOPeGRy(FKWC`46}*x#PMVpN%I?ZruNm$hWSlE4C`Ww8Zk7Ft@nZ|(1*=Pyh4Ui1G`4uD$P5&WRiae8dQ z-XV$HR;0E%9G2~%#5o#94`1qX-LL@4PQKn`d6`^VkGt zM65l|$bUCa0r}@bVt2o-Bmcl_bL3zByMX*>Yz6s`(OhmT_*;i*+CnEq;gJjd(9=wY zT;nP~ELZseDoeEH?wfdk3d3o8Mp*ggs=QPEh4E*);smm>^4a!O=^b4;eEtDDIJPQ! z+Q{PdlSkKq;G%w>+f6L+bxrKzyXpDaUrPeSF1<JaPdqI zE_PPDFw|PaQBK#Q6Hm1kEjiZ6HA@YHJV?c(z&byc-aFUiT67y}_)*)o+SPKp0YBR+Q3g;P0)KH84hmiS98t7c|J zvSmh9!L+5x-`MH}R|%qT8su9MQq3vQIg_t*V!2cn+115rQRfv6wuK-3R+ zAnK=wMqN!Pw*;cRneE*XA)L?~QB|i)pn#CTrqJ1*tJYD_ z$?Y24pJQ|9P>!nO{ub0LoiT_AHxy+J11syxc(jP;nugA0)ZhNY)l*xDwEsxurfN-j zj?-a4?&m#MoF0M&1p}P3Uxo2CgF}1o=bWmKXM7!mrsf~U!Tvyjl$U8W-foDMJ13Y} zIr6ATtn7J}2LA66bWW^1cx7(jpGTTp;y?67t&_u!3t{7-TR==aUlhsLliwyL5bc6! zy75NF`Hk~i#cvgtdsh1Wj|Q7yF5=W~NgohMwEhXG9mjb+&_1T_!gq21+n?EYlJK#4 zZR!X7_P{yOdb_?I+_H{I zsQoN~TsxorH9{y;F`CNBV3m7087A?t|CEb%qbq4LLN1o99$QJ^y){uAK8(# z{{yv1qO5qNN-XZ^#|6!_cs#9N>Oa3FNJa80Jcj8s{*C07x)OWj^qi<-Jw=e_Pw#T# z$rvrg(N=_0_e5q!X~#~Xbn5Xar`HMydFkX+z4uK`?6-`nC3PNdj0tY4e=afX(DK+- zkGb=*rA5zh%RnM_)uQ2<+HHv1xSEia^+hl1>VXk<^}yEL`+^%GR;L^nIvzxr#>J1} zy>^+}-uOv{oEi?0r~MIs7D1ea^!tpX(4dPy`5s=O9KHI`$uqY|ww%r6QHJ#~PNb87 zD`aUu**$c*7Biscf3?0{^XuC>sE^+4&e}q~xnCXZZi6ngn}z^3*iTWXH8S`TUQY$$ z`ow#gYpQTuN6h$p7iw94Fw+-L#J+1;ZtJb2Oo=uATRZAbl~}sBSd_Rd>q_C*Nz&aM zzut5?9_EkGtu0@{@zOB-f?|A+-I8#7itIj4!(w`_$aPmxL@UAMu%E5c&!v}dQu{ZAMkG>dB(~@iW2KUiv zl$CTH>O8|ccJc@En(vZS{LI@Lau?+)zK%j}tetn64Zeep2*=tkWH9)iCL!#Jr?r_n$Iu^E* zHQ2o49ck~6@LF&wO&>zXm!Sds1q4QrW~s9QeSWUSyqarF4={F(xrRcdkQft5AycN~ zR7+zDbB%eocfdaz-1g(}v!&P=ZxylEQ9LK9!<}Ed+4y?-3zx1HO?CcZ9(4j?PY2<8 z{kS&F0(7FaL`yAOjBm$tsID!&B#_z@K_fEei=8pf(|btmaQ+DP1ML*Nhm-wFY>1>X zySy;Jv;ao#2?m{D(6I-zs6A!%v?DBOPb>A*m!>Zu!G;U@$=I03=(9`8E*|msyZrM`9PbO72Qz7u^6drg+t)74Ilm6378Mb-!gA{` zgE>dCfSuYCtgo&G9S+Ep{gwnQkc!p50r;2X^Nh>kAnM)I77(f-Q}IVC&%@!b)Ft0B zO8nO7On^HBMzJrYNoOj~=vrbHC2GAL%}e|n23w-#8kp?N^ z@Aou5>KJ2Fd#2(fKnq=g`i8nXwhCVCnfGFFkj6s#1XQ?xKS&sK_sC0~-?pjo(X@!g zw+SBYdMB4%#DymLew?kS@o1ETn4|i0|2Gc!6=%>Y=I35sdu%~*c}v^SMX?1-N%n1N z8?=aTs(2|NJ&>*4!QH!to2^5E55iW z=!%PbY&4|9smXpfhW|SN<-?!j;P-RzKj8F30Kd%O=f!^qW++mohVc9R7vP(GtM^KE zw}hA)bKE4FDn2)MDt$R3#u$gpBT$|%CgzZt4?uU_fcVTSIUo-iKOGR`{O%hOL-H-I zjT2PTRR2luO`jywm|!qB&FtTZ(E-DJ8{gIT*6P5$ z!nV`(u~Ltj1UH=eD8TI>7Pq_;LzW(qsq3^jn)jCrP@*c~Else%NH?+9lc;KWBbH9G zTT_Jj&Gf3Yt_4JYCWFuGt7P)%$u+KB^KeAlC=b^&%hMHQ^~Wcuv|r1*Ua<>z0&pr* zar*rq6CU{DIgQ7Z7t|Lm?|g%`4!95o=b3CGjYj7k3^s-7WQ|h1Y0q{8(IQ>i%17yB zt@3NrHA#o9p16f}XoMNsK~-qV>Y0f0OM?#web0x=&u#&Z-~$|?((+x=T#IPs8&jyXLXKREtP7O;Z zX9Bg|FK&6gsQITHfNLHgB~$R;gCi5IOLFio5xgG26M*#e=^ZHJ0xAzrh4^p}!a$2i zdKh`5Olb7~0mZuXK|@H|kk%76%xKy$W738hjbR4uJ0#$`4fDix8|I1Yl$SkpGtF(U z7uFdsiJybk|6s>&t5Qo6Ra&G7Fg^$6$@bUTQ|As?s7F^N)6<74si88x#xm)#^mNyi zEw3|xQpy!;W}hrG`cn+v?dF&};3`)~$l!cU1$k&j3~PA&PZ@c%;KvrMOJ>I6CpEmk zCbm_AoJ=>+IDWmP)}~X5tWmOPoD|8gb3FFQJd<9?v<6byoBD_-EE|{je=b!`<0wA7 zYH=~?!s_h5c#Vianjxk5JBGgr{=4`TFl;;97$%blU)08k8Yt3whYlq2=plSmBmQQ( zXVxEjNF?K1FEG7(0WX|fT|HNFHgKpyrM;NvKqKF&5VPfOz)v4)t#ZdiXd%8LofW;c zc@{O>I*(4QvHl%)_D~zoOE|!iXsyr+$BpO3)SkViH&xE;-!Sa#fw2othbfZY$5A|* zq}Q8wP5$QuiK=DdI3k4MC3UBIdW+p5mR6rmd;0Pjma9dRyC*ZneLMPK2IF$fEL)w$ z%aPCa>S$vd)3d1}xukY?^NvXs_LFl95$ucO?9PS-a$OwI07*2xm~5~8@uptL(|D6F8*dlU7#cVWHS?cD?&qU@;ND_sD1 zQvq!2CZYq>)aU*)A8Wb2PL`;-%e4^nuI#U)^x8tchu@U9H03SwFS0AYV)KvSljgXT zaW7dZ&c#ksK`%{MGS?IfN)&Vy>jj-U^;aaCP$ExAW#;yu#VZY^mHwDxQ1%%sO$PNh z25qIk({Y5&CjtQ(v=%#BgnP=kE!;jRO_|`V7sN7KQepOOa`QC-bcxu8+CZoZ1bRTA12B~* zMeAYHld*ZrsU|($6oMr+#=F@ERI{51p$`*bqKYZeFZ)0vhu(#1=>|sU!5F!calG?v za^7{UsPIG%_nl}vUCrct-?_A+JGE{+>=*f*LXd?<&A%^cI%KYj6V?1hHmzA#0W=|z zP@SDpr;{Q^$%4YEL%l4d$(B-Pq=T#S=HjBxiu2hr z%eAs5n1gF#bA21u2~cPdMXVGTWS8T_x~j7x*`*jP1-`?W^vrTNZ2G`yr%3CV#60Go zFhc%Wc2%zS~pli zOwncPhjJ@+xiq8L-EF?t*_{@eYD8m_Ro-wuIn>&bEbN!n74`JR_%Z6vRXOq#0V6o39x#GL2$LCt{Joag*tbTvW-fJ0v zwEJm)+_e$QEUHrla#jrB-zE6mxuxN!M&Rj-Nt;r^Q1R=(`NV{r0pZ@v)760Qaupn_ z3c67N+7EOJRC_vhN)J>Jw05WAuoR;VP12woG;R8WLwULkJw>kBkFhd>H8ko{-PUcq z?5ORvVWyO!jWVSO1(~*NsDg2o5iDh^rw^KZi4ZFjDDEjDDtfZ8$luEU!7dwwM;!kC zqOlX}1J0qv<)T0!>2gy%UH77Y3jvdZ;XV-4Vxo2HT-mckt$zfBfHmW};l8i>Yfw1M zZ@ebzvqy2UO0@2ftK{pmspL{Bk;we6+msN zNwJ`KFJb1INzfu#D}3?!7oCBgV|(d%Y#@d~i0L2GT3OjC*wi6r&VF3eApUrZxbNxu(d7kurOFFF9aCOVbx3)^bpF=y_sg;61%t1Tn!-EhftFD|dDBDO0&aOK{| zY)^M~Qx4j7-=wG0N}U8?vuD%`JEaspvn=%gB2}O}I_6ewyhFJ?OQ7 z&=-I9MQGADkHx=3ssgW}1_)aTg7#L28jsEaP{(yGE4q0#wYXR}Z;!UQ-FY~A6`iL; zFa7MJ%lXLS7`3b@_6}8VZKGQB!DGfMx_v!+IDOi(0^3zhRdzfXG>R{GlJ|fC?VV^9 zuV(x6-QN=+)KQX$S5)H-R-;0YWecswZeBxeKFIU!gVp-rb+%W9Q^U;s500}B-qQz< z+6Sdx!}KnwXpAg1R$iyCuCcGmJBnIXFff;*F>{s5>!rIC;K$M1$BAobLcJgMF8&X> z-h~?mZScJJ2G0!}JWtr*xoLyvNgF&jZt%>CvJ3oQ8~h|C$GK&MwgGt$W=`%g`<-Y{jF8+NwnwMlO5S++jJ;K?Q#APBaVKh>D}8$%|TI@V4`Z)l??Mma+~8|OPf z#A6RMp(hHU-+`$x&|R9W__AE_2UJ`hZejsW6iDaCQMjfCcBu1~j`I2}{XuT!Cd30) z=D77o;?^C^oR(+LV?1rluNp7aH3KJKv5``L71|MN6eK{iYHvAhXnp1ooo=M)*;?PL!cg` z=|*2EV}A&50xis7?VZb@?%L|)pviaW^Z>SUaG5fS_|Z*+ri}>WHz($o8>+p%$5_$W z+y*W6Ab&fJJ{~%GtPCEtATFwI7&Q3_yg%|O^jB`hk}c#Z@kgelceNa}){CSP#1wMZl9$#K?1%GpXSzho4f98J>c&&ZqpZ=;8 zbvxuB=kwnlB*nZw2jhyDf}BE{tvO?Ipzb~+1oZ>}I({AbZCO>(9&0}SUt1t;f9o&1G-4Dr zqa**w;gMsD$Jg|EsFWIQ2XSxIC2Adb^~dPP z>@j$AXZJZpN^E=Q>pF}z;URVc07!VZ$KyIH<1)9{RA#TPnK-p^UvS_urmE!akU6UR z?6+)QZdp{CS8Y8Lt>2x~D4b06e0k(Hw0~IC`9$5YB-;fSpzgABn>#NxhAArHp~*0V z)_Vu+J*3S}%e8!6G`S>M^b&gl89Y>(q?4KL7Ady}E+j>(J7O(gcblr4ri(jtVO^Y4 zcYCv0yWD@0savkW4HCpPd@93hlJSdNWu9IfHTDU|P>i zD8*92G(57_8&!$rr6%RIb=`09a_2#{neo-8@YZ^|o}J67EN8UH6A8WH@eK_jMI7ai zx~JZAxO~I>;Yxr^-g_?Tt}L=XUD#9I6&p^Eu1*At=%Us3eoC(dqLjM_A!S(M?W#~@ zF)Si@tkvE*sl4E8{$fq#1z(+9ZqNNm9~wNDk#`M$t^9=p3YOGX8II(ezx;{wxeKrA zuunq&Ky5+21b;q$Gz-b!ZV$_6$}S_#2uP+!RQYqsGDUTR0JB}jD^>WXK48uVx_t8< zhA1O7J;Wr*jr&F;^+&8$6RnqH{BtAqQC6$rNIjLbjnr=Mx|Ntr!A{-`X=^#C+f>d$ zjILUeC}l1IyRjI$Mh}u}YnmII)3SI+->ZSvz8WF6^98aGoTsCoh@7JroqY%>9t(_rj%q<);WhIRLYbFS^!TATho+ zhjedn1nGtd>0f{&NbeOPU6Dij_O~~rYeIo#q2-GL=3(1GpV2yZDfi#8wW zY|sqDUnlCygGuMpdT*)8ZDQE?$9)ot?%2FL1%rF?U;}XdrG-LCHY#I+B~6dq$OC0{bx6fNusFS3d>K15D-tC(*hi zP#hxvJTcdRt4JHzfg#u-o&M{%*K7i?yn2y8FjN;#b_BQcxwza+hoO>QNf|y&Nq0I| zyB$)`@WMfYr;zeIxTF_BTPJiW8ljyC)U=lyhaBE~5CsD4%R)Brc=IT`c z#?FeHweOC~(8$SAS*0)tno&w5qsZ?~WR$%Z(^nM}Cptekxwv7cW7>>&R@`2wvj|65 z)pHL>AyVog`?lnaPbd{-Cbbs`$c393 zxdt7l*7Yeh`WnFSgmDD*S3u2-{I1qpJ+Eyc12VXfe1y(xPgX6qB4t9Z&D0fnwT0AG zacDo<4)q-+?g+%VJK)?|u`M++lQ#&T37f9kds@pfD%6?gD(Z>boIl2^EvWPdFx{CZ z04;RBG8ylD492aoJ;n7*^w~#IcUsmFb?I60e5Pb;8-Qht%B<0y$T-(kqA&b$5nCCY z^{5h3T8bEAL)vOH6FIg2G|r^DgFPP^|5jVo9W5H1?ke4Ksqw(TEKZ*V`s%pIfg3)7(H0S2UCv;d}aHr3rBwS?b-SggtVGTLI&K^hD|S@Hcmd@$#6Zx zF&uc(={kJE!tiPK08rtE%K>zUALCk!6Xwv)iXlO5iVPE0S3SLa@*}}O=IecghmH~U z;9J-MvdnZeVXwJ!iMU*vM?c1owE$d3AZU-6A<|@vsgia7rqTp*JYxWlV zpvTm`$XG@Ot$nh$wYG!tWt7Qp@+IGO$IfqMiemHP@JpRHA}Kpfom@Sd_e*4I)>}uk ztSdWf3m|^Jl?K&jFtLIFG*kkc+-h+|6M$Oc;T+ zT5#vjf{qNN)wkb@8;`C6kCgD3X?PsZ|J|BLfz5XTHhs?8fzppKi*;7a3LyjHI&!jC zDTHgLVv@c6Mc&)<^p;30AiDi<(iAvVS-*5yJG!h1$~x3#eQVQJNu9C|B&%gz&)B?X zN2n?xSY-%q4Y+pu)R^`oIy6U%>v&Pm|>-$b++h;tdv zKFz!7_?Uogmjfog%>nyzuK?J)Btl>xy=ZZ)+j{1J4cP!#O8_jv8A(iJ=P)hP2ZMfy z7kq?#XGOONym;Y$%linv%As^Md9M8=LX@@*+MkEg4HGtK|JPCb_sM>Y+|$C)E=ghP zkoWu=IjChSu3i*P9oaoMF?HOkdNSL$^NvCfQAkLL`xbBCRRo;Jml6R>kWrHMEz<`T zO#3;bvgYsFO=R|*iwZ??NK% z*`7*g$`0?5W46WPy7ugrhW^oXLD@h)Lp2%1`BxN?lGQ$?P)i`=Z3Cmelv=`~C7e^w z=Zy&vr;{vQ=lF!$Uh6K7D7k`KsC1~Sv|^&Zr_vk=RiWJw%g4KSt?itkwuCpDLz~UJ zh&Eek*!ASwPX>qtBwkg5oy3wSmFPj@K@y=X{sU=9&>(CALAU70t|aJ}15>*Zn2S`Y zz)V#_V1B8Dz#L8@1hX4yz+_8uV2;@U%$;m1NS@MOG%p6Wg2K}O_#j*Vj~%eId3g2G z=6$Wl;fS-PfIjkLmSSJ7?yT5F9ITnOV-K>gCA+htZsRquoDjd5Vl`D0IY3CIaDM8_e zU}|;qaW@qdU{~r#xl+Sisby7Gs8_2Cy0hDEN_XQftZLYGp06HhNM zXyWfo{?6gAg};mWyNbU*@OKM;t^D1?pPfI+zrT6gokZYc`qyZ=ILUDa7Bx(=PwIjD z(r#jvNm~yE=W(A}oQ17)4g|UWk;59ZN8g`m?V4mx5Q)5!JtpMVt}l%b)~=KO!QvYz zcqXw#{;%BMe0mmJ7O+ zF`yVdh?(cDl77eTd$*|r@X+2(LG!$4{c~@YH7b^Fv)Qg>8aTN4L88sjjx*&T)ui|b zO%(8V{FgJyU*W!9+Fv8C zJ3CA7oI=OySYaJ+vZa*oiK|ZBl7B6@6D8#jksrGt@pIaE=++>`fF_PoUPC{Umz+H ztuKImz$HmghAQ*63%TT*!;MS6%^E*L=NrigoN9iu%DA{=&y=?kZ@q-~4neyI z_jjjL1B>H{21danJAUgdj*WToQZd_Z)Zl#6BT|nrBzf_~=00oT#0IUkKAF9%vB~BV z@K776cPWiXw9bYaj!k|Nq73?P9TxP-IV%4aU>bm_P%6W+b#P*vfVfzghoN=m)w30zH&q{&Xj$FX5#C-zZ|fc z&t!#O8Mh&}R5LPm(H})a?6I*LVg>M}WB)^V4u{z0q{)r@w>>Gj>NaE8u1_+Wz36ZCLk4Jt9f9%h)WHZ4lcp_G;MBUIYR}aq=@3`%ZI3 zrmF+`*nS>vmzlCRQJee^IYVY`2{-km57oDv9cQz)y?J87Jkr?{^*_J9Q1NX=z1<-j z0_t~C95WR~Iko1pLqxq3BI;G~98&KeV*=`(y8H(vymY{nE}Sqbs@IyUf8v7>^?buH!-3b#E}Syta%@oD$(CxIwWq zd8B>MQW^D7y3&>2+Dcz6p|#8y;RZ@O?rX@G?f=c-J9yZ8$?fJXn~n*;a%Qz;B~4R! zXzY;o8@D06WJFc)+jU6i4f&SyS=_NCfFE4vBJT4Mi>PN&p661Z`ZCt|EWwY4`% zR^pV`E5&r+F-}BU`qVp&A%Z0V1Cy>4)c87TUZf4}+SgcZqQmrR{qPAh?@nNoXniKa z^zWl{n4V19Dk=a|h1c+|>px4BY|o5zgApSu535pFA`Xlvr1e|D z0LB;x<4?o8wBv=*h80m}9E@=)mMM$p!I;vu#Lcb*Q(;LK9l8sQ?5Ea_MAs6NDdDz+ znIhFC6@99sH;M8NvS$c!}0re2m+G8FJ^5A^Z;({Tul70KzHcPc78A zj|2I|p!%96qy=nZf1K&Lvtp%i$So%e9A;ZP%m(K$n*#Ji>x4*l#PTqE=-_}dhk+T( z$ps9WA#I4D;Z(zVCvQ2am82Gp$O;{*g`;XDH=O)<-g-gLvv_A~OvaLyH|QN&WKO;+ z606rAWL-E3#u2~28jcE<`$VWr@%i3FPmVIDduX-TW)DQ zBXb)P$K>lf=ohv&**N~lp>8c0_vbrI#cV6U4O+artzr7FS0YSH4bvMVl0ICQ!?cMs z#EJWz?HhG=&5`31N79`&EL)kyuG$AB=+hxA`QX7-b&D5K} z+#8q&^YhvOrU%R+A12wZN7G9jJCSQMAwP7v&UF+W(iB_JPXUi6$zCu$Gn{>#!q|m- z$s9C(U~IwY!t~goEnmf_>6FJYVBsu3*=_j%O>auKAs^x#H@S=`lAJkTRGc1J)v*O8 z^PWpaR;f=qiqdDGXD%w{?7>ty&lM{S-^KEsHO23fA+-5z`HrMV)~L~1pg&w8U!7Yh z9yIm=*C3>Py-T48#ezmcom$r;AM(Sc1;1(gqBASM=UVTbVP_1DU2q(TB)uf(qm24{jq>GStvF|`5J+M3Hn`>x&kEB<;r4E5`pg)=Jvt^LY>rG{sY&yGm z)7jWUHdP5a@}{y2H^?S3sF1-$0{q@E{Nx)6jvbA$1uO*Ajy~P#7fOz;vB(0&oAF5f zdgKV5JyMGvIXqXqnYyQAXBAZqd8MP^7xqoJMCao;HM)mzXLq66umg{E z9ccYmHSXW_Uoz;wOW&a z*J0{E9`pN;$FBVc!2JG8{=5EbIlrpg)IQ1d8G|qlW$j$wl97X;dI1AyT3jp=+p&g0 zG;&ZUvZTEfw~7Q_A|nS?pD}bYYr`cSEN@Ht;PH@Xy&%`a2PJIvB7XzAoQ}S+#@0R~ z_>c_xyPk|-1@qd&PFStBFlukE`N|ncXBP# z=a`hR!y#9>_d&~7PVEg;w8eGvyD2@i*85`q@WT3GO-XZOtc}&J;mjWbxOV>JZi!(v z1E+1K?@VURA70c^Su_6>Ldl*|%wGwAasw{qw+G&@LuELU-mjUhMp85h%PRA6(ZKsc z0_Uv5BZi$rE|-thOW5d&4I89hpYHQ}6NsdAK0IPS#L?9gadh=V99{hoM^`_@(an|$ zh54g9J5U1{IjLfZy~y17EpOA2*%8Q2H0 z!V}GtZgf8l!X{3iaBahfSIxWnuperk`1^m=ykytr!PrsrEFCn@(oyqND6e^8p-%Dc z;!;>PHYY*j#7i}(ou~>>QhI31<8j%cC$doJ@O3`a^si|B&n+ti0^@`z=A2Vu(6%9;_D+2wzI*_b+1}NoH*S?_ z^bA~U%|G~Sa}IXLjmv(cGRxM1ly)ov{pTnem1s3*IoGs19s0elzRHPZ+0*y88aVCMwltkB*J`tO0nusm;QLF|GH>7=NjDQCp@ zptO|zCUvJ4oHx6A?&k(_sxWoU>d~Igbbl8r8}bS_#LUI>tJZsK*l|Or?U(evxr{Ka zHBjh8O4rSMdkj%P(6A4OJXA6!R=33O`03|v%r9XwKXK^fr+Gix0r6MzE<}v2zjBIc zVQ1@Kht6NFH*{Q!Zs2n#p@$upjLrSMghZy|Yy6J@=Fj1+&}sy&+<6QRCM8o|pC0us zUv*C1VIEB=dGKwc*l#xPSs)Bm_F&3(Y-ew;b8j!pdt1fZPB?E#Y4veKC;tVQ7ky=D z`J3@ZV@Y(>Y9JM?0hlm!e|)B57pf6}cMi~p9nTvVySD+qDn4WW{&aP@I_3xVr$=Ef z66yWPCkcj;rqj9q?r#z+|DFBmyNEL)*sFlO zjf3rNX0Z3k!Tw@@!OrbZ_sxU-_n`soBY1<_4#*&wx4fm!y zwhP`)bY}~-kh%`++&UtVS4V>to&RlFs3)8QDiCeP)8Pi(PGkgwdcpl0Eu>s`nY_l} z-Rwg7%ao%VoV>4C(zxC9GfZ$J;vRGT6r1)DDfDJH91J~^~Ymj>- zZ~P?JR~gdQSN*}r0-meGoHav=3Dd}0%;y6O7qVC?h>hfGzSH_Qw6ql`kN5q?(Xyns z#qK5mQjyQSU&&@p)volDq1-M zYdJR$RJ+L5)!+uTp8$79RnH-N@l#=U4<;k%?j?V@%w`-<8emc7A@x{h(< zkE!&pm?9Vm-;fb>Lk&1XlAlX~_g=)V>>udMn#@)ZGB(nnWXW?PmjTel*aC;;>r(_; z1rtYI&KK5U7H~(uGiBc$gd%XmC*o5hw{CyB4JS1)TrPIcR2;8y>L`8#m?m2POkJ)i zH9Q;2wBiGd3r6skdl;avWap~ws8Dn7HWF)tyZuZXMw4B=y>s;rge2S}5Y_8bS6J_r zVZ8&aUR3Jz^|SbL$SdjVodN7$(y1!Br)LY@EvTL8DjKE5OzC^_q`A(FX6%IG2T_g> z1RI)fO=NduzB*O}HJ>(CfPqDQ0&DydJ=N&HM*U~X!6c=oCxd7LqOZhX0~fpYER{-7 z()Tnc$|k@XZfAothnmJ!Fz(+DdpIh`<{d1RI8!q67Nc~dbdXHNSEh;>Zx1(0KO9ly z1s3@sMdpyU3b(!+<4Z9$Nuap%Y6)+a)01$}csAv|#QJF7&D27dHy>B;%@^hTpR;oV zpXwOfxIar3TSKmJWKg~2$c^+p9xycO)m}Ed=;2;ufaT#}8Sv@VcZIWq9b@yX_94uT z!I)b0D~+iyaq?sUJ0 zKi%zqD}M|5Tgu-m{#Nt%I)6SJ_xGT%HYD5Mq9toUcgo47OwqMlM?%YcpAXy6RJX3r zSVi;YDOId(JqV-8S?;!atH%8vimNWGeQBC;@kzbElh3 z#-PJ}Lk{7~mbVWg>SY+70JZ z8O~GU_1+B5uf3Hg1UI0kMVkhUNU2!*G+lHvI@YB}FU*lvB;6!AMM|(A0QRBWH+p)X zCcW&2r1T&DE=R}4n?%QSt|`YmmVl1m?P+v;q(?}{o5_HVACeK!aqu0Ej`b<;(tv`u zoK#xiy&xJUy%j>Hd#NK++;4V+TNLbTV0GVRK*2TiQOAYfM?r+5X(sMJTVJwCI%vqV zenB-mNXKhep{48kGEaZu`x;F~lW-SFd86XmYUMb)H@+0=B)ynFV`z~a3B*@TFp0Y} zEkNuQ5QB}N$A^0uJr0cMaU~hhqaw#wJKyf;agfy=!^c16JpmEobZ>#bMYr!l@pCBC zb)xl2asx08(6-~ZOSrMu3I47#scDfB1>Ak68i6O&><;Rr7#u5E?URbbJ~O+U)Tm?NU_T#sn-dAaWtYd8Tkoue_W-g` zmy@m{kxJj+f3^KS-2ZicV;9U~z(7*%VK?EU;XPmLed9m5iv*fEYKXpo>gzzg3@rl! z>;^_I=CQ-@Lhv#!1IGaYj{ak}g42_o*+baJrjbWuqMO@aca|g`g_R0iWM|4ZMK2J; zlXQ}wPMD6Y#i-ym-f{a%5`0Xf-?B+d5mB1*r**`VgrT&zv3d83O}!X<)x^c~*wkYG zD&>8qe(ggU|A4*MvqbiT7LJ2_v~aHyBj!3alp2}8PNo$^U7|AA!arf?2wPZBn%ubm z`Yo=7r2`vT}Egtf0XT>4f$@A7E*i943<$OI$zZ?wMvTh!?3vEu#^|RO0Jq58j zB@SmrvS{+2_m}N$9IjQ@Vpf6RcWJTVHz9}LHQu)lL1vRpf>d)7~L&j0C(}@29x=X$H50(=s8#n@xn4pj2(60ty?ANgt35b^k9ZG*% zWXL@v8uBT``=t?Y&j`7uJmmU>$ldg3M?7s?Mf6c%en+Zd$MkP%J`qopN$!R=^Sc`9 zAkK0S>vIqX8##tW5ZCUWBggMZ8#(q4ApYHUvJP%cCzD)SI?2MmH!i73>LR#0RKN4- z$ADI4D_GvBRfEQOk9*Hc2@@x2qVWf_6OCS!UaF4Gl*|AdsPT3;qsCvSzZGBaM+f{#Z)Y!>zi@+c9{orph@HS3Y0}Rk7JJ5r3pic!MG#^mkG_{3nAO}477l+8{4w0Ya5LsnS z*EXVh&ntO*w_MZblO{Lr?-(F*2cHQM%(bnO5}h?Ef5-#Yy_dYB7KJNJ zBiW)lW{g`OBD0PTfGTek7*%dpcQ~qi3BW|_uOei|=OOdWt^qPv+~laTgOEX>30h(7 zg8LkN;2IJ9hX%Pb0@o^^eb+lo_5YQFxnBv_+|E__jtJ`-@dAag07VsEkyqhqT#ZauGlRObw_@wUYSU!~jn+fjK=7VnqWx|y&ayb@GojhKhqL;rs-0>Rt%t$jt~n?098$k$0MftVj}Qz0GBk#x7cB%&`n{`nC+jh@=-9^C z1X(z6IYWbxf7lpr4KW;E9cG&D5wy3jW4~wD8*K;Vkh$4tyEH;(b^jbPGf9JD-0v45 zGa>ZrCz@So7q!yVtrOnQnGAR3sxlWLcv1Igs^d-2i=7c4j3`go_1a zf8xPG2F|GlaT*5Lhgk31Y!t zHB@ArK4FYE22fAqh}@X+YD&ipSR7l>m-u+ag#%Vi?SZp04f_y_B&&tQpn3w7mrgBX zlE0ezc&KSEOJdlr{^#WR+3UzMMJF4a=9x@~K!qXSS@&EV62mJgFYibcBjlBT z?i_Sa^YuoH4h^D4x7^QuYc&ne)g*c+T3?H5`k2yTO}`Fn`mw7i@@gcMs~kK#D}LUt zzM~s;)WCH){1|Zq8^-Lf1^O&|Dxu?HNzajVpJIS8nd&0;*NwQJh)q)p5%CO1J;r!G zI`7Iej9H0ixfSzVc7piFd(lL$--6AMfx8>+tAbL5OVLy)cJWH?aJVyYD@bJzP&sCT zL1=knyeE*vvb}I3Cerd=7%!o0cU0U5S0!5Gxymk46(576h4-FmuXoP9K0~i1MWh1M z;6N3LduQN@`{@Rg_zDi)oz6_O?wTuIrqb5oEi=)Wrs~8=h@#&%zzm;SQ~i@pOSJBj zEBL`qtSOF>c9zV2|1$%m@2A@L3dk9d;Fx)Db7_|Mq9o$v(~Y>vMC*b2EK{;m zjxh%FHKc@quf{Ef>Ie4kxz;Q)JuAraEC{IEEfH9@eq*yfa9yHvbm zsx}TpI!1PrtiF>p#se>m#4!^Wtv$HBTk~nCKk<||7Psbz-kKH4SRn+e)`N%}=6~@P zAQ+WpBC0Xfg%Q|206>Ua6X*TEqeV+Qvy)h~CDPmV_ZFomR%U;}leZ{) z0zY~DRGPB~g#MGWZTURLd)=%gh>|P+qlbx{s*Zzzy` zo8FfWXB9Q?;ff<3$9PXsylG#jOq)Lm=eyxWZfB>8>>pJQSX6wIt<5ZCNi9K4v@(OJ zf7iC4_aZFi7HH~HNDe?_mS>(E?F#u|i|fzUBr3#+LTDce})YdOqk zgE`020gqfIfG(JxcVVQt9Zm>yO9BfZ~ZcoDSx}# zWQumnM$vQNCF1S)Yt{K)uFgMDXSNPEG95X*uKx{?an=~(Ee7N5eW(X6-WP_;F*#gr10Hd{B3ydr;qsSl0$geXT=qv95GNWg zvANE#@;Yagz>(yLWc4xqr%%=px`Oclylox45A@^OeuA}q*9hLdzzDU9pN8=M@f+9n zFxmiDCyCBT8K_K=f&3BwB~&h+g8V&ae2{Ij&-@PrZ^4Kq#!(=TyI| z_&itf-3}5JZ*LWk%vF5nm5xuGS?tPy5ua^7G+9l&tjSt|_H;zKf?7fHc+rO18HBGI zQGOZ`rHaZ!qTIh#K$OuAK*u>DucQKQkT%+2J(A;>6?%^QyK#Y?c;W0iz<=7?#z8*^ z{%!p@@CgH-h`?VC$PoO#A^4|$&0ZnvQAz$JI^YB}ggb40Ak}!Oa#4@k?Du{L}TaV4T!Ki z$0Ez|z&W5piq4O_RNQ~y3WrI{w)I{eb0otcC7DDBAVlsGDGQ@-5V=DbMX*s4gKa(1rJuArDb@dxycuA5o# z*oOR&IKtcuCk^>9978O6%;_}cedzr&c0oV18#kQJ#B3OJ&}J6&;)Wa|&-V*EsM@6% zK5uMpd2zaHzmIIxjpqkly6IBK9lDbvcFAA)UspFZ)$10J=`>w|RG;>TCkvsiEsyr~ z%ATTH$oBhhL;<$99cMUEvQ&Ufl$`ET@vIKU$arTymqYv5hJ?s|uBT4EJuL5)Y)8FW z?XC)ih7C}7bYvmlg*kVtihIK<913+23LSk7nRs@`d=&oHJ3`^>i&Xw^VR`PZ zr)>5m_(Ui)3WeBQ-N)(3aP&1IgIS~odu31jKkm*1K8hlH{0YgB0Er!30;s4FqXy!E zvMLbJG)!a$CxD8Gii(RyRCYy30B>}NCN_h(9;^Fv6?Z+?dsQ&5M-q;N`;tpM0IwcK zFIt~y?XWP)vH%L3mZ?i5E$>dFf|L z+bcr*e3y1D)f)wXQm)qeDle)^ZmOChtnyx-7qo6<(S1x8S$MxfW~9j$-CKVjlcT(P z*?T2<)Yoi?$FU2;P=1WkaNlrd7LY@R~#IX&uqMUP|47FZlv4bk$zjQ`P4mh^nD;K=FR zK(PPV;91fSEtSKx6HR8*t>AKVnHSo_$9UsK$jW{kuy8fjFWs}!MW-d$3AkEqVukvD+F;OLbV_t`-6mT6W!KbocXrPLM_z9<4z7Zl7LN$~d zdI-oG+;twfYzJJl2um%QJ1>8Z7W6Hd$5!V$?k~YaUO)9PGht)Q~W_e{f;`S;3TreHH}IavD#M zDq>q_kMqWq8I<(nAoB#=rNE7@mLlqvYbubt=B>QH6t})}wtBC(m0iqJN=`Ik<~Pc7 zW1evi_-WdkXj9$dpJqwdd|o7}TswPYwPZSZMR}wH<>+Lz+vrGipeDU%|M5!aSEwk^ z2Ia(Dgoo5D(7jK4qf$I1$zuvcdTK`~P_&aItDrdbM(>?+rcSRV;@+;3n47rjV^u@_ zP2QsoF(q}-tK0k{eT7hu;0i7e*Osb~R~$!UYLyztVyrbvts^O4Pi>CsZUO6JRNQ{> zQ|Te+w-jRwI5XWiF2f!^i3_KsSu=v)SoLva~dX@8^~&7OQitKl{x1Y>x=g(A!#DR zlA+8}2trtC-~0*kp&=xgvp@~1M<;L;13C6*7(}SBr<3C_t{nE`>pv1IEkljS0G*5RexY2g-soq z-+r(&RVnscRJB|8Zm!apMqP`*_D`mTBm^R%z`l?`P&*;bGEbUMXtYSPw?hDwul!m` zb07>L%{ljIX-XX!B2klZz=0;{Le?x(WoxPhBYQJrnHVr}M&9CG4y#C@boiqIJ9#J< z`J5)V0^vK`Bg@+*jpUSGe%C{f z4zSFck9b|T<0X7|+i`cg(~f`CfMr@R9KyxT81P#KdytiVg`&ncd7~owI>C@2I+GtY z(XRpwq8s?#CHiwA`YeIn80WMctCEy6Ue1u^JrN(8GeQ=KMv3l-|9z!v>XjPpJKx2= z=1D1~{39d_{Sw~4u)Qi5XsxnS2))TY*;jL6L1k^Ef2#FevGLR_`)fi`m9?w|4&&== z;?BO=DQU5`;qf^s6+cvNtY|^nJ-1S0X|q!pSfq#vB;g3?-JW4pnoH3m1v9a~9C{>& zfTGe;+wtyi#jJ?_v)40{JuI~q(WLDqUE?emVU>AhDE|lXf1t!Hhshqi5gZ4;iLwJp z8y0qcB=uH(M^F_WA>Jb&yh1;WrAhmhFq7vy8aa;dZu=E+&D}hE>0RQPMb9H>hF*|L zWWF;(bRKhG%cuRqy)2?evSTS&g0X_Qy6-U2ehIGatyJy$)E5eKW&-og3iJ6M=1-d? zFxPRdFb@&TYoa3w00klg3hc*5;}}E+1Z-oY@Yi>kzj|D8Lve_PhqV5~WPr1er;e67 zCq;QMeUXXyU+Ty9_KB0WC^wIHxsR&_sT^_5K|adu6a7O_o7XFHFBPh_pyoo7S#yO) zZkZ+#YthCf_N6-&u^(p3WC-1aqGk4@_b5MIM6rxQJ-~ig!`>qw8uoJv_SGKjy|l91 z2-CP$qA1nSuLiouO!r4*%V-50Trw;Yz(KpPy`4)$(wX9oM%o}eUL%~8KzOD?c)f@4 z@g#&LF2YalQ0#0bwj&6oyi2gaAL*THFa5{nc#ViWA>ab1#}wF)5My)j&qaLT|Jynx z%&Y|dx4~i7RCxH0NWwo0Em8~*)%Zs#{IoPf>srByn$w4h+v^H!DvJjPQ`wby%$p@2 zBE^2~ER|p&V$au99IdIiS3b1ZKL?XpbGt{yLwt9|ey&T!irbYGHpuZIN@u{XF++o# zP%VgohX53I5sQ6#0{gvijSQYzt7MRwz<#yDey4{$Hwk-PyrrZ68v97FvxGfNn6>7W zGXEoEDb2o>z=gi$Gz51^iXM_D&+=4(J%lF`jfB_+qbmdW-#ahR-OR(8wR(5qs!MsM z*^r*EyOI3PyYp#f{cwfD%XxJJ?1JqB16bBY_%tRli+`u-Mf2`_0=Vr+O&5Il#(Z2_|&o9fE$ePO3`+ zLkn}&I|Y>*D!Xxo!MuRq-BIUnP-oZPDl|}DA^L*i-RU0f?S}i@W0_Q1?`F=a2maHa zDNb9uXe{5G0A}{89e<&lBX(X8y2nwhk- z6f?gSWc=ui`)>#|#mtS;Hj&(k{O&UIqA;`V7A4Tnl+Z3|BD8LxvTT42(wL?ydH60M zN(ftX-8xQyLu`FLOsN{vkx7_d5lqz@Q@O&_A540FU*oF$WNbW2bDKQPu5O4K_IQyrB5bxw#2{BjUBvC%PhlLzbQx~n&*PG{;>?)1+ zHicFtUR(&;ERFUn>MVh_e**0eK$|shdT0kEq0Mm7J~>{53fhphI~rD)oe`*flyIlE zM@Wf=JE|0ScWN}>$cHo@KD|OS-9uyVPH;DsYsKB!pz%`{W1xqtox(NG&IXMWB$xe` zZ8e&98qI_RnobIhI645t1Gqa0O?MZ~;&Bcde3wBUs+U=d6AKY4S}TnhH2QvPKYEkb z0WJmYQ}-$Ermj&!Y>`0w4?yd*7LB%L659G*4tJL(qjh9?wnBQI8jp&LgFC)=WGPIh z(n#p#85-OF$cL7tUtz2Bn0x{ZZg(5xVvCJE5R(m(@=z3$d8yjpI4WTD{S>vOE2ajG z*FLcyt`R$_mI8!^QmNk0KD>_=$L$g{6e}9$c{F4u(XjYOhvVxGNJAfJ@C-n@{nwZ> zWkcwbR|E)GAO6?PaVt9=_>@LCMes8cG;0<^5p$c3 z6-<-Z6ya!7R6H{b`Z+tqpf9rzqj-e~bA&i{7xTlla0a;9s}-N83A=#pW@SGH9l;b)yap()>&a&?O~gy zaj?{JdjebQeX!jm*wk3qNSvsc0jrz+#ch%!O5EKqaZQf68p>hW%r1)+B4NFYT&d)w zJhGk0rL`|TT*jMQAkcS~CUE*HC5}8rpz6kRAj7QrTnJSC>o~r4yVDJSgY~TWs@arp6eO|J`Y6C}1@fC*R%=u%tgQd>VpQ;TOVNy%aYAdbV2kz}x& z{rIgyq^~iGzFG4E)^d{^&SUOZIh<0CVa7cQ`J|OfR6odvM!o_BX3Yi<`3Go&i~Lot zm8eP*$X^il9GAvzM@2z9D2Qc=HcM*4V)fXh{6_V;(oWl|778TGec*UCKBsC@Zb*=F zydvd$kCeNTNIBjm<=xSq@r@m;XA=E~8Pig_r6$}d)h?WjSh?}e(R9`6TU3}_4F4x! zqYWxoC^38|A6g8nKLg$v{*a)Sq_sLz(vw%8oFxxqi8)i)!21&I{p8fXC zGSIF~M}BeEqxkfJ;p{cR17Z`{f)Z;ch91`C24=2M zT%$bu;)|8lkl@*_0trR-hh0Zg-xe;Fo?N1@kiU;BRA}}xC9HPH%u$p{H|gNnwaR2T z&d_x*pe7PRgju6!&Bg6ye8q-|$_3Q^T|KK?UEt-X_oo#o9Gak|HF%>E_TC?yo~AVc zCo>)U{sg$sp_9Cl1h{&1A}ZyjMRK9;w=m7oLJhWOzweZ*wosZfL)Y4tCrY~CkyU=X z68FPpoCZv30VYc4(E#G3Y?G2)>Kl;;YkW6*V9r+LP8^6%BLY;iXX%+_e>URjVi_YZ zQ-VJW!C!xeP%%LTdpkxty^2sz&yj|k4E z5;*fcoC`q_8}4p?d_{Vnm*NxUQlUDB+JU`^Jz7TTM3HIs#M$y@kj zo;?42;`srd@UI4mBG6}W1&j7#rS-=p7<+Uij4eWo7TTA;s4R*ko3y8Qm^CjYU|z!e z(S?$(qnd4?1PO;okZ_n6_)g9Tw%CFYvX-)XD}$R#0f_V=MYs>WT5rr{)E_9liF;Cl z=~zH>62V$_S#jre5(s2-|5T}KK+=F*KgMAPQ}|y+VFbI>noAcH7eZi|9D`t1U$hF{ z%(I`(R>>k#Q|d@03DdvPOrIfPxusBG+!JO>IHv0PC-CNogo)%5cFYQ-oIB!0@iURGaZEE`dNn7Ms|?95K* z+o+06IFe-B6rQ4Vn6G9X{Ts)g*r_DjayL&UH1X4^Ps5G1m=|<{);UZ?PzYHXK7N5T z-Uh%p>>dMqn$aOJ$IHhd7;azbyoq8Vbwl>gRvO-L}#8VyK{-B$SM!X$XG*cb*6|(Z(ge(|-yTC9*-HX@j zUVJWEBOB0!BUwp!qn(QOi-Tob2SCx8R{(0(TmWV*%51*7I`hW2j?M%G z0SSDv@Df1#6@u#b@~0|99C)nc)Q*yf#>DI88RjW?w04&_WIoY1QzkD$h5f#oq`fg; zQ9n9C#Tqb~HHpo&*YVw@qR^$HeuSo?mBQeAb8Vb({Ct_9LHdXQ%aj9kP!RJWd0UoB z#x0fx=_Y@=xE)Ij{Xn@hg#WS=T_ye4y<4g-Cb~9#uIgf$n$S7&p~W>?5qcvOXmNcO zOAvYw*WAsskI;n1t`+sFMK-2nbS|>rK<`mz@qnl4KKwjJj&>Z8K+syX$4wrBJCYC_ z=OTFLIwgr%dZ7BMOiBNp3KG4-^ZO%p4iz~5u}qX|oJdO(f4zPmr8-Gp(Nh*|&_qX{ zlVgjS^*oWU{Cg5<^{!3I>jI^{yFV>k>Q~|9LH)3Lhh}Jj>GYB~so_!e?<_v z?|2Q^SGln1RBGi{AySDE?trN4IWhI1R+nVQNa{nW6!vaueQc3YE&GkB2Hx~*dPVj> zMPE=b=K;34{*#cBcgL*R4b5t|oTN_}l^^h!3FPd5639%tBfvA8+!5f! zsl8{+8XwqWr;!Fxaea7~yg+E*`#HQzUSPq)yJOZc+@iw1?T7~{U84JGW1^ovjxd!1 zBiJ9mQR1Z8@6uO!cH3*1t2@lPJx70%PMSLQ4$hm8}`@}_`7JUF?1pSHLPpdR%><0r9Dc{SW|Mwm;NgJ>yn~IqjD>zE!=C` zN}AgIfNM6Ck4Otzdn+!Gjrs4hSzbYm!EMV(x$z?a0$OO zE9p^J-m#>|3|e#^|7(rDjU_#1%9a2A)qKo1dN=y-Uq|zn7OtpR;5;_7u<`C6_kjCO z!+cM=fxr6Nl<0>?BCgJ#n0p1!cTMk|6xf04%&2{Yy#27Q9uV$<)TK^2!b>f=e{JQ`^6s2b9I`&! zr+>8O^;%8dVxw=p5ZQZfFkTl7$5Iif|Nhzvk#8ga6Vg0POS7c9V{|lFOFGZ&8aMaW z`Xl2~<=Ft;YKdLmYD&DJG;KSV72AVF3q_V@b&ptA|NX~7&dHo`~=AKV4eHA=r z51JGGs1Js1h*(?kjT!EdS$;H)Kx#9FWW%BggeK4@K?)o4s_Rz|{J`QiMn zFY_CH7jrR_i{5Jk;reF5!i5!|n8tK^&h)v3jqmJXa$D4RXO`KJmILzWGwmqoP*V6& z877lQ>i=cuFsqAVgPeX{HpQWr1H++rFZ8yePh?mP(3{^!_Yg`BWC^Ve*5a}$#`IK0 zZHpu#>mYJP#aCvyD94!C$>|w#P7HHkV|toAUy)^6trA^hI$dKrU8Ah}H&WNU6drLY z-Oe#xb&h##B9^^rO5)wSaYQ`q8q-@t*?P!7UVtCVjnp#4)>LW`M4n*97lLG8sii2X z{Q=-*X5mM=Da+#ZJm&NP%*lRTpj+$|&`KXTfnY$gerb8Q%~jVd5Z;hlvB3;?%PH>& zmB%RMPwFpa{!YgW%c=)39yH~7C0StffHZQAbq}z?r@hypT1iiOP33Dy4dZQu*Uh`6=CV9BofmBSSXh zKnq3&t4-L`_0Cw>u-5gmDW;WDRL_LEs9rQ3MHf8}Qb1Vrd_8(>hIj25i*~2xfhRhf z{k~?TcQd-J-@?Y*+S<=i7AjO!r!7?XSbr^RhaS=9^d{!s$`5O0KEcnp5y=Ys3mcR_ zVx>Y(hC>G%=+ebd(|+CajENheK#crXe2S45|HVoxsuyK$VOq17t&KOv(#*n{Ousp? zK;B&IwW#cF&5SmO{v;)?D6?Alt##1%ph0 z(NRZDd=Q*yW4cj!?*X;(%_iEocOlw1=)l^Tu>Xh9$LELrB7K}f>-?AX(YS9U)5)fK zn8T2UK8WwJpEeFexOr+oNKyUc{~moD+q$VfUiRtEnr&>P;a+U6@CXhZpZ4ib9Ha5E+)%z1JV+qmQM@`dE-~4?KTF>Elnz zKNy97@U8X_*fKiHZg*vpK7LiPT&h%S!zde(PYy-~=9STZLxo=j(w0}JhKjx#?F$qx zh@}M!=ksOEmr{M^D3f5Bc<1G87Yr7Dg!3`Ve*X&M3f4;3xKjTX4z$bP9SbXUFFcJ7 zcosGj3%e!eSlE!&hT6&-v^UWS3p+zw*q}w80R}c$R3E~?28(8ejNUc|Hd74jy3)es zLD#?*gC=?{>6oS3zy=RxU*N{Qz@DfL>en;>V8GBtDUu7_1af9VieVpZNUsui2|RBy(f4FUj+*n1Y8@dWO1n^h;rVpYKcR7 z11ynt0$Dqh8wNed93mtZ_FF70%*9Ev6y|oQWXsvwUW7Uo3LDzqMnB;vG`hJ_neI*q zH>wGt*5|e8bGX+tbCPF-du2vAAfeXRXs-}QIZr z{@(%TewQcgub#Q?Km9w|nf?)hNSgi?H<|w}FncVQ`JbZe-=F_|fRp>dtOL#eg2o2~ zbD96Cj7(=z5iVl>_Xtwf^FJH?((}J2_7i!z^FPR2Yz$!jx7-Nn`QHYnUQspwE9pGT zoUn7xSZ2V^kgg`vg1Dsmc)=~%piBUn1v34UY2YCgz9@FO|9(+;dY3Z;boEWo|7Mvz zXuib!&zAXL(ShfGh5OC_Tyd-xJy#2&_QB?Vn_LS&VmJQp?1Ot8E`5jaH_jg7_6I%x zGp6GJxlzzNZE8EBM!%x1T%TbBMpxr?j!f&ztWZ zR(Ejg`?KbI(=YtLS08WR^#6xGO3lRoNFPNI|EKg()-$n=Ese?#|J8hNZ}(nze()d8 z_f8s^q>sPqd~d;kL(camyKaO;)c%wC-T*z{3+*@G({`olJkL0I{Nh6VXQ2iE;6Ix0 zh3tLid*8yTOo_ha^WT~8NtiuZjc@vw^SuFjz9$syGvCv)*ms`S+S#9fzG&}Ga{dJFh9YW4Jd3qoiO-oY=%|6vye7W?09<6~A9c&*x>~w-28O=2x++ z!_VJGJ7{;9fb-Y;u={)yb{~2NcKaTH-Qq*?dtSev!S7>uaKD&eA}osEi}?H*{GQBX zG%o?olb9~)%&$BrlnKKqcw|&&ci%J9Vb`C`ZXRk&kKRYW*;Lfq(Qg8n4ffz*Q+{LT ziw&Iszg$G^oWg~C8S|xVwWm(36Tie>2MSk+2HJZ9 z%~M1-M&dAfveGj*iDl;L0GcuffjB9*g!E=t_?T~rHK=tWc}|xo0EVbY9-m1fbL~Mu zDTo^{+l|H}Fydw3!-X<~T3mH}4fncrT9O^yD|V37lYQ)7_(WB5#B-HE3(v4@gWg#?%{e+9$I_V5M1C+y+q zw|`FHJMmS&d; z+1LD(%%y-j%=}A!JMa+Ai^DJBqbBz73mA6o z;luyrO$S zed>|0|3)fRRFB9BRF6u^KfC+eT104c8r5iY8vl;p=u}LBKS@aAGVmCG23GM0ACE8I`-ROvWYKW#u7HT4!Uh`m}9E>rw6b!|?`2!Cq~O-$Q9V)jY+1 zowf?d*pNj#;&M)A`Hm>mM z9=SZB|1+5>a&cxaE=m~At|~K)g8A}i_Eysvy1?E_A5g*D0$Yuz+l-u) zZTwPew5c^NBdBN$nnSW*bZj<}RI&<8R=w85+Ve5yH&z2r_Y4~aI}pKJ`KoZt@E!8g z_4}f+KqG0>k*qfhSCT9UGTq;S(Ap@dKM=}sImT%#$EFztyNuJe%I8+ePf$~iIUYhm zv_SD=p9Bu0&32;=iBp%YfbB~clSNdp%DAinUN4;?4Ab35k<#&hQwflpx%NGP z<7M0Q4mjOBR(bsG5o$w99x4-UhZDNh7;>h0Oa`Ahz6Za^AXij9+RjOu+&m^bAbTq0 z-vqLpw+4)pSE$on0%^oq7qO`0MO$aN3+92MB}7|i$>vJIW>%k^AKpXS#V8brwlXGO z$HmqXW9V0kKP6JTxAb9aJ0jfdw%1Wc!90;)qtRxLaoG}{8?*%Kwh}0}vhuA4m8Nlo z(PoWtnaIEu#M0#=h?8p_hm{Gyo6#SH4rU9qN_W3h0h^WFxW=Ss;LnVl_mfO(IiN3I zYc2Li(kq4Sfb+?#=1$|Z25B%pH;>JNRIj<@*PjmcYwDqXG}90ID-`93Y8aKbvE8hs zX!e-XwZ4VT!o^^10vFOYd%CpVBaX(MqSH>JeTSJu&Wkk^BkKp*@tc5a^}r{#_kXv3l|oQJ=WV{s|PV z6!j64AnH?Hfbx`|EuH)fmPpRDmUR=ROrHrVYAYfMaO^>#fi}21YqcV%DvVD!MxT{*9=NssmLAak&4Xj zr+o1eMliUa5tRI$#Qf}GZ9p6os0k4(IHgw;E4VOrxp?tn1p^Vsi%;@r|CK*~jP~ar z&QAw`Lh6VQ2Vr5aCcms`{ zDfc}~TIX2OOtGZvVr|7-wzf#B&gnS=8IsF^40i;y9lc^-;C%qD^?F<3N|7J$wdRGn zO>vBCgW9beE1BH8lqrxaF`rB%aJT2`CKKR}?Tnp^rlQ%}Vs14qZ4hN&AyDh>7nW%K zw*QjQ@1gC`@3-ZxeN1LCwjlbYST5B4up_j(UrAY%|C-4p)nKG+GKU|)WExQEn9Q;% z$SVz5G&Ygfma&GI%7E42BwV!NXV*^teKFi)&%JpYma-Ci4uf zS~D=2Al?_U=#ZFAtwWz_9pbr!&0P6E*vzSPBT*%7DP2`k?nbSB#Uiarf8j}oKVR!y zROtqJOPR_y98e zYl){?%eYqDo{DTOFqexVPpbPlN1fE|%&XRy+GqWNpFhCA#TDGk8~CfwD2@V=m(}jT zV0b2`UB_F}^W6j+*>JIg$e)fU&bGfkTP*d=^g!Bofx^8NAH@zAgtdSbe|3_AB&XX> ze2iYoT%nO)#LViwwhYqmsYg`wBxvnf_H}%r__dQ1_3~iu*vB!%X#`U;8IFz1cEnC% zeA_H`OzbOer1AB3``Du9DJA$qrg7Pvt;Ut~p&?tgGJ?B<9&%yGdnbB0RLInhZ+vAyvxlA4q!pa;vKHD8HeT)xq^*$n*ItQ#EtL4zBECrcD_-u6mwE}JAyBvy z>R$(%z2i*eEX_HGZ~HYTf@JfM+m-=>Vey61hDojY!k^essDoq8F$Ozg$CVkIwkKMX z4%d0JJ+M2&vNWTq+Oc(4WUu;T#f;d6%oqDNX}dwdw2xM=P*hFlGZ_A=JkMc);`zi7x_eo+LWL2AmeekW*3RlT?7IHj>&oio?K|6v- zicMdf@|B(+tmZ4Fn5ba)DeiFE9;8ZPHzkODP+!Aa-1AX7W;}3^P7g(ou}+VGJ|s zAO_Rg8@VxV7T%Uww<)b?kKbBkJWvN9SPVtlz#aMSeKJK@1B2DQHGBMEgwn{6argtN z@VBTbf3nL02HShoxcqfoWmX*-E8UMf&D`N7g)GrDXHSXC+=>61xx-63rI~Cyir4## z78sM|tU3u$bdg1_ZmKk?v{!(Yt{fezkbQcxy*~3Ir4h38c8K*cM_-avCI{Q(mRfae z540B8kDehu_&~FC_7{C#k%QK~Bhu807jxB; z*6DzTl(3aYso7f7nzMaz#~Dl4_Q^~?Io7wNu+EsaOlv3F7mUvojpE2xfhC&SadDr_ zK_|yW1CKlipsMtf&G@{aOb4rPq*)aIvU^F`iY{=tiHwND&R$0aR~iq9i7SnCI46|$ zG1_&Mzi8$Ec`n%dIm2JH+?agBzI?=JR?Sf;jrdcL7I9TZHMQT_SZ7prQ-w_^m8vyY?DpI>* z`-5sf?MYMA2J!KHb2d?*@QPFbIE{!l&H;OBpJjIGZV>OpwYUs+Z2=p^7sgpvfeJRJ z8`Cs~>XJO~p4ih-qSE8#1jB)l#B!+U<9=i<-KEHqJ>6G9h*@*G#M41US%gP5N(kwo zuh`uCS0<%GM)Yo$S-a~Kxyp|*rcF{@3N3eorb?4kHA#K@r}#*b+xB)pGv_b(Se0+{ zJrsFHy%AtZs%(n*Mq@H4RYg}UmI1ez;$uPUBYV!NQqOBBH(7D@S6}f{DD8_t;qHos z(Nkp1SBsoB(D&nO*N+}w_aSGqhNIS9zCO@Az?WZN!PnfsJAA!Jk8%0x;qoN~CBmQy zMJDCn!zUY6rD2m;MfFpwWK(0NS(Rc|wNN|l*MW6r~LIi9b%gX0VJ6&$a6(cySHr2VQKcm5yYn3F+%9>-s?pn2dn z4$>C1F)N+tVJ#DF?9VT@kJ#xo@N<)e-Ln(yeyUl6-QoHQcB3yi>^8D|>au%@Y;H#3 z2Icn+p>^k}cJ=3*p~%quk}A+NZC1%psN(#CLHWG{k=yhA{Myp2YGzE!2=GE5v#OOb ztyNW8mEmZ7diOJr`7=QrypL;?3cL9fs`XonzUy_y0D3h+^h;~~vO6iiU`5sic8M#(wZ56|xscs5NEJVl9S zJJG}QvW5fA3)S=F-jw)5Z6N&*=`ISSd*>>5X$x zEKE=3M--J6#^`~V{{0W0aCce4bgu-{r)vfg?sO)g0EOvZ`YJEl0ZZvJ{i_(G2mT~o zvkdgf2KeCiH`wW5@^}T(V3}3M16re7zxP<@l%^EYo?f}gi>@H`bs$2MS zeU%rLlVn|X|NGWx$|JfzIM(Q2n4!=~hcH90h)3<3p`U4gF5n#`$g-dmmDBqBLTsr8h7hOx%@tx>eU%rLK#VKIU!|Ljco;eL`C0mM(uoJt7Yi*Rk~0C~ z#75lCSD4Q-Do5|9KA|m&b14tCnySV6R0PKh%h6oI`J;bzIq#sa@rBgD5K{@q|Ca9JDwIU zvL`1Y!E+>)5_edpnq)cQ6MMe0RHPAqx&6f)eBm5KYVERei($%SmPu)>0D4Q+JKN`{ zbfPYwElu_6NLA?RzKN;@UYCZTcjGv3d|LeY(P%H<8#FXR9P=on)6%%ZZ^PAN?#6%rSQ1>xib!Z+YG`X}CD_T0%Vnnj72h;NvB>bb;I zmOOQ{^lb|H42`8D=%G@3F@Cr%xg{@Mu3ma6@zP*sUg_EyFM&kpV5V& zMoOuaVzZ`=w2P>z8M$DqQcKauS8h@oNl80jM2kM|9F6nW^4^}`b$8ydG zN}fHNT@rGrxEyB>i1^*o7+V3MiofbgS^95CB27{#vi(I^DM|0XTP5anU1N08=by>? za;ddBX#ME7mRK=F$`m>#oqeF+n!yJVwOwt>rLN!eN`wV;R7aye_C$Cx5)%qJSDrZ> zS2Dsh+@<={V&qDHGTU<`dmkodR&MV|OO^u4`%(Bz>T>x`RDgRV0N>&PUZcSQ_-Y4m z>{+w>mF+9?&38~`v=l2FYp1_hGfKLj*WNVzwa8a~Fs<|2lvr1ipY)bGsNS^B;`R`3 zcL-5|QR3<))}2}0^K*|l!4vsAxkoUeTnY)3{T)xOI8Ty8Bxr0F?Jy>;<^qzB&lL3> zkRW-XmH@UT&m;L&=5WByi&hFEIa%&eF2Z}8DX!OM2#7%m5S9nxQ~e@b-{^pd5rDg; znKi?-`sdWGOQVX%WGUVRC3ULxuStOMdte^*z~nn%XqD4REJwL}&^WZpOKoYDzhKM5 zds9_yzY7&ONiKC7`3KDojP>=vEq`1y7JCiZRE|ImWnoB`gJMS()m7xk@{39tu-Zpi zLQhf0aWE%-cYS6F41}tmQ8iITJBLxgbfgg;jP|;zIh?4-;_wnRl$TOqy*_esRyt(HSZL8cSo3Hz8adGM~qW3?JIytS2!~m*gNa=vi0|wb~h! zLL?i4);57LqST6UI>HvrT|D<=R@a=`jH3H#C^5qO{&pq2J=8XK#B(L0Fl+vVyl82J zw1JtU8+v<{YiWPGBo`^4EixroJq)8zOlsCf+K>76l8P=tt4+QnN3fYTT%T)acW9o{ zosMTr+`ua#bxNC*V2r;`Kz}8-WEoAW2L>%!ovO{16|M;ajMdd_05OR3c7cn7zKk^tdQAIotsGMtMrOAsl>#5cuLv1X}y==M^i7=2pZc_v>nmL!62NY`T7!?c#! zZ}32p+!xuyUQu!v_iX_rQezXk971g^Q}nx^CJWQFm^q;k7_CP#ShtFElSQN4NLlFM zJV+(ATKU2dmX)ssWWL4_H&RBMk@*9oPb-PXk+>YbDON?!u}=o5Wv#taTFisI57-;X zvlF>Xr)`VEm^G^tY#isY5!S@RMhk}x^sMbIs)3HBoiL44S|j&cMeZ(@66og)7slWb zp-9hJ5SU`k@>@Spd9oq6V@Dt|iW*4Rjwic%wnI)=xddqf{hjd&dYvHA&EFB!X3f`L z^S{dM(QW?GT&w190U5;LjES2-C;fWWQ0Q9Em*T1dYgV0|S=BdHK69%2Hj~fxRejTD z_f6-Z(pdq1^;g~GtLocaf8&`9eZ^DG6M0pADSjf)7F6|Z$yKm!V>iBa;mxe7zO5uN zx^H%A(T)nri#?H_iA2O;aim=?VMqHa4)0-!H#gRYoaC;`v^c#(*>TT+^pAxieRC6r zMlm;(qS5l0od#0@>oa%Y2v+yZ4^(#%Tk?)#TaVw84J?DUY!)og{tJ#*Eq7$e@2V@D zRh5JXr0cD^^>&|($m~cRmn`*mzu|}@+;xGc+oPFQ1+;u9b2#py9vl9soWnJ`?><9Q zsus$#Z#HlV^%zgBaI^QqGRTO^qLlJ_SizxCteStyQKBp%vCiRJF{`B<*BG_SIC0S0 zjKd)Hav#*%*H1I>g?wmk+W?$+6O{8Ennr=b)xPf^bhOVpOmj&e7$#V+oDqoo{s1Cb zBpT9H+|Y^oWKtZ)vMJP)mTKS&>@jD$qBbUO2Zbk~mhsJ)9Vtf4Si}zj%Xh4RqmW>= zFPAG>0U5-wB~D6X_tg-%(OefK)D!8xh~FFh(aEuDkG0iB5Kk zOvh`6dTdt1n#*Rk%jPrLut~-H$eD)&C;E^gY@YoWmhz-EM}pu8ji8xE@IMIz^O2ib zbG3({APK?CQye+=@esVho6$1_LAcfj&&SO~71)MR+l()5_#)i-)8quk@yErf8EV|~ z9AwZ7Wr1FT5!BfCTEp@!0R?nl23X-=j0^)=H{GUBX#D7LNA+0l7yNG4NC|F7jsKpd zIFC-%V%)~>fa#!Z)!Pc18gajdE6&mIxa11it6vX}9s;uoT?1BVO?R`#=U zH&la z`&dnr?%Y;x3ec<5ABxwr6DM~Y0dYXMN9$Y&XmVxzeQ9i2CYy`iPLJ| z-}%LU7WpN`qxOy#rKsaEhuj|_Pb<*nnm80_9g{X86G5M<`TG+eh;l?rtwGXhtn7Zk zuTGyFudB{JR}rzm9(SXTO2}02Hwsl_0@Y*>)n<(VRAU`fv4`+kE(lYcTbzV$C>>i=|@pj zY+N_d*REFf*@CSD*!=2mb@qweDDKwlcs|N6$KJ77vFPLS3yQ0HW)~UY@DVnYI}2sT}0nVm(HP8ks|N6>D*x zGA~xCF=)-?4@DMaI{qkH!;H+)^Re8wZ)RjmOf+gMq;t<2rS!c&L_x}Z@&PUQ=kL%A zX3fpGpIVLDxU@m%W0qDV`!u0aji5t_npoL}pqIcywLhYoYcDkx#N&DNMs;@FcmzKP ztRI30QLLh~fwgr4tHc$|8rd?Zu|5eVx0iKvvDRcXsQ|B0KP;Fqn}M#|UF3IjOGdYg z>F?frNX9p6ur0D5I+gJ)a`cx%gR0514F4|^!@h8nC6J@9&K8`zzERj9WWz~s+H2Pm{zaR zU#w0v`YBwa3VHTSAB`S4`b=Jn9w(rQgJ^(%)Bxwnht}Pn?iYa5J%B@!0De;C034$M ze#vXm#ZV2vsQ^bUO%3qY1i+URz`uF`Gm-#WF2If&;6noNE)DPj4RAA^43;}30A8j5 zKH&kJ1`t=zd$<6Xq$`$t3qU!K-SR!pPY9t4ccAle0ZVUlJcZHvJ^JU)`{c3Q8h&SP z3KGYtOaP#Y48!a98pg$%w8;vT>i8eR8$Pc`+EGcQJq2LZ8fR(Jp69iw6UDlXYluqM z0DC0>{y_n(^Z?EUh|6n#7vNWEir2xs7At|gTn%m7O-gc`+gA*U~p|HEQk^3Ct7}+SLdMII_S3 z%7IH5yu>}_Z-^mMt0}r3hgc(bP?x~1T6>bYa-i!PiTebH@5EG-NK)=gVOq*GdW%1D zQKrABvHbiJPBJL^v4WZ^%vUizBMJjKg@G)q?(y*YjI?@E1PklQPL1^_+F5ayddTGX ziQ=55KbwWCq}LLnv&kM?rQeY! zXf9o6%`>+zF*|N4Uix)$)o%-87m*DbOxtT(OT1*);*KkpuD9kbU0>DaG;%_N@$~$X z>fcDDqLc7g(phTS5AsRM>s*!Tng9!sOkV0lmhRB}Jn@KGsAG447>#aT0;seX^5pOa zL_wTwb3nO3h`lohJA z8Fg`~CEYB(h;uHi9r}m5ooPioE&KM7?*6ra)yhxeA0CKW@q@eryu!bS09%3cf7pfyi*GC3lyi-dRz)LKPnMgqy4oXR8dg+)Ky@dR`d=9$KKAaaPD zzmRxTZDAj+Une(1P3!igxtdy3&ySCnw-Vj!*$gIPiOcw+qD|Hhcl{wLUTLK{^vkLV ziA^Ln`^CGHc z0c&Qcx|qfsNUH`iS%kqsA7vf=+i6%0f*BIr2w6+wtW?F?ytgm{{_gx zlOEMH=p_ADcil^Z!)wiV#UupMup1qe0TSyxsDu@gj$&5_Vv~lHSaS=_oI-6~J=@$E z3-~E%bi5enfjnZO8~fPjMze6v(pW5yUt>o}%Jo3@TGF2ax*ugV3$M#4oVzq8W+q^r zZ5D00v!{S7mh|pI7CBg@VaPWBsdpxPQEW_GXl`HT@3_WX`h`EULoSXhMSU<;?4`n9mg&!5V9yfL$ZcB2l1NxZf@>)n>>G^5r~(tQrE78ta}Ef z5{8U9C521CBZl2W%8rP1g_PrL1O|go0=@25YE-;L=w`t(C^0T$Vnk%D>?V(sq0pwa zGN5AFr&zxML+6lXDC5j((kzwg-g&OM)VAj8ofx$xndzr%Tkn^B2^ooP2{cm%gPAiP zC>Yz9nuU$U9hb|P)!g5SF-zO@M`Q?66eCx9#Z}6(|Nm#tkNkh!o?rG~vFF#kekgl> z9>4xVd)|sy|80A|;~#+j3DHp^39tvQm5Gw9D>{>mf8MUJF@hl2u7MJnxhk=i@cB2Jy``*q*9elI5=aJvgo()I8Z@}x(4dKQH zfh34Vb*352ns(l-@7`MybI6ul^HiSw^bcM`3`q{KXO9;O@OHeg)OCuadG=KgiXQBA z&*v`hU-txp8x(@B9)j5bb@}V(B3QRgBe*_+;4|AnaIr>kjz(~wMzBggGzTpd zf=(WSYm*Q}$2uHLa1h7>s6+-*XdEH~l4GGQ?5NSIE@{T zWZ*J8DYjp{Nme3l>7|!J-PUWHOH;jhg%aRL@}b%GDYiFw)LoiH-B;xf+qZ8~)X9Fn z3A7dld!X#*yV5h-V!%e(C#-k-tt?zjzf>&hcR+Sf{aLpMM*AlX7;AlxgCZ&$L24xS z9;nXty`W;yReSlWI;;FBoi(63>o@Wg>k@;1)F%37kd1~T;xY+jr+CO7@Q~#?$YOWN z>(iZ76!T!M$8(Dh_p&Rb z@0s8O%=*g}m^6W=@y-N?S<@Ood>8up?y6fG2fzK&Ce2nR6?acxrUY0oADX*u3jEO?_`XTtm)-1ecY^~yUo?&ch<+y|60n;OQH1x^ zkVhmSR{+VZInIOp1JGQ;Z*d{#IFMz;;27-JQyY9yHgoV&N|o;$t|U?5Y<^UE0{iuigUKI#ATNet*m@}ar)DQ;ympl-{nlW6*Cl-vI| zYGIz0fM27|JvXYKlO~M#K88!+U!>ueC*VJ;c<$-JPfdbf>B8^ez?ZCtfX<{iV!hC> z4spLz!`^{dp}l!5s)EveMcUu{r* zN)Fu?jZ`VrqK7y1_EM<-q|uMp=pUC4ExNyhf&#Lgw~F?# zZ`umI&o&8b#*QscI8K5PPXeav8hN!#ay(grqpyR}S|;Jqv=uU5X$1B4V{qH;`*e*sYrg z1@RGp^R7tVdmjQ+b%q)0w?JUW57oW80gaG3Mb&jOmqg&b_%qQvwwnx+ai97M%qIxk zQjrRryPmmi=^&3zQg#gYpi-=N|XxW zDCv;3%rsszb0Cw$kNM})Cr3ngXi3aR{TwPL@KQ7=!J47en zSMh!&`*HKepWT0F|K(u)cRxG!U$GzO9TfjBu^&6x-|*|&kHH&$sr?ZB!axzQPC7q< z#UZ=YO0~9ztaV-2NHYF{QjVwR(Jpr3w?1s*d-=6!E!IJLgIEWvZx*IOk4|DEu2DAP zbZmj&x(OSR0?*Q|$)VSWUOD?2r2h(xkgR%yNaB@&hFOtXn-Sx1J_@Kh?{^pn*_)$` zLt|7N3=R5O=cWSvA-9w5-M@tYKdx~Q_?LO`W4Bm++X#Fy0#@JlVhttHz;E^KK+!X< zWH-Q|DPkM6y;mHlRU6J;+{f<2&;bmCvLrytvmeF&D<&}vuLLZ{V0VpVw`IkEc`)Lk{LnKdXG7-tiTtV!ffr{9%@TlD4Jp0XcIKyuC zwn*rYt2f8%)}}Ehl0fITR1h(1u0miA@PxdBfQ&sI-kCS{!tl;wr7_Kw?1m?vu0vv= zy%)5aqAXs*W&c6nfUt+IadaooE`SuRF_*J!InNIChBQKck`bP+=(sKc`6{nf>VOb! zOB5Dc*bZ}1emKt$0)bgGlINp0^HbAgamSx-uj3_me>)2ELf%L21B1!B#_y4RLd@b) zx^A}zWyl0r1x=(&>^V(M5f|z%ahga|cZr_xW!6Z}f#x&QWeXNxAMSLCzH9ZQ>2j1y zZ&=2WqaYIK?N(QM(HYV6L4zbE|E~#tZvue^Jx7BB@Ffo5*mZh39l^$UbLxRIr+xv@ zYOYIlFK13Ic4D&Mn^XTr2};e`G$-pdP_$=+hv>WCdFsAQtNU+}0P0@xx#ma`iLmYg z_7XlO+?1|l@{1*@`zDQfj@sY(*S^vse@}4q7il-yR4k#F`9w{eZZ7fil8`+7YU1Hh z^03MREovBpAz5Ize6|^*U%)8&JP_X9e(Vwa%^R2T&ph6VWxZ1@uNJKe80W2W=KAw7 zH39pXpX`JES9@7%6F`s;$i@6JlU@S*a<>1KoPqGqf{JT_l-nKdig+@tbr!b zHPh(Mmk%xd@1f1CIR${4!eZ!hgA4Po(mli;4oyK12-kH8vBe#i(P2{PGh{TM$7t=|))C`mP@mGW7UR<|~@ z?(6og8oAf3Zf#Dhx9n26|Z1oW?h*OVKvtZP@bS{!!tpvG{xR}q70JyoReVn z6^K#>%IFF5dnA}1&+Wb^!7jK*gAGReXxx+J1E_c{?*regk<40+JD7w!+r|CN$KV!e zORAB4`m6E+kw9vYX_~NHic={&&D0IqYGyiD?b%41R%J3+G*^;RGLbBh@pjHtN-&h= z@Xdl8hvQId7097_!3?Rhvwzx;=%^^as7j!K&RZ?%(T6ZR~(s^wdb*=nacqW>ue5E;hACow#mhR^65+ZTjQ!(!dk*Cx9VTofoJ+Ra|Fg`y(VL zXq3y2wrI}$t2{qPnsK0bGj*lVvt2r9z>e(oR%i)B_|B5LM3Md%ILddI78?EW0!=qY zclTw==zgy?^;+0=jP5-vF}ix4ewcW&;-n5_u^yi$1-0yRu{XeW1k)tWI>R-+*unIo zhbawAtgAzvs;Ytwan=Hysw(&eFiXOvS_lyPvbsnO8!|9XQ{LdE@(5C+Mw=@VuN~=;bGy9OjMtnp8dwQs@y1g|gD&Lk?=Cyp zBcYWd;pzmeSAn1vp^Lzprqbg$*F{DnPfG6aBl(aQ4OVq1!rzj}Txvy`7^^t!EpT39 zbx|jA{9e4HYMB^~C-^&z+2i>cA=%zf=@N>l{HFM!nl&PVWu|m+~arp z7&+F;+Qtn??2~{pJiYk9@H{FXT5``{DxK+R_|U|6<-6ONb_{W}c=B=?)MmIl<<-1K zG!Z*;J1%ENtMvGX@FDAnI$WgU|Q!e%_trUw^@M>@nf$Dij&6$`3aqgEdE#j$dZK z|FKxM!|bPyO$@1_>JvlNB$>~O9wXT(*#_+|{q{ViFWG!3MbYj;|4UE^(HOrKyB&kH%l$6_Br);{db;_6gKbz~5`F|Y$+w#8^{}=QB1OESm|5=DD zh5z4kZS(&V{!6Ek{z8e^yT6?Z7AjfmzEXs1X(!#j!G_G|>VSJ|a6(j7IUV-zp zmRAB6o`W?np4;jNk%7up!8v+SvEkbb8~(_EYm!(k4K?qK4QPtd^elOulleBf0nRu&PfX518)^ffzB#;&TqfD;y#?i|zNm zNf+lyvUj@k0zSeY@wLHqKHr?K+7pt@nwtcGa-JF?%3Zx?hX(abJ9y4q1fEfqss>MnapJU6_*7_Hy z7pC{qUm;t*C`ciwwl))mU})B{NNG9A1aj&aUCCB@nN3JzQp3G=YtZOi7qX70ZuTvi zlyCRl_PviX@t69BLxkNHlOZ?z^F*>yxqsa1DW2po?e3!l_A(#Per-Z- zZIzg0txSvUuNNg+xr}S>=Gl7|Dc0mL87Uhj82r{kSxW%pNFJE9UK)9~3J(n|R;Wyk zYL|QfMB;dWGi%=QP>oAMwfjN`)gLvg$9XEe^QQ`NiB8hAZ|<#Ve=z~@&kEqX9>5cm z0RG(tXexlQ^U*G;2~rOf8u4!$*w6&9QUy#-Sk!`84FFf&uW*5Vzfc6>t)?*g8vAGRp=Fbyn&EN}`$b9EKfl0{ z%~*{c?YVpAR2~&=(L=nB*M)msQc0C%a`r0B248?{8Z5;gP_A}up9=nmDJ|FFJ5`?I zpILJ|e;o=+HIMi-fYo_!ky0Zz0CkUXBlIv`&dr_}cfCVuA^u>K|~F~?9wwfqPQ ziu}Ks%_`Ylc8otgq-?y9TPx)m2NToDZj4-Ms*j ztH7?yEG)bHK5z$|z;2HQ-`!j)cBfWulZAV6+%7&^;w^&{Opfp{{}FuAFZdO>^;tP7}wz zg=iEnTnLd7C}|Qlec5~AvjML8dW$*k=tbiboK&@H&FHSyn z*grOFpb4T#4hSw`hXdQu>PoC7yGFvtJ)8>?LXdDhn#d_J6U`(G=7tP@=TQ2yrRDI#RfHf6w2CSa>+h^bw zgSPx6z@#wg^QSfP{=wg5x0QMKdMsg_SP;{~x(zudIM|z&wxm@(O^}(q129AKsN^+$))zp-As-fp=c|yZ#s4u{MAM<{W!>W9|2WG)^&6 z=f@b+`ZR~q^D|&Jt&yufKJO~spCRs0pGhu8b=>F1KxQA4Od9{r;!!?Hf?%o$q;{?|D8SN!Pu%>YP)jPMtb+>Qt5Z zLbh)Ck6pr4@XBj#^><)rO1FQOtVz%)-JWe-+r4B><&YP>rLF#Ul_jerpsTi5m= zIdTXaTb|^^n#z)P5|!Ov6hlc5`6p$W7e+8hIv8uW%){{ec7m~kg8?=YK&on5x|zFf zX}tursbjO)T#fk2=DrV4|D?m4ovEAGUwd;p)~a%STGtK?>e-uC3X;8C11m$wg`Sqt z_}0TRTC&hGS~BEnH!^lx;~2NLpG$Qio29ySTQjmmyLwdrEDnUtA$kZh$NxxjYio8u z>)Jk*B}?_Xb!5pZZK?EHTHo2W)$gfyt2-6%2OXV?OU|-8BuZ9^^X)@cSF*_aY-cox z(hHLAl4o1r9f(h&WOe0`XC>7|n&I?=f~Cj7o2TdYFL;!Hf8*b6{9DMstN54Z-$neJ z%D*%DSIfVn`KKP5En|B~rw$u?y#JEI0Xng)eoMupoY}GqGk|VS@_x<>4WED6S@u7#hNFd**+m|9xjF-Rt1DNizcvQ)!;T zADV|hp%eU7RRMp__z&<=(YAl0T35T4bFu_C4}HNMOhvJ|&*D0vHx6$2q@(JTR`04= z?U$=VuI_lZW565l7h-o7zm;m9JH4kWogB(oaSZ0-=~c0d82|Aw=;|O%!mB4`PfLB- z(J|mQ3UNEp+y!0iwzH`4fR{0^tRRPoAt3_i|H{ftp-j>XH#%N6s1Z?+u|HHV=LHX# z9L~KMlB2m_K+{2h_TL{P@6SdwE$tYvjlrHj6mlxP`S#wm|MTV0BC;*QCTPt{%a{R}3Y#wki#nl2)b3#Vgl>RO< ze16h~gz4c6QXRJrq8EVSMb%o1cpF#bhuD{;gK-!*{Ln1Z53-03IftU3!IJdcf!Gru;$G=@s1IO|& zKOSKCPn=b{`Q0ZJG#r^^*wEoS*FjYxb6*jwo}*LVtjWlANz2$dEf?*kE5~fg%5`Ju zI^JvU+TyMq>)#uF!KlzCj{vSirP()Ruw`? zE}fk8R>}1+psHy@M}3b}*-=IH#L661tt#g@Yd|DxIaVc&a^h9k(L0(9%d|E!0-s8P z0E-dTyi~dxu&)zjQOzA_O05H|y`5j4S6rNox6KVYe`Em@S1&WD>h9tieY3alfn9}+7gPI444z_ zqHCHltf2nll9tj$ye$#e=h#&()TAF%!hXrYqiXH9%c#1h8N+p3E>joIoK1T38w*gP zv(NZSb2lyi&Y(7l%=Rpn1hse%s&j|5RSXx5Pzl>gMk))WAUuz zRUEeAWmyP8$dE=xUPLX$QoL8$il}LsG&cKDhTtkTgLB!M z`r*v5LjkJ@1gru98)E`C+N)fNfYsCli7C9Ts2-g@fw!gO2gc?c2bj_+#TF&gf(GK~ zxrh^!T$3~X{vlzUIQ@W-6ZO16b`GHj29v|Ck0TcBA1e}JxY4OrW$fX#V^4Vs+-=lo zm35wE(+|`pS@yf4`f2&jQ#z$GHfI*i#c2>$AM1D&uznL(iz)>#s?~Xtr6;^Pxq;(A zsBXIQRjN6K#FUzjaTBb$2alq!RrD*J5|7QcaQ(rf>awd53B{4Yw*SyC9mVv^AaNdS z8v)xUv`sEqmMT4JAX0JMFzY>KbfShskfY;JtBFiQ#Jyb*7AHqflv|2UbcjpE{g)nd z+k40zFI*57{&P_H1{J2Kluj95e|XR{lAJN)zj7*4S+>+p zvp?TJ|2YEvG@!G)Gw14lA*c)OE~nR|e^e!E;wxGK7q*r43FlMwhBfdhSEIqPAnM6-pa1 zR<=t}R$+uJNFj+F?B}JKhrFO)Z6A!_TPD-9 zG%E9>K|35X7Sq#_`Uif+tL6bCCHzpp45SA%CNe`~;h6D4NDL7H4~|NTrvTcjLG z*?0{0F#+}p9XUUY*{Fzr7(GjuA_OZpyeTseTeF~{S5ro&a)D(y$ucx4!#3$7tAmN zjj;sr2+%YenkTmm(TwbbrnM|UQ_~3zAd6#jG~1{uowr1NgcFhzwN>dxJ*b~-kxD1) zbXRf}5#DqY_JNX#jz)DVKHD2_bib-D^VA_L)1!ys0&zkt{tyFK$pFel z&2r^_be(*nwfHmwo}A{`%P4jr9LV0r_|#O|rCxdeYI#qK@|NcF_EFxMmqS&*o#UJ2oZ94W`;AHJKLhs1ptNQvF zUxjxve+E4KXS+Oo)wF%UTzKS8MFrD#)5qyNuA0_EL`@aGbFKVpmA}B@+sEEKt2e*n z4b|=*n_Vo-vl^*9baEy`7yaof9&|G@)?yzfGViO94sa3=4hP+=d6Y0*qV-D4v`(4& zkxAbd1mBIKHO8MqEZ0}crCEv4^s=ikZ{f+i;(8XHwBW$rmrrXwJ)uw=^4ZSZC zp2haMmtHHkcg5YV46^z{eJL4^vkd)|K_}+(R=k;YKWPyg5F_=w9wnd13?yf+79tuf zUs$P5`L4Endnn& zJan{vlU=<*j`E$mtvd6CK6uTW8f*0-k)EgAGt@Fi8WA-Rk)4Y1Rnhd=)!q5VPOIf6 zM%&u=6ZQQVzE`!r({qeByGuKH#x-BnrH`gvqSzw2&Bl0_by-Cg>1P#F zn(R`omR$)`=VY&S*#%j8dQAFy!=hgmLtjht3d(dndh<@;2h}a@+tF%O-?YNXQ(CF3 z@{LSi-zcV_zr}(?(-QqS;oQl@dxokw1p~D6FfcY42DVH^!@!vIWky&q6p%%gR!v1$ zFdT%-WRX4N&H4-rK@u>lfP5DyKz^8EzIoe%P}0WHzDU(sdxa9P-2o;6XATJ^;EG{+ z3E1Orp#&tCRy8-O*-Dl%CYKh~@5RY&y)TR@u93^H@trp@Tg~A>?R|mz(Dl~a?gMY` z(m-ZbSG3JuU@cFV3z+*;3F^$y%YKoe&szs-1$=gpWX6rFRfJX3)(r#4G|%o@Csw6x zyyeG++Fi#qUuEw*4Y}~Ts{|q#Zv&RY>#imP9gSjT!1BP}5y##5tL|3m`djRB`7HAL zF{@5lpbijJ{jvp;jONi@HWi;u`b%t{PLW6I=ZQ~MtgSU2#Fk8*zmcSFqrKTi>k%x$ zsd{5e&1%@VTcoU8jHUu%w@@+HE$02STacY@q3q&m3$;J9P_XG4UI0*2u{8 zcJvo9XJqNs`iadiq9r(LEwr2ySb-?&?GS;!NW^G+{4tWmr%Ox{whr9O(TV=ygYn3s3 zhWg?6ya-yQ$2}Xjiql270;dv0~MWID^C{Q7EUcxoN~GL zlJrOpc{bIpl@WeG*HvA>Z4?OW^uCHXUz47{uFV$MMtsdoNRiunxaTU(@=ivCo9UH z3D9!CH@ms91NWyb=QICGWV&vj7n%)@S4=gY+c#{>(JmFw{g%252HYD?DQYhE4JD8l z4R9i);=I2qJoJaNcKCY<{n?Zw2Y;NEOu{r%t zCpI`Cl8XAp8%H}TT)P}Cg($H#@LjN+WLOqQ0x>v`V>ktWa_>M*z`8?8lPm7Od^-)YqGJ=n2;Rf3;Z+RuiV~&VzkD7uL_Mb1h$CaZ z(wh?3T~^g?73FAjo`pC*F$ zm2_0cSx!iMs#0^|pPBX~GCS^sWuR57DEr-mT2%Xng8F^c?1Kd$#R=22(A8Fyz*JkH zwT&t#psT9;r5g%zI3`@iWOJB<8_YUoVi?dAsD~j=39&A!k>t+VlEhCO*aMD;WGI6U zI2bJTwxm9&Ao`lf??(i3Q)KQHS z!a@NLVgiuJTpOV~WUzHwl=^+eAimRpj0eYMrpp-q*cOC8ijg>QfxFc!`M%?Fg^#Pt z_0*7()|+9Xj?56)Wv+Z-ITm%oXEk6lJ*HoJN_o;7mDGIgR9&X+)Z{Z&-b;i*>*C9J z$12EGB+i*kU=o?PB65F|CpQVXXdCp-GkcmbGY(G(w!}Ps0>fqOEvAQb6`&N1woL^0 zC+D?gsM+1`ChJ+Y(#NSRI6v?{XAVuz9G;$eaC&BWdS+#MX0qcLwJve>zXvQ#JE?0=k#wv`5-p5XY&u-E2RLmP z!JkOMs7ZWR_<=SZj)F{oCy0TR96s`ywfsZV%w(LHIbE@kS_KSz{q^oHE*1mDulUA; zVlXBqj!78zG%k)TSvS`ER%4=t624F4bMExND@)y}HL=+lLl!9nzKf2BM5Yo1=}2=_*QbA^vX z6t0Tb>rbOAZzEQy3oPO#(IknEr?R~;L@ViA4TF=VXVy0CR#|#xal?-M9oF!3*)Uyq$m|hEOs|NgM-uH79=ChYn^0Vb4hl8FW4i_YYWyNO z&z@&W#F-@d^RvnmlfpANxa@-eHtp}a>Hur5JmqBx4f_Ch9h+XbOg-hj>#yP%fnW>E zI4JukI}o63i>-~aEhEaFg9dkVv?PhULU|7R{SG00ix(=^n0}S*jTMxUO!zw=4ubUD zMp{G4McWmg+S#gq&j3Sm@3#5s?@5&tnRl@?9Fnq5NFLrbV8RiGB-=1o($=N6~Z zXAEr{RV~`nlO~V>g*6GiF{v)SaIz|qUMP#lfB077ilpQDZ|z~p%=4yeKRztIaGvFw zs|ik+p`e^%3*7swJH7wxFy3G7-fN~6=3k}vi`;w7hLTHmy3J4S+wm#jZ@1SAH4Kqy ziTi1Bc~_A)y-*s5Pi|o9Y>=cEs#EakGxxLJ{iuti7ZwRmdSO5JGtm9S?NhP+-P^s= z)GEDjxcxoY{+8R{O8&OZP4d@tL3*KPMCpY!{7tsat>veH<19%|wq!vUlGCHNjU=m= z!t{mnNF~$r7x2?IcM*RLUfbN;Nfs2u$#Fj^s>xsSt>LF_?ppo^`FxUwYRR?N=)XG- zYn%HS?+_R=eidd+&s|S4y|4iPPupBMysSLeb1#%jG`+BvzsctL@*5?a|176gWphPu zHXsh$f~jk#^UKoP;d)x1OmCY?R~4lv_DfIPDm`&vdg2c0iShKrLFtLbth<-<4mlPm6Tc0ISw7j`9Us(Hs_cQH_L>IA4=0M%)G z)FZA4WBYJY7<6>&zzS)*PKv|LAiOWN7+vU>T*bTQzO&_(^)mpkHcHw%qwj5ZfO@44qu~iJ)ggxUm#92MbrF%q|5N?AOwyx>IZhk4kYx{hV z3R7K%c&3E;(OrQg|2e>Gj90lFWl~+^5}g9P#-t}KXPfHifvK|P4YxF%UtD0D><*?i z5}D=9WrJ2|gIP4s$Ndl^)0XG$WVXrDt?UKt%ECVhUY>T{CG+5Ol?ryN!!CVrt^rsp*IafI@ zfFA-dn_^Uc?m07c+zw(hZ)mDSypmz8MLou;OxHJRuyDReuq#4CNnlIMFVLE3`k+gq z>D?|CLF8=!f4NFnGtmT5Al?q_7s319!Su9jbWv=!%!shHKGb0r(&zH4&}Qx?mt=Ze z5trPe+F!+7lKX`&v~wywZeV&^aeCU`>1h}?#|~>76&J?zv_XlEBe5J~=|Y~V?jGy4 zw%n_?;|7E5F7Vzp%auD=iuRc3Qt@mr4J-dmtP!DwF4jnie5?@9Y7*U=gA0G)bu!QT=Jo36&ux_Y(}otr;0VhKpOqkgzN^qxtxgyTT&z3w>mz+n<;tRpKt5h+OFpI zMim9lF}8fUPJ)Hh6X{<*nPrFcC|YVdEyZI;1@T(+fi4v_rPIu}V-M!ru~aRuW3w-T zTv{3Z=E>lq+c2qn)$hY0Jy5ANwE~Q;MCOkXm1pLueBRMU3yuk>L?YL3W)j&8Rfa^q z@JEx#?9Je58|7j(n_d87Ei1jsl77@)&~(Wzo>@Ok zjgda5ROC5z^W>;6Mf2l+oyQ*nV876YNMeMwiTe5-#Ix_Bc((&xd(yJk3=E}e8<&d1 zv+j;(FK=!<`wN4jw0d5}YxtYARMB1y!!3+VZs~r8@itC+a@Quk zBl{)GUWv_C0Hz%L)g|jne;WqnmdCDoy6FjZXbn(hUrZgZK^R|0+1#PLJg+jvqqoxE zl5_9_>7Bg2>bPYT#?~{B|3QZpxhM(n+a;MzGZ~=pg8(@17ipTwkl5+n0yRsRV^j?; zuFxxKAxF;P(=;<)AeYNDi$0uI+cb0dMnI+mMDK7G^oz~84ABeh4#hnxG;!}T6YmpY z<1T2hT9X8cjE~TcL*c$?y2kh0?n(E8{37QIiZrWa(gf@;r#^#GM zyzncdyyjY`b)8@Ze>a_vF&C>Y=?M1jI6-3qx&^kC3s8&A=^-aF(kK7him4jEeiLK3 z{+_{!MOBMDSZJ@$(Chbk{fJYKEBuA8sZA|XYM8a@xnv3Kl9{gkV8-eJc*UMnIot~H zbA^9KRPHgeXQoBvD)Qy7QMo^;928o){i1S}R_@}cT%UZolU430m6L5QeBb;tPv;xV z027(HQMngg13~9tmD`STT7#P1!2af@;$SRjYm$Jg+HFn4gOqnfIfm#k+j;}*&NRS_ z0Gw^5o^F`98o=`Pw7Q9^_wM2FRGo^BmJlVdzUu?Q2u~F^Q(P4r|HiT zA;c~QafKkB3&hgL6{%CdIn-TxKPfFvO{pz?Tt-ZNuVm@ti%3izr_b|(&*zQG9TpVZ zBP^ENx^s>xJL^d!k%8+UfQtp-M5^kW^bQ}IEIquqVGBok`cd)2eJi!fH1zh`N-b}F z;S!UNW-nfjwwhj5)PQIK7JlF^bPCr2iF6~*F5B$Hb9Y+=xHoVn@mjx#MFE@30hrhIrl zzl7?h3oJ2!E4Y94SuwU@)`c7C}MwaPm@?v0Npbb$~nf)ztY zdavnrRCd35?~dgD)Ygx>u3nd5#@3qco@o8Jk7fp}5^lytB7MdWVudi#^e_Ic?b7rP zKkxDH{VsD}z?;gs6N%VWt%RS&E}sRmkwm(w-7m2gJYp+arJZiq(Vhk=T8-tAy9S0;7s;5^0rl20CjdbY4z)+{8Gw}k^Isc`;@3uSCz60wxH1zbIQAyYJ(iL%C)VCExGn27_kLJ+8 zIjT-I84}jyVuN#6MAPqR+3cHiil8RB!NA!)tin*s{$P~-FlE1&?CB}PK%hmos~M)C zF+Eoana@8TZNDhYrbL+SXQS=sDT7sFu10u~ekjXeOI$hGX$#HD3q$VhLs}vCX5Ix} zE33U*PQl6x7YeO(Xyw&l<+%@5+F5y<2JEM&u=OEce{^N9;}2$03KyeuON8b@-;(^! zg{}}n!Rd+k?oXGB0qcOq3lKLa0xt1iOg5fm^~IBLEzF3T@OoY(RwTd zj`dbnc`GVoBUdGvkBKjp=~I|u;)kdto&aAe?xxT!?*Gv=ax9$yAZ%D+rdn@>&3=Vr zqLalGsUi;6q!<_lHTN6Eo*f1 z&Wtxr$4+HbDlZZr$bhGebLOhNm2$V}Sv8OQ_q^57k=rZPt&WcF@=97d=Z)v{&LS`C z2o1tfR(fWf#gNh|wGI7cNH_G6iQT}gc`0j=j{K0BL4&9^a}E(dy>Z3K(s4D6p>s7^ z)_)}}DcoM=@_i*c2}?De1UcVM)Gp1z%u%>kC<<&_vsoyMkM{}3mV;eN_HwIO*gRkZ zD3}f|oon`T?$4c>t0R(i%Gn}FaPeyqe~;$d*g5aTX3Oy;{<2clz-%jGlUgohX&eZS zrN_N#Y}#Sd>a&8!#=3>sADm}vW`EV@oTxUv^R@Y`cUYTqTq>U32O3Q3%8zz(<`(y8 zb1m+@muPwi-qqpp;zsBVm8G{9&@1tKp#fAiwW%gvH3{$g%JePf^QpuwP<;{B!1Mmq zub4@3JUqS?9>coR9qXRZ4|0Mdsu8|&^!fJaYV#7Npe$TdX~$t@(lqH(t@%91n$J`8 z^t$|7&xuP*FPz{tJB8|oX0Wqhfu}Qe`PU3V`FKufpA6R6?EN4*=`}a<;6Hq!+>W1F ze>jxfI(268ryF?ZLUa1^E<6w4%T<=15|7RPlsc0d9-IBXYL68^D-WffwXk;o_jw2n zajJfvHOQ7bh*ehQW~!KN0Cbi2MrG+~#q~Wq+OqQv>}kX5N9mgB;nF3Gc%}U_VhnfN z7LD|tajqWaD0V_I@^!Iz#su)Zi+duGIT<`z(e2?S4s}(sIaqUrJ42h=X<6AtbmLAy z9lU?rOeQu@v9LO~e@=H3QqEVe^4Re842pdx^;2$gI>$`WfajS6Y$-^2( z2&)dz676~HsKJe}CZei1tQQAZU!|0U`KDyY(;zEK&iX+@cEKNPc^cS3W2=$pEW4sgk>*k2mUL+LgS* zK0Xbs+_@B0JW4qL_vdv0Y7)8WCFMW#40^+%J#FZl?_Mw#KG;LubN%6sb}1~dS3B`4fZqW)Fk>{o4;(PP^1WdK@Cm#?sqGG?0P;tdxK0-u zy|#Yv(;gU%S#JM=Qya_BYBm0y-n64#m=kK`mTrrMYO6 z7rrqrUH4s{OG#_=BYK2fDuy#5mu~4EaH(vTTNlrME=7h?o-Q?rWyAhWIiZpGyPO8R zhNqDXKywz5kZm`Q-@sd1vd+J0dZZMOw=eDfUp|sjTyKi9Kk!dqko4ZJEIk)}IF&4< z(1%kj^9uj>c>r>Mw)-dN3pX4{HGgAMxwolJAOsh>hDIvKxym4p&v$dx%Ic)-l8%v_ zz1f>UiGyRaHG5UyME&Lo3%F3+gLY;Ml?wwt@B6V134%)(ZU?9x7DwwUZCA zLk;3WBN6w%u~~L(o~$_IS#I3_4d7n`fLDY7O{cbKq*l}o!AF&By!f`eWF~gmalEIM zG_$yK1W3&!E~F)qy8cE3LZzofz%UosWd8b^SSE|q2u>*JDXGmn;*(T5K2ORkmzYf*+#g6<{^k@m1SNL^7e#z6rYM>t35eT@~ z)Q&M3?#+wM*_U@XNEWZ+ZS0abPod|jw1NPhUiQL;eJI3e0ri!f&f^!sZ{)}B>XAr4 z;1*}(K$v9>^cD1G$JnT@RVf@?gx@zkMw!yP6c

9bum?lk3JVSJ0W9eLq}eXSfvh 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νm-pAsy3MyjjBBt*N*hi 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 z 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ɔ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 z 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

Z*J}Hpf$(7nzpd>*8{uq( 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

    1@s#VH})K(=lRa&heS`_`gpL6dd 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 1: - print('\n'.join(fn)) - raise LookupError('more than one file found.') -elif len(fn) == 0: - raise LookupError('no file found.') -else: - fn = fn[0] - -cm.load(fn).play(fr=30,magnification=1,gain=2.) - -# fn_parts = fn.split('_') -# d1 = int(fn_parts[fn_parts.index('d1') + 1]) # column, x -# d2 = int(fn_parts[fn_parts.index('d2') + 1]) # row, y -# d3 = int(fn_parts[fn_parts.index('d3') + 1]) # channel -# d4 = int(fn_parts[fn_parts.index('frames') + 1]) # frame, T -# order = fn_parts[fn_parts.index('order') + 1] -# -# print('playing {} ...'.format(fn)) -# -# mov = np.memmap(filename=fn, shape=(d1, d2, d4), order=order, dtype=np.float32, mode='r') -# mov = mov.transpose((2, 1, 0)) -# -# cm.movie(mov).play(fr=30,magnification=1,gain=2.) - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/060_caiman_segmentation.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/060_caiman_segmentation.py deleted file mode 100644 index 13ac843..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/060_caiman_segmentation.py +++ /dev/null @@ -1,116 +0,0 @@ -import sys; print('Python %s on %s' % (sys.version, sys.platform)) -sys.path.extend([r"E:\data\github_packages\CaImAn"]) - -import os -import numpy as np -import caiman as cm -import matplotlib.pyplot as plt -from caiman.source_extraction.cnmf import cnmf as cnmf -import h5py -from shutil import copyfile - -def run(): - - data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180309-M360495-deepscope\03\03_" - play_movie = False - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - - plane_ns = [f for f in os.listdir(data_folder) if os.path.isdir(f) and f[:5] == 'plane'] - plane_ns.sort() - print('planes:') - print('\n'.join(plane_ns)) - - # %% start cluster - c, dview, n_processes = cm.cluster.setup_cluster(backend='local', n_processes=3, single_thread=False) - - for plane_n in plane_ns: - - print('\nsegmenting plane: {}'.format(plane_n)) - - plane_folder = os.path.join(data_folder, plane_n, 'green', 'corrected') - os.chdir(plane_folder) - - fn = [f for f in os.listdir(plane_folder) if f[-5:] == '.mmap'] - if len(fn) > 1: - print('\n'.join(fn)) - raise LookupError('more than one file found.') - elif len(fn) == 0: - raise LookupError('no file found.') - else: - fn = fn[0] - - fn_parts = fn.split('_') - d1 = int(fn_parts[fn_parts.index('d1') + 1]) # column, x - d2 = int(fn_parts[fn_parts.index('d2') + 1]) # row, y - d3 = int(fn_parts[fn_parts.index('d3') + 1]) # channel - d4 = int(fn_parts[fn_parts.index('frames') + 1]) # frame, T - order = fn_parts[fn_parts.index('order') + 1] - - print('playing {} ...'.format(fn)) - - mov = np.memmap(filename=fn, shape=(d1, d2, d4), order=order, dtype=np.float32, mode='r') - mov = mov.transpose((2, 1, 0)) - - print('shape of joined movie: {}.'.format(mov.shape)) - - #%% play movie, press q to quit - if play_movie: - cm.movie(mov).play(fr=50,magnification=1,gain=2.) - - #%% movie cannot be negative! - mov_min = float(np.amin(mov)) - print('minimum pixel value: {}.'.format(mov_min)) - if mov_min < 0: - raise Exception('Movie too negative, add_to_movie should be larger') - - #%% correlation image. From here infer neuron size and density - Cn = cm.movie(mov).local_correlations(swap_dim=False) - plt.imshow(Cn, cmap='gray') - plt.show() - - K = 100 # number of neurons expected per patch - gSig = [5, 5] # expected half size of neurons - merge_thresh = 0.9 # merging threshold, max correlation allowed - p = 2 # order of the autoregressive system - cnm = cnmf.CNMF(n_processes, - k=10, # number of neurons expected per patch - gSig=[5, 5] , # expected half size of neurons - merge_thresh=0.9, # merging threshold, max correlation allowed - p=2, # order of the autoregressive system - dview=dview, - Ain=None, - method_deconvolution='oasis', - rolling_sum = False, - method_init='sparse_nmf', - alpha_snmf=10e1, - ssub=1, - tsub=1, - p_ssub=1, - p_tsub=1, - rf=256, # half-size of the patches in pixels - border_pix=20, - do_merge=False) - cnm = cnm.fit(mov) - A, C, b, f, YrA, sn = cnm.A, cnm.C, cnm.b, cnm.f, cnm.YrA, cnm.sn - #%% - crd = cm.utils.visualization.plot_contours(cnm.A, Cn) - plt.show() - # input("Press enter to continue ...") - - roi_num = cnm.A.shape[1] - save_fn = h5py.File('caiman_segmentation_results.hdf5') - bias = save_fn['bias_added_to_movie'].value - save_fn['masks'] = np.array(cnm.A.todense()).T.reshape((roi_num, 512, 512), order='F') - save_fn['traces'] = cnm.C - bias - save_fn.close() - - copyfile(os.path.join(plane_folder, 'caiman_segmentation_results.hdf5'), - os.path.join(curr_folder, plane_n, 'caiman_segmentation_results.hdf5')) - - plt.close('all') - - -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/070_generate_nwb.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/070_generate_nwb.py deleted file mode 100644 index 3f762ab..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/070_generate_nwb.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import corticalmapping.NwbTools as nt - -date_recorded = '180502' -mouse_id = '376019' -sess_num = '02' - -experimenter = 'Jun' -genotype = 'Vipr2-IRES2-Cre-neo' -sex = 'male' -age = '120' -indicator = 'GCaMP6s' -imaging_rate = 37. -imaging_depth = '50/100/150/200/250 microns' -imaging_location = 'visual cortex' -imaging_device = 'DeepScope' -imaging_excitation_lambda = '940 nanometers' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -notebook_path = os.path.join(curr_folder, 'notebook.txt') -with open(notebook_path, 'r') as ff: - notes = ff.read() - -general = nt.DEFAULT_GENERAL -general['experimenter'] = experimenter -general['subject']['subject_id'] = mouse_id -general['subject']['genotype'] = genotype -general['subject']['sex'] = sex -general['subject']['age'] = age -general['optophysiology'].update({'imaging_plane_1': {}}) -general['optophysiology']['imaging_plane_1'].update({'indicator': indicator}) -general['optophysiology']['imaging_plane_1'].update({'imaging_rate': imaging_rate}) -general['optophysiology']['imaging_plane_1'].update({'imaging_depth': imaging_depth}) -general['optophysiology']['imaging_plane_1'].update({'location': imaging_location}) -general['optophysiology']['imaging_plane_1'].update({'device': imaging_device}) -general['optophysiology']['imaging_plane_1'].update({'excitation_lambda': imaging_excitation_lambda}) -general['notes'] = notes - -file_name = date_recorded + '_M' + mouse_id + '_' + sess_num + '.nwb' - -rf = nt.RecordedFile(os.path.join(curr_folder, file_name), identifier=file_name[:-4], description='') -rf.add_general(general=general) -rf.close() - - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/071_add_vasmaps.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/071_add_vasmaps.py deleted file mode 100644 index 1a12531..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/071_add_vasmaps.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -import corticalmapping.NwbTools as nt -import matplotlib.pyplot as plt -import tifffile as tf - - -vasmap_name_wf = 'vasmap_wf.tif' -# vasmap_name_2p_zoom1_g = 'vasmap_2p_zoom1_green.tif' -vasmap_name_2p_zoom1_r = 'vasmap_2p_zoom1.tif' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -vasmap_wf = tf.imread(vasmap_name_wf) -# vasmap_2p_zoom1_g = tf.imread(vasmap_name_2p_zoom1_g) -vasmap_2p_zoom1_r = tf.imread(vasmap_name_2p_zoom1_r) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] - -nwb_f = nt.RecordedFile(nwb_fn) -nwb_f.add_acquisition_image('surface_vas_map_wf', vasmap_wf, - description='wide field surface vasculature map through cranial window') -# nwb_f.add_acquisition_image('surface_vas_map_2p_zoom1_green', vasmap_2p_zoom1_g, -# description='2-photon surface vasculature map through cranial window, zoom 1, green') -nwb_f.add_acquisition_image('surface_vas_map_2p_zoom1_red', vasmap_2p_zoom1_r, - description='2-photon surface vasculature map through cranial window, zoom 1, red') -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/080_add_sync_data.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/080_add_sync_data.py deleted file mode 100644 index 0f4f6fa..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/080_add_sync_data.py +++ /dev/null @@ -1,23 +0,0 @@ -import os -import corticalmapping.NwbTools as nt - -record_date = '180502' -mouse_id = '376019' -session_id = '02' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = record_date + '_M' + mouse_id + '_' + session_id + '.nwb' - -sync_fn = [f for f in os.listdir(curr_folder) if f[-3:] == '.h5' and record_date in f and 'M' + mouse_id in f] -if len(sync_fn) == 0: - raise LookupError('Did not find sync .h5 file.') -elif len(sync_fn) > 1: - raise LookupError('More than one sync .h5 files found.') -else: - sync_fn = sync_fn[0] - -nwb_f = nt.RecordedFile(nwb_fn) -nwb_f.add_sync_data(sync_fn) -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/090_add_image_data.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/090_add_image_data.py deleted file mode 100644 index d403cea..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/090_add_image_data.py +++ /dev/null @@ -1,52 +0,0 @@ -import os -import h5py -import corticalmapping.NwbTools as nt - -dset_ns = ['plane0', 'plane1', 'plane2', 'plane3', 'plane4'] -imaging_depths = [250, 200, 150, 100, 50] -temporal_downsample_rate = 2 -pixel_size = 0.0000002 # meter, 0.2 micron, deepscope 12K Hz scanner, zoom 4 - -description = '2-photon imaging data' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) -ts_2p_tot = nwb_f.file_pointer['/acquisition/timeseries/digital_2p_vsync_rise/timestamps'].value -print('total 2p timestamps count: {}'.format(len(ts_2p_tot))) - -mov_fn = os.path.splitext(nwb_fn)[0] + '_2p_movies.hdf5' -mov_f = h5py.File(mov_fn, 'r') - -for mov_i, mov_dn in enumerate(dset_ns): - - if mov_dn is not None: - - curr_dset = mov_f[mov_dn] - if mov_dn is not None: - mov_ts = ts_2p_tot[mov_i::len(dset_ns)] - print('\n{}: total 2p timestamps count: {}'.format(mov_dn, len(mov_ts))) - - mov_ts_d = mov_ts[::temporal_downsample_rate] - print('{}: downsampled 2p timestamps count: {}'.format(mov_dn, len(mov_ts_d))) - print('{}: downsampled 2p movie frame num: {}'.format(mov_dn, curr_dset.shape[0])) - - if len(mov_ts_d) == curr_dset.shape[0]: - pass - elif len(mov_ts_d) == curr_dset.shape[0] + 1: - mov_ts_d = mov_ts_d[0: -1] - else: - raise ValueError('the timestamp count of {} movie ({}) does not equal (or is not greater by one) ' - 'the frame cound in the movie ({})'.format(mov_dn, len(mov_ts_d), curr_dset.shape[0])) - - curr_description = '{}. Imaging depth: {} micron.'.format(description, imaging_depths[mov_i]) - nwb_f.add_acquired_image_series_as_remote_link('2p_movie_' + mov_dn, image_file_path=mov_fn, - dataset_path=mov_dn, timestamps=mov_ts_d, - description=curr_description, comments='', - data_format='zyx', pixel_size=[pixel_size, pixel_size], - pixel_size_unit='meter') - -mov_f.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/110_add_motion_correction_module.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/110_add_motion_correction_module.py deleted file mode 100644 index 94e79bd..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/110_add_motion_correction_module.py +++ /dev/null @@ -1,56 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import h5py -import corticalmapping.NwbTools as nt - -movie_2p_fn = '180309_M360495_03_2p_movies.hdf5' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -input_parameters = [] - -for i in range(5): - - plane_n = 'plane{}'.format(i) - - offsets_path = os.path.join(plane_n, 'correction_offsets.hdf5') - offsets_f = h5py.File(offsets_path) - offsets_keys = offsets_f.keys() - if 'path_list' in offsets_keys: - offsets_keys.remove('path_list') - - offsets_keys.sort() - offsets = [] - for offsets_key in offsets_keys: - offsets.append(offsets_f[offsets_key].value) - offsets = np.concatenate(offsets, axis=0) - offsets = np.array(zip(offsets[:, 1], offsets[:, 0])) - offsets_f.close() - - mean_projection = tf.imread(os.path.join(plane_n, 'corrected_mean_projection.tif')) - max_projection = tf.imread(os.path.join(plane_n, 'corrected_max_projection.tif')) - - input_dict = {'field_name': plane_n, - 'original_timeseries_path': '/acquisition/timeseries/2p_movie_plane' + str(i), - 'corrected_file_path': movie_2p_fn, - 'corrected_dataset_path': plane_n, - 'xy_translation_offsets': offsets, - 'mean_projection': mean_projection, - 'max_projection': max_projection, - 'description': '', - 'comments': '', - 'source': ''} - - input_parameters.append(input_dict) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -nwb_f.add_muliple_dataset_to_motion_correction_module(input_parameters=input_parameters, - module_name='motion_correction') -nwb_f.close() - - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/120_add_rois_and_traces_caiman_segmentation.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/120_add_rois_and_traces_caiman_segmentation.py deleted file mode 100644 index 68a3f3c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/120_add_rois_and_traces_caiman_segmentation.py +++ /dev/null @@ -1,162 +0,0 @@ -import os -import h5py -import numpy as np -import matplotlib.pyplot as plt -import tifffile as tf -import corticalmapping.NwbTools as nt -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -plane_ns = ['plane0', 'plane1', 'plane2', 'plane3', 'plane4'] -plane_depths = [250, 200, 150, 100, 50] - -def add_rois_and_traces(data_folder, nwb_f, plane_n, imaging_depth, - mov_path='/processing/motion_correction/MotionCorrection'): - - mov_grp = nwb_f.file_pointer[mov_path + '/' + plane_n + '/corrected'] - - data_f = h5py.File(os.path.join(data_folder, 'rois_and_traces.hdf5'), 'r') - mask_arr_c = data_f['masks_center'].value - mask_arr_s = data_f['masks_surround'].value - traces_center_raw = data_f['traces_center_raw'].value - # traces_center_demixed = data_f['traces_center_demixed'].value - traces_center_subtracted = data_f['traces_center_subtracted'].value - # traces_center_dff = data_f['traces_center_dff'].value - traces_surround_raw = data_f['traces_surround_raw'].value - neuropil_r = data_f['neuropil_r'].value - neuropil_err = data_f['neuropil_err'].value - data_f.close() - - - if traces_center_raw.shape[1] != mov_grp['num_samples'].value: - raise ValueError('number of trace time points ({}) does not match frame number of ' - 'corresponding movie ({}).'.format(traces_center_raw.shape[0], mov_grp['num_samples'].value)) - - rf_img = tf.imread(os.path.join(data_folder, 'corrected_mean_projection.tif')) - - print 'adding segmentation results ...' - rt_mo = nwb_f.create_module('rois_and_traces_' + plane_n) - rt_mo.set_value('imaging_depth_micron', imaging_depth) - is_if = rt_mo.create_interface('ImageSegmentation') - is_if.create_imaging_plane('imaging_plane', description='') - is_if.add_reference_image('imaging_plane', 'mean_projection', rf_img) - - for i in range(mask_arr_c.shape[0]): - curr_cen = mask_arr_c[i] - curr_cen_n = 'roi_' + ft.int2str(i, 4) - curr_cen_roi = ia.WeightedROI(curr_cen) - curr_cen_pixels_yx = curr_cen_roi.get_pixel_array() - curr_cen_pixels_xy = np.array([curr_cen_pixels_yx[:, 1], curr_cen_pixels_yx[:, 0]]).transpose() - is_if.add_roi_mask_pixels(image_plane='imaging_plane', roi_name=curr_cen_n, desc='', - pixel_list=curr_cen_pixels_xy, weights=curr_cen_roi.weights, width=512, height=512) - - curr_sur = mask_arr_s[i] - curr_sur_n = 'surround_' + ft.int2str(i, 4) - curr_sur_roi = ia.ROI(curr_sur) - curr_sur_pixels_yx = curr_sur_roi.get_pixel_array() - curr_sur_pixels_xy = np.array([curr_sur_pixels_yx[:, 1], curr_sur_pixels_yx[:, 0]]).transpose() - is_if.add_roi_mask_pixels(image_plane='imaging_plane', roi_name=curr_sur_n, desc='', - pixel_list=curr_sur_pixels_xy, weights=None, width=512, height=512) - is_if.finalize() - - - - trace_f_if = rt_mo.create_interface('Fluorescence') - seg_if_path = '/processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane' - # print seg_if_path - ts_path = mov_path + '/' + plane_n + '/corrected' - - print 'adding center fluorescence raw' - trace_raw_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_raw') - trace_raw_ts.set_data(traces_center_raw, unit='au', conversion=np.nan, resolution=np.nan) - trace_raw_ts.set_value('data_format', 'roi (row) x time (column)') - trace_raw_ts.set_value('data_range', '[-8192, 8191]') - trace_raw_ts.set_description('fluorescence traces extracted from the center region of each roi') - trace_raw_ts.set_time_as_link(ts_path) - trace_raw_ts.set_value_as_link('segmentation_interface', seg_if_path) - roi_names = ['roi_' + ft.int2str(ind, 4) for ind in range(traces_center_raw.shape[0])] - trace_raw_ts.set_value('roi_names', roi_names) - trace_raw_ts.set_value('num_samples', traces_center_raw.shape[1]) - trace_f_if.add_timeseries(trace_raw_ts) - trace_raw_ts.finalize() - - print 'adding neuropil fluorescence raw' - trace_sur_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_surround_raw') - trace_sur_ts.set_data(traces_surround_raw, unit='au', conversion=np.nan, resolution=np.nan) - trace_sur_ts.set_value('data_format', 'roi (row) x time (column)') - trace_sur_ts.set_value('data_range', '[-8192, 8191]') - trace_sur_ts.set_description('neuropil traces extracted from the surroud region of each roi') - trace_sur_ts.set_time_as_link(ts_path) - trace_sur_ts.set_value_as_link('segmentation_interface', seg_if_path) - sur_names = ['surround_' + ft.int2str(ind, 4) for ind in range(traces_center_raw.shape[0])] - trace_sur_ts.set_value('roi_names', sur_names) - trace_sur_ts.set_value('num_samples', traces_surround_raw.shape[1]) - trace_f_if.add_timeseries(trace_sur_ts) - trace_sur_ts.finalize() - - roi_center_n_path = '/processing/rois_and_traces_' + plane_n + '/Fluorescence/f_center_raw/roi_names' - # print 'adding center fluorescence demixed' - # trace_demix_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_demixed') - # trace_demix_ts.set_data(traces_center_demixed, unit='au', conversion=np.nan, resolution=np.nan) - # trace_demix_ts.set_value('data_format', 'roi (row) x time (column)') - # trace_demix_ts.set_description('center traces after overlapping demixing for each roi') - # trace_demix_ts.set_time_as_link(mov_path + '/' + plane_n + '/corrected') - # trace_demix_ts.set_value_as_link('segmentation_interface', seg_if_path) - # trace_demix_ts.set_value('roi_names', roi_names) - # trace_demix_ts.set_value('num_samples', traces_center_demixed.shape[1]) - # trace_f_if.add_timeseries(trace_demix_ts) - # trace_demix_ts.finalize() - - print 'adding center fluorescence after neuropil subtraction' - trace_sub_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_subtracted') - trace_sub_ts.set_data(traces_center_subtracted, unit='au', conversion=np.nan, resolution=np.nan) - trace_sub_ts.set_value('data_format', 'roi (row) x time (column)') - trace_sub_ts.set_description('center traces after overlap demixing and neuropil subtraction for each roi') - trace_sub_ts.set_time_as_link(mov_path + '/' + plane_n + '/corrected') - trace_sub_ts.set_value_as_link('segmentation_interface', seg_if_path) - trace_sub_ts.set_value_as_link('roi_names', roi_center_n_path) - trace_sub_ts.set_value('num_samples', traces_center_subtracted.shape[1]) - trace_sub_ts.set_value('r', neuropil_r, dtype='float32') - trace_sub_ts.set_value('rmse', neuropil_err, dtype='float32') - trace_sub_ts.set_comments('value "r": neuropil contribution ratio for each roi. ' - 'value "rmse": RMS error of neuropil subtraction for each roi') - trace_f_if.add_timeseries(trace_sub_ts) - trace_sub_ts.finalize() - - trace_f_if.finalize() - - # print 'adding global dF/F traces for each roi' - # trace_dff_if = rt_mo.create_interface('DfOverF') - # - # trace_dff_ts = nwb_f.create_timeseries('RoiResponseSeries', 'dff_center') - # trace_dff_ts.set_data(traces_center_dff, unit='au', conversion=np.nan, resolution=np.nan) - # trace_dff_ts.set_value('data_format', 'roi (row) x time (column)') - # trace_dff_ts.set_description('global df/f traces for each roi center, input fluorescence is the trace after demixing' - # ' and neuropil subtraction. global df/f is calculated by ' - # 'allensdk.brain_observatory.dff.compute_dff() function.') - # trace_dff_ts.set_time_as_link(ts_path) - # trace_dff_ts.set_value_as_link('segmentation_interface', seg_if_path) - # trace_dff_ts.set_value('roi_names', roi_names) - # trace_dff_ts.set_value('num_samples', traces_center_dff.shape[1]) - # trace_dff_if.add_timeseries(trace_dff_ts) - # trace_dff_ts.finalize() - # trace_dff_if.finalize() - - rt_mo.finalize() - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -for plane_i, plane_n in enumerate(plane_ns): - - print('\n\n' + plane_n) - - data_folder = os.path.join(curr_folder, plane_n) - add_rois_and_traces(data_folder, nwb_f, plane_n, imaging_depth=plane_depths[plane_i]) - -nwb_f.close() - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/130_add_visual_stimuli_retinotopic_mapping.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/130_add_visual_stimuli_retinotopic_mapping.py deleted file mode 100644 index 0414239..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/130_add_visual_stimuli_retinotopic_mapping.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -import retinotopic_mapping.DisplayLogAnalysis as dla -import corticalmapping.NwbTools as nt - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -stim_pkl_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.pkl'][0] -stim_log = dla.DisplayLogAnalyzer(stim_pkl_fn) - -nwb_f.add_visual_display_log_retinotopic_mapping(stim_log=stim_log) -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/135_get_photodiode_onset_timestamps.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/135_get_photodiode_onset_timestamps.py deleted file mode 100644 index 498d9c4..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/135_get_photodiode_onset_timestamps.py +++ /dev/null @@ -1,45 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.HighLevel as hl - -# photodiode -digitizeThr = 0.02 -filterSize = 0.01 -segmentThr = 0.02 -smallestInterval = 0.03 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] - -nwb_f = nt.RecordedFile(nwb_fn) -pd, pd_t = nwb_f.get_analog_data(ch_n='analog_photodiode') -fs = 1. / np.mean(np.diff(pd_t)) -# print fs - -pd_onsets = hl.segmentPhotodiodeSignal(pd, digitizeThr=digitizeThr, filterSize=filterSize, - segmentThr=segmentThr, Fs=fs, smallestInterval=smallestInterval) - -raw_input('press enter to continue ...') - -pdo_ts = nwb_f.create_timeseries('TimeSeries', 'digital_photodiode_rise', modality='other') -pdo_ts.set_time(pd_onsets) -pdo_ts.set_data([], unit='', conversion=np.nan, resolution=np.nan) -pdo_ts.set_value('digitize_threshold', digitizeThr) -pdo_ts.set_value('filter_size', filterSize) -pdo_ts.set_value('segment_threshold', segmentThr) -pdo_ts.set_value('smallest_interval', smallestInterval) -pdo_ts.set_description('Real Timestamps (master acquisition clock) of photodiode onset. ' - 'Extracted from analog photodiode signal by the function:' - 'corticalmapping.HighLevel.segmentPhotodiodeSignal() using parameters saved in the' - 'current timeseries.') -pdo_ts.set_path('/analysis') -pdo_ts.finalize() - -nwb_f.close() - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/140_analyze_analog_photodiode_onsets.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/140_analyze_analog_photodiode_onsets.py deleted file mode 100644 index bd58c7e..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/140_analyze_analog_photodiode_onsets.py +++ /dev/null @@ -1,39 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import retinotopic_mapping.DisplayLogAnalysis as dla -import corticalmapping.core.TimingAnalysis as ta - - -pd_ts_pd_path = 'analysis/digital_photodiode_rise' -vsync_frame_path = 'acquisition/timeseries/digital_stim_vsync_rise' -pd_thr = -0.5 # this is color threshold, not analog photodiode threshold -ccg_t_range = (0., 0.1) -ccg_bins = 100 -is_plot = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -stim_pkl_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.pkl'][0] -stim_log = dla.DisplayLogAnalyzer(stim_pkl_fn) - -# get display lag -display_delay = nwb_f.get_display_delay_retinotopic_mapping(stim_log=stim_log, indicator_color_thr=pd_thr, - ccg_t_range=ccg_t_range, ccg_bins=ccg_bins, - is_plot=is_plot, pd_onset_ts_path=pd_ts_pd_path, - vsync_frame_ts_path=vsync_frame_path) - -# analyze photodiode onset -stim_dict = stim_log.get_stim_dict() -pd_onsets_seq = stim_log.analyze_photodiode_onsets_sequential(stim_dict=stim_dict, pd_thr=pd_thr) -pd_onsets_com = stim_log.analyze_photodiode_onsets_combined(pd_onsets_seq=pd_onsets_seq, - is_dgc_blocked=True) -nwb_f.add_photodiode_onsets_combined_retinotopic_mapping(pd_onsets_com=pd_onsets_com, - display_delay=display_delay, - vsync_frame_path=vsync_frame_path) -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/140_analyze_digital_photodiode_onsets.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/140_analyze_digital_photodiode_onsets.py deleted file mode 100644 index 0f5da9f..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/140_analyze_digital_photodiode_onsets.py +++ /dev/null @@ -1,39 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import retinotopic_mapping.DisplayLogAnalysis as dla -import corticalmapping.core.TimingAnalysis as ta - - -pd_ts_pd_path = 'acquisition/timeseries/digital_photodiode_rise' -vsync_frame_path = 'acquisition/timeseries/digital_stim_vsync_rise' -pd_thr = 0.5 # this is color threshold, not analog photodiode threshold -ccg_t_range = (0., 0.1) -ccg_bins = 100 -is_plot = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -stim_pkl_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.pkl'][0] -stim_log = dla.DisplayLogAnalyzer(stim_pkl_fn) - -# get display lag -display_delay = nwb_f.get_display_delay_retinotopic_mapping(stim_log=stim_log, indicator_color_thr=pd_thr, - ccg_t_range=ccg_t_range, ccg_bins=ccg_bins, - is_plot=is_plot, pd_onset_ts_path=pd_ts_pd_path, - vsync_frame_ts_path=vsync_frame_path) - -# analyze photodiode onset -stim_dict = stim_log.get_stim_dict() -pd_onsets_seq = stim_log.analyze_photodiode_onsets_sequential(stim_dict=stim_dict, pd_thr=pd_thr) -pd_onsets_com = stim_log.analyze_photodiode_onsets_combined(pd_onsets_seq=pd_onsets_seq, - is_dgc_blocked=True) -nwb_f.add_photodiode_onsets_combined_retinotopic_mapping(pd_onsets_com=pd_onsets_com, - display_delay=display_delay, - vsync_frame_path=vsync_frame_path) -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/150_get_STRFs.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/150_get_STRFs.py deleted file mode 100644 index 8642a33..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/150_get_STRFs.py +++ /dev/null @@ -1,96 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -stim_name = '001_LocallySparseNoiseRetinotopicMapping' -trace_source = 'f_center_subtracted' -start_time = -1. -end_time = 2. - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -probe_grp = nwb_f.file_pointer['analysis/photodiode_onsets/' + stim_name] -probe_ns = probe_grp.keys() -probe_ns.sort() - -probe_locations = [[float(pn[3: 9]), float(pn[13: 19])] for pn in probe_ns] -probe_signs = [float(pn[-2:]) for pn in probe_ns] -# print(probe_locations) - -plane_ns = nwb_f.file_pointer['processing'].keys() -plane_ns = [pn.split('_')[-1] for pn in plane_ns if 'rois_and_traces_plane' in pn] -plane_ns.sort() -print('\n'.join(plane_ns)) - -strf_grp = nwb_f.file_pointer['analysis'].create_group('STRFs') - -for plane_n in plane_ns: - print('\ngetting STRFs for {} ...'.format(plane_n)) - - roi_ns = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + - '/ImageSegmentation/imaging_plane/roi_list'].value - roi_ns = [rn for rn in roi_ns if rn[0: 4] == 'roi_'] - roi_ns.sort() - roi_num = len(roi_ns) - - plane_strf_grp = strf_grp.create_group(plane_n) - plane_traces = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + '/Fluorescence/' + - trace_source + '/data'].value - plane_trace_ts = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + '/Fluorescence/' + - trace_source + '/timestamps'].value - - plane_mean_frame_dur = np.mean(np.diff(plane_trace_ts)) - plane_chunk_frame_dur = int(np.ceil((end_time - start_time) / plane_mean_frame_dur)) - plane_chunk_frame_start = int(np.floor(start_time / plane_mean_frame_dur)) - plane_t = (np.arange(plane_chunk_frame_dur) + plane_chunk_frame_start) * plane_mean_frame_dur - print '{}: STRF time axis: \n{}'.format(plane_n, plane_t) - - plane_roi_traces = [] - trigger_ts = [] - - for probe_ind, probe_n in enumerate(probe_ns): - - probe_ts = probe_grp[probe_n]['pd_onset_ts_sec'].value - probe_traces = [] - probe_trigger_ts = [] - for curr_probe_ts in probe_ts: - curr_frame_start = ta.find_nearest(plane_trace_ts, curr_probe_ts) + plane_chunk_frame_start - curr_frame_end = curr_frame_start + plane_chunk_frame_dur - if curr_frame_start >= 0 and curr_frame_end <= len(plane_trace_ts): - probe_traces.append(plane_traces[:, curr_frame_start: curr_frame_end]) - probe_trigger_ts.append(curr_probe_ts) - - plane_roi_traces.append(np.array(probe_traces)) - trigger_ts.append(probe_trigger_ts) - print('probe: {} / {}; shape: {}'.format(probe_ind + 1, len(probe_ns), np.array(probe_traces).shape)) - - # plane_roi_traces = np.array(plane_roi_traces) - - print('saving ...') - for roi_ind in range(roi_num): - - print "roi: {} / {}".format(roi_ind + 1, roi_num) - curr_unit_traces = [pt[:, roi_ind, :] for pt in plane_roi_traces] - curr_unit_traces = [list(t) for t in curr_unit_traces] - curr_strf = sca.SpatialTemporalReceptiveField2(locations=probe_locations, - signs=probe_signs, - traces=curr_unit_traces, - trigger_ts=trigger_ts, - time=plane_t, - name='roi_{:04d}'.format(roi_ind), - trace_data_type=trace_source) - - curr_strf_grp = plane_strf_grp.create_group('strf_roi_{:04d}'.format(roi_ind)) - curr_strf.to_h5_group(curr_strf_grp) - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/160_get_drifting_grating_response_tables.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/160_get_drifting_grating_response_tables.py deleted file mode 100644 index 65ba54c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/160_get_drifting_grating_response_tables.py +++ /dev/null @@ -1,18 +0,0 @@ -import os -import h5py -import numpy as np -import corticalmapping.NwbTools as nt - -plane_ns = ['plane0', 'plane1', 'plane2', 'plane3', 'plane4'] -stim_name = '001_DriftingGratingCircleRetinotopicMapping' -t_win = [-1, 2.5] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -nwb_f.get_drifting_grating_response_table_retinotopic_mapping(stim_name=stim_name, time_window=t_win) - -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/170_plot_STRFs.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/170_plot_STRFs.py deleted file mode 100644 index 3de24ad..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/170_plot_STRFs.py +++ /dev/null @@ -1,61 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import h5py -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -save_folder = 'figures' -is_local_dff = True -is_add_to_traces = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -strf_grp = nwb_f['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File("{}/caiman_segmentation_results.hdf5".format(plane_n))['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - pdff = PdfPages(os.path.join(save_folder, 'STRFs_' + plane_n + '.pdf')) - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - - v_min, v_max = curr_strf_dff.get_data_range() - f = curr_strf_dff.plot_traces(yRange=(v_min, v_max * 1.1), figSize=(16, 10), - columnSpacing=0.002, rowSpacing=0.002) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/180_plot_zscore_RFs.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/180_plot_zscore_RFs.py deleted file mode 100644 index 73f92f3..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/180_plot_zscore_RFs.py +++ /dev/null @@ -1,68 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import h5py -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -save_folder = 'figures' -is_local_dff = True -zscore_range = [0., 4.] -t_window = [0., 1.] -is_add_to_traces = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -strf_grp = nwb_f['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File("{}/caiman_segmentation_results.hdf5".format(plane_n))['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - pdff = PdfPages(os.path.join(save_folder, 'zscore_RFs_' + plane_n + '.pdf')) - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - v_min, v_max = curr_strf_dff.get_data_range() - - rf_on, rf_off = curr_strf_dff.get_zscore_receptive_field(timeWindow=t_window) - f = plt.figure(figsize=(15, 4)) - ax_on = f.add_subplot(121) - rf_on.plot_rf(plot_axis=ax_on, is_colorbar=True, cmap='Reds', vmin=zscore_range[0], vmax=zscore_range[1]) - ax_off = f.add_subplot(122) - rf_off.plot_rf(plot_axis=ax_off, is_colorbar=True, cmap='Blues', vmin=zscore_range[0], vmax=zscore_range[1]) - plt.close() - - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/190_plot_RF_contours.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/190_plot_RF_contours.py deleted file mode 100644 index 1e7b31c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/190_plot_RF_contours.py +++ /dev/null @@ -1,112 +0,0 @@ -import os -import numpy as np -import h5py -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -roi_t_window = [0., 1.] -zscore_range = [0., 4.] -save_folder = 'figures' -is_add_to_traces = True - -# plot control -thr_ratio = 0.4 -filter_sigma = 1. -interpolate_rate = 5 -absolute_thr = 1.6 -level_num = 1 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'] -print('\n'.join(nwb_fn)) - -if len(nwb_fn) != 1: - raise LookupError - -nwb_fn = nwb_fn[0] -rff = h5py.File(nwb_fn, 'r') - -strf_grp = rff['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -X = None -Y = None - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File("{}/caiman_segmentation_results.hdf5".format(plane_n))['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - f_all = plt.figure(figsize=(10, 10)) - ax_all = f_all.add_subplot(111) - - pdff = PdfPages(os.path.join(save_folder, 'RF_contours_' + plane_n + '.pdf')) - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - rf_on, rf_off, _ = curr_strf_dff.get_zscore_thresholded_receptive_fields(timeWindow=roi_t_window, - thr_ratio=thr_ratio, - filter_sigma=filter_sigma, - interpolate_rate=interpolate_rate, - absolute_thr=absolute_thr) - - if X is None and Y is None: - X, Y = np.meshgrid(np.arange(len(rf_on.aziPos)), - np.arange(len(rf_on.altPos))) - - levels_on = [np.max(rf_on.get_weighted_mask().flat) * thr_ratio] - levels_off = [np.max(rf_off.get_weighted_mask().flat) * thr_ratio] - ax_all.contour(X, Y, rf_on.get_weighted_mask(), levels=levels_on, colors='r', lw=5) - ax_all.contour(X, Y, rf_off.get_weighted_mask(), levels=levels_off, colors='b', lw=5) - - f_single = plt.figure(figsize=(10, 10)) - ax_single = f_single.add_subplot(111) - ax_single.contour(X, Y, rf_on.get_weighted_mask(), levels=levels_on, colors='r', lw=5) - ax_single.contour(X, Y, rf_off.get_weighted_mask(), levels=levels_off, colors='b', lw=5) - ax_single.set_xticks(range(len(rf_on.aziPos))[::10]) - ax_single.set_xticklabels(['{:05.1f}'.format(l) for l in rf_on.aziPos[::10]]) - ax_single.set_yticks(range(len(rf_on.altPos))[::10]) - ax_single.set_yticklabels(['{:05.1f}'.format(l) for l in rf_on.altPos[::-1][::10]]) - ax_single.set_aspect('equal') - ax_single.set_title('{}: {}. ON thr:{}; OFF thr:{}.'.format(plane_n, roi_n, rf_on.thr, rf_off.thr)) - pdff.savefig(f_single) - f_single.clear() - plt.close(f_single) - - pdff.close() - - ax_all.set_xticks(range(len(rf_on.aziPos))[::10]) - ax_all.set_xticklabels(['{:05.1f}'.format(l) for l in rf_on.aziPos[::10]]) - ax_all.set_yticks(range(len(rf_on.altPos))[::10]) - ax_all.set_yticklabels(['{:05.1f}'.format(l) for l in rf_on.altPos[::-1][::10]]) - ax_all.set_aspect('equal') - ax_all.set_title('{}, abs_zscore_thr:{}'.format(plane_n, absolute_thr)) - - f_all.savefig(os.path.join(save_folder, 'RF_contours_' + plane_n + '_all.pdf'), dpi=300) - -rff.close() - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/200_plot_dgc_response_all.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/200_plot_dgc_response_all.py deleted file mode 100644 index b1b2f1f..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/200_plot_dgc_response_all.py +++ /dev/null @@ -1,160 +0,0 @@ -import os -import h5py -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.core.PlottingTools as pt -from matplotlib.backends.backend_pdf import PdfPages -import matplotlib.gridspec as gridspec - -trace_type = 'f_center_subtracted' -response_table_path = 'analysis/response_table_001_DriftingGratingCircleRetinotopicMapping' - -baseline_span = [-0.5, 0.] -response_span = [0., 1.5] -is_add_to_trace = True - -face_cmap = 'RdBu_r' - -def get_dff(traces, t_axis, response_span, baseline_span): - """ - - :param traces: dimension, trial x timepoint - :param t_axis: - :return: - """ - - baseline_ind = np.logical_and(t_axis > baseline_span[0], t_axis <= baseline_span[1]) - response_ind = np.logical_and(t_axis > response_span[0], t_axis <= response_span[1]) - baseline = np.mean(traces[:, baseline_ind], axis=1, keepdims=True) - dff_traces = (traces - baseline) / baseline - - trace_mean = np.mean(traces, axis=0) - baseline_mean = np.mean(trace_mean[baseline_ind]) - dff_trace_mean = (trace_mean - baseline_mean) / baseline_mean - dff_mean = np.mean(dff_trace_mean[response_ind]) - - return dff_traces, dff_trace_mean, dff_mean - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, 'figures') -if not os.path.isdir(save_folder): - os.mkdir(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -print(nwb_fn) -nwb_f = h5py.File(nwb_fn, 'r') - -plane_ns = nwb_f[response_table_path].keys() -plane_ns.sort() - -for plane_n in plane_ns: - - print('\nprocessing {} ...'.format(plane_n)) - - if is_add_to_trace: - add_to_trace = h5py.File(os.path.join(plane_n, 'caiman_segmentation_results.hdf5'), - 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0 - - res_grp = nwb_f['{}/{}'.format(response_table_path, plane_n)] - t_axis = res_grp.attrs['sta_timestamps'] - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - grating_ns = res_grp.keys() - - # remove blank sweep - grating_ns = [gn for gn in grating_ns if gn[-37:] != '_sf0.00_tf00.0_dire000_con0.00_rad000'] - - dire_lst = np.array(list(set([str(gn[38:41]) for gn in grating_ns]))) - tf_lst = np.array(list(set([str(gn[29:33]) for gn in grating_ns]))) - sf_lst = np.array(list(set([str(gn[22:26]) for gn in grating_ns]))) - - - pdff = PdfPages(os.path.join(save_folder, 'STA_DriftingGrating_' + plane_n + '_all.pdf')) - - for roi_i, roi_n in enumerate(roi_lst): - print(roi_n) - - f = plt.figure(figsize=(8.5, 11)) - gs_out = gridspec.GridSpec(len(tf_lst), 1) - gs_in_dict = {} - for gs_ind, gs_o in enumerate(gs_out): - curr_gs_in = gridspec.GridSpecFromSubplotSpec(len(sf_lst), len(dire_lst), subplot_spec=gs_o, - wspace=0.0, hspace=0.0) - gs_in_dict[gs_ind] = curr_gs_in - - v_max = 0 - v_min = 0 - dff_mean_max=0 - dff_mean_min=0 - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - dff_traces, dff_trace_mean, dff_mean = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, - baseline_span=baseline_span) - v_max = max([np.amax(dff_traces), v_max]) - v_min = min([np.amin(dff_traces), v_min]) - dff_mean_max = max([dff_mean, dff_mean_max]) - dff_mean_min = min([dff_mean, dff_mean_min]) - - dff_mean_max = max([abs(dff_mean_max), abs(dff_mean_min)]) - dff_mean_min = - dff_mean_max - - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - dff_traces, dff_trace_mean, dff_mean = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, - baseline_span=baseline_span) - - curr_tf = grating_n[29:33] - tf_i = np.where(tf_lst == curr_tf)[0][0] - curr_sf = grating_n[22:26] - sf_i = np.where(sf_lst == curr_sf)[0][0] - curr_dire = grating_n[38:41] - dire_i = np.where(dire_lst == curr_dire)[0][0] - ax = plt.Subplot(f, gs_in_dict[tf_i][sf_i * len(dire_lst) + dire_i]) - f_color = pt.value_2_rgb(value=(dff_mean - dff_mean_min) / (dff_mean_max - dff_mean_min), - cmap=face_cmap) - - # f_color = pt.value_2_rgb(value=dff_mean / dff_mean_max, cmap=face_cmap) - - # print f_color - ax.set_axis_bgcolor(f_color) - ax.set_xticks([]) - ax.set_yticks([]) - for sp in ax.spines.values(): - sp.set_visible(False) - ax.axhline(y=0, ls='--', color='#888888', lw=1) - ax.axvspan(response_span[0], response_span[1], alpha=0.5, color='#888888', ec='none') - for t in dff_traces: - ax.plot(t_axis, t, '-', color='#888888', lw=0.5) - ax.plot(t_axis, dff_trace_mean, '-r', lw=1) - f.add_subplot(ax) - - all_axes = f.get_axes() - for ax in all_axes: - ax.set_ylim([v_min, v_max]) - ax.set_xlim([t_axis[0], t_axis[-1]]) - - f.suptitle('roi:{:04d}; trace type:{}; baseline:{}; response:{}; \ntrace range:{}; color range:{}' - .format(roi_i, trace_type, baseline_span, response_span, [v_min, v_max], - [dff_mean_min, dff_mean_max]), fontsize=8) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/210_plot_dgc_response_mean.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/210_plot_dgc_response_mean.py deleted file mode 100644 index 604a4db..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/210_plot_dgc_response_mean.py +++ /dev/null @@ -1,157 +0,0 @@ -import os -import h5py -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.core.PlottingTools as pt -from matplotlib.backends.backend_pdf import PdfPages -import matplotlib.gridspec as gridspec - -trace_type = 'f_center_subtracted' -response_table_path = 'analysis/response_table_001_DriftingGratingCircleRetinotopicMapping' - -baseline_span = [-0.5, 0.] -response_span = [0., 1.5] -is_add_to_trace = True - -face_cmap = 'RdBu_r' - -def get_dff(traces, t_axis, response_span, baseline_span): - """ - - :param traces: dimension, trial x timepoint - :param t_axis: - :return: - """ - - trace_mean = np.mean(traces, axis=0) - trace_std = np.std(traces, axis=0) - trace_sem = trace_std / np.sqrt(traces.shape[0]) - - baseline_ind = np.logical_and(t_axis > baseline_span[0], t_axis <= baseline_span[1]) - response_ind = np.logical_and(t_axis > response_span[0], t_axis <= response_span[1]) - baseline = np.mean(trace_mean[baseline_ind]) - dff_trace_mean = (trace_mean - baseline) / baseline - dff_trace_std = trace_std / baseline - dff_trace_sem = trace_sem / baseline - dff_mean = np.mean(dff_trace_mean[response_ind]) - - return dff_trace_mean, dff_trace_std, dff_trace_sem, dff_mean - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, 'figures') -if not os.path.isdir(save_folder): - os.mkdir(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -print(nwb_fn) -nwb_f = h5py.File(nwb_fn, 'r') - -plane_ns = nwb_f[response_table_path].keys() -plane_ns.sort() - -for plane_n in plane_ns: - - print('\nprocessing {} ...'.format(plane_n)) - - if is_add_to_trace: - add_to_trace = h5py.File(os.path.join(plane_n, 'caiman_segmentation_results.hdf5'), - 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0 - - res_grp = nwb_f['{}/{}'.format(response_table_path, plane_n)] - t_axis = res_grp.attrs['sta_timestamps'] - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - grating_ns = res_grp.keys() - # remove blank sweep - grating_ns = [gn for gn in grating_ns if gn[-37:] != '_sf0.00_tf00.0_dire000_con0.00_rad000'] - - dire_lst = np.array(list(set([str(gn[38:41]) for gn in grating_ns]))) - dire_lst.sort() - tf_lst = np.array(list(set([str(gn[29:33]) for gn in grating_ns]))) - tf_lst.sort() - sf_lst = np.array(list(set([str(gn[22:26]) for gn in grating_ns]))) - sf_lst.sort() - - pdff = PdfPages(os.path.join(save_folder, 'STA_DriftingGrating_' + plane_n + '_mean.pdf')) - - for roi_i, roi_n in enumerate(roi_lst): - print(roi_n) - - f = plt.figure(figsize=(8.5, 11)) - gs_out = gridspec.GridSpec(len(tf_lst), 1) - gs_in_dict = {} - for gs_ind, gs_o in enumerate(gs_out): - curr_gs_in = gridspec.GridSpecFromSubplotSpec(len(sf_lst), len(dire_lst), subplot_spec=gs_o, - wspace=0.05, hspace=0.05) - gs_in_dict[gs_ind] = curr_gs_in - - v_max = 0 - v_min = 0 - dff_mean_max=0 - dff_mean_min=0 - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - _ = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, baseline_span=baseline_span) - dff_trace_mean, dff_trace_std, dff_trace_sem, dff_mean = _ - v_max = max([np.amax(dff_trace_mean + dff_trace_sem), v_max]) - v_min = min([np.amin(dff_trace_mean - dff_trace_sem), v_min]) - dff_mean_max = max([dff_mean, dff_mean_max]) - dff_mean_min = min([dff_mean, dff_mean_min]) - dff_mean_max = max([abs(dff_mean_max), abs(dff_mean_min)]) - dff_mean_min = - dff_mean_max - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - _ = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, baseline_span=baseline_span) - dff_trace_mean, dff_trace_std, dff_trace_sem, dff_mean = _ - curr_tf = grating_n[29:33] - tf_i = np.where(tf_lst == curr_tf)[0][0] - curr_sf = grating_n[22:26] - sf_i = np.where(sf_lst == curr_sf)[0][0] - curr_dire = grating_n[38:41] - dire_i = np.where(dire_lst == curr_dire)[0][0] - ax = plt.Subplot(f, gs_in_dict[tf_i][sf_i * len(dire_lst) + dire_i]) - f_color = pt.value_2_rgb(value=(dff_mean - dff_mean_min) / (dff_mean_max - dff_mean_min), - cmap=face_cmap) - - # f_color = pt.value_2_rgb(value=dff_mean / dff_mean_max, cmap=face_cmap) - - # print f_color - ax.set_axis_bgcolor(f_color) - ax.set_xticks([]) - ax.set_yticks([]) - for sp in ax.spines.values(): - sp.set_visible(False) - ax.axhline(y=0, ls='--', color='#888888', lw=1) - ax.axvspan(response_span[0], response_span[1], alpha=0.5, color='#888888', ec='none') - ax.fill_between(t_axis, dff_trace_mean - dff_trace_sem, dff_trace_mean + dff_trace_sem, edgecolor='none', - facecolor='#880000', alpha=0.5) - ax.plot(t_axis, dff_trace_mean, '-r', lw=1) - f.add_subplot(ax) - - all_axes = f.get_axes() - for ax in all_axes: - ax.set_ylim([v_min, v_max]) - ax.set_xlim([t_axis[0], t_axis[-1]]) - - f.suptitle('roi:{:04d}; trace type:{}; baseline:{}; response:{}; \ntrace range:{}; color range:{}' - .format(roi_i, trace_type, baseline_span, response_span, [v_min, v_max], - [dff_mean_min, dff_mean_max]), fontsize=8) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/220_plot_dgc_tuning_curves.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/220_plot_dgc_tuning_curves.py deleted file mode 100644 index a5d8a6f..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/220_plot_dgc_tuning_curves.py +++ /dev/null @@ -1,196 +0,0 @@ -import os -import h5py -import numpy as np -import pandas as pd -import matplotlib.pyplot as plt -from matplotlib.backends.backend_pdf import PdfPages - -trace_type = 'f_center_subtracted' -response_table_path = 'analysis/response_table_001_DriftingGratingCircleRetinotopicMapping' - -baseline_span = [-0.5, 0.] -response_span = [0., 1.5] -is_add_to_trace = True - -def get_response(traces, t_axis, response_span, baseline_span): - """ - - :param traces: dimension, trial x timepoint - :param t_axis: - :return: - """ - - baseline_ind = np.logical_and(t_axis > baseline_span[0], t_axis <= baseline_span[1]) - response_ind = np.logical_and(t_axis > response_span[0], t_axis <= response_span[1]) - - trace_mean = np.mean(traces, axis=0) - baseline_mean = np.mean(trace_mean[baseline_ind]) - dff_trace_mean = (trace_mean - baseline_mean) / baseline_mean - dff_mean = np.mean(dff_trace_mean[response_ind]) - - baselines = np.mean(traces[:, baseline_ind], axis=1, keepdims=True) - dff_traces = (traces - baselines) / baselines - dffs = np.mean(dff_traces[:, response_ind], axis=1) - dff_std = np.std(dffs) - dff_sem = dff_std / np.sqrt(traces.shape[0]) - - return dff_mean, dff_std, dff_sem - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, 'figures') -if not os.path.isdir(save_folder): - os.mkdir(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -print(nwb_fn) -nwb_f = h5py.File(nwb_fn, 'r') - -plane_ns = nwb_f[response_table_path].keys() -plane_ns.sort() - -for plane_n in plane_ns: - - print('\nprocessing {} ...'.format(plane_n)) - - if is_add_to_trace: - add_to_trace = h5py.File(os.path.join(plane_n, 'caiman_segmentation_results.hdf5'), - 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0 - - res_grp = nwb_f['{}/{}'.format(response_table_path, plane_n)] - t_axis = res_grp.attrs['sta_timestamps'] - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - grating_ns = res_grp.keys() - - # remove blank sweep - grating_ns = [gn for gn in grating_ns if gn[-37:] != '_sf0.00_tf00.0_dire000_con0.00_rad000'] - - dire_lst = np.array(list(set([str(gn[38:41]) for gn in grating_ns]))) - dire_lst.sort() - tf_lst = np.array(list(set([str(gn[29:33]) for gn in grating_ns]))) - tf_lst.sort() - sf_lst = np.array(list(set([str(gn[22:26]) for gn in grating_ns]))) - sf_lst.sort() - - pdff = PdfPages(os.path.join(save_folder, 'tuning_curve_DriftingGrating_' + plane_n + '_mean.pdf')) - - for roi_i, roi_n in enumerate(roi_lst): - print(roi_n) - - # get response table - res_tab = pd.DataFrame(columns=['con', 'tf', 'sf', 'dire', 'dff_mean', 'dff_std', 'dff_sem']) - row_ind = 0 - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - _ = get_response(traces=curr_sta, t_axis=t_axis, response_span=response_span, baseline_span=baseline_span) - dff_mean, dff_std, dff_sem = _ - - con = float(grating_n.split('_')[5][3:]) - tf = float(grating_n.split('_')[3][2:]) - sf = float(grating_n.split('_')[2][2:]) - dire = int(grating_n.split('_')[4][4:]) - - res_tab.loc[row_ind] = [con, tf, sf, dire, dff_mean, dff_std, dff_sem] - row_ind += 1 - - # find the preferred condition - top_condition = res_tab[res_tab.dff_mean == max(res_tab.dff_mean)] - - # make figure - f = plt.figure(figsize=(8.5, 11)) - - # get tf plot - tf_conditions = res_tab[(res_tab.sf == float(top_condition.sf)) & \ - (res_tab.dire == int(top_condition.dire))] - tf_conditions = tf_conditions.sort_values(by='tf') - - tf_log = np.log(tf_conditions.tf) - - ax_tf = f.add_subplot(311) - ax_tf.fill_between(x=tf_log, y1=tf_conditions.dff_mean + tf_conditions.dff_sem, - y2=tf_conditions.dff_mean - tf_conditions.dff_sem, edgecolor='none', - facecolor='#888888', alpha=0.5) - ax_tf.axhline(y=0, ls='--', color='k', lw=1) - ax_tf.plot(tf_log, tf_conditions.dff_mean, 'r-', lw=2) - ax_tf.set_title('temporal frequency tuning', rotation='vertical', x=-0.4, y=0.5, va='center', ha='center', - size=10) - ax_tf.set_xticks(tf_log) - ax_tf.set_xticklabels(list(tf_conditions.tf)) - ax_tf.set_xlim(np.log([0.9, 16])) - ax_tf_xrange = ax_tf.get_xlim()[1] - ax_tf.get_xlim()[0] - ax_tf_yrange = ax_tf.get_ylim()[1] - ax_tf.get_ylim()[0] - ax_tf.set_aspect(aspect=(ax_tf_xrange / ax_tf_yrange)) - ax_tf.set_ylabel('mean df/f', size=10) - ax_tf.set_xlabel('temporal freqency (Hz)', size=10) - ax_tf.tick_params(axis='both', which='major', labelsize=10) - - # get sf plot - sf_conditions = res_tab[(res_tab.tf == float(top_condition.tf)) & \ - (res_tab.dire == int(top_condition.dire))] - sf_conditions = sf_conditions.sort_values(by='sf') - - sf_log = np.log(sf_conditions.sf) - - ax_sf = f.add_subplot(312) - ax_sf.fill_between(x=sf_log, y1=sf_conditions.dff_mean + sf_conditions.dff_sem, - y2=sf_conditions.dff_mean - sf_conditions.dff_sem, edgecolor='none', - facecolor='#888888', alpha=0.5) - ax_sf.axhline(y=0, ls='--', color='k', lw=1) - ax_sf.plot(sf_log, sf_conditions.dff_mean, '-r', lw=2) - ax_sf.set_title('spatial frequency tuning', rotation='vertical', x=-0.4, y=0.5, va='center', ha='center', - size=10) - ax_sf.set_xticks(sf_log) - ax_sf.set_xticklabels(['{:04.2f}'.format(s) for s in list(sf_conditions.sf)]) - ax_sf.set_xlim(np.log([0.008, 0.4])) - ax_sf_xrange = ax_sf.get_xlim()[1] - ax_sf.get_xlim()[0] - ax_sf_yrange = ax_sf.get_ylim()[1] - ax_sf.get_ylim()[0] - ax_sf.set_aspect(aspect=(ax_sf_xrange / ax_sf_yrange)) - ax_sf.set_ylabel('mean df/f', size=10) - ax_sf.set_xlabel('spatial freqency (cpd)', size=10) - ax_sf.tick_params(axis='both', which='major', labelsize=10) - - # get dire plot - dire_conditions = res_tab[(res_tab.tf == float(top_condition.tf)) & \ - (res_tab.sf == float(top_condition.sf))] - dire_conditions = dire_conditions.sort_values(by='dire') - dire_arc = list(dire_conditions.dire * np.pi / 180.) - dire_arc.append(dire_arc[0]) - dire_dff = np.array(dire_conditions.dff_mean) - dire_dff[dire_dff < 0.] = 0. - dire_dff = list(dire_dff) - dire_dff.append(dire_dff[0]) - dire_dff_sem = list(dire_conditions.dff_sem) - dire_dff_sem.append(dire_dff_sem[0]) - dire_dff_low = np.array(dire_dff) - np.array(dire_dff_sem) - dire_dff_low[dire_dff_low < 0.] = 0. - dire_dff_high = np.array(dire_dff) + np.array(dire_dff_sem) - - r_ticks = [0, round(max(dire_dff) * 10000.) / 10000.] - - ax_dire = f.add_subplot(313, projection='polar') - ax_dire.fill_between(x=dire_arc, y1=dire_dff_low, y2=dire_dff_high, edgecolor='none', facecolor='#888888', - alpha=0.5) - ax_dire.plot(dire_arc, dire_dff, '-r', lw=2) - ax_dire.set_title('orientation tuning', rotation='vertical', x=-0.4, y=0.5, va='center', ha='center', size=10) - ax_dire.set_rticks(r_ticks) - ax_dire.tick_params(axis='both', which='major', labelsize=10) - - f.suptitle('roi:{:04d}; trace type:{}; baseline:{}; response:{}' - .format(roi_i, trace_type, baseline_span, response_span), fontsize=10) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/040_get_cells_file.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/040_get_cells_file.py deleted file mode 100644 index ff29a37..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/040_get_cells_file.py +++ /dev/null @@ -1,87 +0,0 @@ -import os -import numpy as np -import h5py -import tifffile as tf -import allensdk_internal.brain_observatory.mask_set as mask_set -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import scipy.ndimage as ni -import matplotlib.pyplot as plt - - -isSave = True -is_filter = True - -filter_sigma = 0.5 # parameters only used if filter the rois -# dilation_iterations = 0 # parameters only used if filter the rois -cut_thr = 3. # parameters only used if filter the rois - -bg_fn = "corrected_mean_projection.tif" -save_folder = 'figures' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -data_f = h5py.File('caiman_segmentation_results.hdf5') -masks = data_f['masks'].value -data_f.close() - -bg = tf.imread(bg_fn) - -final_roi_dict = {} - -for i, mask in enumerate(masks): - - if is_filter: - mask_nor = (mask - np.mean(mask.flatten())) / np.abs(np.std(mask.flatten())) - mask_nor_f = ni.filters.gaussian_filter(mask_nor, filter_sigma) - mask_bin = np.zeros(mask_nor_f.shape, dtype=np.uint8) - mask_bin[mask_nor_f > cut_thr] = 1 - - else: - mask_bin = np.zeros(mask.shape, dtype=np.uint8) - mask_bin[mask > 0] = 1 - - mask_labeled, mask_num = ni.label(mask_bin) - curr_mask_dict = ia.get_masks(labeled=mask_labeled, keyPrefix='caiman_mask_{:03d}'.format(i), labelLength=5) - for roi_key, roi_mask in curr_mask_dict.items(): - final_roi_dict.update({roi_key: ia.WeightedROI(roi_mask * mask)}) - -print 'Total number of ROIs:',len(final_roi_dict) - -f = plt.figure(figsize=(15, 8)) -ax1 = f.add_subplot(121) -ax1.imshow(ia.array_nor(bg), vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') -colors1 = pt.random_color(masks.shape[0]) -for i, mask in enumerate(masks): - pt.plot_mask_borders(mask, plotAxis=ax1, color=colors1[i]) -ax1.set_title('original ROIs') -ax1.set_axis_off() -ax2 = f.add_subplot(122) -ax2.imshow(ia.array_nor(bg), vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') -colors2 = pt.random_color(len(final_roi_dict)) -i = 0 -for roi in final_roi_dict.values(): - pt.plot_mask_borders(roi.get_binary_mask(), plotAxis=ax2, color=colors2[i]) - i = i + 1 -ax2.set_title('filtered ROIs') -ax2.set_axis_off() -plt.show() - -if isSave: - - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - f.savefig(os.path.join(save_folder, 'caiman_segmentation_filtering.pdf'), dpi=300) - - cell_file = h5py.File('cells.hdf5', 'w') - - i = 0 - for key, value in sorted(final_roi_dict.iteritems()): - curr_grp = cell_file.create_group('cell{:04d}'.format(i)) - curr_grp.attrs['name'] = key - value.to_h5_group(curr_grp) - i += 1 - - cell_file.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/050_refine_cells.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/050_refine_cells.py deleted file mode 100644 index f0b8c31..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/050_refine_cells.py +++ /dev/null @@ -1,173 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Jun 30 17:44:42 2015 - -@author: junz -""" -import os -import h5py -import numpy as np -import operator -import matplotlib.pyplot as plt -import scipy.ndimage as ni -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.FileTools as ft -import corticalmapping.core.PlottingTools as pt -import corticalmapping.SingleCellAnalysis as sca - -plt.ioff() - -# pixels, masks with center location within this pixel region at the image border will be discarded -center_margin = [20, 20] - -# area range, range of number of pixels of a valid roi -area_range = [20, 500] - -# for the two masks that are overlapping, if the ratio between overlap and the area of the smaller mask is larger than -# this value, the smaller mask will be discarded. -overlap_thr = 0.5 - -save_folder = 'figures' - -data_file_name = 'cells.hdf5' -save_file_name = 'cells_refined.hdf5' -background_file_name = "corrected_mean_projection.tif" - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -# read cells -dfile = h5py.File(data_file_name) -cells = {} -for cellname in dfile.iterkeys(): - cells.update({cellname:ia.WeightedROI.from_h5_group(dfile[cellname])}) - -print 'total number of cells:', len(cells) - -# get the names of cells which are on the edge -edge_cells = [] -for cellname, cellmask in cells.iteritems(): - dimension = cellmask.dimension - center = cellmask.get_center() - if center[0] < center_margin[0] or \ - center[0] > dimension[0] - center_margin[0] or \ - center[1] < center_margin[1] or \ - center[1] > dimension[1] - center_margin[1]: - - # cellmask.plot_binary_mask_border(color='#ff0000', borderWidth=1) - # plt.title(cellname) - # plt.show() - - edge_cells.append(cellname) - -print '\ncells to be removed because they are on the edges:' -print '\n'.join(edge_cells) - -# remove edge cells -for edge_cell in edge_cells: - _ = cells.pop(edge_cell) - -# get dictionary of cell areas -cell_areas = {} -for cellname, cellmask in cells.iteritems(): - cell_areas.update({cellname: cellmask.get_binary_area()}) - - -# remove cellnames that have area outside of the area_range -invalid_cell_ns = [] -for cellname, cellarea in cell_areas.items(): - if cellarea < area_range[0] or cellarea > area_range[1]: - invalid_cell_ns.append(cellname) -print "cells to be removed because they do not meet area criterion:" -print "\n".join(invalid_cell_ns) -for invalid_cell_n in invalid_cell_ns: - cell_areas.pop(invalid_cell_n) - - -# sort cells with their binary area -cell_areas_sorted = sorted(cell_areas.items(), key=operator.itemgetter(1)) -cell_areas_sorted.reverse() -cell_names_sorted = [c[0] for c in cell_areas_sorted] -# print '\n'.join([str(c) for c in cell_areas_sorted]) - -# get the name of cells that needs to be removed because of overlapping -retain_cells = [] -remove_cells = [] -for cell1_name in cell_names_sorted: - cell1_mask = cells[cell1_name] - is_remove = 0 - cell1_area = cell1_mask.get_binary_area() - for cell2_name in retain_cells: - cell2_mask = cells[cell2_name] - cell2_area = cell2_mask.get_binary_area() - curr_overlap = cell1_mask.binary_overlap(cell2_mask) - - if float(curr_overlap) / cell1_area > overlap_thr: - remove_cells.append(cell1_name) - is_remove = 1 - print cell1_name, ':', cell1_mask.get_binary_area(), ': removed' - - # f = plt.figure(figsize=(10,10)) - # ax = f.add_subplot(111) - # cell1_mask.plot_binary_mask_border(plotAxis=ax, color='#ff0000', borderWidth=1) - # cell2_mask.plot_binary_mask_border(plotAxis=ax, color='#0000ff', borderWidth=1) - # ax.set_title('red:'+cell1_name+'; blue:'+cell2_name) - # plt.show() - break - - if is_remove == 0: - retain_cells.append(cell1_name) - print cell1_name, ':', cell1_mask.get_binary_area(), ': retained' - -print '\ncells to be removed because of overlapping:' -print '\n'.join(remove_cells) - -print '\ntotal number of reatined cells:', len(retain_cells) - -# plotting -colors = pt.random_color(len(cells.keys())) -bgImg = tf.imread(background_file_name) - -f = plt.figure(figsize=(10, 10)) -ax = f.add_subplot(111) -ax.imshow(ia.array_nor(bgImg), cmap='gray', vmin=0, vmax=0.5, interpolation='nearest') - -f2 = plt.figure(figsize=(10, 10)) -ax2 = f2.add_subplot(111) -ax2.imshow(np.zeros(bgImg.shape, dtype=np.uint8), vmin=0, vmax=1, cmap='gray', interpolation='nearest') - -i = 0 -for retain_cell in retain_cells: - cells[retain_cell].plot_binary_mask_border(plotAxis=ax, color=colors[i], borderWidth=1) - cells[retain_cell].plot_binary_mask_border(plotAxis=ax2, color=colors[i], borderWidth=1) - i += 1 -plt.show() - -# save figures -pt.save_figure_without_borders(f, os.path.join(save_folder, '2P_refined_ROIs_with_background.png'), dpi=300) -pt.save_figure_without_borders(f2, os.path.join(save_folder, '2P_refined_ROIs_without_background.png'), dpi=300) - -# save h5 file -save_file = h5py.File(save_file_name, 'w') -i = 0 -for retain_cell in retain_cells: - print retain_cell, ':', cells[retain_cell].get_binary_area() - - currGroup = save_file.create_group('cell' + ft.int2str(i, 4)) - currGroup.attrs['name'] = retain_cell - roiGroup = currGroup.create_group('roi') - cells[retain_cell].to_h5_group(roiGroup) - i += 1 - -for attr, value in dfile.attrs.iteritems(): - save_file.attrs[attr] = value - -save_file.close() -dfile.close() - - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/060_get_weighted_rois_and_surrounds.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/060_get_weighted_rois_and_surrounds.py deleted file mode 100644 index b31e405..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/060_get_weighted_rois_and_surrounds.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Jun 30 17:44:42 2015 - -@author: junz -""" - -import os -import numpy as np -import h5py -import tifffile as tf -import allensdk_internal.brain_observatory.mask_set as mask_set -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import scipy.ndimage as ni -import matplotlib.pyplot as plt - -plt.ioff() - -data_file_name = 'cells_refined.hdf5' -background_file_name = "corrected_mean_projection.tif" -save_folder = 'figures' - -overlap_threshold = 0.9 -surround_limit = [1, 8] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -print 'reading cells file ...' -data_f = h5py.File(data_file_name, 'r') - -cell_ns = data_f.keys() -cell_ns.sort() - -binary_mask_array = [] -weight_mask_array = [] - -for cell_n in cell_ns: - curr_roi = ia.ROI.from_h5_group(data_f[cell_n]['roi']) - binary_mask_array.append(curr_roi.get_binary_mask()) - weight_mask_array.append(curr_roi.get_weighted_mask()) - -data_f.close() -binary_mask_array = np.array(binary_mask_array) -weight_mask_array = np.array(weight_mask_array) -print 'starting mask_array shape:', weight_mask_array.shape - -print 'getting total mask ...' -total_mask = np.zeros((binary_mask_array.shape[1], binary_mask_array.shape[2]), dtype=np.uint8) -for curr_mask in binary_mask_array: - total_mask = np.logical_or(total_mask, curr_mask) -total_mask = np.logical_not(total_mask) - -plt.imshow(total_mask, interpolation='nearest') -plt.title('total_mask') -plt.show() - -print 'getting and surround masks ...' -binary_surround_array = [] -for binary_center in binary_mask_array: - curr_surround = np.logical_xor(ni.binary_dilation(binary_center, iterations=surround_limit[1]), - ni.binary_dilation(binary_center, iterations=surround_limit[0])) - curr_surround = np.logical_and(curr_surround, total_mask).astype(np.uint8) - binary_surround_array.append(curr_surround) - # plt.imshow(curr_surround) - # plt.show() -binary_surround_array = np.array(binary_surround_array) - -print "saving rois ..." -center_areas = [] -surround_areas = [] -for mask_ind in range(binary_mask_array.shape[0]): - center_areas.append(np.sum(binary_mask_array[mask_ind].flat)) - surround_areas.append(np.sum(binary_surround_array[mask_ind].flat)) -roi_f = h5py.File('rois_and_traces.hdf5') -roi_f['masks_center'] = weight_mask_array -roi_f['masks_surround'] = binary_surround_array - -roi_f.close() -print 'minimum surround area:', min(surround_areas), 'pixels.' - -f = plt.figure(figsize=(10, 10)) -ax_center = f.add_subplot(211) -ax_center.hist(center_areas, bins=30) -ax_center.set_title('roi center area distribution') -ax_surround = f.add_subplot(212) -ax_surround.hist(surround_areas, bins=30) -ax_surround.set_title('roi surround area distribution') -plt.show() - -print 'plotting ...' -colors = pt.random_color(weight_mask_array.shape[0]) -bg = ia.array_nor(tf.imread('corrected_mean_projection.tif')) - -f_c_bg = plt.figure(figsize=(10, 10)) -ax_c_bg = f_c_bg.add_subplot(111) -ax_c_bg.imshow(bg, cmap='gray', vmin=0, vmax=0.5, interpolation='nearest') -f_c_nbg = plt.figure(figsize=(10, 10)) -ax_c_nbg = f_c_nbg.add_subplot(111) -ax_c_nbg.imshow(np.zeros(bg.shape,dtype=np.uint8),vmin=0,vmax=1,cmap='gray',interpolation='nearest') -f_s_nbg = plt.figure(figsize=(10, 10)) -ax_s_nbg = f_s_nbg.add_subplot(111) -ax_s_nbg.imshow(np.zeros(bg.shape,dtype=np.uint8),vmin=0,vmax=1,cmap='gray',interpolation='nearest') - -i = 0 -for mask_ind in range(binary_mask_array.shape[0]): - pt.plot_mask_borders(binary_mask_array[mask_ind], plotAxis=ax_c_bg, color=colors[i], borderWidth=1) - pt.plot_mask_borders(binary_mask_array[mask_ind], plotAxis=ax_c_nbg, color=colors[i], borderWidth=1) - pt.plot_mask_borders(binary_surround_array[mask_ind], plotAxis=ax_s_nbg, color=colors[i], borderWidth=1) - i += 1 - -plt.show() - -print 'saving figures ...' -pt.save_figure_without_borders(f_c_bg, os.path.join(save_folder, '2P_ROIs_with_background.png'), dpi=300) -pt.save_figure_without_borders(f_c_nbg, os.path.join(save_folder, '2P_ROIs_without_background.png'), dpi=300) -pt.save_figure_without_borders(f_s_nbg, os.path.join(save_folder, '2P_ROI_surrounds_background.png'), dpi=300) -f.savefig(os.path.join(save_folder, 'roi_area_distribution.pdf'), dpi=300) diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/070_get_raw_center_and_surround_traces.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/070_get_raw_center_and_surround_traces.py deleted file mode 100644 index 0e57582..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/070_get_raw_center_and_surround_traces.py +++ /dev/null @@ -1,129 +0,0 @@ -import os -import numpy as np -import h5py -import time -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import corticalmapping.core.FileTools as ft -import corticalmapping.NwbTools as nt -import matplotlib.pyplot as plt -from multiprocessing import Pool - -CHUNK_SIZE = 2000 -PROCESS_NUM = 5 - -def get_chunk_frames(frame_num, chunk_size): - chunk_num = frame_num // chunk_size - if frame_num % chunk_size > 0: - chunk_num = chunk_num + 1 - - print("total number of frames:", frame_num) - print("total number of chunks:", chunk_num) - - chunk_ind = [] - chunk_starts = [] - chunk_ends = [] - - for chunk_i in range(chunk_num): - chunk_ind.append(chunk_i) - chunk_starts.append(chunk_i * chunk_size) - - if chunk_i < chunk_num - 1: - chunk_ends.append((chunk_i + 1) * chunk_size) - else: - chunk_ends.append(frame_num) - - return zip(chunk_ind, chunk_starts, chunk_ends) - -def get_traces(params): - t0 = time.time() - - chunk_ind, chunk_start, chunk_end, nwb_path, data_path, curr_folder, center_array, surround_array = params - - nwb_f = h5py.File(nwb_path, 'r') - print('\nstart analyzing chunk: {}'.format(chunk_ind)) - curr_mov = nwb_f[data_path][chunk_start: chunk_end] - nwb_f.close() - - # print 'extracting traces' - curr_traces_center = np.empty((center_array.shape[0], curr_mov.shape[0]), dtype=np.float32) - curr_traces_surround = np.empty((center_array.shape[0], curr_mov.shape[0]), dtype=np.float32) - for i in range(center_array.shape[0]): - curr_center = ia.WeightedROI(center_array[i]) - curr_surround = ia.ROI(surround_array[i]) - curr_traces_center[i, :] = curr_center.get_weighted_trace_pixelwise(curr_mov) - - # scale surround trace to be similar as center trace - mean_center_weight = curr_center.get_mean_weight() - curr_traces_surround[i, :] = curr_surround.get_binary_trace_pixelwise(curr_mov) * mean_center_weight - - # print 'saveing chunk {} ...'.format(chunk_ind) - chunk_folder = os.path.join(curr_folder, 'chunks') - if not os.path.isdir(chunk_folder): - os.mkdir(chunk_folder) - chunk_f = h5py.File(os.path.join(chunk_folder, 'chunk_temp_' + ft.int2str(chunk_ind, 4) + '.hdf5')) - chunk_f['traces_center'] = curr_traces_center - chunk_f['traces_surround'] = curr_traces_surround - chunk_f.close() - - print('\n\t{:06d} seconds: chunk: {}; demixing finished.'.format(int(time.time() - t0), chunk_ind)) - - return None - -def run(): - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - plane_n = os.path.split(curr_folder)[1] - print(plane_n) - - print('getting masks ...') - rois_f = h5py.File('rois_and_traces.hdf5') - center_array = rois_f['masks_center'].value - surround_array = rois_f['masks_surround'].value - - print('\nanalyzing movie in chunks of size:', CHUNK_SIZE , 'frames.') - - nwb_folder = os.path.dirname(curr_folder) - nwb_fn = [f for f in os.listdir(nwb_folder) if f[-4:] == '.nwb'][0] - nwb_path = os.path.join(nwb_folder, nwb_fn) - print('\n' + nwb_path) - data_path = '/processing/motion_correction/MotionCorrection/' + plane_n + '/corrected/data' - - nwb_f = h5py.File(nwb_path, 'r') - total_frame = nwb_f[data_path].shape[0] - nwb_f.close() - - chunk_frames = get_chunk_frames(total_frame, CHUNK_SIZE) - chunk_params = [(cf[0], cf[1], cf[2], nwb_path, data_path, - curr_folder, center_array, surround_array) for cf in chunk_frames] - - p = Pool(PROCESS_NUM) - p.map(get_traces, chunk_params) - - chunk_folder = os.path.join(curr_folder, 'chunks') - chunk_fns = [f for f in os.listdir(chunk_folder) if f[0:11] == 'chunk_temp_'] - chunk_fns.sort() - print('\nreading chunks files ...') - print('\n'.join(chunk_fns)) - - traces_raw = [] - traces_surround = [] - - for chunk_fn in chunk_fns: - curr_chunk_f = h5py.File(os.path.join(chunk_folder, chunk_fn)) - traces_raw.append(curr_chunk_f['traces_center'].value) - traces_surround.append(curr_chunk_f['traces_surround'].value) - - print("saving ...") - traces_raw = np.concatenate(traces_raw, axis=1) - traces_surround = np.concatenate(traces_surround, axis=1) - rois_f['traces_center_raw'] = traces_raw - rois_f['traces_surround_raw'] = traces_surround - print('done.') - - -if __name__ == '__main__': - run() - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/090_get_neuropil_subtracted_traces.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/090_get_neuropil_subtracted_traces.py deleted file mode 100644 index 551768f..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/090_get_neuropil_subtracted_traces.py +++ /dev/null @@ -1,101 +0,0 @@ -import sys -import os -import h5py -import numpy as np -import corticalmapping.HighLevel as hl -import corticalmapping.core.FileTools as ft -import matplotlib.pyplot as plt - - -lam = 1. # 100. -plot_chunk_size = 5000 - - -def plot_traces_chunks(traces, labels, chunk_size, roi_ind): - """ - - :param traces: np.array, shape=[trace_type, t_num] - :param labels: - :param chunk_size: - :param figures_folder: - :param roi_ind: - :return: - """ - - t_num = traces.shape[1] - chunk_num = t_num // chunk_size - - chunks = [] - for chunk_ind in range(chunk_num): - chunks.append([chunk_ind * chunk_size, (chunk_ind + 1) * chunk_size]) - - if t_num % chunk_size != 0: - chunks.append([chunk_num * chunk_size, t_num]) - - v_max = np.amax(traces) - v_min = np.amin(traces) - - fig = plt.figure(figsize=(75, 20)) - fig.suptitle('neuropil subtraction for ROI: {}'.format(roi_ind)) - for chunk_ind, chunk in enumerate(chunks): - curr_ax = fig.add_subplot(len(chunks), 1, chunk_ind + 1) - for trace_ind in range(traces.shape[0]): - curr_ax.plot(traces[trace_ind, chunk[0]: chunk[1]], label=labels[trace_ind]) - - curr_ax.set_xlim([0, chunk_size]) - curr_ax.set_ylim([v_min, v_max * 1.2]) - curr_ax.legend() - - return fig - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -data_f = h5py.File('rois_and_traces.hdf5') -traces_raw = data_f['traces_center_raw'].value -traces_srround = data_f['traces_surround_raw'].value - -traces_subtracted = np.zeros(traces_raw.shape, np.float32) -ratio = np.zeros(traces_raw.shape[0], np.float32) -err = np.zeros(traces_raw.shape[0], np.float32) - -for i in range(traces_raw.shape[0]): - curr_trace_c = traces_raw[i] - curr_trace_s = traces_srround[i] - curr_r, curr_err, curr_trace_sub = hl.neural_pil_subtraction(curr_trace_c, curr_trace_s, lam=lam) - print "roi_%s \tr = %.4f; error = %.4f." % (ft.int2str(i, 5), curr_r, curr_err) - traces_subtracted[i] = curr_trace_sub - ratio[i] = curr_r - err[i] = curr_err - -print('\nplotting neuropil subtraction results ...') -figures_folder = 'figures/neuropil_subtraction_lam_{}'.format(lam) -if not os.path.isdir(figures_folder): - os.makedirs(figures_folder) -for roi_ind in range(traces_raw.shape[0]): - print('roi_{:04d}'.format(roi_ind)) - curr_traces = np.array([traces_raw[roi_ind], traces_srround[roi_ind], traces_subtracted[roi_ind]]) - curr_fig = plot_traces_chunks(traces=curr_traces, - labels=['center', 'surround', 'subtracted'], - chunk_size=plot_chunk_size, - roi_ind=roi_ind) - curr_fig.savefig(os.path.join(figures_folder, 'neuropil_subtraction_ROI_{:04d}.png'.format(roi_ind))) - curr_fig.clear() - plt.close(curr_fig) - -# wait for keyboard abortion -msg = raw_input('Do you want to save? (y/n)\n') -while True: - if msg == 'y': - break - elif msg == 'n': - sys.exit('Stop process without saving.') - else: - msg = raw_input('Do you want to save? (y/n)\n') - -data_f['traces_center_subtracted'] = traces_subtracted -data_f['neuropil_r'] = ratio -data_f['neuropil_err'] = err - -data_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/120_check_correlation.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/120_check_correlation.py deleted file mode 100644 index 65c3f07..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/120_check_correlation.py +++ /dev/null @@ -1,99 +0,0 @@ -import os -import h5py -import tifffile as tf -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.core.PlottingTools as pt -import corticalmapping.core.ImageAnalysis as ia - - -cor_thr = 0.8 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -def merger_pairs(pairs): - - total_set = set([]) - for pair in pairs: - total_set.update(set(pair)) - - all_nodes = list(total_set) - node_grps = [{n} for n in all_nodes] - - for pair in pairs: - - node0 = pair[0] - node1 = pair[1] - - for node_grp in node_grps: - if node0 in node_grp: - node_grp0 = node_grp - if node1 in node_grp: - node_grp1 = node_grp - - if node_grp0 != node_grp1: - node_grp0.update(node_grp1) - node_grps.remove(node_grp1) - - return node_grps - - -save_plot_dir = os.path.join(curr_folder, 'figures', 'dff_extraction') -if not os.path.isdir(save_plot_dir): - os.makedirs(save_plot_dir) - -bg = ia.array_nor(tf.imread('corrected_mean_projection.tif')) - -data_f = h5py.File('rois_and_traces.hdf5') -traces_subtracted = data_f['traces_center_subtracted'].value -masks = data_f['masks_center'].value - -f, axs = plt.subplots(1, 2, figsize=(16, 5)) - -cor_mat = np.corrcoef(traces_subtracted) -fig = axs[0].imshow(cor_mat, vmin=-1, vmax=1, cmap='jet', interpolation='nearest') -axs[0].set_title('coriance matrix') -f.colorbar(fig, ax=axs[0]) - -cors = cor_mat[np.tril_indices(cor_mat.shape[0], k=-1)] -cor_dist = axs[1].hist(cors, range=[-1., 1.], bins=40) -axs[1].set_title('coriance distribution') - -# cors = np.sort(cors) -# cor_thr = cors[int(cors.shape[0] * 0.99)] -# print('Cutoff threshold for coriance: {}'.format(cor_thr)) - -pos_cor_loc = np.where(cor_mat > cor_thr) - -roi_pairs = [] -for ind in range(len(pos_cor_loc[0])): - if pos_cor_loc[0][ind] < pos_cor_loc[1][ind]: - roi_pairs.append([pos_cor_loc[0][ind], pos_cor_loc[1][ind]]) -print(roi_pairs) - -roi_grps = merger_pairs(roi_pairs) -print roi_grps - -cor_grps = [] -for roi_grp in roi_grps: - grp_traces = traces_subtracted[list(roi_grp)] - grp_cors = np.corrcoef(grp_traces)[np.tril_indices(len(roi_grp), k=-1)] - cor_grps.append(np.mean(grp_cors)) - -cor_grps = np.array(cor_grps) -cor_scalars = [(c + 1) / 2 for c in cor_grps] -print cor_scalars -cor_colors = [pt.value_2_rgb(c, cmap='inferno') for c in cor_scalars] - -f_roi = plt.figure() -ax_roi = f_roi.add_subplot(111) -ax_roi.imshow(bg, vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') -for grp_ind, roi_grp in enumerate(roi_grps): - for roi_ind in roi_grp: - print roi_ind, cor_colors[grp_ind] - pt.plot_mask_borders(masks[roi_ind], plotAxis=ax_roi, color=cor_colors[grp_ind]) - -plt.show() - -data_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/old/100_get_dff_traces.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/old/100_get_dff_traces.py deleted file mode 100644 index 3cd09ff..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_multi_channel_deepscope/within_plane_folder/old/100_get_dff_traces.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import h5py -import allensdk.brain_observatory.dff as dff -import numpy as np -import corticalmapping.HighLevel as hl -import corticalmapping.core.FileTools as ft - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_plot_dir = os.path.join(curr_folder, 'figures', 'dff_extraction') -if not os.path.isdir(save_plot_dir): - os.makedirs(save_plot_dir) - -data_f = h5py.File('rois_and_traces.hdf5') -traces_subtracted = data_f['traces_center_subtracted'].value - -traces_dff = dff.compute_dff(traces_subtracted, save_plot_dir=save_plot_dir, - mode_kernelsize=100, mean_kernelsize=100) -data_f['traces_center_dff'] = traces_dff -data_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/000_reorganize_data.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/000_reorganize_data.py deleted file mode 100644 index 9d83ccf..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/000_reorganize_data.py +++ /dev/null @@ -1,65 +0,0 @@ -import os -import numpy as np -import tifffile as tf - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180328-M360495-deepscope\04" - -identifier = '04_' -plane_num = 5 -temporal_downsample_rate = 1 -frame_each_file = 2000 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -fns = np.array([f for f in os.listdir(data_folder) if f[-4:] == '.tif' and identifier in f]) -f_nums = [int(os.path.splitext(fn)[0].split('_')[1]) for fn in fns] -fns = fns[np.argsort(f_nums)] -print('total file number: {}'.format(len(fns))) - -# print('\n'.join(fns)) - -save_folders = [] -for i in range(plane_num): - curr_save_folder = os.path.join(data_folder, identifier, 'plane{}'.format(i)) - if not os.path.isdir(curr_save_folder): - os.makedirs(curr_save_folder) - save_folders.append(curr_save_folder) - -# frame_per_plane = len(fns) // plane_num -for plane_ind in range(plane_num): - print('\nprocessing plane: {}'.format(plane_ind)) - curr_fns = fns[plane_ind::plane_num] - - total_frames_down = len(curr_fns) // temporal_downsample_rate - curr_fns = curr_fns[: total_frames_down * temporal_downsample_rate].reshape((total_frames_down, temporal_downsample_rate)) - - # print curr_fns - - print('current file ind: 000') - curr_file_ind = 0 - curr_frame_ind = 0 - curr_mov = [] - - for fgs in curr_fns: - - curr_frame = np.mean([tf.imread(os.path.join(data_folder, fn)) for fn in fgs], axis=0).astype(np.int16) - curr_frame = curr_frame.transpose()[::-1, ::-1] - - if curr_frame_ind < frame_each_file: - curr_mov.append(curr_frame) - curr_frame_ind = curr_frame_ind + 1 - else: - curr_mov = np.array(curr_mov, dtype=np.int16) - save_name = 'plane{}_{:03d}.tif'.format(plane_ind, curr_file_ind) - tf.imsave(os.path.join(save_folders[plane_ind], save_name), curr_mov) - curr_file_ind += 1 - curr_frame_ind = 1 - curr_mov = [curr_frame] - print('current file ind: {:03d}'.format(curr_file_ind)) - - curr_mov = np.array(curr_mov, dtype=np.int16) - save_name = 'plane{}_{:03d}.tif'.format(plane_ind, curr_file_ind) - tf.imsave(os.path.join(save_folders[plane_ind], save_name), curr_mov) - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/001_get_vasculature_map.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/001_get_vasculature_map.py deleted file mode 100644 index d714731..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/001_get_vasculature_map.py +++ /dev/null @@ -1,37 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import skimage.io as io -import matplotlib.pyplot as plt -import corticalmapping.core.ImageAnalysis as ia - -vasmap_wf_path = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180328-M360495-deepscope\Widefield.tif" - -vasmap_2p_zoom1_path = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180328-M360495-deepscope\01\01_00001.tif" - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -vasmap_wf = io.imread(vasmap_wf_path, as_grey=True) -vasmap_wf = vasmap_wf.transpose()[::-1, ::-1] - -vasmap_2p_zoom1 = tf.imread(vasmap_2p_zoom1_path).astype(np.float32) -vasmap_2p_zoom1 = np.mean(vasmap_2p_zoom1, axis=0) -vasmap_2p_zoom1 = vasmap_2p_zoom1.transpose()[::-1, ::-1] - -f = plt.figure(figsize=(12, 5)) -ax_wf = f.add_subplot(121) -ax_wf.imshow(ia.array_nor(vasmap_wf), vmin=0., vmax=1., cmap='gray', interpolation='nearest') -ax_wf.set_title('vasmap wide field') -ax_wf.set_axis_off() -ax_2p = f.add_subplot(122) -ax_2p.imshow(ia.array_nor(vasmap_2p_zoom1), vmin=0., vmax=0.15, cmap='gray', interpolation='nearest') -ax_2p.set_title('vasmap 2p zoom1') -ax_2p.set_axis_off() - -plt.show() - -tf.imsave('vasmap_wf.tif', vasmap_wf) -tf.imsave('vasmap_2p_zoom1.tif', vasmap_2p_zoom1) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/010_motion_correction.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/010_motion_correction.py deleted file mode 100644 index 906f6d7..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/010_motion_correction.py +++ /dev/null @@ -1,46 +0,0 @@ -import os -import stia.motion_correction as mc - -def run(): - - data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180328-M360495-deepscope\04\04_" - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - plane_ns = [p for p in os.listdir(data_folder) if os.path.isdir(os.path.join(data_folder, p))] - plane_ns.sort() - print('planes:') - print('\n'.join(plane_ns)) - - for plane_n in plane_ns: - print('\nprocessing plane: {}'.format(plane_n)) - plane_folder = os.path.join(data_folder, plane_n) - f_paths, _ = mc.motion_correction(input_folder=plane_folder, - input_path_identifier='.tif', - process_num=3, - output_folder=os.path.join(plane_folder, 'corrected'), - anchor_frame_ind_chunk=10, - anchor_frame_ind_projection=0, - iteration_chunk=10, - iteration_projection=10, - max_offset_chunk=(50., 50.), - max_offset_projection=(50., 50.), - align_func=mc.phase_correlation, - preprocessing_type=0, - fill_value=0.) - - offsets_path = os.path.join(plane_folder, 'corrected', 'correction_offsets.hdf5') - - mc.apply_correction_offsets(offsets_path=offsets_path, - path_pairs=zip(f_paths, f_paths), - output_folder=os.path.join(plane_folder, 'corrected'), - process_num=3, - fill_value=0., - avi_downsample_rate=20, - is_equalizing_histogram=True) - -if __name__ == "__main__": - run() - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/020_downsample_from_server.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/020_downsample_from_server.py deleted file mode 100644 index 9fa58d7..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/020_downsample_from_server.py +++ /dev/null @@ -1,45 +0,0 @@ -import os -import numpy as np -import tifffile as tf - -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180328-M360495-deepscope\04\04_" -xy_downsample_rate = 2 -t_downsample_rate = 10 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -plane_ns = [f for f in os.listdir(data_folder) if os.path.isdir(os.path.join(data_folder, f)) and f[:5] == 'plane'] -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('\nprocessing plane: {}'.format(plane_n)) - plane_folder = os.path.join(data_folder, plane_n, 'corrected') - - f_ns = [f for f in os.listdir(plane_folder) if f[-14:] == '_corrected.tif'] - f_ns.sort() - print('\n'.join(f_ns)) - - mov_d = [] - - for f_n in f_ns: - print('processing {} ...'.format(f_n)) - curr_mov = tf.imread(os.path.join(plane_folder, f_n)) - curr_mov_d = ia.rigid_transform_cv2(img=curr_mov, zoom=(1. / xy_downsample_rate)) - curr_mov_d = ia.z_downsample(curr_mov_d, downSampleRate=t_downsample_rate) - mov_d.append(curr_mov_d) - - mov_d = np.concatenate(mov_d, axis=0) - save_n = os.path.split(data_folder)[1] + '_' + plane_n + '_downsampled.tif' - save_folder = os.path.join(curr_folder, plane_n) - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - tf.imsave(os.path.join(save_folder, save_n), mov_d) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/030_get_movie_data.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/030_get_movie_data.py deleted file mode 100644 index ba3ee27..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/030_get_movie_data.py +++ /dev/null @@ -1,65 +0,0 @@ -import os -import h5py -import numpy as np -import skimage.external.tifffile as tf - -file_prefix = '180328_M360495_04' -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180328-M360495-deepscope\04\04_" - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -plane_fns = [f for f in os.listdir(data_folder) if f[:5] == 'plane'] -plane_fns.sort() -print('\n'.join(plane_fns)) - -data_f = h5py.File(file_prefix + '_2p_movies.hdf5') - -for plane_fn in plane_fns: - print('\nprocessing {} ...'.format(plane_fn)) - plane_folder = os.path.join(data_folder, plane_fn, 'corrected') - mov_fns = [f for f in os.listdir(plane_folder) if f[-14:] == '_corrected.tif'] - mov_fns.sort() - print('\n'.join(mov_fns)) - - frame_num_tot = 0 - x = None - y = None - z = 0 - for mov_fn in mov_fns: - print('reading {} ...'.format(mov_fn)) - curr_z, curr_y, curr_x = tf.imread(os.path.join(plane_folder, mov_fn)).shape - - if y is None: - y = curr_y - else: - if y != curr_y: - raise ValueError('y dimension ({}) of file "{}" does not agree with previous file(s) ({}).' - .format(curr_y, mov_fn, y)) - - if x is None: - x = curr_x - else: - if x != curr_x: - raise ValueError('x dimension ({}) of file "{}" does not agree with previous file(s) ({}).' - .format(curr_x, mov_fn, x)) - - z = z + curr_z - - dset = data_f.create_dataset(plane_fn, (z, y, x), dtype=np.int16, compression='lzf') - - start_frame = 0 - end_frame = 0 - for mov_fn in mov_fns: - print('reading {} ...'.format(mov_fn)) - curr_mov = tf.imread(os.path.join(plane_folder, mov_fn)) - end_frame = start_frame + curr_mov.shape[0] - dset[start_frame : end_frame] = curr_mov - start_frame = end_frame - - dset.attrs['conversion'] = 1. - dset.attrs['resolution'] = 1. - dset.attrs['unit'] = 'arbiturary_unit' - -data_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/045_get_mmap_files_for_caiman_from_tiff.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/045_get_mmap_files_for_caiman_from_tiff.py deleted file mode 100644 index 3d6f832..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/045_get_mmap_files_for_caiman_from_tiff.py +++ /dev/null @@ -1,54 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import h5py - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180328-M360495-deepscope\04\04_" -base_name = '180328_M360495_04' -t_downsample_rate = 5 - -plane_ns = [p for p in os.listdir(data_folder) if os.path.isdir(os.path.join(data_folder, p))] -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('\nprocessing {} ...'.format(plane_n)) - - plane_folder = os.path.join(data_folder, plane_n, 'corrected') - os.chdir(plane_folder) - - f_ns = [f for f in os.listdir(plane_folder) if f[-14:] == '_corrected.tif'] - f_ns.sort() - print('\n'.join(f_ns)) - - mov_join = [] - for f_n in f_ns: - - curr_mov = tf.imread(os.path.join(plane_folder, f_n)) - - if curr_mov.shape[0] % t_downsample_rate != 0: - print('the frame number of {} ({}) is not divisible by t_downsample_rate ({}).' - .format(f_n, curr_mov.shape[0], t_downsample_rate)) - - curr_mov_d = ia.z_downsample(curr_mov, downSampleRate=t_downsample_rate) - mov_join.append(curr_mov_d) - - mov_join = np.concatenate(mov_join, axis=0) - add_to_mov = 10 - np.amin(mov_join) - - save_name = '{}_d1_{}_d2_{}_d3_1_order_C_frames_{}_.mmap'\ - .format(base_name, mov_join.shape[2], mov_join.shape[1], mov_join.shape[0]) - - mov_join = mov_join.reshape((mov_join.shape[0], mov_join.shape[1] * mov_join.shape[2]), order='F').transpose() - mov_join_mmap = np.memmap(os.path.join(plane_folder, save_name), shape=mov_join.shape, order='C', - dtype=np.float32, mode='w+') - mov_join_mmap[:] = mov_join + add_to_mov - mov_join_mmap.flush() - del mov_join_mmap - - save_file = h5py.File(os.path.join(plane_folder, 'caiman_segmentation_results.hdf5')) - save_file['bias_added_to_movie'] = add_to_mov - save_file.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/050_show_mmap_movie.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/050_show_mmap_movie.py deleted file mode 100644 index 0cce508..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/050_show_mmap_movie.py +++ /dev/null @@ -1,39 +0,0 @@ -import sys; print('Python %s on %s' % (sys.version, sys.platform)) -sys.path.extend([r"E:\data\github_packages\CaImAn"]) - -import os -import numpy as np -import caiman as cm - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180212-M360495-deepscope\2p_movies\04\04_" -plane_n = 'plane0' - -plane_folder = os.path.join(data_folder, plane_n, 'corrected') -os.chdir(plane_folder) - -fn = [f for f in os.listdir(plane_folder) if f[-5:] == '.mmap'] -if len(fn) > 1: - print('\n'.join(fn)) - raise LookupError('more than one file found.') -elif len(fn) == 0: - raise LookupError('no file found.') -else: - fn = fn[0] - -cm.load(fn).play(fr=30,magnification=1,gain=2.) - -# fn_parts = fn.split('_') -# d1 = int(fn_parts[fn_parts.index('d1') + 1]) # column, x -# d2 = int(fn_parts[fn_parts.index('d2') + 1]) # row, y -# d3 = int(fn_parts[fn_parts.index('d3') + 1]) # channel -# d4 = int(fn_parts[fn_parts.index('frames') + 1]) # frame, T -# order = fn_parts[fn_parts.index('order') + 1] -# -# print('playing {} ...'.format(fn)) -# -# mov = np.memmap(filename=fn, shape=(d1, d2, d4), order=order, dtype=np.float32, mode='r') -# mov = mov.transpose((2, 1, 0)) -# -# cm.movie(mov).play(fr=30,magnification=1,gain=2.) - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/060_caiman_segmentation.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/060_caiman_segmentation.py deleted file mode 100644 index 3623654..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/060_caiman_segmentation.py +++ /dev/null @@ -1,116 +0,0 @@ -import sys; print('Python %s on %s' % (sys.version, sys.platform)) -sys.path.extend([r"E:\data\github_packages\CaImAn"]) - -import os -import numpy as np -import caiman as cm -import matplotlib.pyplot as plt -from caiman.source_extraction.cnmf import cnmf as cnmf -import h5py -from shutil import copyfile - -def run(): - - data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180328-M360495-deepscope\04\04_" - play_movie = False - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - - plane_ns = [f for f in os.listdir(data_folder) if os.path.isdir(f) and f[:5] == 'plane'] - plane_ns.sort() - print('planes:') - print('\n'.join(plane_ns)) - - # %% start cluster - c, dview, n_processes = cm.cluster.setup_cluster(backend='local', n_processes=3, single_thread=False) - - for plane_n in plane_ns: - - print('\nsegmenting plane: {}'.format(plane_n)) - - plane_folder = os.path.join(data_folder, plane_n, 'corrected') - os.chdir(plane_folder) - - fn = [f for f in os.listdir(plane_folder) if f[-5:] == '.mmap'] - if len(fn) > 1: - print('\n'.join(fn)) - raise LookupError('more than one file found.') - elif len(fn) == 0: - raise LookupError('no file found.') - else: - fn = fn[0] - - fn_parts = fn.split('_') - d1 = int(fn_parts[fn_parts.index('d1') + 1]) # column, x - d2 = int(fn_parts[fn_parts.index('d2') + 1]) # row, y - d3 = int(fn_parts[fn_parts.index('d3') + 1]) # channel - d4 = int(fn_parts[fn_parts.index('frames') + 1]) # frame, T - order = fn_parts[fn_parts.index('order') + 1] - - print('playing {} ...'.format(fn)) - - mov = np.memmap(filename=fn, shape=(d1, d2, d4), order=order, dtype=np.float32, mode='r') - mov = mov.transpose((2, 1, 0)) - - print('shape of joined movie: {}.'.format(mov.shape)) - - #%% play movie, press q to quit - if play_movie: - cm.movie(mov).play(fr=50,magnification=1,gain=2.) - - #%% movie cannot be negative! - mov_min = float(np.amin(mov)) - print('minimum pixel value: {}.'.format(mov_min)) - if mov_min < 0: - raise Exception('Movie too negative, add_to_movie should be larger') - - #%% correlation image. From here infer neuron size and density - Cn = cm.movie(mov).local_correlations(swap_dim=False) - plt.imshow(Cn, cmap='gray') - plt.show() - - K = 100 # number of neurons expected per patch - gSig = [5, 5] # expected half size of neurons - merge_thresh = 0.9 # merging threshold, max correlation allowed - p = 2 # order of the autoregressive system - cnm = cnmf.CNMF(n_processes, - k=10, # number of neurons expected per patch - gSig=[5, 5] , # expected half size of neurons - merge_thresh=0.9, # merging threshold, max correlation allowed - p=2, # order of the autoregressive system - dview=dview, - Ain=None, - method_deconvolution='oasis', - rolling_sum = False, - method_init='sparse_nmf', - alpha_snmf=10e1, - ssub=1, - tsub=1, - p_ssub=1, - p_tsub=1, - rf=256, # half-size of the patches in pixels - border_pix=20, - do_merge=False) - cnm = cnm.fit(mov) - A, C, b, f, YrA, sn = cnm.A, cnm.C, cnm.b, cnm.f, cnm.YrA, cnm.sn - #%% - crd = cm.utils.visualization.plot_contours(cnm.A, Cn) - plt.show() - # input("Press enter to continue ...") - - roi_num = cnm.A.shape[1] - save_fn = h5py.File('caiman_segmentation_results.hdf5') - bias = save_fn['bias_added_to_movie'].value - save_fn['masks'] = np.array(cnm.A.todense()).T.reshape((roi_num, 512, 512), order='F') - save_fn['traces'] = cnm.C - bias - save_fn.close() - - copyfile(os.path.join(plane_folder, 'caiman_segmentation_results.hdf5'), - os.path.join(curr_folder, plane_n, 'caiman_segmentation_results.hdf5')) - - plt.close('all') - - -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/070_generate_nwb.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/070_generate_nwb.py deleted file mode 100644 index 0703a70..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/070_generate_nwb.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import corticalmapping.NwbTools as nt - -date_recorded = '180328' -mouse_id = '360495' -sess_num = '04' - -experimenter = 'Jun' -genotype = 'Vipr2-IRES2-Cre-neo' -sex = 'male' -age = '173' -indicator = 'GCaMP6s' -imaging_rate = 37. -imaging_depth = '250/200/150/100/50 microns' -imaging_location = 'visual cortex' -imaging_device = 'DeepScope' -imaging_excitation_lambda = '940 nanometers' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -notebook_path = os.path.join(curr_folder, 'notebook.txt') -with open(notebook_path, 'r') as ff: - notes = ff.read() - -general = nt.DEFAULT_GENERAL -general['experimenter'] = experimenter -general['subject']['subject_id'] = mouse_id -general['subject']['genotype'] = genotype -general['subject']['sex'] = sex -general['subject']['age'] = age -general['optophysiology'].update({'imaging_plane_1': {}}) -general['optophysiology']['imaging_plane_1'].update({'indicator': indicator}) -general['optophysiology']['imaging_plane_1'].update({'imaging_rate': imaging_rate}) -general['optophysiology']['imaging_plane_1'].update({'imaging_depth': imaging_depth}) -general['optophysiology']['imaging_plane_1'].update({'location': imaging_location}) -general['optophysiology']['imaging_plane_1'].update({'device': imaging_device}) -general['optophysiology']['imaging_plane_1'].update({'excitation_lambda': imaging_excitation_lambda}) -general['notes'] = notes - -file_name = date_recorded + '_M' + mouse_id + '_' + sess_num + '.nwb' - -rf = nt.RecordedFile(os.path.join(curr_folder, file_name), identifier=file_name[:-4], description='') -rf.add_general(general=general) -rf.close() - - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/071_add_vasmaps.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/071_add_vasmaps.py deleted file mode 100644 index d933a14..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/071_add_vasmaps.py +++ /dev/null @@ -1,36 +0,0 @@ -import os -import corticalmapping.NwbTools as nt -import matplotlib.pyplot as plt -import tifffile as tf - - -vasmap_name_wf = 'vasmap_wf.tif' -vasmap_name_2p_zoom1 = 'vasmap_2p_zoom1.tif' -# vasmap_name_2p_zoom4 = 'vasmap_2p_zoom4.tif' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -vasmap_wf = tf.imread(vasmap_name_wf) -vasmap_2p_zoom1 = tf.imread(vasmap_name_2p_zoom1) -# vasmap_2p_zoom4 = tf.imread(vasmap_name_2p_zoom4) - -# f = plt.figure(figsize=(15, 7)) -# ax1 = f.add_subplot(121) -# ax1.imshow(vasmap_wf, cmap='gray', interpolation='nearest') -# ax1.set_title('wide field surface vasculature') -# ax2 = f.add_subplot(122) -# ax2.imshow(vasmap_2p_zoom1, cmap='gray', interpolation='nearest') -# ax2.set_title('two photon surface vasculature') -# plt.show() - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] - -nwb_f = nt.RecordedFile(nwb_fn) -nwb_f.add_acquisition_image('surface_vas_map_wf', vasmap_wf, - description='wide field surface vasculature map through cranial window') -nwb_f.add_acquisition_image('surface_vas_map_2p_zoom1', vasmap_2p_zoom1, - description='2-photon surface vasculature map through cranial window, zoom 1') -# nwb_f.add_acquisition_image('surface_vas_map_2p_zoom2', vasmap_2p_zoom4, -# description='2-photon surface vasculature map through cranial window, zoom 4') -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/080_add_sync_data.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/080_add_sync_data.py deleted file mode 100644 index 642dfb4..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/080_add_sync_data.py +++ /dev/null @@ -1,23 +0,0 @@ -import os -import corticalmapping.NwbTools as nt - -record_date = '180328' -mouse_id = '360495' -session_id = '04' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = record_date + '_M' + mouse_id + '_' + session_id + '.nwb' - -sync_fn = [f for f in os.listdir(curr_folder) if f[-3:] == '.h5' and record_date in f and 'M' + mouse_id in f] -if len(sync_fn) == 0: - raise LookupError('Did not find sync .h5 file.') -elif len(sync_fn) > 1: - raise LookupError('More than one sync .h5 files found.') -else: - sync_fn = sync_fn[0] - -nwb_f = nt.RecordedFile(nwb_fn) -nwb_f.add_sync_data(sync_fn) -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/090_add_image_data.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/090_add_image_data.py deleted file mode 100644 index 1b62215..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/090_add_image_data.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -import h5py -import corticalmapping.NwbTools as nt - -dset_ns = ['plane0', 'plane1', 'plane2', 'plane3', 'plane4'] -imaging_depths = [250, 200, 150, 100, 50] -temporal_downsample_rate = 1 -pixel_size = 0.0000002 # meter, 0.2 micron, deepscope 12K Hz scanner, zoom 4 - -description = '2-photon imaging data' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) -ts_2p_tot = nwb_f.file_pointer['/acquisition/timeseries/digital_2p_vsync_rise/timestamps'].value -print('total 2p timestamps count: {}'.format(len(ts_2p_tot))) - -mov_fn = os.path.splitext(nwb_fn)[0] + '_2p_movies.hdf5' -mov_f = h5py.File(mov_fn, 'r') - -for mov_i, mov_dn in enumerate(dset_ns): - - if mov_dn is not None: - - curr_dset = mov_f[mov_dn] - if mov_dn is not None: - mov_ts = ts_2p_tot[mov_i::len(dset_ns)] - print('\n{}: total 2p timestamps count: {}'.format(mov_dn, len(mov_ts))) - - mov_ts_d = mov_ts[::temporal_downsample_rate] - print('{}: downsampled 2p timestamps count: {}'.format(mov_dn, len(mov_ts_d))) - print('{}: downsampled 2p movie frame num: {}'.format(mov_dn, curr_dset.shape[0])) - - # if len(mov_ts_d) == curr_dset.shape[0]: - # pass - # elif len(mov_ts_d) == curr_dset.shape[0] + 1: - # mov_ts_d = mov_ts_d[0: -1] - # else: - # raise ValueError('the timestamp count of {} movie ({}) does not equal (or is not greater by one) ' - # 'the frame cound in the movie ({})'.format(mov_dn, len(mov_ts_d), curr_dset.shape[0])) - mov_ts_d = mov_ts_d[:curr_dset.shape[0]] - - curr_description = '{}. Imaging depth: {} micron.'.format(description, imaging_depths[mov_i]) - nwb_f.add_acquired_image_series_as_remote_link('2p_movie_' + mov_dn, image_file_path=mov_fn, - dataset_path=mov_dn, timestamps=mov_ts_d, - description=curr_description, comments='', - data_format='zyx', pixel_size=[pixel_size, pixel_size], - pixel_size_unit='meter') - -mov_f.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/110_add_motion_correction_module.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/110_add_motion_correction_module.py deleted file mode 100644 index a75dd4a..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/110_add_motion_correction_module.py +++ /dev/null @@ -1,56 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import h5py -import corticalmapping.NwbTools as nt - -movie_2p_fn = '180328_M360495_04_2p_movies.hdf5' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -input_parameters = [] - -for i in range(5): - - plane_n = 'plane{}'.format(i) - - offsets_path = os.path.join(plane_n, 'correction_offsets.hdf5') - offsets_f = h5py.File(offsets_path) - offsets_keys = offsets_f.keys() - if 'path_list' in offsets_keys: - offsets_keys.remove('path_list') - - offsets_keys.sort() - offsets = [] - for offsets_key in offsets_keys: - offsets.append(offsets_f[offsets_key].value) - offsets = np.concatenate(offsets, axis=0) - offsets = np.array(zip(offsets[:, 1], offsets[:, 0])) - offsets_f.close() - - mean_projection = tf.imread(os.path.join(plane_n, 'corrected_mean_projection.tif')) - max_projection = tf.imread(os.path.join(plane_n, 'corrected_max_projection.tif')) - - input_dict = {'field_name': plane_n, - 'original_timeseries_path': '/acquisition/timeseries/2p_movie_plane' + str(i), - 'corrected_file_path': movie_2p_fn, - 'corrected_dataset_path': plane_n, - 'xy_translation_offsets': offsets, - 'mean_projection': mean_projection, - 'max_projection': max_projection, - 'description': '', - 'comments': '', - 'source': ''} - - input_parameters.append(input_dict) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -nwb_f.add_muliple_dataset_to_motion_correction_module(input_parameters=input_parameters, - module_name='motion_correction') -nwb_f.close() - - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/120_add_rois_and_traces_caiman_segmentation.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/120_add_rois_and_traces_caiman_segmentation.py deleted file mode 100644 index 68a3f3c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/120_add_rois_and_traces_caiman_segmentation.py +++ /dev/null @@ -1,162 +0,0 @@ -import os -import h5py -import numpy as np -import matplotlib.pyplot as plt -import tifffile as tf -import corticalmapping.NwbTools as nt -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -plane_ns = ['plane0', 'plane1', 'plane2', 'plane3', 'plane4'] -plane_depths = [250, 200, 150, 100, 50] - -def add_rois_and_traces(data_folder, nwb_f, plane_n, imaging_depth, - mov_path='/processing/motion_correction/MotionCorrection'): - - mov_grp = nwb_f.file_pointer[mov_path + '/' + plane_n + '/corrected'] - - data_f = h5py.File(os.path.join(data_folder, 'rois_and_traces.hdf5'), 'r') - mask_arr_c = data_f['masks_center'].value - mask_arr_s = data_f['masks_surround'].value - traces_center_raw = data_f['traces_center_raw'].value - # traces_center_demixed = data_f['traces_center_demixed'].value - traces_center_subtracted = data_f['traces_center_subtracted'].value - # traces_center_dff = data_f['traces_center_dff'].value - traces_surround_raw = data_f['traces_surround_raw'].value - neuropil_r = data_f['neuropil_r'].value - neuropil_err = data_f['neuropil_err'].value - data_f.close() - - - if traces_center_raw.shape[1] != mov_grp['num_samples'].value: - raise ValueError('number of trace time points ({}) does not match frame number of ' - 'corresponding movie ({}).'.format(traces_center_raw.shape[0], mov_grp['num_samples'].value)) - - rf_img = tf.imread(os.path.join(data_folder, 'corrected_mean_projection.tif')) - - print 'adding segmentation results ...' - rt_mo = nwb_f.create_module('rois_and_traces_' + plane_n) - rt_mo.set_value('imaging_depth_micron', imaging_depth) - is_if = rt_mo.create_interface('ImageSegmentation') - is_if.create_imaging_plane('imaging_plane', description='') - is_if.add_reference_image('imaging_plane', 'mean_projection', rf_img) - - for i in range(mask_arr_c.shape[0]): - curr_cen = mask_arr_c[i] - curr_cen_n = 'roi_' + ft.int2str(i, 4) - curr_cen_roi = ia.WeightedROI(curr_cen) - curr_cen_pixels_yx = curr_cen_roi.get_pixel_array() - curr_cen_pixels_xy = np.array([curr_cen_pixels_yx[:, 1], curr_cen_pixels_yx[:, 0]]).transpose() - is_if.add_roi_mask_pixels(image_plane='imaging_plane', roi_name=curr_cen_n, desc='', - pixel_list=curr_cen_pixels_xy, weights=curr_cen_roi.weights, width=512, height=512) - - curr_sur = mask_arr_s[i] - curr_sur_n = 'surround_' + ft.int2str(i, 4) - curr_sur_roi = ia.ROI(curr_sur) - curr_sur_pixels_yx = curr_sur_roi.get_pixel_array() - curr_sur_pixels_xy = np.array([curr_sur_pixels_yx[:, 1], curr_sur_pixels_yx[:, 0]]).transpose() - is_if.add_roi_mask_pixels(image_plane='imaging_plane', roi_name=curr_sur_n, desc='', - pixel_list=curr_sur_pixels_xy, weights=None, width=512, height=512) - is_if.finalize() - - - - trace_f_if = rt_mo.create_interface('Fluorescence') - seg_if_path = '/processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane' - # print seg_if_path - ts_path = mov_path + '/' + plane_n + '/corrected' - - print 'adding center fluorescence raw' - trace_raw_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_raw') - trace_raw_ts.set_data(traces_center_raw, unit='au', conversion=np.nan, resolution=np.nan) - trace_raw_ts.set_value('data_format', 'roi (row) x time (column)') - trace_raw_ts.set_value('data_range', '[-8192, 8191]') - trace_raw_ts.set_description('fluorescence traces extracted from the center region of each roi') - trace_raw_ts.set_time_as_link(ts_path) - trace_raw_ts.set_value_as_link('segmentation_interface', seg_if_path) - roi_names = ['roi_' + ft.int2str(ind, 4) for ind in range(traces_center_raw.shape[0])] - trace_raw_ts.set_value('roi_names', roi_names) - trace_raw_ts.set_value('num_samples', traces_center_raw.shape[1]) - trace_f_if.add_timeseries(trace_raw_ts) - trace_raw_ts.finalize() - - print 'adding neuropil fluorescence raw' - trace_sur_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_surround_raw') - trace_sur_ts.set_data(traces_surround_raw, unit='au', conversion=np.nan, resolution=np.nan) - trace_sur_ts.set_value('data_format', 'roi (row) x time (column)') - trace_sur_ts.set_value('data_range', '[-8192, 8191]') - trace_sur_ts.set_description('neuropil traces extracted from the surroud region of each roi') - trace_sur_ts.set_time_as_link(ts_path) - trace_sur_ts.set_value_as_link('segmentation_interface', seg_if_path) - sur_names = ['surround_' + ft.int2str(ind, 4) for ind in range(traces_center_raw.shape[0])] - trace_sur_ts.set_value('roi_names', sur_names) - trace_sur_ts.set_value('num_samples', traces_surround_raw.shape[1]) - trace_f_if.add_timeseries(trace_sur_ts) - trace_sur_ts.finalize() - - roi_center_n_path = '/processing/rois_and_traces_' + plane_n + '/Fluorescence/f_center_raw/roi_names' - # print 'adding center fluorescence demixed' - # trace_demix_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_demixed') - # trace_demix_ts.set_data(traces_center_demixed, unit='au', conversion=np.nan, resolution=np.nan) - # trace_demix_ts.set_value('data_format', 'roi (row) x time (column)') - # trace_demix_ts.set_description('center traces after overlapping demixing for each roi') - # trace_demix_ts.set_time_as_link(mov_path + '/' + plane_n + '/corrected') - # trace_demix_ts.set_value_as_link('segmentation_interface', seg_if_path) - # trace_demix_ts.set_value('roi_names', roi_names) - # trace_demix_ts.set_value('num_samples', traces_center_demixed.shape[1]) - # trace_f_if.add_timeseries(trace_demix_ts) - # trace_demix_ts.finalize() - - print 'adding center fluorescence after neuropil subtraction' - trace_sub_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_subtracted') - trace_sub_ts.set_data(traces_center_subtracted, unit='au', conversion=np.nan, resolution=np.nan) - trace_sub_ts.set_value('data_format', 'roi (row) x time (column)') - trace_sub_ts.set_description('center traces after overlap demixing and neuropil subtraction for each roi') - trace_sub_ts.set_time_as_link(mov_path + '/' + plane_n + '/corrected') - trace_sub_ts.set_value_as_link('segmentation_interface', seg_if_path) - trace_sub_ts.set_value_as_link('roi_names', roi_center_n_path) - trace_sub_ts.set_value('num_samples', traces_center_subtracted.shape[1]) - trace_sub_ts.set_value('r', neuropil_r, dtype='float32') - trace_sub_ts.set_value('rmse', neuropil_err, dtype='float32') - trace_sub_ts.set_comments('value "r": neuropil contribution ratio for each roi. ' - 'value "rmse": RMS error of neuropil subtraction for each roi') - trace_f_if.add_timeseries(trace_sub_ts) - trace_sub_ts.finalize() - - trace_f_if.finalize() - - # print 'adding global dF/F traces for each roi' - # trace_dff_if = rt_mo.create_interface('DfOverF') - # - # trace_dff_ts = nwb_f.create_timeseries('RoiResponseSeries', 'dff_center') - # trace_dff_ts.set_data(traces_center_dff, unit='au', conversion=np.nan, resolution=np.nan) - # trace_dff_ts.set_value('data_format', 'roi (row) x time (column)') - # trace_dff_ts.set_description('global df/f traces for each roi center, input fluorescence is the trace after demixing' - # ' and neuropil subtraction. global df/f is calculated by ' - # 'allensdk.brain_observatory.dff.compute_dff() function.') - # trace_dff_ts.set_time_as_link(ts_path) - # trace_dff_ts.set_value_as_link('segmentation_interface', seg_if_path) - # trace_dff_ts.set_value('roi_names', roi_names) - # trace_dff_ts.set_value('num_samples', traces_center_dff.shape[1]) - # trace_dff_if.add_timeseries(trace_dff_ts) - # trace_dff_ts.finalize() - # trace_dff_if.finalize() - - rt_mo.finalize() - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -for plane_i, plane_n in enumerate(plane_ns): - - print('\n\n' + plane_n) - - data_folder = os.path.join(curr_folder, plane_n) - add_rois_and_traces(data_folder, nwb_f, plane_n, imaging_depth=plane_depths[plane_i]) - -nwb_f.close() - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/130_add_visual_stimuli_retinotopic_mapping.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/130_add_visual_stimuli_retinotopic_mapping.py deleted file mode 100644 index 0414239..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/130_add_visual_stimuli_retinotopic_mapping.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -import retinotopic_mapping.DisplayLogAnalysis as dla -import corticalmapping.NwbTools as nt - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -stim_pkl_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.pkl'][0] -stim_log = dla.DisplayLogAnalyzer(stim_pkl_fn) - -nwb_f.add_visual_display_log_retinotopic_mapping(stim_log=stim_log) -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/135_get_photodiode_onset_timestamps.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/135_get_photodiode_onset_timestamps.py deleted file mode 100644 index e43be8b..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/135_get_photodiode_onset_timestamps.py +++ /dev/null @@ -1,45 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.HighLevel as hl - -# photodiode -digitizeThr = 0.2 -filterSize = 0.01 -segmentThr = 0.02 -smallestInterval = 0.03 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] - -nwb_f = nt.RecordedFile(nwb_fn) -pd, pd_t = nwb_f.get_analog_data(ch_n='analog_photodiode') -fs = 1. / np.mean(np.diff(pd_t)) -# print fs - -pd_onsets = hl.segmentPhotodiodeSignal(pd, digitizeThr=digitizeThr, filterSize=filterSize, - segmentThr=segmentThr, Fs=fs, smallestInterval=smallestInterval) - -raw_input('press enter to continue ...') - -pdo_ts = nwb_f.create_timeseries('TimeSeries', 'digital_photodiode_rise', modality='other') -pdo_ts.set_time(pd_onsets) -pdo_ts.set_data([], unit='', conversion=np.nan, resolution=np.nan) -pdo_ts.set_value('digitize_threshold', digitizeThr) -pdo_ts.set_value('filter_size', filterSize) -pdo_ts.set_value('segment_threshold', segmentThr) -pdo_ts.set_value('smallest_interval', smallestInterval) -pdo_ts.set_description('Real Timestamps (master acquisition clock) of photodiode onset. ' - 'Extracted from analog photodiode signal by the function:' - 'corticalmapping.HighLevel.segmentPhotodiodeSignal() using parameters saved in the' - 'current timeseries.') -pdo_ts.set_path('/analysis') -pdo_ts.finalize() - -nwb_f.close() - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/140_analyze_analog_photodiode_onsets.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/140_analyze_analog_photodiode_onsets.py deleted file mode 100644 index bd58c7e..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/140_analyze_analog_photodiode_onsets.py +++ /dev/null @@ -1,39 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import retinotopic_mapping.DisplayLogAnalysis as dla -import corticalmapping.core.TimingAnalysis as ta - - -pd_ts_pd_path = 'analysis/digital_photodiode_rise' -vsync_frame_path = 'acquisition/timeseries/digital_stim_vsync_rise' -pd_thr = -0.5 # this is color threshold, not analog photodiode threshold -ccg_t_range = (0., 0.1) -ccg_bins = 100 -is_plot = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -stim_pkl_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.pkl'][0] -stim_log = dla.DisplayLogAnalyzer(stim_pkl_fn) - -# get display lag -display_delay = nwb_f.get_display_delay_retinotopic_mapping(stim_log=stim_log, indicator_color_thr=pd_thr, - ccg_t_range=ccg_t_range, ccg_bins=ccg_bins, - is_plot=is_plot, pd_onset_ts_path=pd_ts_pd_path, - vsync_frame_ts_path=vsync_frame_path) - -# analyze photodiode onset -stim_dict = stim_log.get_stim_dict() -pd_onsets_seq = stim_log.analyze_photodiode_onsets_sequential(stim_dict=stim_dict, pd_thr=pd_thr) -pd_onsets_com = stim_log.analyze_photodiode_onsets_combined(pd_onsets_seq=pd_onsets_seq, - is_dgc_blocked=True) -nwb_f.add_photodiode_onsets_combined_retinotopic_mapping(pd_onsets_com=pd_onsets_com, - display_delay=display_delay, - vsync_frame_path=vsync_frame_path) -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/140_analyze_digital_photodiode_onsets.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/140_analyze_digital_photodiode_onsets.py deleted file mode 100644 index 0f5da9f..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/140_analyze_digital_photodiode_onsets.py +++ /dev/null @@ -1,39 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import retinotopic_mapping.DisplayLogAnalysis as dla -import corticalmapping.core.TimingAnalysis as ta - - -pd_ts_pd_path = 'acquisition/timeseries/digital_photodiode_rise' -vsync_frame_path = 'acquisition/timeseries/digital_stim_vsync_rise' -pd_thr = 0.5 # this is color threshold, not analog photodiode threshold -ccg_t_range = (0., 0.1) -ccg_bins = 100 -is_plot = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -stim_pkl_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.pkl'][0] -stim_log = dla.DisplayLogAnalyzer(stim_pkl_fn) - -# get display lag -display_delay = nwb_f.get_display_delay_retinotopic_mapping(stim_log=stim_log, indicator_color_thr=pd_thr, - ccg_t_range=ccg_t_range, ccg_bins=ccg_bins, - is_plot=is_plot, pd_onset_ts_path=pd_ts_pd_path, - vsync_frame_ts_path=vsync_frame_path) - -# analyze photodiode onset -stim_dict = stim_log.get_stim_dict() -pd_onsets_seq = stim_log.analyze_photodiode_onsets_sequential(stim_dict=stim_dict, pd_thr=pd_thr) -pd_onsets_com = stim_log.analyze_photodiode_onsets_combined(pd_onsets_seq=pd_onsets_seq, - is_dgc_blocked=True) -nwb_f.add_photodiode_onsets_combined_retinotopic_mapping(pd_onsets_com=pd_onsets_com, - display_delay=display_delay, - vsync_frame_path=vsync_frame_path) -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/150_get_STRFs.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/150_get_STRFs.py deleted file mode 100644 index 8642a33..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/150_get_STRFs.py +++ /dev/null @@ -1,96 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -stim_name = '001_LocallySparseNoiseRetinotopicMapping' -trace_source = 'f_center_subtracted' -start_time = -1. -end_time = 2. - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -probe_grp = nwb_f.file_pointer['analysis/photodiode_onsets/' + stim_name] -probe_ns = probe_grp.keys() -probe_ns.sort() - -probe_locations = [[float(pn[3: 9]), float(pn[13: 19])] for pn in probe_ns] -probe_signs = [float(pn[-2:]) for pn in probe_ns] -# print(probe_locations) - -plane_ns = nwb_f.file_pointer['processing'].keys() -plane_ns = [pn.split('_')[-1] for pn in plane_ns if 'rois_and_traces_plane' in pn] -plane_ns.sort() -print('\n'.join(plane_ns)) - -strf_grp = nwb_f.file_pointer['analysis'].create_group('STRFs') - -for plane_n in plane_ns: - print('\ngetting STRFs for {} ...'.format(plane_n)) - - roi_ns = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + - '/ImageSegmentation/imaging_plane/roi_list'].value - roi_ns = [rn for rn in roi_ns if rn[0: 4] == 'roi_'] - roi_ns.sort() - roi_num = len(roi_ns) - - plane_strf_grp = strf_grp.create_group(plane_n) - plane_traces = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + '/Fluorescence/' + - trace_source + '/data'].value - plane_trace_ts = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + '/Fluorescence/' + - trace_source + '/timestamps'].value - - plane_mean_frame_dur = np.mean(np.diff(plane_trace_ts)) - plane_chunk_frame_dur = int(np.ceil((end_time - start_time) / plane_mean_frame_dur)) - plane_chunk_frame_start = int(np.floor(start_time / plane_mean_frame_dur)) - plane_t = (np.arange(plane_chunk_frame_dur) + plane_chunk_frame_start) * plane_mean_frame_dur - print '{}: STRF time axis: \n{}'.format(plane_n, plane_t) - - plane_roi_traces = [] - trigger_ts = [] - - for probe_ind, probe_n in enumerate(probe_ns): - - probe_ts = probe_grp[probe_n]['pd_onset_ts_sec'].value - probe_traces = [] - probe_trigger_ts = [] - for curr_probe_ts in probe_ts: - curr_frame_start = ta.find_nearest(plane_trace_ts, curr_probe_ts) + plane_chunk_frame_start - curr_frame_end = curr_frame_start + plane_chunk_frame_dur - if curr_frame_start >= 0 and curr_frame_end <= len(plane_trace_ts): - probe_traces.append(plane_traces[:, curr_frame_start: curr_frame_end]) - probe_trigger_ts.append(curr_probe_ts) - - plane_roi_traces.append(np.array(probe_traces)) - trigger_ts.append(probe_trigger_ts) - print('probe: {} / {}; shape: {}'.format(probe_ind + 1, len(probe_ns), np.array(probe_traces).shape)) - - # plane_roi_traces = np.array(plane_roi_traces) - - print('saving ...') - for roi_ind in range(roi_num): - - print "roi: {} / {}".format(roi_ind + 1, roi_num) - curr_unit_traces = [pt[:, roi_ind, :] for pt in plane_roi_traces] - curr_unit_traces = [list(t) for t in curr_unit_traces] - curr_strf = sca.SpatialTemporalReceptiveField2(locations=probe_locations, - signs=probe_signs, - traces=curr_unit_traces, - trigger_ts=trigger_ts, - time=plane_t, - name='roi_{:04d}'.format(roi_ind), - trace_data_type=trace_source) - - curr_strf_grp = plane_strf_grp.create_group('strf_roi_{:04d}'.format(roi_ind)) - curr_strf.to_h5_group(curr_strf_grp) - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/160_get_drifting_grating_response_tables.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/160_get_drifting_grating_response_tables.py deleted file mode 100644 index 65ba54c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/160_get_drifting_grating_response_tables.py +++ /dev/null @@ -1,18 +0,0 @@ -import os -import h5py -import numpy as np -import corticalmapping.NwbTools as nt - -plane_ns = ['plane0', 'plane1', 'plane2', 'plane3', 'plane4'] -stim_name = '001_DriftingGratingCircleRetinotopicMapping' -t_win = [-1, 2.5] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -nwb_f.get_drifting_grating_response_table_retinotopic_mapping(stim_name=stim_name, time_window=t_win) - -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/170_plot_STRFs.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/170_plot_STRFs.py deleted file mode 100644 index 3de24ad..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/170_plot_STRFs.py +++ /dev/null @@ -1,61 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import h5py -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -save_folder = 'figures' -is_local_dff = True -is_add_to_traces = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -strf_grp = nwb_f['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File("{}/caiman_segmentation_results.hdf5".format(plane_n))['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - pdff = PdfPages(os.path.join(save_folder, 'STRFs_' + plane_n + '.pdf')) - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - - v_min, v_max = curr_strf_dff.get_data_range() - f = curr_strf_dff.plot_traces(yRange=(v_min, v_max * 1.1), figSize=(16, 10), - columnSpacing=0.002, rowSpacing=0.002) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/180_plot_zscore_RFs.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/180_plot_zscore_RFs.py deleted file mode 100644 index 73f92f3..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/180_plot_zscore_RFs.py +++ /dev/null @@ -1,68 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import h5py -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -save_folder = 'figures' -is_local_dff = True -zscore_range = [0., 4.] -t_window = [0., 1.] -is_add_to_traces = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -strf_grp = nwb_f['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File("{}/caiman_segmentation_results.hdf5".format(plane_n))['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - pdff = PdfPages(os.path.join(save_folder, 'zscore_RFs_' + plane_n + '.pdf')) - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - v_min, v_max = curr_strf_dff.get_data_range() - - rf_on, rf_off = curr_strf_dff.get_zscore_receptive_field(timeWindow=t_window) - f = plt.figure(figsize=(15, 4)) - ax_on = f.add_subplot(121) - rf_on.plot_rf(plot_axis=ax_on, is_colorbar=True, cmap='Reds', vmin=zscore_range[0], vmax=zscore_range[1]) - ax_off = f.add_subplot(122) - rf_off.plot_rf(plot_axis=ax_off, is_colorbar=True, cmap='Blues', vmin=zscore_range[0], vmax=zscore_range[1]) - plt.close() - - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/190_plot_RF_contours.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/190_plot_RF_contours.py deleted file mode 100644 index 1e7b31c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/190_plot_RF_contours.py +++ /dev/null @@ -1,112 +0,0 @@ -import os -import numpy as np -import h5py -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -roi_t_window = [0., 1.] -zscore_range = [0., 4.] -save_folder = 'figures' -is_add_to_traces = True - -# plot control -thr_ratio = 0.4 -filter_sigma = 1. -interpolate_rate = 5 -absolute_thr = 1.6 -level_num = 1 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'] -print('\n'.join(nwb_fn)) - -if len(nwb_fn) != 1: - raise LookupError - -nwb_fn = nwb_fn[0] -rff = h5py.File(nwb_fn, 'r') - -strf_grp = rff['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -X = None -Y = None - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File("{}/caiman_segmentation_results.hdf5".format(plane_n))['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - f_all = plt.figure(figsize=(10, 10)) - ax_all = f_all.add_subplot(111) - - pdff = PdfPages(os.path.join(save_folder, 'RF_contours_' + plane_n + '.pdf')) - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - rf_on, rf_off, _ = curr_strf_dff.get_zscore_thresholded_receptive_fields(timeWindow=roi_t_window, - thr_ratio=thr_ratio, - filter_sigma=filter_sigma, - interpolate_rate=interpolate_rate, - absolute_thr=absolute_thr) - - if X is None and Y is None: - X, Y = np.meshgrid(np.arange(len(rf_on.aziPos)), - np.arange(len(rf_on.altPos))) - - levels_on = [np.max(rf_on.get_weighted_mask().flat) * thr_ratio] - levels_off = [np.max(rf_off.get_weighted_mask().flat) * thr_ratio] - ax_all.contour(X, Y, rf_on.get_weighted_mask(), levels=levels_on, colors='r', lw=5) - ax_all.contour(X, Y, rf_off.get_weighted_mask(), levels=levels_off, colors='b', lw=5) - - f_single = plt.figure(figsize=(10, 10)) - ax_single = f_single.add_subplot(111) - ax_single.contour(X, Y, rf_on.get_weighted_mask(), levels=levels_on, colors='r', lw=5) - ax_single.contour(X, Y, rf_off.get_weighted_mask(), levels=levels_off, colors='b', lw=5) - ax_single.set_xticks(range(len(rf_on.aziPos))[::10]) - ax_single.set_xticklabels(['{:05.1f}'.format(l) for l in rf_on.aziPos[::10]]) - ax_single.set_yticks(range(len(rf_on.altPos))[::10]) - ax_single.set_yticklabels(['{:05.1f}'.format(l) for l in rf_on.altPos[::-1][::10]]) - ax_single.set_aspect('equal') - ax_single.set_title('{}: {}. ON thr:{}; OFF thr:{}.'.format(plane_n, roi_n, rf_on.thr, rf_off.thr)) - pdff.savefig(f_single) - f_single.clear() - plt.close(f_single) - - pdff.close() - - ax_all.set_xticks(range(len(rf_on.aziPos))[::10]) - ax_all.set_xticklabels(['{:05.1f}'.format(l) for l in rf_on.aziPos[::10]]) - ax_all.set_yticks(range(len(rf_on.altPos))[::10]) - ax_all.set_yticklabels(['{:05.1f}'.format(l) for l in rf_on.altPos[::-1][::10]]) - ax_all.set_aspect('equal') - ax_all.set_title('{}, abs_zscore_thr:{}'.format(plane_n, absolute_thr)) - - f_all.savefig(os.path.join(save_folder, 'RF_contours_' + plane_n + '_all.pdf'), dpi=300) - -rff.close() - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/200_plot_dgc_response_all.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/200_plot_dgc_response_all.py deleted file mode 100644 index b1b2f1f..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/200_plot_dgc_response_all.py +++ /dev/null @@ -1,160 +0,0 @@ -import os -import h5py -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.core.PlottingTools as pt -from matplotlib.backends.backend_pdf import PdfPages -import matplotlib.gridspec as gridspec - -trace_type = 'f_center_subtracted' -response_table_path = 'analysis/response_table_001_DriftingGratingCircleRetinotopicMapping' - -baseline_span = [-0.5, 0.] -response_span = [0., 1.5] -is_add_to_trace = True - -face_cmap = 'RdBu_r' - -def get_dff(traces, t_axis, response_span, baseline_span): - """ - - :param traces: dimension, trial x timepoint - :param t_axis: - :return: - """ - - baseline_ind = np.logical_and(t_axis > baseline_span[0], t_axis <= baseline_span[1]) - response_ind = np.logical_and(t_axis > response_span[0], t_axis <= response_span[1]) - baseline = np.mean(traces[:, baseline_ind], axis=1, keepdims=True) - dff_traces = (traces - baseline) / baseline - - trace_mean = np.mean(traces, axis=0) - baseline_mean = np.mean(trace_mean[baseline_ind]) - dff_trace_mean = (trace_mean - baseline_mean) / baseline_mean - dff_mean = np.mean(dff_trace_mean[response_ind]) - - return dff_traces, dff_trace_mean, dff_mean - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, 'figures') -if not os.path.isdir(save_folder): - os.mkdir(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -print(nwb_fn) -nwb_f = h5py.File(nwb_fn, 'r') - -plane_ns = nwb_f[response_table_path].keys() -plane_ns.sort() - -for plane_n in plane_ns: - - print('\nprocessing {} ...'.format(plane_n)) - - if is_add_to_trace: - add_to_trace = h5py.File(os.path.join(plane_n, 'caiman_segmentation_results.hdf5'), - 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0 - - res_grp = nwb_f['{}/{}'.format(response_table_path, plane_n)] - t_axis = res_grp.attrs['sta_timestamps'] - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - grating_ns = res_grp.keys() - - # remove blank sweep - grating_ns = [gn for gn in grating_ns if gn[-37:] != '_sf0.00_tf00.0_dire000_con0.00_rad000'] - - dire_lst = np.array(list(set([str(gn[38:41]) for gn in grating_ns]))) - tf_lst = np.array(list(set([str(gn[29:33]) for gn in grating_ns]))) - sf_lst = np.array(list(set([str(gn[22:26]) for gn in grating_ns]))) - - - pdff = PdfPages(os.path.join(save_folder, 'STA_DriftingGrating_' + plane_n + '_all.pdf')) - - for roi_i, roi_n in enumerate(roi_lst): - print(roi_n) - - f = plt.figure(figsize=(8.5, 11)) - gs_out = gridspec.GridSpec(len(tf_lst), 1) - gs_in_dict = {} - for gs_ind, gs_o in enumerate(gs_out): - curr_gs_in = gridspec.GridSpecFromSubplotSpec(len(sf_lst), len(dire_lst), subplot_spec=gs_o, - wspace=0.0, hspace=0.0) - gs_in_dict[gs_ind] = curr_gs_in - - v_max = 0 - v_min = 0 - dff_mean_max=0 - dff_mean_min=0 - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - dff_traces, dff_trace_mean, dff_mean = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, - baseline_span=baseline_span) - v_max = max([np.amax(dff_traces), v_max]) - v_min = min([np.amin(dff_traces), v_min]) - dff_mean_max = max([dff_mean, dff_mean_max]) - dff_mean_min = min([dff_mean, dff_mean_min]) - - dff_mean_max = max([abs(dff_mean_max), abs(dff_mean_min)]) - dff_mean_min = - dff_mean_max - - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - dff_traces, dff_trace_mean, dff_mean = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, - baseline_span=baseline_span) - - curr_tf = grating_n[29:33] - tf_i = np.where(tf_lst == curr_tf)[0][0] - curr_sf = grating_n[22:26] - sf_i = np.where(sf_lst == curr_sf)[0][0] - curr_dire = grating_n[38:41] - dire_i = np.where(dire_lst == curr_dire)[0][0] - ax = plt.Subplot(f, gs_in_dict[tf_i][sf_i * len(dire_lst) + dire_i]) - f_color = pt.value_2_rgb(value=(dff_mean - dff_mean_min) / (dff_mean_max - dff_mean_min), - cmap=face_cmap) - - # f_color = pt.value_2_rgb(value=dff_mean / dff_mean_max, cmap=face_cmap) - - # print f_color - ax.set_axis_bgcolor(f_color) - ax.set_xticks([]) - ax.set_yticks([]) - for sp in ax.spines.values(): - sp.set_visible(False) - ax.axhline(y=0, ls='--', color='#888888', lw=1) - ax.axvspan(response_span[0], response_span[1], alpha=0.5, color='#888888', ec='none') - for t in dff_traces: - ax.plot(t_axis, t, '-', color='#888888', lw=0.5) - ax.plot(t_axis, dff_trace_mean, '-r', lw=1) - f.add_subplot(ax) - - all_axes = f.get_axes() - for ax in all_axes: - ax.set_ylim([v_min, v_max]) - ax.set_xlim([t_axis[0], t_axis[-1]]) - - f.suptitle('roi:{:04d}; trace type:{}; baseline:{}; response:{}; \ntrace range:{}; color range:{}' - .format(roi_i, trace_type, baseline_span, response_span, [v_min, v_max], - [dff_mean_min, dff_mean_max]), fontsize=8) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/210_plot_dgc_response_mean.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/210_plot_dgc_response_mean.py deleted file mode 100644 index 604a4db..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/210_plot_dgc_response_mean.py +++ /dev/null @@ -1,157 +0,0 @@ -import os -import h5py -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.core.PlottingTools as pt -from matplotlib.backends.backend_pdf import PdfPages -import matplotlib.gridspec as gridspec - -trace_type = 'f_center_subtracted' -response_table_path = 'analysis/response_table_001_DriftingGratingCircleRetinotopicMapping' - -baseline_span = [-0.5, 0.] -response_span = [0., 1.5] -is_add_to_trace = True - -face_cmap = 'RdBu_r' - -def get_dff(traces, t_axis, response_span, baseline_span): - """ - - :param traces: dimension, trial x timepoint - :param t_axis: - :return: - """ - - trace_mean = np.mean(traces, axis=0) - trace_std = np.std(traces, axis=0) - trace_sem = trace_std / np.sqrt(traces.shape[0]) - - baseline_ind = np.logical_and(t_axis > baseline_span[0], t_axis <= baseline_span[1]) - response_ind = np.logical_and(t_axis > response_span[0], t_axis <= response_span[1]) - baseline = np.mean(trace_mean[baseline_ind]) - dff_trace_mean = (trace_mean - baseline) / baseline - dff_trace_std = trace_std / baseline - dff_trace_sem = trace_sem / baseline - dff_mean = np.mean(dff_trace_mean[response_ind]) - - return dff_trace_mean, dff_trace_std, dff_trace_sem, dff_mean - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, 'figures') -if not os.path.isdir(save_folder): - os.mkdir(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -print(nwb_fn) -nwb_f = h5py.File(nwb_fn, 'r') - -plane_ns = nwb_f[response_table_path].keys() -plane_ns.sort() - -for plane_n in plane_ns: - - print('\nprocessing {} ...'.format(plane_n)) - - if is_add_to_trace: - add_to_trace = h5py.File(os.path.join(plane_n, 'caiman_segmentation_results.hdf5'), - 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0 - - res_grp = nwb_f['{}/{}'.format(response_table_path, plane_n)] - t_axis = res_grp.attrs['sta_timestamps'] - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - grating_ns = res_grp.keys() - # remove blank sweep - grating_ns = [gn for gn in grating_ns if gn[-37:] != '_sf0.00_tf00.0_dire000_con0.00_rad000'] - - dire_lst = np.array(list(set([str(gn[38:41]) for gn in grating_ns]))) - dire_lst.sort() - tf_lst = np.array(list(set([str(gn[29:33]) for gn in grating_ns]))) - tf_lst.sort() - sf_lst = np.array(list(set([str(gn[22:26]) for gn in grating_ns]))) - sf_lst.sort() - - pdff = PdfPages(os.path.join(save_folder, 'STA_DriftingGrating_' + plane_n + '_mean.pdf')) - - for roi_i, roi_n in enumerate(roi_lst): - print(roi_n) - - f = plt.figure(figsize=(8.5, 11)) - gs_out = gridspec.GridSpec(len(tf_lst), 1) - gs_in_dict = {} - for gs_ind, gs_o in enumerate(gs_out): - curr_gs_in = gridspec.GridSpecFromSubplotSpec(len(sf_lst), len(dire_lst), subplot_spec=gs_o, - wspace=0.05, hspace=0.05) - gs_in_dict[gs_ind] = curr_gs_in - - v_max = 0 - v_min = 0 - dff_mean_max=0 - dff_mean_min=0 - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - _ = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, baseline_span=baseline_span) - dff_trace_mean, dff_trace_std, dff_trace_sem, dff_mean = _ - v_max = max([np.amax(dff_trace_mean + dff_trace_sem), v_max]) - v_min = min([np.amin(dff_trace_mean - dff_trace_sem), v_min]) - dff_mean_max = max([dff_mean, dff_mean_max]) - dff_mean_min = min([dff_mean, dff_mean_min]) - dff_mean_max = max([abs(dff_mean_max), abs(dff_mean_min)]) - dff_mean_min = - dff_mean_max - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - _ = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, baseline_span=baseline_span) - dff_trace_mean, dff_trace_std, dff_trace_sem, dff_mean = _ - curr_tf = grating_n[29:33] - tf_i = np.where(tf_lst == curr_tf)[0][0] - curr_sf = grating_n[22:26] - sf_i = np.where(sf_lst == curr_sf)[0][0] - curr_dire = grating_n[38:41] - dire_i = np.where(dire_lst == curr_dire)[0][0] - ax = plt.Subplot(f, gs_in_dict[tf_i][sf_i * len(dire_lst) + dire_i]) - f_color = pt.value_2_rgb(value=(dff_mean - dff_mean_min) / (dff_mean_max - dff_mean_min), - cmap=face_cmap) - - # f_color = pt.value_2_rgb(value=dff_mean / dff_mean_max, cmap=face_cmap) - - # print f_color - ax.set_axis_bgcolor(f_color) - ax.set_xticks([]) - ax.set_yticks([]) - for sp in ax.spines.values(): - sp.set_visible(False) - ax.axhline(y=0, ls='--', color='#888888', lw=1) - ax.axvspan(response_span[0], response_span[1], alpha=0.5, color='#888888', ec='none') - ax.fill_between(t_axis, dff_trace_mean - dff_trace_sem, dff_trace_mean + dff_trace_sem, edgecolor='none', - facecolor='#880000', alpha=0.5) - ax.plot(t_axis, dff_trace_mean, '-r', lw=1) - f.add_subplot(ax) - - all_axes = f.get_axes() - for ax in all_axes: - ax.set_ylim([v_min, v_max]) - ax.set_xlim([t_axis[0], t_axis[-1]]) - - f.suptitle('roi:{:04d}; trace type:{}; baseline:{}; response:{}; \ntrace range:{}; color range:{}' - .format(roi_i, trace_type, baseline_span, response_span, [v_min, v_max], - [dff_mean_min, dff_mean_max]), fontsize=8) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/220_plot_dgc_tuning_curves.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/220_plot_dgc_tuning_curves.py deleted file mode 100644 index a5d8a6f..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/220_plot_dgc_tuning_curves.py +++ /dev/null @@ -1,196 +0,0 @@ -import os -import h5py -import numpy as np -import pandas as pd -import matplotlib.pyplot as plt -from matplotlib.backends.backend_pdf import PdfPages - -trace_type = 'f_center_subtracted' -response_table_path = 'analysis/response_table_001_DriftingGratingCircleRetinotopicMapping' - -baseline_span = [-0.5, 0.] -response_span = [0., 1.5] -is_add_to_trace = True - -def get_response(traces, t_axis, response_span, baseline_span): - """ - - :param traces: dimension, trial x timepoint - :param t_axis: - :return: - """ - - baseline_ind = np.logical_and(t_axis > baseline_span[0], t_axis <= baseline_span[1]) - response_ind = np.logical_and(t_axis > response_span[0], t_axis <= response_span[1]) - - trace_mean = np.mean(traces, axis=0) - baseline_mean = np.mean(trace_mean[baseline_ind]) - dff_trace_mean = (trace_mean - baseline_mean) / baseline_mean - dff_mean = np.mean(dff_trace_mean[response_ind]) - - baselines = np.mean(traces[:, baseline_ind], axis=1, keepdims=True) - dff_traces = (traces - baselines) / baselines - dffs = np.mean(dff_traces[:, response_ind], axis=1) - dff_std = np.std(dffs) - dff_sem = dff_std / np.sqrt(traces.shape[0]) - - return dff_mean, dff_std, dff_sem - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, 'figures') -if not os.path.isdir(save_folder): - os.mkdir(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -print(nwb_fn) -nwb_f = h5py.File(nwb_fn, 'r') - -plane_ns = nwb_f[response_table_path].keys() -plane_ns.sort() - -for plane_n in plane_ns: - - print('\nprocessing {} ...'.format(plane_n)) - - if is_add_to_trace: - add_to_trace = h5py.File(os.path.join(plane_n, 'caiman_segmentation_results.hdf5'), - 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0 - - res_grp = nwb_f['{}/{}'.format(response_table_path, plane_n)] - t_axis = res_grp.attrs['sta_timestamps'] - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - grating_ns = res_grp.keys() - - # remove blank sweep - grating_ns = [gn for gn in grating_ns if gn[-37:] != '_sf0.00_tf00.0_dire000_con0.00_rad000'] - - dire_lst = np.array(list(set([str(gn[38:41]) for gn in grating_ns]))) - dire_lst.sort() - tf_lst = np.array(list(set([str(gn[29:33]) for gn in grating_ns]))) - tf_lst.sort() - sf_lst = np.array(list(set([str(gn[22:26]) for gn in grating_ns]))) - sf_lst.sort() - - pdff = PdfPages(os.path.join(save_folder, 'tuning_curve_DriftingGrating_' + plane_n + '_mean.pdf')) - - for roi_i, roi_n in enumerate(roi_lst): - print(roi_n) - - # get response table - res_tab = pd.DataFrame(columns=['con', 'tf', 'sf', 'dire', 'dff_mean', 'dff_std', 'dff_sem']) - row_ind = 0 - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - _ = get_response(traces=curr_sta, t_axis=t_axis, response_span=response_span, baseline_span=baseline_span) - dff_mean, dff_std, dff_sem = _ - - con = float(grating_n.split('_')[5][3:]) - tf = float(grating_n.split('_')[3][2:]) - sf = float(grating_n.split('_')[2][2:]) - dire = int(grating_n.split('_')[4][4:]) - - res_tab.loc[row_ind] = [con, tf, sf, dire, dff_mean, dff_std, dff_sem] - row_ind += 1 - - # find the preferred condition - top_condition = res_tab[res_tab.dff_mean == max(res_tab.dff_mean)] - - # make figure - f = plt.figure(figsize=(8.5, 11)) - - # get tf plot - tf_conditions = res_tab[(res_tab.sf == float(top_condition.sf)) & \ - (res_tab.dire == int(top_condition.dire))] - tf_conditions = tf_conditions.sort_values(by='tf') - - tf_log = np.log(tf_conditions.tf) - - ax_tf = f.add_subplot(311) - ax_tf.fill_between(x=tf_log, y1=tf_conditions.dff_mean + tf_conditions.dff_sem, - y2=tf_conditions.dff_mean - tf_conditions.dff_sem, edgecolor='none', - facecolor='#888888', alpha=0.5) - ax_tf.axhline(y=0, ls='--', color='k', lw=1) - ax_tf.plot(tf_log, tf_conditions.dff_mean, 'r-', lw=2) - ax_tf.set_title('temporal frequency tuning', rotation='vertical', x=-0.4, y=0.5, va='center', ha='center', - size=10) - ax_tf.set_xticks(tf_log) - ax_tf.set_xticklabels(list(tf_conditions.tf)) - ax_tf.set_xlim(np.log([0.9, 16])) - ax_tf_xrange = ax_tf.get_xlim()[1] - ax_tf.get_xlim()[0] - ax_tf_yrange = ax_tf.get_ylim()[1] - ax_tf.get_ylim()[0] - ax_tf.set_aspect(aspect=(ax_tf_xrange / ax_tf_yrange)) - ax_tf.set_ylabel('mean df/f', size=10) - ax_tf.set_xlabel('temporal freqency (Hz)', size=10) - ax_tf.tick_params(axis='both', which='major', labelsize=10) - - # get sf plot - sf_conditions = res_tab[(res_tab.tf == float(top_condition.tf)) & \ - (res_tab.dire == int(top_condition.dire))] - sf_conditions = sf_conditions.sort_values(by='sf') - - sf_log = np.log(sf_conditions.sf) - - ax_sf = f.add_subplot(312) - ax_sf.fill_between(x=sf_log, y1=sf_conditions.dff_mean + sf_conditions.dff_sem, - y2=sf_conditions.dff_mean - sf_conditions.dff_sem, edgecolor='none', - facecolor='#888888', alpha=0.5) - ax_sf.axhline(y=0, ls='--', color='k', lw=1) - ax_sf.plot(sf_log, sf_conditions.dff_mean, '-r', lw=2) - ax_sf.set_title('spatial frequency tuning', rotation='vertical', x=-0.4, y=0.5, va='center', ha='center', - size=10) - ax_sf.set_xticks(sf_log) - ax_sf.set_xticklabels(['{:04.2f}'.format(s) for s in list(sf_conditions.sf)]) - ax_sf.set_xlim(np.log([0.008, 0.4])) - ax_sf_xrange = ax_sf.get_xlim()[1] - ax_sf.get_xlim()[0] - ax_sf_yrange = ax_sf.get_ylim()[1] - ax_sf.get_ylim()[0] - ax_sf.set_aspect(aspect=(ax_sf_xrange / ax_sf_yrange)) - ax_sf.set_ylabel('mean df/f', size=10) - ax_sf.set_xlabel('spatial freqency (cpd)', size=10) - ax_sf.tick_params(axis='both', which='major', labelsize=10) - - # get dire plot - dire_conditions = res_tab[(res_tab.tf == float(top_condition.tf)) & \ - (res_tab.sf == float(top_condition.sf))] - dire_conditions = dire_conditions.sort_values(by='dire') - dire_arc = list(dire_conditions.dire * np.pi / 180.) - dire_arc.append(dire_arc[0]) - dire_dff = np.array(dire_conditions.dff_mean) - dire_dff[dire_dff < 0.] = 0. - dire_dff = list(dire_dff) - dire_dff.append(dire_dff[0]) - dire_dff_sem = list(dire_conditions.dff_sem) - dire_dff_sem.append(dire_dff_sem[0]) - dire_dff_low = np.array(dire_dff) - np.array(dire_dff_sem) - dire_dff_low[dire_dff_low < 0.] = 0. - dire_dff_high = np.array(dire_dff) + np.array(dire_dff_sem) - - r_ticks = [0, round(max(dire_dff) * 10000.) / 10000.] - - ax_dire = f.add_subplot(313, projection='polar') - ax_dire.fill_between(x=dire_arc, y1=dire_dff_low, y2=dire_dff_high, edgecolor='none', facecolor='#888888', - alpha=0.5) - ax_dire.plot(dire_arc, dire_dff, '-r', lw=2) - ax_dire.set_title('orientation tuning', rotation='vertical', x=-0.4, y=0.5, va='center', ha='center', size=10) - ax_dire.set_rticks(r_ticks) - ax_dire.tick_params(axis='both', which='major', labelsize=10) - - f.suptitle('roi:{:04d}; trace type:{}; baseline:{}; response:{}' - .format(roi_i, trace_type, baseline_span, response_span), fontsize=10) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/old/040_get_mmap_files_for_caiman.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/old/040_get_mmap_files_for_caiman.py deleted file mode 100644 index 1acc083..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/old/040_get_mmap_files_for_caiman.py +++ /dev/null @@ -1,64 +0,0 @@ -import sys; print('Python %s on %s' % (sys.version, sys.platform)) -sys.path.extend([r"E:\data\github_packages\CaImAn"]) - -import os -import numpy as np -import tifffile as tf -import caiman as cm -import h5py - -def run(): - - data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180323-M360495-deepscope\02\02_" - base_name = '180323_M360495_02' - t_downsample_rate = 10. - - plane_ns = [p for p in os.listdir(data_folder) if os.path.isdir(os.path.join(data_folder, p))] - plane_ns.sort() - print('planes:') - print('\n'.join(plane_ns)) - - ## start cluster - c, dview, n_processes = cm.cluster.setup_cluster(backend='local', n_processes=3, single_thread=False) - - for plane_n in plane_ns: - print('\nprocessing {} ...'.format(plane_n)) - - plane_folder = os.path.join(data_folder, plane_n, 'corrected') - os.chdir(plane_folder) - - f_ns = [f for f in os.listdir(plane_folder) if f[-14:] == '_corrected.tif'] - f_ns.sort() - print('\n'.join(f_ns)) - - min_tot = 0 - for fn in f_ns: - min_tot = min([min_tot, np.min(tf.imread(os.path.join(plane_folder, fn)))]) - print('minimum pixel value of entire movie: ' + str(min_tot)) - - add_to_movie = 10. - min_tot # the movie must be positive!!! - t_ds_factor = 1. / t_downsample_rate # use .2 or .1 if file is large and you want a quick answer - f_paths = [os.path.join(plane_folder, f) for f in f_ns] - - name_new = cm.save_memmap_each(f_paths, - dview=dview, - base_name=base_name + '_' + plane_n + '_each', - resize_fact=(1., 1., t_ds_factor), - add_to_movie=add_to_movie) - name_new.sort() - - fname_new = cm.save_memmap_join(name_new, base_name=base_name + '_' + plane_n, dview=dview, - n_chunks=100) - print('\n{}'.format(fname_new)) - - save_file = h5py.File(os.path.join(plane_folder, 'caiman_segmentation_results.hdf5')) - save_file['bias_added_to_movie'] = add_to_movie - save_file.close() - - single_fns = [f for f in os.listdir(plane_folder) if '_each' in f] - for single_fn in single_fns: - os.remove(os.path.join(plane_folder, single_fn)) - -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/040_get_cells_file.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/040_get_cells_file.py deleted file mode 100644 index ff29a37..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/040_get_cells_file.py +++ /dev/null @@ -1,87 +0,0 @@ -import os -import numpy as np -import h5py -import tifffile as tf -import allensdk_internal.brain_observatory.mask_set as mask_set -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import scipy.ndimage as ni -import matplotlib.pyplot as plt - - -isSave = True -is_filter = True - -filter_sigma = 0.5 # parameters only used if filter the rois -# dilation_iterations = 0 # parameters only used if filter the rois -cut_thr = 3. # parameters only used if filter the rois - -bg_fn = "corrected_mean_projection.tif" -save_folder = 'figures' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -data_f = h5py.File('caiman_segmentation_results.hdf5') -masks = data_f['masks'].value -data_f.close() - -bg = tf.imread(bg_fn) - -final_roi_dict = {} - -for i, mask in enumerate(masks): - - if is_filter: - mask_nor = (mask - np.mean(mask.flatten())) / np.abs(np.std(mask.flatten())) - mask_nor_f = ni.filters.gaussian_filter(mask_nor, filter_sigma) - mask_bin = np.zeros(mask_nor_f.shape, dtype=np.uint8) - mask_bin[mask_nor_f > cut_thr] = 1 - - else: - mask_bin = np.zeros(mask.shape, dtype=np.uint8) - mask_bin[mask > 0] = 1 - - mask_labeled, mask_num = ni.label(mask_bin) - curr_mask_dict = ia.get_masks(labeled=mask_labeled, keyPrefix='caiman_mask_{:03d}'.format(i), labelLength=5) - for roi_key, roi_mask in curr_mask_dict.items(): - final_roi_dict.update({roi_key: ia.WeightedROI(roi_mask * mask)}) - -print 'Total number of ROIs:',len(final_roi_dict) - -f = plt.figure(figsize=(15, 8)) -ax1 = f.add_subplot(121) -ax1.imshow(ia.array_nor(bg), vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') -colors1 = pt.random_color(masks.shape[0]) -for i, mask in enumerate(masks): - pt.plot_mask_borders(mask, plotAxis=ax1, color=colors1[i]) -ax1.set_title('original ROIs') -ax1.set_axis_off() -ax2 = f.add_subplot(122) -ax2.imshow(ia.array_nor(bg), vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') -colors2 = pt.random_color(len(final_roi_dict)) -i = 0 -for roi in final_roi_dict.values(): - pt.plot_mask_borders(roi.get_binary_mask(), plotAxis=ax2, color=colors2[i]) - i = i + 1 -ax2.set_title('filtered ROIs') -ax2.set_axis_off() -plt.show() - -if isSave: - - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - f.savefig(os.path.join(save_folder, 'caiman_segmentation_filtering.pdf'), dpi=300) - - cell_file = h5py.File('cells.hdf5', 'w') - - i = 0 - for key, value in sorted(final_roi_dict.iteritems()): - curr_grp = cell_file.create_group('cell{:04d}'.format(i)) - curr_grp.attrs['name'] = key - value.to_h5_group(curr_grp) - i += 1 - - cell_file.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/050_refine_cells.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/050_refine_cells.py deleted file mode 100644 index f0b8c31..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/050_refine_cells.py +++ /dev/null @@ -1,173 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Jun 30 17:44:42 2015 - -@author: junz -""" -import os -import h5py -import numpy as np -import operator -import matplotlib.pyplot as plt -import scipy.ndimage as ni -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.FileTools as ft -import corticalmapping.core.PlottingTools as pt -import corticalmapping.SingleCellAnalysis as sca - -plt.ioff() - -# pixels, masks with center location within this pixel region at the image border will be discarded -center_margin = [20, 20] - -# area range, range of number of pixels of a valid roi -area_range = [20, 500] - -# for the two masks that are overlapping, if the ratio between overlap and the area of the smaller mask is larger than -# this value, the smaller mask will be discarded. -overlap_thr = 0.5 - -save_folder = 'figures' - -data_file_name = 'cells.hdf5' -save_file_name = 'cells_refined.hdf5' -background_file_name = "corrected_mean_projection.tif" - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -# read cells -dfile = h5py.File(data_file_name) -cells = {} -for cellname in dfile.iterkeys(): - cells.update({cellname:ia.WeightedROI.from_h5_group(dfile[cellname])}) - -print 'total number of cells:', len(cells) - -# get the names of cells which are on the edge -edge_cells = [] -for cellname, cellmask in cells.iteritems(): - dimension = cellmask.dimension - center = cellmask.get_center() - if center[0] < center_margin[0] or \ - center[0] > dimension[0] - center_margin[0] or \ - center[1] < center_margin[1] or \ - center[1] > dimension[1] - center_margin[1]: - - # cellmask.plot_binary_mask_border(color='#ff0000', borderWidth=1) - # plt.title(cellname) - # plt.show() - - edge_cells.append(cellname) - -print '\ncells to be removed because they are on the edges:' -print '\n'.join(edge_cells) - -# remove edge cells -for edge_cell in edge_cells: - _ = cells.pop(edge_cell) - -# get dictionary of cell areas -cell_areas = {} -for cellname, cellmask in cells.iteritems(): - cell_areas.update({cellname: cellmask.get_binary_area()}) - - -# remove cellnames that have area outside of the area_range -invalid_cell_ns = [] -for cellname, cellarea in cell_areas.items(): - if cellarea < area_range[0] or cellarea > area_range[1]: - invalid_cell_ns.append(cellname) -print "cells to be removed because they do not meet area criterion:" -print "\n".join(invalid_cell_ns) -for invalid_cell_n in invalid_cell_ns: - cell_areas.pop(invalid_cell_n) - - -# sort cells with their binary area -cell_areas_sorted = sorted(cell_areas.items(), key=operator.itemgetter(1)) -cell_areas_sorted.reverse() -cell_names_sorted = [c[0] for c in cell_areas_sorted] -# print '\n'.join([str(c) for c in cell_areas_sorted]) - -# get the name of cells that needs to be removed because of overlapping -retain_cells = [] -remove_cells = [] -for cell1_name in cell_names_sorted: - cell1_mask = cells[cell1_name] - is_remove = 0 - cell1_area = cell1_mask.get_binary_area() - for cell2_name in retain_cells: - cell2_mask = cells[cell2_name] - cell2_area = cell2_mask.get_binary_area() - curr_overlap = cell1_mask.binary_overlap(cell2_mask) - - if float(curr_overlap) / cell1_area > overlap_thr: - remove_cells.append(cell1_name) - is_remove = 1 - print cell1_name, ':', cell1_mask.get_binary_area(), ': removed' - - # f = plt.figure(figsize=(10,10)) - # ax = f.add_subplot(111) - # cell1_mask.plot_binary_mask_border(plotAxis=ax, color='#ff0000', borderWidth=1) - # cell2_mask.plot_binary_mask_border(plotAxis=ax, color='#0000ff', borderWidth=1) - # ax.set_title('red:'+cell1_name+'; blue:'+cell2_name) - # plt.show() - break - - if is_remove == 0: - retain_cells.append(cell1_name) - print cell1_name, ':', cell1_mask.get_binary_area(), ': retained' - -print '\ncells to be removed because of overlapping:' -print '\n'.join(remove_cells) - -print '\ntotal number of reatined cells:', len(retain_cells) - -# plotting -colors = pt.random_color(len(cells.keys())) -bgImg = tf.imread(background_file_name) - -f = plt.figure(figsize=(10, 10)) -ax = f.add_subplot(111) -ax.imshow(ia.array_nor(bgImg), cmap='gray', vmin=0, vmax=0.5, interpolation='nearest') - -f2 = plt.figure(figsize=(10, 10)) -ax2 = f2.add_subplot(111) -ax2.imshow(np.zeros(bgImg.shape, dtype=np.uint8), vmin=0, vmax=1, cmap='gray', interpolation='nearest') - -i = 0 -for retain_cell in retain_cells: - cells[retain_cell].plot_binary_mask_border(plotAxis=ax, color=colors[i], borderWidth=1) - cells[retain_cell].plot_binary_mask_border(plotAxis=ax2, color=colors[i], borderWidth=1) - i += 1 -plt.show() - -# save figures -pt.save_figure_without_borders(f, os.path.join(save_folder, '2P_refined_ROIs_with_background.png'), dpi=300) -pt.save_figure_without_borders(f2, os.path.join(save_folder, '2P_refined_ROIs_without_background.png'), dpi=300) - -# save h5 file -save_file = h5py.File(save_file_name, 'w') -i = 0 -for retain_cell in retain_cells: - print retain_cell, ':', cells[retain_cell].get_binary_area() - - currGroup = save_file.create_group('cell' + ft.int2str(i, 4)) - currGroup.attrs['name'] = retain_cell - roiGroup = currGroup.create_group('roi') - cells[retain_cell].to_h5_group(roiGroup) - i += 1 - -for attr, value in dfile.attrs.iteritems(): - save_file.attrs[attr] = value - -save_file.close() -dfile.close() - - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/060_get_weighted_rois_and_surrounds.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/060_get_weighted_rois_and_surrounds.py deleted file mode 100644 index b31e405..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/060_get_weighted_rois_and_surrounds.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Jun 30 17:44:42 2015 - -@author: junz -""" - -import os -import numpy as np -import h5py -import tifffile as tf -import allensdk_internal.brain_observatory.mask_set as mask_set -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import scipy.ndimage as ni -import matplotlib.pyplot as plt - -plt.ioff() - -data_file_name = 'cells_refined.hdf5' -background_file_name = "corrected_mean_projection.tif" -save_folder = 'figures' - -overlap_threshold = 0.9 -surround_limit = [1, 8] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -print 'reading cells file ...' -data_f = h5py.File(data_file_name, 'r') - -cell_ns = data_f.keys() -cell_ns.sort() - -binary_mask_array = [] -weight_mask_array = [] - -for cell_n in cell_ns: - curr_roi = ia.ROI.from_h5_group(data_f[cell_n]['roi']) - binary_mask_array.append(curr_roi.get_binary_mask()) - weight_mask_array.append(curr_roi.get_weighted_mask()) - -data_f.close() -binary_mask_array = np.array(binary_mask_array) -weight_mask_array = np.array(weight_mask_array) -print 'starting mask_array shape:', weight_mask_array.shape - -print 'getting total mask ...' -total_mask = np.zeros((binary_mask_array.shape[1], binary_mask_array.shape[2]), dtype=np.uint8) -for curr_mask in binary_mask_array: - total_mask = np.logical_or(total_mask, curr_mask) -total_mask = np.logical_not(total_mask) - -plt.imshow(total_mask, interpolation='nearest') -plt.title('total_mask') -plt.show() - -print 'getting and surround masks ...' -binary_surround_array = [] -for binary_center in binary_mask_array: - curr_surround = np.logical_xor(ni.binary_dilation(binary_center, iterations=surround_limit[1]), - ni.binary_dilation(binary_center, iterations=surround_limit[0])) - curr_surround = np.logical_and(curr_surround, total_mask).astype(np.uint8) - binary_surround_array.append(curr_surround) - # plt.imshow(curr_surround) - # plt.show() -binary_surround_array = np.array(binary_surround_array) - -print "saving rois ..." -center_areas = [] -surround_areas = [] -for mask_ind in range(binary_mask_array.shape[0]): - center_areas.append(np.sum(binary_mask_array[mask_ind].flat)) - surround_areas.append(np.sum(binary_surround_array[mask_ind].flat)) -roi_f = h5py.File('rois_and_traces.hdf5') -roi_f['masks_center'] = weight_mask_array -roi_f['masks_surround'] = binary_surround_array - -roi_f.close() -print 'minimum surround area:', min(surround_areas), 'pixels.' - -f = plt.figure(figsize=(10, 10)) -ax_center = f.add_subplot(211) -ax_center.hist(center_areas, bins=30) -ax_center.set_title('roi center area distribution') -ax_surround = f.add_subplot(212) -ax_surround.hist(surround_areas, bins=30) -ax_surround.set_title('roi surround area distribution') -plt.show() - -print 'plotting ...' -colors = pt.random_color(weight_mask_array.shape[0]) -bg = ia.array_nor(tf.imread('corrected_mean_projection.tif')) - -f_c_bg = plt.figure(figsize=(10, 10)) -ax_c_bg = f_c_bg.add_subplot(111) -ax_c_bg.imshow(bg, cmap='gray', vmin=0, vmax=0.5, interpolation='nearest') -f_c_nbg = plt.figure(figsize=(10, 10)) -ax_c_nbg = f_c_nbg.add_subplot(111) -ax_c_nbg.imshow(np.zeros(bg.shape,dtype=np.uint8),vmin=0,vmax=1,cmap='gray',interpolation='nearest') -f_s_nbg = plt.figure(figsize=(10, 10)) -ax_s_nbg = f_s_nbg.add_subplot(111) -ax_s_nbg.imshow(np.zeros(bg.shape,dtype=np.uint8),vmin=0,vmax=1,cmap='gray',interpolation='nearest') - -i = 0 -for mask_ind in range(binary_mask_array.shape[0]): - pt.plot_mask_borders(binary_mask_array[mask_ind], plotAxis=ax_c_bg, color=colors[i], borderWidth=1) - pt.plot_mask_borders(binary_mask_array[mask_ind], plotAxis=ax_c_nbg, color=colors[i], borderWidth=1) - pt.plot_mask_borders(binary_surround_array[mask_ind], plotAxis=ax_s_nbg, color=colors[i], borderWidth=1) - i += 1 - -plt.show() - -print 'saving figures ...' -pt.save_figure_without_borders(f_c_bg, os.path.join(save_folder, '2P_ROIs_with_background.png'), dpi=300) -pt.save_figure_without_borders(f_c_nbg, os.path.join(save_folder, '2P_ROIs_without_background.png'), dpi=300) -pt.save_figure_without_borders(f_s_nbg, os.path.join(save_folder, '2P_ROI_surrounds_background.png'), dpi=300) -f.savefig(os.path.join(save_folder, 'roi_area_distribution.pdf'), dpi=300) diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/070_get_raw_center_and_surround_traces.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/070_get_raw_center_and_surround_traces.py deleted file mode 100644 index 0e57582..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/070_get_raw_center_and_surround_traces.py +++ /dev/null @@ -1,129 +0,0 @@ -import os -import numpy as np -import h5py -import time -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import corticalmapping.core.FileTools as ft -import corticalmapping.NwbTools as nt -import matplotlib.pyplot as plt -from multiprocessing import Pool - -CHUNK_SIZE = 2000 -PROCESS_NUM = 5 - -def get_chunk_frames(frame_num, chunk_size): - chunk_num = frame_num // chunk_size - if frame_num % chunk_size > 0: - chunk_num = chunk_num + 1 - - print("total number of frames:", frame_num) - print("total number of chunks:", chunk_num) - - chunk_ind = [] - chunk_starts = [] - chunk_ends = [] - - for chunk_i in range(chunk_num): - chunk_ind.append(chunk_i) - chunk_starts.append(chunk_i * chunk_size) - - if chunk_i < chunk_num - 1: - chunk_ends.append((chunk_i + 1) * chunk_size) - else: - chunk_ends.append(frame_num) - - return zip(chunk_ind, chunk_starts, chunk_ends) - -def get_traces(params): - t0 = time.time() - - chunk_ind, chunk_start, chunk_end, nwb_path, data_path, curr_folder, center_array, surround_array = params - - nwb_f = h5py.File(nwb_path, 'r') - print('\nstart analyzing chunk: {}'.format(chunk_ind)) - curr_mov = nwb_f[data_path][chunk_start: chunk_end] - nwb_f.close() - - # print 'extracting traces' - curr_traces_center = np.empty((center_array.shape[0], curr_mov.shape[0]), dtype=np.float32) - curr_traces_surround = np.empty((center_array.shape[0], curr_mov.shape[0]), dtype=np.float32) - for i in range(center_array.shape[0]): - curr_center = ia.WeightedROI(center_array[i]) - curr_surround = ia.ROI(surround_array[i]) - curr_traces_center[i, :] = curr_center.get_weighted_trace_pixelwise(curr_mov) - - # scale surround trace to be similar as center trace - mean_center_weight = curr_center.get_mean_weight() - curr_traces_surround[i, :] = curr_surround.get_binary_trace_pixelwise(curr_mov) * mean_center_weight - - # print 'saveing chunk {} ...'.format(chunk_ind) - chunk_folder = os.path.join(curr_folder, 'chunks') - if not os.path.isdir(chunk_folder): - os.mkdir(chunk_folder) - chunk_f = h5py.File(os.path.join(chunk_folder, 'chunk_temp_' + ft.int2str(chunk_ind, 4) + '.hdf5')) - chunk_f['traces_center'] = curr_traces_center - chunk_f['traces_surround'] = curr_traces_surround - chunk_f.close() - - print('\n\t{:06d} seconds: chunk: {}; demixing finished.'.format(int(time.time() - t0), chunk_ind)) - - return None - -def run(): - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - plane_n = os.path.split(curr_folder)[1] - print(plane_n) - - print('getting masks ...') - rois_f = h5py.File('rois_and_traces.hdf5') - center_array = rois_f['masks_center'].value - surround_array = rois_f['masks_surround'].value - - print('\nanalyzing movie in chunks of size:', CHUNK_SIZE , 'frames.') - - nwb_folder = os.path.dirname(curr_folder) - nwb_fn = [f for f in os.listdir(nwb_folder) if f[-4:] == '.nwb'][0] - nwb_path = os.path.join(nwb_folder, nwb_fn) - print('\n' + nwb_path) - data_path = '/processing/motion_correction/MotionCorrection/' + plane_n + '/corrected/data' - - nwb_f = h5py.File(nwb_path, 'r') - total_frame = nwb_f[data_path].shape[0] - nwb_f.close() - - chunk_frames = get_chunk_frames(total_frame, CHUNK_SIZE) - chunk_params = [(cf[0], cf[1], cf[2], nwb_path, data_path, - curr_folder, center_array, surround_array) for cf in chunk_frames] - - p = Pool(PROCESS_NUM) - p.map(get_traces, chunk_params) - - chunk_folder = os.path.join(curr_folder, 'chunks') - chunk_fns = [f for f in os.listdir(chunk_folder) if f[0:11] == 'chunk_temp_'] - chunk_fns.sort() - print('\nreading chunks files ...') - print('\n'.join(chunk_fns)) - - traces_raw = [] - traces_surround = [] - - for chunk_fn in chunk_fns: - curr_chunk_f = h5py.File(os.path.join(chunk_folder, chunk_fn)) - traces_raw.append(curr_chunk_f['traces_center'].value) - traces_surround.append(curr_chunk_f['traces_surround'].value) - - print("saving ...") - traces_raw = np.concatenate(traces_raw, axis=1) - traces_surround = np.concatenate(traces_surround, axis=1) - rois_f['traces_center_raw'] = traces_raw - rois_f['traces_surround_raw'] = traces_surround - print('done.') - - -if __name__ == '__main__': - run() - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/090_get_neuropil_subtracted_traces.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/090_get_neuropil_subtracted_traces.py deleted file mode 100644 index 551768f..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/090_get_neuropil_subtracted_traces.py +++ /dev/null @@ -1,101 +0,0 @@ -import sys -import os -import h5py -import numpy as np -import corticalmapping.HighLevel as hl -import corticalmapping.core.FileTools as ft -import matplotlib.pyplot as plt - - -lam = 1. # 100. -plot_chunk_size = 5000 - - -def plot_traces_chunks(traces, labels, chunk_size, roi_ind): - """ - - :param traces: np.array, shape=[trace_type, t_num] - :param labels: - :param chunk_size: - :param figures_folder: - :param roi_ind: - :return: - """ - - t_num = traces.shape[1] - chunk_num = t_num // chunk_size - - chunks = [] - for chunk_ind in range(chunk_num): - chunks.append([chunk_ind * chunk_size, (chunk_ind + 1) * chunk_size]) - - if t_num % chunk_size != 0: - chunks.append([chunk_num * chunk_size, t_num]) - - v_max = np.amax(traces) - v_min = np.amin(traces) - - fig = plt.figure(figsize=(75, 20)) - fig.suptitle('neuropil subtraction for ROI: {}'.format(roi_ind)) - for chunk_ind, chunk in enumerate(chunks): - curr_ax = fig.add_subplot(len(chunks), 1, chunk_ind + 1) - for trace_ind in range(traces.shape[0]): - curr_ax.plot(traces[trace_ind, chunk[0]: chunk[1]], label=labels[trace_ind]) - - curr_ax.set_xlim([0, chunk_size]) - curr_ax.set_ylim([v_min, v_max * 1.2]) - curr_ax.legend() - - return fig - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -data_f = h5py.File('rois_and_traces.hdf5') -traces_raw = data_f['traces_center_raw'].value -traces_srround = data_f['traces_surround_raw'].value - -traces_subtracted = np.zeros(traces_raw.shape, np.float32) -ratio = np.zeros(traces_raw.shape[0], np.float32) -err = np.zeros(traces_raw.shape[0], np.float32) - -for i in range(traces_raw.shape[0]): - curr_trace_c = traces_raw[i] - curr_trace_s = traces_srround[i] - curr_r, curr_err, curr_trace_sub = hl.neural_pil_subtraction(curr_trace_c, curr_trace_s, lam=lam) - print "roi_%s \tr = %.4f; error = %.4f." % (ft.int2str(i, 5), curr_r, curr_err) - traces_subtracted[i] = curr_trace_sub - ratio[i] = curr_r - err[i] = curr_err - -print('\nplotting neuropil subtraction results ...') -figures_folder = 'figures/neuropil_subtraction_lam_{}'.format(lam) -if not os.path.isdir(figures_folder): - os.makedirs(figures_folder) -for roi_ind in range(traces_raw.shape[0]): - print('roi_{:04d}'.format(roi_ind)) - curr_traces = np.array([traces_raw[roi_ind], traces_srround[roi_ind], traces_subtracted[roi_ind]]) - curr_fig = plot_traces_chunks(traces=curr_traces, - labels=['center', 'surround', 'subtracted'], - chunk_size=plot_chunk_size, - roi_ind=roi_ind) - curr_fig.savefig(os.path.join(figures_folder, 'neuropil_subtraction_ROI_{:04d}.png'.format(roi_ind))) - curr_fig.clear() - plt.close(curr_fig) - -# wait for keyboard abortion -msg = raw_input('Do you want to save? (y/n)\n') -while True: - if msg == 'y': - break - elif msg == 'n': - sys.exit('Stop process without saving.') - else: - msg = raw_input('Do you want to save? (y/n)\n') - -data_f['traces_center_subtracted'] = traces_subtracted -data_f['neuropil_r'] = ratio -data_f['neuropil_err'] = err - -data_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/120_check_correlation.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/120_check_correlation.py deleted file mode 100644 index 65c3f07..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/120_check_correlation.py +++ /dev/null @@ -1,99 +0,0 @@ -import os -import h5py -import tifffile as tf -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.core.PlottingTools as pt -import corticalmapping.core.ImageAnalysis as ia - - -cor_thr = 0.8 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -def merger_pairs(pairs): - - total_set = set([]) - for pair in pairs: - total_set.update(set(pair)) - - all_nodes = list(total_set) - node_grps = [{n} for n in all_nodes] - - for pair in pairs: - - node0 = pair[0] - node1 = pair[1] - - for node_grp in node_grps: - if node0 in node_grp: - node_grp0 = node_grp - if node1 in node_grp: - node_grp1 = node_grp - - if node_grp0 != node_grp1: - node_grp0.update(node_grp1) - node_grps.remove(node_grp1) - - return node_grps - - -save_plot_dir = os.path.join(curr_folder, 'figures', 'dff_extraction') -if not os.path.isdir(save_plot_dir): - os.makedirs(save_plot_dir) - -bg = ia.array_nor(tf.imread('corrected_mean_projection.tif')) - -data_f = h5py.File('rois_and_traces.hdf5') -traces_subtracted = data_f['traces_center_subtracted'].value -masks = data_f['masks_center'].value - -f, axs = plt.subplots(1, 2, figsize=(16, 5)) - -cor_mat = np.corrcoef(traces_subtracted) -fig = axs[0].imshow(cor_mat, vmin=-1, vmax=1, cmap='jet', interpolation='nearest') -axs[0].set_title('coriance matrix') -f.colorbar(fig, ax=axs[0]) - -cors = cor_mat[np.tril_indices(cor_mat.shape[0], k=-1)] -cor_dist = axs[1].hist(cors, range=[-1., 1.], bins=40) -axs[1].set_title('coriance distribution') - -# cors = np.sort(cors) -# cor_thr = cors[int(cors.shape[0] * 0.99)] -# print('Cutoff threshold for coriance: {}'.format(cor_thr)) - -pos_cor_loc = np.where(cor_mat > cor_thr) - -roi_pairs = [] -for ind in range(len(pos_cor_loc[0])): - if pos_cor_loc[0][ind] < pos_cor_loc[1][ind]: - roi_pairs.append([pos_cor_loc[0][ind], pos_cor_loc[1][ind]]) -print(roi_pairs) - -roi_grps = merger_pairs(roi_pairs) -print roi_grps - -cor_grps = [] -for roi_grp in roi_grps: - grp_traces = traces_subtracted[list(roi_grp)] - grp_cors = np.corrcoef(grp_traces)[np.tril_indices(len(roi_grp), k=-1)] - cor_grps.append(np.mean(grp_cors)) - -cor_grps = np.array(cor_grps) -cor_scalars = [(c + 1) / 2 for c in cor_grps] -print cor_scalars -cor_colors = [pt.value_2_rgb(c, cmap='inferno') for c in cor_scalars] - -f_roi = plt.figure() -ax_roi = f_roi.add_subplot(111) -ax_roi.imshow(bg, vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') -for grp_ind, roi_grp in enumerate(roi_grps): - for roi_ind in roi_grp: - print roi_ind, cor_colors[grp_ind] - pt.plot_mask_borders(masks[roi_ind], plotAxis=ax_roi, color=cor_colors[grp_ind]) - -plt.show() - -data_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/old/100_get_dff_traces.py b/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/old/100_get_dff_traces.py deleted file mode 100644 index 3cd09ff..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_multi_plane_single_channel_deepscope/within_plane_folder/old/100_get_dff_traces.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import h5py -import allensdk.brain_observatory.dff as dff -import numpy as np -import corticalmapping.HighLevel as hl -import corticalmapping.core.FileTools as ft - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_plot_dir = os.path.join(curr_folder, 'figures', 'dff_extraction') -if not os.path.isdir(save_plot_dir): - os.makedirs(save_plot_dir) - -data_f = h5py.File('rois_and_traces.hdf5') -traces_subtracted = data_f['traces_center_subtracted'].value - -traces_dff = dff.compute_dff(traces_subtracted, save_plot_dir=save_plot_dir, - mode_kernelsize=100, mean_kernelsize=100) -data_f['traces_center_dff'] = traces_dff -data_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/000_reorganize_data.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/000_reorganize_data.py deleted file mode 100644 index 9d1ed92..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/000_reorganize_data.py +++ /dev/null @@ -1,30 +0,0 @@ -import os -import numpy as np -import tifffile as tf - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180215-M371139-2p" -file_identifier = 'Posterior_FOV_00001' -ch_ns = ['green', 'red'] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -f_ns = [f for f in os.listdir(data_folder) if file_identifier in f and f[-4:] == '.tif'] -f_ns.sort() -print('\n'.join(f_ns)) - -save_folders = [] -for ch_n in ch_ns: - curr_save_folder = os.path.join(data_folder, file_identifier, ch_n) - if not os.path.isdir(curr_save_folder): - os.makedirs(curr_save_folder) - save_folders.append(curr_save_folder) - -for f_n in f_ns: - print('procesing: {} ...'.format(f_n)) - curr_mov = tf.imread(os.path.join(data_folder, f_n)) - for ch_num, ch_n in enumerate(ch_ns): - curr_mov_ch = curr_mov[ch_num::len(ch_ns)].transpose((0, 2, 1))[:, ::-1, :] - curr_save_name = os.path.splitext(f_n)[0] + '_' + ch_n + '.tif' - tf.imsave(os.path.join(save_folders[ch_num], curr_save_name), curr_mov_ch) diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/010_motion_correction.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/010_motion_correction.py deleted file mode 100644 index 7487ac3..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/010_motion_correction.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -import stia.motion_correction as mc - -def run(): - data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180104-M361012-2p\FOV1_injection_site_00001" - ref_ch_n = 'red' - apply_ch_ns = ['green', 'red'] - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - ref_data_folder = os.path.join(data_folder, ref_ch_n) - - mc.motion_correction(input_folder=ref_data_folder, - input_path_identifier='.tif', - process_num=3, - output_folder=os.path.join(ref_data_folder, 'corrected'), - anchor_frame_ind_chunk=10, - anchor_frame_ind_projection=0, - iteration_chunk=10, - iteration_projection=10, - max_offset_chunk=(20., 20.), - max_offset_projection=(20., 20.), - align_func=mc.phase_correlation, - preprocessing_type=0, - fill_value=0.) - - offsets_path = os.path.join(ref_data_folder, 'corrected', 'correction_offsets.hdf5') - ref_fns = [f for f in os.listdir(ref_data_folder) if f[-4:] == '.tif'] - ref_fns.sort() - ref_paths = [os.path.join(ref_data_folder, f) for f in ref_fns] - print('\nreference paths:') - print('\n'.join(ref_paths)) - - for apply_ch_i, apply_ch_n in enumerate(apply_ch_ns): - apply_data_folder = os.path.join(data_folder, apply_ch_n) - apply_fns = [f for f in os.listdir(apply_data_folder) if f[-4:] == '.tif'] - apply_fns.sort() - apply_paths = [os.path.join(apply_data_folder, f) for f in apply_fns] - print('\napply paths:') - print('\n'.join(apply_paths)) - - mc.apply_correction_offsets(offsets_path=offsets_path, - path_pairs=zip(ref_paths, apply_paths), - output_folder=os.path.join(apply_data_folder, 'corrected'), - process_num=3, - fill_value=0., - avi_downsample_rate=20, - is_equalizing_histogram=False) - -if __name__ == "__main__": - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/020_downsample.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/020_downsample.py deleted file mode 100644 index 6b35f29..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/020_downsample.py +++ /dev/null @@ -1,21 +0,0 @@ -import os -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia - -xy_downsample_rate = 2 -t_downsample_rate = 10 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -f_ns = [f for f in os.listdir(curr_folder) if f[-4:] == '.tif' and 'downsampled' not in f] -f_ns.sort() -print('\n'.join(f_ns)) - -for f_n in f_ns: - print('processing {} ...'.format(f_n)) - mov = tf.imread(f_n) - mov_d = ia.rigid_transform_cv2(img=mov, zoom=(1. / xy_downsample_rate)) - mov_d = ia.z_downsample(mov_d, downSampleRate=t_downsample_rate) - save_n = os.path.splitext(f_n)[0] + '_downsampled.tif' - tf.imsave(save_n, mov_d) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/030_downsample_from_server.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/030_downsample_from_server.py deleted file mode 100644 index 08e0ec6..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_multi_channel_regular_2p/030_downsample_from_server.py +++ /dev/null @@ -1,30 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180104-M361012-2p\FOV1_injection_site_00001" -chn = 'red' -xy_downsample_rate = 2 -t_downsample_rate = 30 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -f_ns = [f for f in os.listdir(os.path.join(data_folder, chn, 'corrected')) if f[-14:] == '_corrected.tif'] -f_ns.sort() -print('\n'.join(f_ns)) - -mov_d = [] - -for f_n in f_ns: - print('processing {} ...'.format(f_n)) - curr_mov = tf.imread(os.path.join(data_folder, chn, 'corrected', f_n)) - curr_mov_d = ia.rigid_transform_cv2(img=curr_mov, zoom=(1. / xy_downsample_rate)) - curr_mov_d = ia.z_downsample(curr_mov_d, downSampleRate=t_downsample_rate) - mov_d.append(curr_mov_d) - -mov_d = np.concatenate(mov_d, axis=0) -save_n = os.path.split(data_folder)[1] + '_' + chn + '_downsampled.tif' -tf.imsave(save_n, mov_d) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/010_get_wf_vas_maps.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/010_get_wf_vas_maps.py deleted file mode 100644 index 6d2f8d7..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/010_get_wf_vas_maps.py +++ /dev/null @@ -1,33 +0,0 @@ -import os -import numpy as np -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -import tifffile as tf - - -vas_map_paths= [r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" - r"\180404-M360495-2p\vasmap_wf\180404JCamF100", - r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" - r"\180404-M360495-2p\vasmap_wf\180404JCamF101", - r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" - r"\180404-M360495-2p\vasmap_wf\180404JCamF102", - ] - -saveFolder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(saveFolder) - -vas_maps = [] - -for vas_map_path in vas_map_paths: - - vas_map_focused, _, _ = ft.importRawJCamF(vas_map_path, column=1024, row=1024, headerLength = 116, - tailerLength=452) - vas_map_focused = vas_map_focused[2:] - vas_map_focused = vas_map_focused[:, ::-1, :] - vas_map_focused[vas_map_focused > 50000] = 400 - vas_map_focused = np.mean(vas_map_focused, axis=0) - vas_maps.append(ia.array_nor(vas_map_focused)) - -vas_map = ia.array_nor(np.mean(vas_maps, axis=0)) - -tf.imsave('vas_map_focused_wf.tif', vas_map.astype(np.float32)) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/020_rotate_2P_vas_maps.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/020_rotate_2P_vas_maps.py deleted file mode 100644 index 67f2c6f..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/020_rotate_2P_vas_maps.py +++ /dev/null @@ -1,38 +0,0 @@ -import os -import numpy as np -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -import tifffile as tf - - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180404-M360495-2p\vasmap_2p" - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -data_folder = os.path.join(curr_folder, data_folder) - -file_list = [f for f in os.listdir(data_folder) if f[-4:] == '.tif'] -file_list.sort() -print '\n'.join(file_list) - -file_paths = [os.path.join(data_folder, f) for f in file_list] - -for file_path in file_paths: - fn, ext = os.path.splitext(os.path.split(file_path)[1]) - save_path = os.path.join(data_folder, fn + '_rotated.tif') - print save_path - - curr_mov = tf.imread(file_path) - curr_mov = curr_mov.transpose((0, 2, 1))[:, ::-1, :] - tf.imsave(save_path, curr_mov) - - -# correction = mc.align_multiple_files_iterate(file_paths, output_folder=data_folder, is_output_mov=True, iteration=10, -# max_offset=(10., 10.), align_func=mc.phase_correlation, fill_value=0., -# verbose=True, offset_file_name=offset_file_name, -# mean_projection_file_name=mean_projection_file_name) - - -# print correction \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/030_get_2p_vas_maps.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/030_get_2p_vas_maps.py deleted file mode 100644 index 40bd360..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/030_get_2p_vas_maps.py +++ /dev/null @@ -1,34 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import matplotlib.pyplot as plt - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180404-M360495-2p\vasmap_2p" - -zoom1_paths = [os.path.join(data_folder, f) for f in os.listdir(data_folder) - if f[-12:] == '_rotated.tif' and '_zoom1_' in f] - -# zoom2_paths = [os.path.join(data_folder, f) for f in os.listdir(data_folder) -# if f[-12:] == '_rotated.tif' and '_zoom2_' in f] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -vas_map_zoom1 = [] -# vas_map_zoom2 = [] - -for zoom1_path in zoom1_paths: - curr_vasmap = np.mean(tf.imread(zoom1_path), axis=0) - vas_map_zoom1.append(curr_vasmap) - -# for zoom2_path in zoom2_paths: -# curr_vasmap = np.mean(tf.imread(zoom2_path), axis=0) -# vas_map_zoom2.append(curr_vasmap) - -vas_map_zoom1 = ia.array_nor(np.mean(vas_map_zoom1, axis=0)) -# vas_map_zoom2 = ia.array_nor(np.mean(vas_map_zoom2, axis=0)) - -tf.imsave('vas_map_focused_2p_zoom1.tif', vas_map_zoom1.astype(np.float32)) -# tf.imsave('vas_map_focused_2p_zoom2.tif', vas_map_zoom2.astype(np.float32)) diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/040_reorganize_2P_movies.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/040_reorganize_2P_movies.py deleted file mode 100644 index e3b74fe..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/040_reorganize_2P_movies.py +++ /dev/null @@ -1,61 +0,0 @@ -import os -import numpy as np -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -import tifffile as tf - -data_folder = r'\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180404-M360495-2p\2p_movie' -frames_per_file = 500 -temporal_downsample_rate = 2 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -file_list = [f for f in os.listdir(data_folder) if f[-4:] == '.tif'] -file_list.sort() -print '\n'.join(file_list) - -file_paths = [os.path.join(data_folder, f) for f in file_list] - -file_id_save = 0 -total_mov = None -base_name = '_'.join(file_list[0].split('_')[:-1]) -save_folder = os.path.join(data_folder, 'reorged') -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -for file_path in file_paths: - print('\nprocessing {} ...'.format(os.path.split(file_path)[1])) - - curr_mov = tf.imread(file_path) - curr_mov = curr_mov.transpose((0, 2, 1))[:, ::-1, :] - - if temporal_downsample_rate != 1: - curr_mov = ia.z_downsample(curr_mov, downSampleRate=temporal_downsample_rate) - - if total_mov is None: - total_mov = curr_mov - else: - total_mov = np.concatenate((total_mov, curr_mov), axis=0) - - while (total_mov is not None) and (total_mov.shape[0] >= frames_per_file): - - num_file_to_save = total_mov.shape[0] // frames_per_file - - for save_file_id in range(num_file_to_save): - save_chunk = total_mov[save_file_id * frames_per_file : (save_file_id + 1) * frames_per_file] - save_path = os.path.join(save_folder, '{}_{:05d}_reorged.tif'.format(base_name, file_id_save)) - print('saving {} ...'.format(os.path.split(save_path)[1])) - tf.imsave(save_path, save_chunk) - file_id_save = file_id_save + 1 - - if total_mov.shape[0] % frames_per_file == 0: - total_mov = None - else: - frame_num_left = total_mov.shape[0] % frames_per_file - total_mov = total_mov[-frame_num_left:] - -if total_mov is not None: - save_path = os.path.join(save_folder, '{}_{:05d}_reorged.tif'.format(base_name, file_id_save)) - print('saving {} ...'.format(os.path.split(save_path)[1])) - tf.imsave(save_path, total_mov) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/050_motion_correction.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/050_motion_correction.py deleted file mode 100644 index 42e1658..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/050_motion_correction.py +++ /dev/null @@ -1,41 +0,0 @@ -import os -import stia.motion_correction as mc - -def run(): - data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180404-M360495-2p\2p_movie\reorged" - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - mc.motion_correction(input_folder=data_folder, - input_path_identifier='.tif', - process_num=6, - output_folder=os.path.join(data_folder, 'corrected'), - anchor_frame_ind_chunk=10, - anchor_frame_ind_projection=0, - iteration_chunk=10, - iteration_projection=10, - max_offset_chunk=(40., 40.), - max_offset_projection=(40., 40.), - align_func=mc.phase_correlation, - preprocessing_type=0, - fill_value=0.) - - offsets_path = os.path.join(data_folder, 'corrected', 'correction_offsets.hdf5') - fns = [f for f in os.listdir(data_folder) if f[-4:] == '.tif'] - fns.sort() - f_paths = [os.path.join(data_folder, f) for f in fns] - print('\nfile paths:') - print('\n'.join(f_paths)) - - mc.apply_correction_offsets(offsets_path=offsets_path, - path_pairs=zip(f_paths, f_paths), - output_folder=os.path.join(data_folder, 'corrected'), - process_num=6, - fill_value=0., - avi_downsample_rate=10, - is_equalizing_histogram=True) - -if __name__ == "__main__": - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/055_downsample_from_server.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/055_downsample_from_server.py deleted file mode 100644 index 0719386..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/055_downsample_from_server.py +++ /dev/null @@ -1,34 +0,0 @@ -import os -import numpy as np -import tifffile as tf - -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180404-M360495-2p\2p_movie\reorged" -xy_downsample_rate = 2 -t_downsample_rate = 10 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -corr_folder = os.path.join(data_folder, 'corrected') - -f_ns = [f for f in os.listdir(corr_folder) if f[-14:] == '_corrected.tif'] -f_ns.sort() -print('\n'.join(f_ns)) - -mov_d = [] - -for f_n in f_ns: - print('processing {} ...'.format(f_n)) - curr_mov = tf.imread(os.path.join(corr_folder, f_n)) - curr_mov_d = ia.rigid_transform_cv2(img=curr_mov, zoom=(1. / xy_downsample_rate)) - curr_mov_d = ia.z_downsample(curr_mov_d, downSampleRate=t_downsample_rate) - mov_d.append(curr_mov_d) - -mov_d = np.concatenate(mov_d, axis=0).astype(np.int16) -tf.imsave('2p_movie_downsampled.tif', mov_d) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/060_get_image_data.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/060_get_image_data.py deleted file mode 100644 index 20db02c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/060_get_image_data.py +++ /dev/null @@ -1,40 +0,0 @@ -import os -import h5py -import numpy as np -import tifffile as tf -from toolbox.misc import BinarySlicer -import corticalmapping.core.FileTools as ft - -save_fn = '180404_M360495_110_2p_movies.hdf5' -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180404-M360495-2p\2p_movie" \ - r"\reorged\corrected" -identifier = '_00110_' -frame_num_tot = 16000 -resolution = [1024, 1024] # rows, columns of each frame - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -data_shape = (frame_num_tot, resolution[0], resolution[1]) - -curr_flist = [f for f in os.listdir(data_folder) if identifier in f and f[-14:] == '_corrected.tif'] -curr_flist.sort() -print('\n'.join(curr_flist)) - -print ('\nWriting file: ' + save_fn) -save_f = h5py.File(save_fn) -save_dset = save_f.create_dataset('2p_movie', data_shape, dtype=np.int16, compression='lzf') - -start_frame = 0 -for curr_f in curr_flist: - print curr_f - curr_mov = tf.imread(os.path.join(data_folder, curr_f)) - end_frame = start_frame + curr_mov.shape[0] - save_dset[start_frame : end_frame, :, :] = curr_mov - start_frame = end_frame - -save_dset.attrs['conversion'] = 1. -save_dset.attrs['resolution'] = 1. -save_dset.attrs['unit'] = 'arbiturary_unit' - -save_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/070_get_mmap_files_for_caiman_from_tif.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/070_get_mmap_files_for_caiman_from_tif.py deleted file mode 100644 index d19d61d..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/070_get_mmap_files_for_caiman_from_tif.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import h5py - - -file_range = None # [0, 37] # None - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180404-M360495-2p\2p_movie\reorged\corrected" -base_name = '180404_M360495_110' -t_downsample_rate = 10 - -f_ns = [f for f in os.listdir(data_folder) if f[-14:] == '_corrected.tif'] -f_ns.sort() -if file_range is not None: - f_ns = f_ns[file_range[0] : file_range[1]] -print('\n'.join(f_ns)) - -mov_join = [] -for f_n in f_ns: - curr_mov = tf.imread(os.path.join(data_folder, f_n)) - - if curr_mov.shape[0] % t_downsample_rate !=0: - raise ValueError('the frame number of {} ({}) is not divisible by t_downsample_rate ({}).' - .format(f_n, curr_mov.shape[0], t_downsample_rate)) - - curr_mov_d = ia.z_downsample(curr_mov, downSampleRate=t_downsample_rate) - mov_join.append(curr_mov_d) - -mov_join = np.concatenate(mov_join, axis=0) -add_to_mov = 10 - np.amin(mov_join) - -save_name = '{}_d1_{}_d2_{}_d3_1_order_C_frames_{}_.mmap'\ - .format(base_name, mov_join.shape[2], mov_join.shape[1], mov_join.shape[0]) - -mov_join = mov_join.reshape((mov_join.shape[0], mov_join.shape[1] * mov_join.shape[2]), order='F').transpose() - -mov_join_mmap = np.memmap(os.path.join(data_folder, save_name), shape=mov_join.shape, order='C', dtype=np.float32, - mode='w+') -mov_join_mmap[:] = mov_join + add_to_mov -mov_join_mmap.flush() -del mov_join_mmap - -save_file = h5py.File(os.path.join(data_folder, 'caiman_segmentation_results.hdf5')) -save_file['bias_added_to_movie'] = add_to_mov -save_file.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/090_show_mmap_movie.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/090_show_mmap_movie.py deleted file mode 100644 index 1b69a19..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/090_show_mmap_movie.py +++ /dev/null @@ -1,35 +0,0 @@ -import sys; print('Python %s on %s' % (sys.version, sys.platform)) -sys.path.extend([r"E:\data\github_packages\CaImAn"]) - -import os -import numpy as np -import caiman as cm -import matplotlib.pyplot as plt - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180209-M360495-2p\2p_movie\rotated\corrected" -fn = '180209_M360495_110_d1_512_d2_512_d3_1_order_C_frames_400_.mmap' - -fn_parts = fn.split('_') -d1 = int(fn_parts[fn_parts.index('d1') + 1]) # column, x -d2 = int(fn_parts[fn_parts.index('d2') + 1]) # row, y -d3 = int(fn_parts[fn_parts.index('d3') + 1]) # channel -d4 = int(fn_parts[fn_parts.index('frames') + 1]) # frame, T -order = fn_parts[fn_parts.index('order') + 1] - -mov = np.memmap(filename=os.path.join(data_folder, fn), shape=(d1, d2, d4), order=order, dtype=np.float32, mode='r') -mov = mov.transpose((2, 1, 0)) - -print('movie shape: {}'.format(mov.shape)) - -f = plt.figure(figsize=(8, 5)) -ax = f.add_subplot(111) -fig = ax.imshow(np.mean(mov, axis=0), vmin=300, vmax=1500, cmap='inferno', interpolation='nearest') -f.colorbar(fig) -plt.show() - -input("Press enter to continue ...") - -print('playing {} ...'.format(fn)) -cm.movie(mov).play(fr=30,magnification=1,gain=2.) - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/110_caiman_segmentation.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/110_caiman_segmentation.py deleted file mode 100644 index bd6b4b4..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/110_caiman_segmentation.py +++ /dev/null @@ -1,103 +0,0 @@ -import sys; print('Python %s on %s' % (sys.version, sys.platform)) -sys.path.extend([r"E:\data\github_packages\CaImAn"]) - -import os -import numpy as np -import caiman as cm -import matplotlib.pyplot as plt -from caiman.source_extraction.cnmf import cnmf as cnmf -import h5py -from shutil import copyfile - -def run(): - - data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180404-M360495-2p\2p_movie\reorged\corrected" - play_movie = False - resolution = 1024 - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - - # %% start cluster - c, dview, n_processes = cm.cluster.setup_cluster(backend='local', n_processes=6, single_thread=False) - - os.chdir(data_folder) - - fn = [f for f in os.listdir(data_folder) if f[-5:] == '.mmap'] - if len(fn) > 1: - print('\n'.join(fn)) - raise LookupError('more than one file found.') - elif len(fn) == 0: - raise LookupError('no file found.') - else: - fn = fn[0] - - fn_parts = fn.split('_') - d1 = int(fn_parts[fn_parts.index('d1') + 1]) # column, x - d2 = int(fn_parts[fn_parts.index('d2') + 1]) # row, y - d3 = int(fn_parts[fn_parts.index('d3') + 1]) # channel - d4 = int(fn_parts[fn_parts.index('frames') + 1]) # frame, T - order = fn_parts[fn_parts.index('order') + 1] - - print('playing {} ...'.format(fn)) - - mov = np.memmap(filename=fn, shape=(d1, d2, d4), order=order, dtype=np.float32, mode='r') - mov = mov.transpose((2, 1, 0)) - - # mov = cm.load(os.path.join(data_folder, fn)) - - print('shape of joined movie: {}.'.format(mov.shape)) - - #%% play movie, press q to quit - if play_movie: - cm.movie(mov).play(fr=50,magnification=1,gain=2.) - - #%% movie cannot be negative! - mov_min = float(np.amin(mov)) - print('minimum pixel value: {}.'.format(mov_min)) - if mov_min < 0: - raise Exception('Movie too negative, add_to_movie should be larger') - - #%% correlation image. From here infer neuron size and density - Cn = cm.movie(mov).local_correlations(swap_dim=False) - plt.imshow(Cn, cmap='gray') - plt.show() - - cnm = cnmf.CNMF(n_processes, - k=40, # number of neurons expected per patch - gSig=[5, 5] , # expected half size of neurons - merge_thresh=0.9, # merging threshold, max correlation allowed - p=2, # order of the autoregressive system - dview=dview, - Ain=None, - method_deconvolution='oasis', - rolling_sum = False, - method_init='sparse_nmf', - alpha_snmf=10e1, - ssub=1, - tsub=1, - p_ssub=1, - p_tsub=1, - rf=int(resolution / 2), # half-size of the patches in pixels - border_pix=20, - do_merge=False) - cnm = cnm.fit(mov) - A, C, b, f, YrA, sn = cnm.A, cnm.C, cnm.b, cnm.f, cnm.YrA, cnm.sn - #%% - crd = cm.utils.visualization.plot_contours(cnm.A, Cn) - plt.show() - input("Press enter to continue ...") - - roi_num = cnm.A.shape[1] - save_fn = h5py.File('caiman_segmentation_results.hdf5') - bias = save_fn['bias_added_to_movie'].value - save_fn['masks'] = np.array(cnm.A.todense()).T.reshape((roi_num, resolution, resolution), order='F') - save_fn['traces'] = cnm.C - bias - save_fn.close() - - copyfile(os.path.join(data_folder, 'caiman_segmentation_results.hdf5'), - os.path.join(curr_folder, 'caiman_segmentation_results.hdf5')) - - -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/120_get_cells_file.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/120_get_cells_file.py deleted file mode 100644 index 97a3a27..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/120_get_cells_file.py +++ /dev/null @@ -1,87 +0,0 @@ -import os -import numpy as np -import h5py -import tifffile as tf -import allensdk_internal.brain_observatory.mask_set as mask_set -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import scipy.ndimage as ni -import matplotlib.pyplot as plt - - -isSave = True -is_filter = False - -filter_sigma = 0.5 # parameters only used if filter the rois -dilation_iterations = 0 # parameters only used if filter the rois -cut_thr = 3. # parameters only used if filter the rois - -bg_fn = "corrected_mean_projection.tif" -save_folder = 'figures' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -data_f = h5py.File('caiman_segmentation_results.hdf5') -masks = data_f['masks'].value -data_f.close() - -bg = tf.imread(bg_fn) - -final_roi_dict = {} - -for i, mask in enumerate(masks): - - if is_filter: - mask_nor = (mask - np.mean(mask.flatten())) / np.abs(np.std(mask.flatten())) - mask_nor_f = ni.filters.gaussian_filter(mask_nor, filter_sigma) - mask_bin = np.zeros(mask_nor_f.shape, dtype=np.uint8) - mask_bin[mask_nor_f > cut_thr] = 1 - - else: - mask_bin = np.zeros(mask.shape, dtype=np.uint8) - mask_bin[mask > 0] = 1 - - mask_labeled, mask_num = ni.label(mask_bin) - curr_mask_dict = ia.get_masks(labeled=mask_labeled, keyPrefix='caiman_mask_{:03d}'.format(i), labelLength=5) - for roi_key, roi_mask in curr_mask_dict.items(): - final_roi_dict.update({roi_key: ia.WeightedROI(roi_mask * mask)}) - -print 'Total number of ROIs:',len(final_roi_dict) - -f = plt.figure(figsize=(15, 8)) -ax1 = f.add_subplot(121) -ax1.imshow(ia.array_nor(bg), vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') -colors1 = pt.random_color(masks.shape[0]) -for i, mask in enumerate(masks): - pt.plot_mask_borders(mask, plotAxis=ax1, color=colors1[i]) -ax1.set_title('original ROIs') -ax1.set_axis_off() -ax2 = f.add_subplot(122) -ax2.imshow(ia.array_nor(bg), vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') -colors2 = pt.random_color(len(final_roi_dict)) -i = 0 -for roi in final_roi_dict.values(): - pt.plot_mask_borders(roi.get_binary_mask(), plotAxis=ax2, color=colors2[i]) - i = i + 1 -ax2.set_title('filtered ROIs') -ax2.set_axis_off() -plt.show() - -if isSave: - - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - f.savefig(os.path.join(save_folder, 'caiman_segmentation_filtering.pdf'), dpi=300) - - cell_file = h5py.File('cells.hdf5', 'w') - - i = 0 - for key, value in sorted(final_roi_dict.iteritems()): - curr_grp = cell_file.create_group('cell{:04d}'.format(i)) - curr_grp.attrs['name'] = key - value.to_h5_group(curr_grp) - i += 1 - - cell_file.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/130_refine_cells.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/130_refine_cells.py deleted file mode 100644 index 88b88e5..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/130_refine_cells.py +++ /dev/null @@ -1,173 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Jun 30 17:44:42 2015 - -@author: junz -""" -import os -import h5py -import numpy as np -import operator -import matplotlib.pyplot as plt -import scipy.ndimage as ni -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.FileTools as ft -import corticalmapping.core.PlottingTools as pt -import corticalmapping.SingleCellAnalysis as sca - -plt.ioff() - -# pixels, masks with center location within this pixel region at the image border will be discarded -center_margin = [20, 20] - -# area range, range of number of pixels of a valid roi -area_range = [20, 10000] - -# for the two masks that are overlapping, if the ratio between overlap and the area of the smaller mask is larger than -# this value, the smaller mask will be discarded. -overlap_thr = 0.5 - -save_folder = 'figures' - -data_file_name = 'cells.hdf5' -save_file_name = 'cells_refined.hdf5' -background_file_name = "corrected_mean_projection.tif" - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -# read cells -dfile = h5py.File(data_file_name) -cells = {} -for cellname in dfile.iterkeys(): - cells.update({cellname:ia.WeightedROI.from_h5_group(dfile[cellname])}) - -print 'total number of cells:', len(cells) - -# get the names of cells which are on the edge -edge_cells = [] -for cellname, cellmask in cells.iteritems(): - dimension = cellmask.dimension - center = cellmask.get_center() - if center[0] < center_margin[0] or \ - center[0] > dimension[0] - center_margin[0] or \ - center[1] < center_margin[1] or \ - center[1] > dimension[1] - center_margin[1]: - - # cellmask.plot_binary_mask_border(color='#ff0000', borderWidth=1) - # plt.title(cellname) - # plt.show() - - edge_cells.append(cellname) - -print '\ncells to be removed because they are on the edges:' -print '\n'.join(edge_cells) - -# remove edge cells -for edge_cell in edge_cells: - _ = cells.pop(edge_cell) - -# get dictionary of cell areas -cell_areas = {} -for cellname, cellmask in cells.iteritems(): - cell_areas.update({cellname: cellmask.get_binary_area()}) - - -# remove cellnames that have area outside of the area_range -invalid_cell_ns = [] -for cellname, cellarea in cell_areas.items(): - if cellarea < area_range[0] or cellarea > area_range[1]: - invalid_cell_ns.append(cellname) -print "cells to be removed because they do not meet area criterion:" -print "\n".join(invalid_cell_ns) -for invalid_cell_n in invalid_cell_ns: - cell_areas.pop(invalid_cell_n) - - -# sort cells with their binary area -cell_areas_sorted = sorted(cell_areas.items(), key=operator.itemgetter(1)) -cell_areas_sorted.reverse() -cell_names_sorted = [c[0] for c in cell_areas_sorted] -# print '\n'.join([str(c) for c in cell_areas_sorted]) - -# get the name of cells that needs to be removed because of overlapping -retain_cells = [] -remove_cells = [] -for cell1_name in cell_names_sorted: - cell1_mask = cells[cell1_name] - is_remove = 0 - cell1_area = cell1_mask.get_binary_area() - for cell2_name in retain_cells: - cell2_mask = cells[cell2_name] - cell2_area = cell2_mask.get_binary_area() - curr_overlap = cell1_mask.binary_overlap(cell2_mask) - - if float(curr_overlap) / cell1_area > overlap_thr: - remove_cells.append(cell1_name) - is_remove = 1 - print cell1_name, ':', cell1_mask.get_binary_area(), ': removed' - - # f = plt.figure(figsize=(10,10)) - # ax = f.add_subplot(111) - # cell1_mask.plot_binary_mask_border(plotAxis=ax, color='#ff0000', borderWidth=1) - # cell2_mask.plot_binary_mask_border(plotAxis=ax, color='#0000ff', borderWidth=1) - # ax.set_title('red:'+cell1_name+'; blue:'+cell2_name) - # plt.show() - break - - if is_remove == 0: - retain_cells.append(cell1_name) - print cell1_name, ':', cell1_mask.get_binary_area(), ': retained' - -print '\ncells to be removed because of overlapping:' -print '\n'.join(remove_cells) - -print '\ntotal number of reatined cells:', len(retain_cells) - -# plotting -colors = pt.random_color(len(cells.keys())) -bgImg = tf.imread(background_file_name) - -f = plt.figure(figsize=(10, 10)) -ax = f.add_subplot(111) -ax.imshow(ia.array_nor(bgImg), cmap='gray', vmin=0, vmax=0.5, interpolation='nearest') - -f2 = plt.figure(figsize=(10, 10)) -ax2 = f2.add_subplot(111) -ax2.imshow(np.zeros(bgImg.shape, dtype=np.uint8), vmin=0, vmax=1, cmap='gray', interpolation='nearest') - -i = 0 -for retain_cell in retain_cells: - cells[retain_cell].plot_binary_mask_border(plotAxis=ax, color=colors[i], borderWidth=1) - cells[retain_cell].plot_binary_mask_border(plotAxis=ax2, color=colors[i], borderWidth=1) - i += 1 -plt.show() - -# save figures -pt.save_figure_without_borders(f, os.path.join(save_folder, '2P_refined_ROIs_with_background.png'), dpi=300) -pt.save_figure_without_borders(f2, os.path.join(save_folder, '2P_refined_ROIs_without_background.png'), dpi=300) - -# save h5 file -save_file = h5py.File(save_file_name, 'w') -i = 0 -for retain_cell in retain_cells: - print retain_cell, ':', cells[retain_cell].get_binary_area() - - currGroup = save_file.create_group('cell' + ft.int2str(i, 4)) - currGroup.attrs['name'] = retain_cell - roiGroup = currGroup.create_group('roi') - cells[retain_cell].to_h5_group(roiGroup) - i += 1 - -for attr, value in dfile.attrs.iteritems(): - save_file.attrs[attr] = value - -save_file.close() -dfile.close() - - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/140_get_weighted_rois_and_surrounds.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/140_get_weighted_rois_and_surrounds.py deleted file mode 100644 index b31e405..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/140_get_weighted_rois_and_surrounds.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Jun 30 17:44:42 2015 - -@author: junz -""" - -import os -import numpy as np -import h5py -import tifffile as tf -import allensdk_internal.brain_observatory.mask_set as mask_set -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import scipy.ndimage as ni -import matplotlib.pyplot as plt - -plt.ioff() - -data_file_name = 'cells_refined.hdf5' -background_file_name = "corrected_mean_projection.tif" -save_folder = 'figures' - -overlap_threshold = 0.9 -surround_limit = [1, 8] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -print 'reading cells file ...' -data_f = h5py.File(data_file_name, 'r') - -cell_ns = data_f.keys() -cell_ns.sort() - -binary_mask_array = [] -weight_mask_array = [] - -for cell_n in cell_ns: - curr_roi = ia.ROI.from_h5_group(data_f[cell_n]['roi']) - binary_mask_array.append(curr_roi.get_binary_mask()) - weight_mask_array.append(curr_roi.get_weighted_mask()) - -data_f.close() -binary_mask_array = np.array(binary_mask_array) -weight_mask_array = np.array(weight_mask_array) -print 'starting mask_array shape:', weight_mask_array.shape - -print 'getting total mask ...' -total_mask = np.zeros((binary_mask_array.shape[1], binary_mask_array.shape[2]), dtype=np.uint8) -for curr_mask in binary_mask_array: - total_mask = np.logical_or(total_mask, curr_mask) -total_mask = np.logical_not(total_mask) - -plt.imshow(total_mask, interpolation='nearest') -plt.title('total_mask') -plt.show() - -print 'getting and surround masks ...' -binary_surround_array = [] -for binary_center in binary_mask_array: - curr_surround = np.logical_xor(ni.binary_dilation(binary_center, iterations=surround_limit[1]), - ni.binary_dilation(binary_center, iterations=surround_limit[0])) - curr_surround = np.logical_and(curr_surround, total_mask).astype(np.uint8) - binary_surround_array.append(curr_surround) - # plt.imshow(curr_surround) - # plt.show() -binary_surround_array = np.array(binary_surround_array) - -print "saving rois ..." -center_areas = [] -surround_areas = [] -for mask_ind in range(binary_mask_array.shape[0]): - center_areas.append(np.sum(binary_mask_array[mask_ind].flat)) - surround_areas.append(np.sum(binary_surround_array[mask_ind].flat)) -roi_f = h5py.File('rois_and_traces.hdf5') -roi_f['masks_center'] = weight_mask_array -roi_f['masks_surround'] = binary_surround_array - -roi_f.close() -print 'minimum surround area:', min(surround_areas), 'pixels.' - -f = plt.figure(figsize=(10, 10)) -ax_center = f.add_subplot(211) -ax_center.hist(center_areas, bins=30) -ax_center.set_title('roi center area distribution') -ax_surround = f.add_subplot(212) -ax_surround.hist(surround_areas, bins=30) -ax_surround.set_title('roi surround area distribution') -plt.show() - -print 'plotting ...' -colors = pt.random_color(weight_mask_array.shape[0]) -bg = ia.array_nor(tf.imread('corrected_mean_projection.tif')) - -f_c_bg = plt.figure(figsize=(10, 10)) -ax_c_bg = f_c_bg.add_subplot(111) -ax_c_bg.imshow(bg, cmap='gray', vmin=0, vmax=0.5, interpolation='nearest') -f_c_nbg = plt.figure(figsize=(10, 10)) -ax_c_nbg = f_c_nbg.add_subplot(111) -ax_c_nbg.imshow(np.zeros(bg.shape,dtype=np.uint8),vmin=0,vmax=1,cmap='gray',interpolation='nearest') -f_s_nbg = plt.figure(figsize=(10, 10)) -ax_s_nbg = f_s_nbg.add_subplot(111) -ax_s_nbg.imshow(np.zeros(bg.shape,dtype=np.uint8),vmin=0,vmax=1,cmap='gray',interpolation='nearest') - -i = 0 -for mask_ind in range(binary_mask_array.shape[0]): - pt.plot_mask_borders(binary_mask_array[mask_ind], plotAxis=ax_c_bg, color=colors[i], borderWidth=1) - pt.plot_mask_borders(binary_mask_array[mask_ind], plotAxis=ax_c_nbg, color=colors[i], borderWidth=1) - pt.plot_mask_borders(binary_surround_array[mask_ind], plotAxis=ax_s_nbg, color=colors[i], borderWidth=1) - i += 1 - -plt.show() - -print 'saving figures ...' -pt.save_figure_without_borders(f_c_bg, os.path.join(save_folder, '2P_ROIs_with_background.png'), dpi=300) -pt.save_figure_without_borders(f_c_nbg, os.path.join(save_folder, '2P_ROIs_without_background.png'), dpi=300) -pt.save_figure_without_borders(f_s_nbg, os.path.join(save_folder, '2P_ROI_surrounds_background.png'), dpi=300) -f.savefig(os.path.join(save_folder, 'roi_area_distribution.pdf'), dpi=300) diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/150_get_raw_center_and_surround_traces.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/150_get_raw_center_and_surround_traces.py deleted file mode 100644 index 0e57582..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/150_get_raw_center_and_surround_traces.py +++ /dev/null @@ -1,129 +0,0 @@ -import os -import numpy as np -import h5py -import time -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import corticalmapping.core.FileTools as ft -import corticalmapping.NwbTools as nt -import matplotlib.pyplot as plt -from multiprocessing import Pool - -CHUNK_SIZE = 2000 -PROCESS_NUM = 5 - -def get_chunk_frames(frame_num, chunk_size): - chunk_num = frame_num // chunk_size - if frame_num % chunk_size > 0: - chunk_num = chunk_num + 1 - - print("total number of frames:", frame_num) - print("total number of chunks:", chunk_num) - - chunk_ind = [] - chunk_starts = [] - chunk_ends = [] - - for chunk_i in range(chunk_num): - chunk_ind.append(chunk_i) - chunk_starts.append(chunk_i * chunk_size) - - if chunk_i < chunk_num - 1: - chunk_ends.append((chunk_i + 1) * chunk_size) - else: - chunk_ends.append(frame_num) - - return zip(chunk_ind, chunk_starts, chunk_ends) - -def get_traces(params): - t0 = time.time() - - chunk_ind, chunk_start, chunk_end, nwb_path, data_path, curr_folder, center_array, surround_array = params - - nwb_f = h5py.File(nwb_path, 'r') - print('\nstart analyzing chunk: {}'.format(chunk_ind)) - curr_mov = nwb_f[data_path][chunk_start: chunk_end] - nwb_f.close() - - # print 'extracting traces' - curr_traces_center = np.empty((center_array.shape[0], curr_mov.shape[0]), dtype=np.float32) - curr_traces_surround = np.empty((center_array.shape[0], curr_mov.shape[0]), dtype=np.float32) - for i in range(center_array.shape[0]): - curr_center = ia.WeightedROI(center_array[i]) - curr_surround = ia.ROI(surround_array[i]) - curr_traces_center[i, :] = curr_center.get_weighted_trace_pixelwise(curr_mov) - - # scale surround trace to be similar as center trace - mean_center_weight = curr_center.get_mean_weight() - curr_traces_surround[i, :] = curr_surround.get_binary_trace_pixelwise(curr_mov) * mean_center_weight - - # print 'saveing chunk {} ...'.format(chunk_ind) - chunk_folder = os.path.join(curr_folder, 'chunks') - if not os.path.isdir(chunk_folder): - os.mkdir(chunk_folder) - chunk_f = h5py.File(os.path.join(chunk_folder, 'chunk_temp_' + ft.int2str(chunk_ind, 4) + '.hdf5')) - chunk_f['traces_center'] = curr_traces_center - chunk_f['traces_surround'] = curr_traces_surround - chunk_f.close() - - print('\n\t{:06d} seconds: chunk: {}; demixing finished.'.format(int(time.time() - t0), chunk_ind)) - - return None - -def run(): - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - plane_n = os.path.split(curr_folder)[1] - print(plane_n) - - print('getting masks ...') - rois_f = h5py.File('rois_and_traces.hdf5') - center_array = rois_f['masks_center'].value - surround_array = rois_f['masks_surround'].value - - print('\nanalyzing movie in chunks of size:', CHUNK_SIZE , 'frames.') - - nwb_folder = os.path.dirname(curr_folder) - nwb_fn = [f for f in os.listdir(nwb_folder) if f[-4:] == '.nwb'][0] - nwb_path = os.path.join(nwb_folder, nwb_fn) - print('\n' + nwb_path) - data_path = '/processing/motion_correction/MotionCorrection/' + plane_n + '/corrected/data' - - nwb_f = h5py.File(nwb_path, 'r') - total_frame = nwb_f[data_path].shape[0] - nwb_f.close() - - chunk_frames = get_chunk_frames(total_frame, CHUNK_SIZE) - chunk_params = [(cf[0], cf[1], cf[2], nwb_path, data_path, - curr_folder, center_array, surround_array) for cf in chunk_frames] - - p = Pool(PROCESS_NUM) - p.map(get_traces, chunk_params) - - chunk_folder = os.path.join(curr_folder, 'chunks') - chunk_fns = [f for f in os.listdir(chunk_folder) if f[0:11] == 'chunk_temp_'] - chunk_fns.sort() - print('\nreading chunks files ...') - print('\n'.join(chunk_fns)) - - traces_raw = [] - traces_surround = [] - - for chunk_fn in chunk_fns: - curr_chunk_f = h5py.File(os.path.join(chunk_folder, chunk_fn)) - traces_raw.append(curr_chunk_f['traces_center'].value) - traces_surround.append(curr_chunk_f['traces_surround'].value) - - print("saving ...") - traces_raw = np.concatenate(traces_raw, axis=1) - traces_surround = np.concatenate(traces_surround, axis=1) - rois_f['traces_center_raw'] = traces_raw - rois_f['traces_surround_raw'] = traces_surround - print('done.') - - -if __name__ == '__main__': - run() - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/160_get_neuropil_subtracted_traces.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/160_get_neuropil_subtracted_traces.py deleted file mode 100644 index 71da943..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/160_get_neuropil_subtracted_traces.py +++ /dev/null @@ -1,101 +0,0 @@ -import sys -import os -import h5py -import numpy as np -import corticalmapping.HighLevel as hl -import corticalmapping.core.FileTools as ft -import matplotlib.pyplot as plt - - -lam = 100. -plot_chunk_size = 5000 - - -def plot_traces_chunks(traces, labels, chunk_size, roi_ind): - """ - - :param traces: np.array, shape=[trace_type, t_num] - :param labels: - :param chunk_size: - :param figures_folder: - :param roi_ind: - :return: - """ - - t_num = traces.shape[1] - chunk_num = t_num // chunk_size - - chunks = [] - for chunk_ind in range(chunk_num): - chunks.append([chunk_ind * chunk_size, (chunk_ind + 1) * chunk_size]) - - if t_num % chunk_size != 0: - chunks.append([chunk_num * chunk_size, t_num]) - - v_max = np.amax(traces) - v_min = np.amin(traces) - - fig = plt.figure(figsize=(75, 20)) - fig.suptitle('neuropil subtraction for ROI: {}'.format(roi_ind)) - for chunk_ind, chunk in enumerate(chunks): - curr_ax = fig.add_subplot(len(chunks), 1, chunk_ind + 1) - for trace_ind in range(traces.shape[0]): - curr_ax.plot(traces[trace_ind, chunk[0]: chunk[1]], label=labels[trace_ind]) - - curr_ax.set_xlim([0, chunk_size]) - curr_ax.set_ylim([v_min, v_max * 1.2]) - curr_ax.legend() - - return fig - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -data_f = h5py.File('rois_and_traces.hdf5') -traces_raw = data_f['traces_center_raw'].value -traces_srround = data_f['traces_surround_raw'].value - -traces_subtracted = np.zeros(traces_raw.shape, np.float32) -ratio = np.zeros(traces_raw.shape[0], np.float32) -err = np.zeros(traces_raw.shape[0], np.float32) - -for i in range(traces_raw.shape[0]): - curr_trace_c = traces_raw[i] - curr_trace_s = traces_srround[i] - curr_r, curr_err, curr_trace_sub = hl.neural_pil_subtraction(curr_trace_c, curr_trace_s, lam=lam) - print "roi_%s \tr = %.4f; error = %.4f." % (ft.int2str(i, 5), curr_r, curr_err) - traces_subtracted[i] = curr_trace_sub - ratio[i] = curr_r - err[i] = curr_err - -print('\nplotting neuropil subtraction results ...') -figures_folder = 'figures/neuropil_subtraction_lam_{}'.format(lam) -if not os.path.isdir(figures_folder): - os.makedirs(figures_folder) -for roi_ind in range(traces_raw.shape[0]): - print('roi_{:04d}'.format(roi_ind)) - curr_traces = np.array([traces_raw[roi_ind], traces_srround[roi_ind], traces_subtracted[roi_ind]]) - curr_fig = plot_traces_chunks(traces=curr_traces, - labels=['center', 'surround', 'subtracted'], - chunk_size=plot_chunk_size, - roi_ind=roi_ind) - curr_fig.savefig(os.path.join(figures_folder, 'neuropil_subtraction_ROI_{:04d}.png'.format(roi_ind))) - curr_fig.clear() - plt.close(curr_fig) - -# wait for keyboard abortion -msg = raw_input('Do you want to save? (y/n)\n') -while True: - if msg == 'y': - break - elif msg == 'n': - sys.exit('Stop process without saving.') - else: - msg = raw_input('Do you want to save? (y/n)\n') - -data_f['traces_center_subtracted'] = traces_subtracted -data_f['neuropil_r'] = ratio -data_f['neuropil_err'] = err - -data_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/180_check_correlation.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/180_check_correlation.py deleted file mode 100644 index 65c3f07..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/180_check_correlation.py +++ /dev/null @@ -1,99 +0,0 @@ -import os -import h5py -import tifffile as tf -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.core.PlottingTools as pt -import corticalmapping.core.ImageAnalysis as ia - - -cor_thr = 0.8 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -def merger_pairs(pairs): - - total_set = set([]) - for pair in pairs: - total_set.update(set(pair)) - - all_nodes = list(total_set) - node_grps = [{n} for n in all_nodes] - - for pair in pairs: - - node0 = pair[0] - node1 = pair[1] - - for node_grp in node_grps: - if node0 in node_grp: - node_grp0 = node_grp - if node1 in node_grp: - node_grp1 = node_grp - - if node_grp0 != node_grp1: - node_grp0.update(node_grp1) - node_grps.remove(node_grp1) - - return node_grps - - -save_plot_dir = os.path.join(curr_folder, 'figures', 'dff_extraction') -if not os.path.isdir(save_plot_dir): - os.makedirs(save_plot_dir) - -bg = ia.array_nor(tf.imread('corrected_mean_projection.tif')) - -data_f = h5py.File('rois_and_traces.hdf5') -traces_subtracted = data_f['traces_center_subtracted'].value -masks = data_f['masks_center'].value - -f, axs = plt.subplots(1, 2, figsize=(16, 5)) - -cor_mat = np.corrcoef(traces_subtracted) -fig = axs[0].imshow(cor_mat, vmin=-1, vmax=1, cmap='jet', interpolation='nearest') -axs[0].set_title('coriance matrix') -f.colorbar(fig, ax=axs[0]) - -cors = cor_mat[np.tril_indices(cor_mat.shape[0], k=-1)] -cor_dist = axs[1].hist(cors, range=[-1., 1.], bins=40) -axs[1].set_title('coriance distribution') - -# cors = np.sort(cors) -# cor_thr = cors[int(cors.shape[0] * 0.99)] -# print('Cutoff threshold for coriance: {}'.format(cor_thr)) - -pos_cor_loc = np.where(cor_mat > cor_thr) - -roi_pairs = [] -for ind in range(len(pos_cor_loc[0])): - if pos_cor_loc[0][ind] < pos_cor_loc[1][ind]: - roi_pairs.append([pos_cor_loc[0][ind], pos_cor_loc[1][ind]]) -print(roi_pairs) - -roi_grps = merger_pairs(roi_pairs) -print roi_grps - -cor_grps = [] -for roi_grp in roi_grps: - grp_traces = traces_subtracted[list(roi_grp)] - grp_cors = np.corrcoef(grp_traces)[np.tril_indices(len(roi_grp), k=-1)] - cor_grps.append(np.mean(grp_cors)) - -cor_grps = np.array(cor_grps) -cor_scalars = [(c + 1) / 2 for c in cor_grps] -print cor_scalars -cor_colors = [pt.value_2_rgb(c, cmap='inferno') for c in cor_scalars] - -f_roi = plt.figure() -ax_roi = f_roi.add_subplot(111) -ax_roi.imshow(bg, vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') -for grp_ind, roi_grp in enumerate(roi_grps): - for roi_ind in roi_grp: - print roi_ind, cor_colors[grp_ind] - pt.plot_mask_borders(masks[roi_ind], plotAxis=ax_roi, color=cor_colors[grp_ind]) - -plt.show() - -data_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/200_generate_nwb.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/200_generate_nwb.py deleted file mode 100644 index 06c0370..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/200_generate_nwb.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import corticalmapping.NwbTools as nt - -date_recorded = '180404' -mouse_id = '360495' -sess_num = '110' - -experimenter = 'Jun' -genotype = 'Vipr2-IRES2-Cre-neo' -sex = 'male' -age = '180' -indicator = 'GCaMP6s' -imaging_rate = 15.24 -imaging_depth = '150 microns' -imaging_location = 'visual cortex' -imaging_device = 'Sutter 2p Scope' -imaging_excitation_lambda = '920 nanometers' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -notebook_path = os.path.join(curr_folder, 'notebook.txt') -with open(notebook_path, 'r') as ff: - notes = ff.read() - -general = nt.DEFAULT_GENERAL -general['experimenter'] = experimenter -general['subject']['subject_id'] = mouse_id -general['subject']['genotype'] = genotype -general['subject']['sex'] = sex -general['subject']['age'] = age -general['optophysiology'].update({'imaging_plane_1': {}}) -general['optophysiology']['imaging_plane_1'].update({'indicator': indicator}) -general['optophysiology']['imaging_plane_1'].update({'imaging_rate': imaging_rate}) -general['optophysiology']['imaging_plane_1'].update({'imaging_depth': imaging_depth}) -general['optophysiology']['imaging_plane_1'].update({'location': imaging_location}) -general['optophysiology']['imaging_plane_1'].update({'device': imaging_device}) -general['optophysiology']['imaging_plane_1'].update({'excitation_lambda': imaging_excitation_lambda}) -general['notes'] = notes - -file_name = date_recorded + '_M' + mouse_id + '_' + sess_num + '.nwb' - -rf = nt.RecordedFile(os.path.join(curr_folder, file_name), identifier=file_name[:-4], description='') -rf.add_general(general=general) -rf.close() - - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/210_add_vasmap.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/210_add_vasmap.py deleted file mode 100644 index a35455f..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/210_add_vasmap.py +++ /dev/null @@ -1,38 +0,0 @@ -import os -import corticalmapping.NwbTools as nt -import corticalmapping.core.ImageAnalysis as ia -import matplotlib.pyplot as plt -import tifffile as tf - - -vasmap_name_wf = 'vas_map_focused_wf.tif' -vasmap_name_2p_zoom1 = 'vas_map_focused_2p_zoom1.tif' -# vasmap_name_2p_zoom2 = 'vas_map_focused_2p_zoom2.tif' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -vasmap_wf = tf.imread(vasmap_name_wf) -vasmap_2p_zoom1 = ia.array_nor(tf.imread(vasmap_name_2p_zoom1)) -# vasmap_2p_zoom2 = ia.array_nor(tf.imread(vasmap_name_2p_zoom2)) - -f = plt.figure(figsize=(15, 7)) -ax1 = f.add_subplot(121) -ax1.imshow(vasmap_wf, cmap='gray', interpolation='nearest') -ax1.set_title('wide field surface vasculature') -ax2 = f.add_subplot(122) -ax2.imshow(vasmap_2p_zoom1, vmin=0, vmax=0.1, cmap='gray', interpolation='nearest') -ax2.set_title('two photon surface vasculature') -plt.show() - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -nwb_f.add_acquisition_image('surface_vas_map_wf', vasmap_wf, - description='wide field surface vasculature map through cranial window') -nwb_f.add_acquisition_image('surface_vas_map_2p_zoom1', vasmap_2p_zoom1, - description='2-photon surface vasculature map through cranial window, zoom 1') -# nwb_f.add_acquisition_image('surface_vas_map_2p_zoom2', vasmap_2p_zoom2, -# description='2-photon surface vasculature map through cranial window, zoom 2') - -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/220_add_sync_data.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/220_add_sync_data.py deleted file mode 100644 index 1341950..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/220_add_sync_data.py +++ /dev/null @@ -1,23 +0,0 @@ -import os -import corticalmapping.NwbTools as nt - -record_date = '180404' -mouse_id = '360495' -session_id = '110' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = record_date + '_M' + mouse_id + '_' + session_id + '.nwb' - -sync_fn = [f for f in os.listdir(curr_folder) if f[-3:] == '.h5' and record_date in f and 'M' + mouse_id in f] -if len(sync_fn) == 0: - raise LookupError('Did not find sync .h5 file.') -elif len(sync_fn) > 1: - raise LookupError('More than one sync .h5 files found.') -else: - sync_fn = sync_fn[0] - -nwb_f = nt.RecordedFile(nwb_fn) -nwb_f.add_sync_data(sync_fn) -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/230_add_image_data.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/230_add_image_data.py deleted file mode 100644 index 3304bd7..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/230_add_image_data.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -import h5py -import corticalmapping.NwbTools as nt - -dset_n = '2p_movie' -temporal_downsample_rate = 2 -pixel_size = 0.00000035 # meter, 0.2 micron, deepscope 8K Hz scanner, zoom 2, 1024 x 1024 - -description = '2-photon imaging data' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) -mov_ts = nwb_f.file_pointer['/acquisition/timeseries/digital_vsync_2p_rise/timestamps'].value -print('\ntotal 2p timestamps count: {}'.format(len(mov_ts))) - -mov_ts_d = mov_ts[::temporal_downsample_rate] -print('downsampled 2p timestamps count: {}'.format(len(mov_ts_d))) - -mov_fn = os.path.splitext(nwb_fn)[0] + '_2p_movies.hdf5' -mov_f = h5py.File(mov_fn, 'r') -mov_dset = mov_f[dset_n] -print('downsampled 2p movie frame num: {}'.format(mov_dset.shape[0])) - -mov_ts_d = mov_ts_d[0: mov_dset.shape[0]] - -# if len(mov_ts) == mov_dset.shape[0]: -# pass -# elif len(mov_ts) == mov_dset.shape[0] + 1: -# mov_ts = mov_ts[0: -1] -# else: -# raise ValueError('the number of timestamps of {} movie ({}) does not equal (or is not greater by one) ' -# 'the number of frames in the movie ({})'.format(mov_dn, len(mov_ts), curr_dset.shape[0])) - -nwb_f.add_acquired_image_series_as_remote_link('2p_movie', image_file_path=mov_fn, dataset_path=dset_n, - timestamps=mov_ts_d, description=description, comments='', - data_format='zyx', pixel_size=[pixel_size, pixel_size], - pixel_size_unit='meter') - -mov_f.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/240_add_motion_correction_module.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/240_add_motion_correction_module.py deleted file mode 100644 index 335058c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/240_add_motion_correction_module.py +++ /dev/null @@ -1,42 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import h5py -import corticalmapping.NwbTools as nt - -corrected_file_path = '180404_M360495_110_2p_movies.hdf5' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -input_parameters = [] - -offsets_path = 'correction_offsets.hdf5' -offsets_f = h5py.File(offsets_path) -offsets_keys = offsets_f.keys() -if 'path_list' in offsets_keys: - offsets_keys.remove('path_list') - -offsets_keys.sort() -offsets = [] -for offsets_key in offsets_keys: - offsets.append(offsets_f[offsets_key].value) -offsets = np.concatenate(offsets, axis=0) -offsets = np.array(zip(offsets[:, 1], offsets[:, 0])) -offsets_f.close() - -mean_projection = tf.imread('corrected_mean_projection.tif') -max_projection = tf.imread('corrected_max_projection.tif') - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -nwb_f.add_motion_correction_module(module_name='motion_correction', - original_timeseries_path='/acquisition/timeseries/2p_movie', - corrected_file_path=corrected_file_path, corrected_dataset_path='2p_movie', - xy_translation_offsets=offsets, mean_projection=mean_projection, - max_projection=max_projection) -nwb_f.close() - - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/250_get_photodiode_onset.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/250_get_photodiode_onset.py deleted file mode 100644 index fe4ec1d..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/250_get_photodiode_onset.py +++ /dev/null @@ -1,45 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.HighLevel as hl - -# photodiode -digitizeThr = 0.8 -filterSize = 0.01 -segmentThr = 0.01 -smallestInterval = 0.05 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] - -nwb_f = nt.RecordedFile(nwb_fn) -pd, pd_t = nwb_f.get_analog_data(ch_n='analog_photodiode') -fs = 1. / np.mean(np.diff(pd_t)) -# print fs - -pd_onsets = hl.segmentPhotodiodeSignal(pd, digitizeThr=digitizeThr, filterSize=filterSize, - segmentThr=segmentThr, Fs=fs, smallestInterval=smallestInterval) - -raw_input('press enter to continue ...') - -pdo_ts = nwb_f.create_timeseries('TimeSeries', 'digital_photodiode_rise', modality='other') -pdo_ts.set_time(pd_onsets) -pdo_ts.set_data([], unit='', conversion=np.nan, resolution=np.nan) -pdo_ts.set_value('digitize_threshold', digitizeThr) -pdo_ts.set_value('filter_size', filterSize) -pdo_ts.set_value('segment_threshold', segmentThr) -pdo_ts.set_value('smallest_interval', smallestInterval) -pdo_ts.set_description('Real Timestamps (master acquisition clock) of photodiode onset. ' - 'Extracted from analog photodiode signal by the function:' - 'corticalmapping.HighLevel.segmentPhotodiodeSignal() using parameters saved in the' - 'current timeseries.') -pdo_ts.set_path('/analysis') -pdo_ts.finalize() - -nwb_f.close() - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/260_add_visual_stimuli_retinotopic_mapping.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/260_add_visual_stimuli_retinotopic_mapping.py deleted file mode 100644 index 0414239..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/260_add_visual_stimuli_retinotopic_mapping.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -import retinotopic_mapping.DisplayLogAnalysis as dla -import corticalmapping.NwbTools as nt - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -stim_pkl_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.pkl'][0] -stim_log = dla.DisplayLogAnalyzer(stim_pkl_fn) - -nwb_f.add_visual_display_log_retinotopic_mapping(stim_log=stim_log) -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/270_analyze_photodiode_onsets.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/270_analyze_photodiode_onsets.py deleted file mode 100644 index a064337..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/270_analyze_photodiode_onsets.py +++ /dev/null @@ -1,38 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import retinotopic_mapping.DisplayLogAnalysis as dla -import corticalmapping.core.TimingAnalysis as ta - - -pd_ts_pd_path = 'analysis/digital_photodiode_rise' -pd_thr = 0.5 # this is color threshold, not analog photodiode threshold -ccg_t_range = (0., 0.1) -ccg_bins = 100 -is_plot = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -stim_pkl_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.pkl'][0] -stim_log = dla.DisplayLogAnalyzer(stim_pkl_fn) - -# get display lag -display_delay = nwb_f.get_display_delay_retinotopic_mapping(stim_log=stim_log, indicator_color_thr=pd_thr, - ccg_t_range=ccg_t_range, ccg_bins=ccg_bins, - is_plot=is_plot, pd_onset_ts_path=pd_ts_pd_path) - -# analyze photodiode onset -stim_dict = stim_log.get_stim_dict() -pd_onsets_seq = stim_log.analyze_photodiode_onsets_sequential(stim_dict=stim_dict, pd_thr=pd_thr) -pd_onsets_com = stim_log.analyze_photodiode_onsets_combined(pd_onsets_seq=pd_onsets_seq, - is_dgc_blocked=True) -nwb_f.add_photodiode_onsets_combined_retinotopic_mapping(pd_onsets_com=pd_onsets_com, - display_delay=display_delay, - vsync_frame_path='acquisition/timeseries' - '/digital_vsync_visual_rise') -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/280_add_rois_and_traces_caiman_segmentation.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/280_add_rois_and_traces_caiman_segmentation.py deleted file mode 100644 index f0d74aa..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/280_add_rois_and_traces_caiman_segmentation.py +++ /dev/null @@ -1,154 +0,0 @@ -import os -import h5py -import numpy as np -import matplotlib.pyplot as plt -import tifffile as tf -import corticalmapping.NwbTools as nt -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia - -resolution = 1024 # 512 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -def add_rois_and_traces(data_folder, nwb_f, mov_path): - - mov_grp = nwb_f.file_pointer[mov_path + '/corrected'] - - data_f = h5py.File(os.path.join(data_folder, 'rois_and_traces.hdf5'), 'r') - mask_arr_c = data_f['masks_center'].value - mask_arr_s = data_f['masks_surround'].value - traces_center_raw = data_f['traces_center_raw'].value - # traces_center_demixed = data_f['traces_center_demixed'].value - traces_center_subtracted = data_f['traces_center_subtracted'].value - # traces_center_dff = data_f['traces_center_dff'].value - traces_surround_raw = data_f['traces_surround_raw'].value - neuropil_r = data_f['neuropil_r'].value - neuropil_err = data_f['neuropil_err'].value - data_f.close() - - - if traces_center_raw.shape[1] != mov_grp['num_samples'].value: - raise ValueError('number of trace time points ({}) does not match frame number of ' - 'corresponding movie ({}).'.format(traces_center_raw.shape[0], mov_grp['num_samples'].value)) - - rf_img = tf.imread(os.path.join(data_folder, 'corrected_mean_projection.tif')) - - print 'adding segmentation results ...' - rt_mo = nwb_f.create_module('rois_and_traces_plane0') - is_if = rt_mo.create_interface('ImageSegmentation') - is_if.create_imaging_plane('imaging_plane', description='') - is_if.add_reference_image('imaging_plane', 'mean_projection', rf_img) - - for i in range(mask_arr_c.shape[0]): - curr_cen = mask_arr_c[i] - curr_cen_n = 'roi_' + ft.int2str(i, 4) - curr_cen_roi = ia.WeightedROI(curr_cen) - curr_cen_pixels_yx = curr_cen_roi.get_pixel_array() - curr_cen_pixels_xy = np.array([curr_cen_pixels_yx[:, 1], curr_cen_pixels_yx[:, 0]]).transpose() - is_if.add_roi_mask_pixels(image_plane='imaging_plane', roi_name=curr_cen_n, desc='', - pixel_list=curr_cen_pixels_xy, weights=curr_cen_roi.weights, width=resolution, height=resolution) - - curr_sur = mask_arr_s[i] - curr_sur_n = 'surround_' + ft.int2str(i, 4) - curr_sur_roi = ia.ROI(curr_sur) - curr_sur_pixels_yx = curr_sur_roi.get_pixel_array() - curr_sur_pixels_xy = np.array([curr_sur_pixels_yx[:, 1], curr_sur_pixels_yx[:, 0]]).transpose() - is_if.add_roi_mask_pixels(image_plane='imaging_plane', roi_name=curr_sur_n, desc='', - pixel_list=curr_sur_pixels_xy, weights=None, width=resolution, height=resolution) - is_if.finalize() - - - - trace_f_if = rt_mo.create_interface('Fluorescence') - seg_if_path = '/processing/rois_and_traces_plane0'+ '/ImageSegmentation/imaging_plane' - # print seg_if_path - ts_path = mov_path + '/corrected' - - print 'adding center fluorescence raw' - trace_raw_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_raw') - trace_raw_ts.set_data(traces_center_raw, unit='au', conversion=np.nan, resolution=np.nan) - trace_raw_ts.set_value('data_format', 'roi (row) x time (column)') - trace_raw_ts.set_value('data_range', '[-8192, 8191]') - trace_raw_ts.set_description('fluorescence traces extracted from the center region of each roi') - trace_raw_ts.set_time_as_link(ts_path) - trace_raw_ts.set_value_as_link('segmentation_interface', seg_if_path) - roi_names = ['roi_' + ft.int2str(ind, 4) for ind in range(traces_center_raw.shape[0])] - trace_raw_ts.set_value('roi_names', roi_names) - trace_raw_ts.set_value('num_samples', traces_center_raw.shape[1]) - trace_f_if.add_timeseries(trace_raw_ts) - trace_raw_ts.finalize() - - print 'adding neuropil fluorescence raw' - trace_sur_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_surround_raw') - trace_sur_ts.set_data(traces_surround_raw, unit='au', conversion=np.nan, resolution=np.nan) - trace_sur_ts.set_value('data_format', 'roi (row) x time (column)') - trace_sur_ts.set_value('data_range', '[-8192, 8191]') - trace_sur_ts.set_description('neuropil traces extracted from the surroud region of each roi') - trace_sur_ts.set_time_as_link(ts_path) - trace_sur_ts.set_value_as_link('segmentation_interface', seg_if_path) - sur_names = ['surround_' + ft.int2str(ind, 4) for ind in range(traces_center_raw.shape[0])] - trace_sur_ts.set_value('roi_names', sur_names) - trace_sur_ts.set_value('num_samples', traces_surround_raw.shape[1]) - trace_f_if.add_timeseries(trace_sur_ts) - trace_sur_ts.finalize() - - roi_center_n_path = '/processing/rois_and_traces_plane0/Fluorescence/f_center_raw/roi_names' - # print 'adding center fluorescence demixed' - # trace_demix_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_demixed') - # trace_demix_ts.set_data(traces_center_demixed, unit='au', conversion=np.nan, resolution=np.nan) - # trace_demix_ts.set_value('data_format', 'roi (row) x time (column)') - # trace_demix_ts.set_description('center traces after overlapping demixing for each roi') - # trace_demix_ts.set_time_as_link(ts_path) - # trace_demix_ts.set_value_as_link('segmentation_interface', seg_if_path) - # trace_demix_ts.set_value('roi_names', roi_names) - # trace_demix_ts.set_value('num_samples', traces_center_demixed.shape[1]) - # trace_f_if.add_timeseries(trace_demix_ts) - # trace_demix_ts.finalize() - - print 'adding center fluorescence after neuropil subtraction' - trace_sub_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_subtracted') - trace_sub_ts.set_data(traces_center_subtracted, unit='au', conversion=np.nan, resolution=np.nan) - trace_sub_ts.set_value('data_format', 'roi (row) x time (column)') - trace_sub_ts.set_description('center traces after overlap demixing and neuropil subtraction for each roi') - trace_sub_ts.set_time_as_link(ts_path) - trace_sub_ts.set_value_as_link('segmentation_interface', seg_if_path) - trace_sub_ts.set_value_as_link('roi_names', roi_center_n_path) - trace_sub_ts.set_value('num_samples', traces_center_subtracted.shape[1]) - trace_sub_ts.set_value('r', neuropil_r, dtype='float32') - trace_sub_ts.set_value('rmse', neuropil_err, dtype='float32') - trace_sub_ts.set_comments('value "r": neuropil contribution ratio for each roi. ' - 'value "rmse": RMS error of neuropil subtraction for each roi') - trace_f_if.add_timeseries(trace_sub_ts) - trace_sub_ts.finalize() - - trace_f_if.finalize() - - # print 'adding global dF/F traces for each roi' - # trace_dff_if = rt_mo.create_interface('DfOverF') - # - # trace_dff_ts = nwb_f.create_timeseries('RoiResponseSeries', 'dff_center') - # trace_dff_ts.set_data(traces_center_dff, unit='au', conversion=np.nan, resolution=np.nan) - # trace_dff_ts.set_value('data_format', 'roi (row) x time (column)') - # trace_dff_ts.set_description('global df/f traces for each roi center, input fluorescence is the trace after demixing' - # ' and neuropil subtraction. global df/f is calculated by ' - # 'allensdk.brain_observatory.dff.compute_dff() function.') - # trace_dff_ts.set_time_as_link(ts_path) - # trace_dff_ts.set_value_as_link('segmentation_interface', seg_if_path) - # trace_dff_ts.set_value('roi_names', roi_names) - # trace_dff_ts.set_value('num_samples', traces_center_dff.shape[1]) - # trace_dff_if.add_timeseries(trace_dff_ts) - # trace_dff_ts.finalize() - # trace_dff_if.finalize() - - rt_mo.finalize() - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) -add_rois_and_traces(data_folder=curr_folder, nwb_f=nwb_f, - mov_path='/processing/motion_correction/MotionCorrection/MotionCorrection') - -nwb_f.close() - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/290_get_STRFs.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/290_get_STRFs.py deleted file mode 100644 index ea10f52..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/290_get_STRFs.py +++ /dev/null @@ -1,90 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -stim_name = '001_LocallySparseNoiseRetinotopicMapping' -trace_source = 'f_center_subtracted' -start_time = -0.5 -end_time = 1.5 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -probe_grp = nwb_f.file_pointer['analysis/photodiode_onsets/' + stim_name] -probe_ns = probe_grp.keys() -probe_ns.sort() - -probe_locations = [[float(pn[3: 9]), float(pn[13: 19])] for pn in probe_ns] -probe_signs = [float(pn[-2:]) for pn in probe_ns] -# print(probe_locations) - -plane_ns = nwb_f.file_pointer['processing'].keys() -plane_ns = [pn.split('_')[-1] for pn in plane_ns if 'rois_and_traces_plane' in pn] -plane_ns.sort() -print('\n'.join(plane_ns)) - -strf_grp = nwb_f.file_pointer['analysis'].create_group('STRFs') - -for plane_n in plane_ns: - print('\ngetting STRFs for {} ...'.format(plane_n)) - - roi_ns = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + - '/ImageSegmentation/imaging_plane/roi_list'].value - roi_ns = [rn for rn in roi_ns if rn[0: 4] == 'roi_'] - roi_ns.sort() - roi_num = len(roi_ns) - - plane_strf_grp = strf_grp.create_group(plane_n) - plane_traces = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + '/Fluorescence/' + - trace_source + '/data'].value - plane_trace_ts = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + '/Fluorescence/' + - trace_source + '/timestamps'].value - - plane_mean_frame_dur = np.mean(np.diff(plane_trace_ts)) - plane_chunk_frame_dur = int(np.ceil((end_time - start_time) / plane_mean_frame_dur)) - plane_chunk_frame_start = int(np.floor(start_time / plane_mean_frame_dur)) - plane_t = (np.arange(plane_chunk_frame_dur) + plane_chunk_frame_start) * plane_mean_frame_dur - print '{}: STRF time axis: \n{}'.format(plane_n, plane_t) - - plane_roi_traces = [] - - for probe_ind, probe_n in enumerate(probe_ns): - - probe_ts = probe_grp[probe_n]['pd_onset_ts_sec'].value - probe_traces = [] - for curr_probe_ts in probe_ts: - curr_frame_start = ta.find_nearest(plane_trace_ts, curr_probe_ts) + plane_chunk_frame_start - curr_frame_end = curr_frame_start + plane_chunk_frame_dur - if curr_frame_start >= 0 and curr_frame_end <= len(plane_trace_ts): - probe_traces.append(plane_traces[:, curr_frame_start: curr_frame_end]) - - plane_roi_traces.append(np.array(probe_traces)) - print('probe: {} / {}; shape: {}'.format(probe_ind + 1, len(probe_ns), np.array(probe_traces).shape)) - - # plane_roi_traces = np.array(plane_roi_traces) - - print('saving ...') - for roi_ind in range(roi_num): - - print "roi: {} / {}".format(roi_ind + 1, roi_num) - curr_unit_traces = [pt[:, roi_ind, :] for pt in plane_roi_traces] - curr_unit_traces = [list(t) for t in curr_unit_traces] - curr_strf = sca.SpatialTemporalReceptiveField(probe_locations, probe_signs, curr_unit_traces, plane_t, - name='roi_{:04d}'.format(roi_ind), - trace_data_type=trace_source) - - curr_strf_grp = plane_strf_grp.create_group('strf_roi_{:04d}'.format(roi_ind)) - curr_strf.to_h5_group(curr_strf_grp) - -nwb_f.close() - - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/310_plot_STRFs.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/310_plot_STRFs.py deleted file mode 100644 index dabe157..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/310_plot_STRFs.py +++ /dev/null @@ -1,61 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import h5py -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -save_folder = 'figures' -is_local_dff = True -is_add_to_traces = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -strf_grp = nwb_f['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File("caiman_segmentation_results.hdf5", 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - pdff = PdfPages(os.path.join(save_folder, 'STRFs_' + plane_n + '.pdf')) - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - - v_min, v_max = curr_strf_dff.get_data_range() - f = curr_strf_dff.plot_traces(yRange=(v_min, v_max * 1.1), figSize=(16, 10), - columnSpacing=0.002, rowSpacing=0.002) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/320_plot_zscore_RFs.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/320_plot_zscore_RFs.py deleted file mode 100644 index 8bc3863..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/320_plot_zscore_RFs.py +++ /dev/null @@ -1,68 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import h5py -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -save_folder = 'figures' -is_local_dff = True -zscore_range = [0., 4.] -t_window = [0., 1.] -is_add_to_traces = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -strf_grp = nwb_f['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File("caiman_segmentation_results.hdf5", 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - pdff = PdfPages(os.path.join(save_folder, 'zscore_RFs_' + plane_n + '.pdf')) - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - v_min, v_max = curr_strf_dff.get_data_range() - - rf_on, rf_off = curr_strf_dff.get_zscore_receptive_field(timeWindow=t_window) - f = plt.figure(figsize=(15, 4)) - ax_on = f.add_subplot(121) - rf_on.plot_rf(plot_axis=ax_on, is_colorbar=True, cmap='Reds', vmin=zscore_range[0], vmax=zscore_range[1]) - ax_off = f.add_subplot(122) - rf_off.plot_rf(plot_axis=ax_off, is_colorbar=True, cmap='Blues', vmin=zscore_range[0], vmax=zscore_range[1]) - plt.close() - - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/330_plot_RF_contours.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/330_plot_RF_contours.py deleted file mode 100644 index 9f55594..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/330_plot_RF_contours.py +++ /dev/null @@ -1,112 +0,0 @@ -import os -import numpy as np -import h5py -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -roi_t_window = [0., 1.] -zscore_range = [0., 4.] -save_folder = 'figures' -is_add_to_traces = True - -# plot control -thr_ratio = 0.4 -filter_sigma = 1. -interpolate_rate = 5 -absolute_thr = 1.6 -level_num = 1 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'] -print('\n'.join(nwb_fn)) - -if len(nwb_fn) != 1: - raise LookupError - -nwb_fn = nwb_fn[0] -rff = h5py.File(nwb_fn, 'r') - -strf_grp = rff['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -X = None -Y = None - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File("caiman_segmentation_results.hdf5", 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - f_all = plt.figure(figsize=(10, 10)) - ax_all = f_all.add_subplot(111) - - pdff = PdfPages(os.path.join(save_folder, 'RF_contours_' + plane_n + '.pdf')) - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - rf_on, rf_off, _ = curr_strf_dff.get_zscore_thresholded_receptive_fields(timeWindow=roi_t_window, - thr_ratio=thr_ratio, - filter_sigma=filter_sigma, - interpolate_rate=interpolate_rate, - absolute_thr=absolute_thr) - - if X is None and Y is None: - X, Y = np.meshgrid(np.arange(len(rf_on.aziPos)), - np.arange(len(rf_on.altPos))) - - levels_on = [np.max(rf_on.get_weighted_mask().flat) * thr_ratio] - levels_off = [np.max(rf_off.get_weighted_mask().flat) * thr_ratio] - ax_all.contour(X, Y, rf_on.get_weighted_mask(), levels=levels_on, colors='r', lw=5) - ax_all.contour(X, Y, rf_off.get_weighted_mask(), levels=levels_off, colors='b', lw=5) - - f_single = plt.figure(figsize=(10, 10)) - ax_single = f_single.add_subplot(111) - ax_single.contour(X, Y, rf_on.get_weighted_mask(), levels=levels_on, colors='r', lw=5) - ax_single.contour(X, Y, rf_off.get_weighted_mask(), levels=levels_off, colors='b', lw=5) - ax_single.set_xticks(range(len(rf_on.aziPos))[::10]) - ax_single.set_xticklabels(['{:05.1f}'.format(l) for l in rf_on.aziPos[::10]]) - ax_single.set_yticks(range(len(rf_on.altPos))[::10]) - ax_single.set_yticklabels(['{:05.1f}'.format(l) for l in rf_on.altPos[::-1][::10]]) - ax_single.set_aspect('equal') - ax_single.set_title('{}: {}. ON thr:{}; OFF thr:{}.'.format(plane_n, roi_n, rf_on.thr, rf_off.thr)) - pdff.savefig(f_single) - f_single.clear() - plt.close(f_single) - - pdff.close() - - ax_all.set_xticks(range(len(rf_on.aziPos))[::10]) - ax_all.set_xticklabels(['{:05.1f}'.format(l) for l in rf_on.aziPos[::10]]) - ax_all.set_yticks(range(len(rf_on.altPos))[::10]) - ax_all.set_yticklabels(['{:05.1f}'.format(l) for l in rf_on.altPos[::-1][::10]]) - ax_all.set_aspect('equal') - ax_all.set_title('{}, abs_zscore_thr:{}'.format(plane_n, absolute_thr)) - - f_all.savefig(os.path.join(save_folder, 'RF_contours_' + plane_n + '_all.pdf'), dpi=300) - -rff.close() - diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/old/120_get_cells_file.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/old/120_get_cells_file.py deleted file mode 100644 index 5f9731a..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/old/120_get_cells_file.py +++ /dev/null @@ -1,76 +0,0 @@ -import os -import numpy as np -import h5py -import tifffile as tf -import allensdk_internal.brain_observatory.mask_set as mask_set -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import scipy.ndimage as ni -import matplotlib.pyplot as plt - -isSave = True -filter_sigma = 1. -cut_thr = 4. -dilation_iterations = 0 -bg_fn = "corrected_mean_projection.tif" -save_folder = 'figures' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -data_f = h5py.File('caiman_segmentation_results.hdf5') -masks = data_f['masks'].value -data_f.close() - -bg = tf.imread(bg_fn) - -final_roi_dict = {} - -for i, mask in enumerate(masks): - mask_nor = (mask - np.mean(mask.flatten())) / np.abs(np.std(mask.flatten())) - mask_nor_f = ni.filters.gaussian_filter(mask_nor, filter_sigma) - mask_bin = np.zeros(mask_nor_f.shape, dtype=np.uint8) - mask_bin[mask_nor_f > cut_thr] = 1 - mask_labeled, mask_num = ni.label(mask_bin) - curr_mask_dict = ia.get_masks(labeled=mask_labeled, keyPrefix='caiman_mask_{:03d}'.format(i), labelLength=5) - for roi_key, roi_mask in curr_mask_dict.items(): - final_roi_dict.update({roi_key: ia.WeightedROI(roi_mask * mask)}) - -print 'Total number of ROIs:',len(final_roi_dict) - -f = plt.figure(figsize=(15, 8)) -ax1 = f.add_subplot(121) -ax1.imshow(ia.array_nor(bg), vmin=0, vmax=0.1, cmap='gray', interpolation='nearest') -colors1 = pt.random_color(masks.shape[0]) -for i, mask in enumerate(masks): - pt.plot_mask_borders(mask, plotAxis=ax1, color=colors1[i]) -ax1.set_title('original ROIs') -ax1.set_axis_off() -ax2 = f.add_subplot(122) -ax2.imshow(ia.array_nor(bg), vmin=0, vmax=0.1, cmap='gray', interpolation='nearest') -colors2 = pt.random_color(len(final_roi_dict)) -i = 0 -for roi in final_roi_dict.values(): - pt.plot_mask_borders(roi.get_binary_mask(), plotAxis=ax2, color=colors2[i]) - i = i + 1 -ax2.set_title('filtered ROIs') -ax2.set_axis_off() -plt.show() - -if isSave: - - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - f.savefig(os.path.join(save_folder, 'caiman_segmentation_filtering.pdf'), dpi=300) - - cell_file = h5py.File('cells.hdf5', 'w') - - i = 0 - for key, value in sorted(final_roi_dict.iteritems()): - curr_grp = cell_file.create_group('cell{:04d}'.format(i)) - curr_grp.attrs['name'] = key - value.to_h5_group(curr_grp) - i += 1 - - cell_file.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/old/170_get_dff_traces.py b/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/old/170_get_dff_traces.py deleted file mode 100644 index 3cd09ff..0000000 --- a/corticalmapping/scripts/post_recording/00_old/movie_single_plane_single_clannel_regular_2p/old/170_get_dff_traces.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import h5py -import allensdk.brain_observatory.dff as dff -import numpy as np -import corticalmapping.HighLevel as hl -import corticalmapping.core.FileTools as ft - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_plot_dir = os.path.join(curr_folder, 'figures', 'dff_extraction') -if not os.path.isdir(save_plot_dir): - os.makedirs(save_plot_dir) - -data_f = h5py.File('rois_and_traces.hdf5') -traces_subtracted = data_f['traces_center_subtracted'].value - -traces_dff = dff.compute_dff(traces_subtracted, save_plot_dir=save_plot_dir, - mode_kernelsize=100, mean_kernelsize=100) -data_f['traces_center_dff'] = traces_dff -data_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/000_reorganized_data.py b/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/000_reorganized_data.py deleted file mode 100644 index 3297f9c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/000_reorganized_data.py +++ /dev/null @@ -1,37 +0,0 @@ -import os -import tifffile as tf -import numpy as np - -file_identifier = 'cell1_zoom4' -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180419-388189-2p" -frame_per_step = 500 - -save_folder = os.path.join(data_folder, file_identifier) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -fns = [f for f in os.listdir(data_folder) if file_identifier in f and f[-4:] == '.tif'] -fns.sort() -print('\n'.join(fns)) - -curr_step = 0 - -print('\n') -for fn in fns: - print(fn) - curr_mov = tf.imread(os.path.join(data_folder, fn)) - - # reorient movie - curr_mov = curr_mov.transpose((0, 2, 1))[:, ::-1, :] - - steps = curr_mov.shape[0] / frame_per_step - - for step in range(steps): - # print(curr_step) - curr_step_mov = curr_mov[step * frame_per_step : (step + 1) * frame_per_step] - curr_fn = '{}_step_{:03d}'.format(file_identifier, curr_step) - curr_save_folder = os.path.join(save_folder, curr_fn) - if not os.path.isdir(curr_save_folder): - os.makedirs(curr_save_folder) - tf.imsave(os.path.join(curr_save_folder, curr_fn + '.tif'), curr_step_mov) - curr_step += 1 \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/005_remove_correction_files.py b/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/005_remove_correction_files.py deleted file mode 100644 index 36e614c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/005_remove_correction_files.py +++ /dev/null @@ -1,31 +0,0 @@ -import os - -base_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180502-M386507-2p\FOV4_zoom10" - -step_fns = [f for f in os.listdir(base_folder) if f.split('_')[-2] == 'step'] -step_fns.sort() -print('\n'.join(step_fns)) - -for step_fn in step_fns: - - print('\n' + step_fn) - step_folder = os.path.join(base_folder, step_fn) - fns = os.listdir(step_folder) - - if 'corrected_max_projection.tif' in fns: - print('removing corrected_max_projection.tif') - os.remove(os.path.join(step_folder, 'corrected_max_projection.tif')) - - if 'corrected_mean_projection.tif' in fns: - print('removing corrected_mean_projection.tif') - os.remove(os.path.join(step_folder, 'corrected_mean_projection.tif')) - - if 'correction_offsets.hdf5' in fns: - print('removing correction_offsets.hdf5') - os.remove(os.path.join(step_folder, 'correction_offsets.hdf5')) - - fn_cor = [f for f in fns if f[-14:] == '_corrected.tif'] - if len(fn_cor) == 1: - print('removing ' + fn_cor[0]) - os.remove(os.path.join(step_folder, fn_cor[0])) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/010_motion_correction_zstack_caiman.py b/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/010_motion_correction_zstack_caiman.py deleted file mode 100644 index a20586b..0000000 --- a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/010_motion_correction_zstack_caiman.py +++ /dev/null @@ -1,128 +0,0 @@ -import sys -sys.path.extend([r"E:\data\github_packages\CaImAn"]) - -import caiman as cm -import numpy as np -import os -from caiman.motion_correction import MotionCorrect, tile_and_correct, motion_correction_piecewise -import tifffile as tf -import h5py -import warnings - -base_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180427-M386507-2p\FOV1_zstack\FOV1_zstack" - -identifier = 'FOV1_zstack' - -n_processes = 3 - - -def correct_single_movie(data_folder, identifier, dview): - - #=======================================setup parameters============================================== - # number of iterations for rigid motion correction - niter_rig = 5 - - # maximum allowed rigid shift in pixels (view the movie to get a sense of motion) - max_shifts = (30, 30) - - # for parallelization split the movies in num_splits chuncks across time - # if none all the splits are processed and the movie is saved - splits_rig = 56 - - # intervals at which patches are laid out for motion correction - # num_splits_to_process_rig = None - - # create a new patch every x pixels for pw-rigid correction - strides = (48, 48) - - # overlap between pathes (size of patch strides+overlaps) - overlaps = (24, 24) - - # for parallelization split the movies in num_splits chuncks across time - splits_els = 56 - - # num_splits_to_process_els = [28, None] - - # upsample factor to avoid smearing when merging patches - upsample_factor_grid = 4 - - # maximum deviation allowed for patch with respect to rigid shifts - max_deviation_rigid = 3 - - # if True, apply shifts fast way (but smoothing results) by using opencv - shifts_opencv = True - - # if True, make the SAVED movie and template mostly nonnegative by removing min_mov from movie - nonneg_movie = False - # =======================================setup parameters============================================== - - - fname = [f for f in os.listdir(data_folder) if f[-4:] == '.tif' and identifier in f] - - if len(fname) == 0: - print('\ndid not find movie file in directory: {}.'.format(data_folder)) - print('Do nothing.') - return - elif len(fname) > 1: - fname.sort() - print('\n') - print('\n'.join(fname)) - warnings.warn('more than one movie file in directory: {}. skip ...'.format(data_folder)) - return - else: - fname = fname[0] - print('\ncorrecting {} in directory {}.'.format(fname, data_folder)) - - # m_orig = cm.load(os.path.join(data_folder, fname)) - # offset_mov = np.min(m_orig) # if the data has very negative values compute an offset value - - offset_mov = 0. - - # create a motion correction object# creat - mc = MotionCorrect(os.path.join(data_folder, fname), offset_mov, - dview=dview, max_shifts=max_shifts, niter_rig=niter_rig, - splits_rig=splits_rig, strides=strides, overlaps=overlaps, - splits_els=splits_els, upsample_factor_grid=upsample_factor_grid, - max_deviation_rigid=max_deviation_rigid, - shifts_opencv=shifts_opencv, nonneg_movie=nonneg_movie) - - mc.motion_correct_rigid(save_movie=True) - # load motion corrected movie - m_rig = cm.load(mc.fname_tot_rig) - m_rig = m_rig.astype(np.int16) - save_name = os.path.splitext(fname)[0] + '_corrected.tif' - tf.imsave(os.path.join(data_folder, save_name), m_rig) - tf.imsave(os.path.join(data_folder, 'corrected_mean_projection.tif'), - np.mean(m_rig, axis=0).astype(np.float32)) - tf.imsave(os.path.join(data_folder, 'corrected_max_projection.tif'), - np.max(m_rig, axis=0).astype(np.float32)) - - offset_f = h5py.File(os.path.join(data_folder, 'correction_offsets.hdf5')) - offsets = mc.shifts_rig - offsets = np.array([np.array(o) for o in offsets]).astype(np.float32) - offset_dset = offset_f.create_dataset(name='file_0000', data=offsets) - offset_dset.attrs['format'] = 'height, width' - offset_dset.attrs['path'] = os.path.join(data_folder, fname) - - os.remove(mc.fname_tot_rig[0]) - - -if __name__ == '__main__': - subfolder_ns = [f for f in os.listdir(base_folder) if identifier in f] - subfolder_ns.sort() - print('\n'.join(subfolder_ns)) - - # %% start the cluster (if a cluster already exists terminate it) - if 'dview' in locals(): - dview.terminate() - - c, dview, n_processes = cm.cluster.setup_cluster(backend='local', - n_processes=n_processes, - single_thread=False) - - for subfolder_n in subfolder_ns: - - correct_single_movie(data_folder=os.path.join(base_folder, subfolder_n), - identifier=identifier, - dview=dview) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/020_get_images.py b/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/020_get_images.py deleted file mode 100644 index 08fe803..0000000 --- a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/020_get_images.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -import tifffile as tf -import numpy as np - -identifier = 'cell1_zoom4' -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180419-M388189-2p" - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -# save_folder = os.path.join(curr_folder, identifier) -# if not os.path.isdir(save_folder): -# os.makedirs(save_folder) - -source_folder = os.path.join(data_folder, identifier) -folder_ns = [f for f in os.listdir(source_folder) if os.path.isdir(os.path.join(source_folder, f))] -folder_ns.sort() -print('\n'.join(folder_ns)) - -stack = [] -for folder_n in folder_ns: - curr_source_folder = os.path.join(source_folder, folder_n) - stack.append(tf.imread(os.path.join(curr_source_folder, 'corrected_mean_projection.tif'))) - -stack = np.array(stack) -tf.imsave(os.path.join(curr_folder, '{}_zstack.tif'.format(identifier)), stack) -# tf.imsave(os.path.join(save_folder, '{}_zstack.tif'.format(identifier)), stack) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/030_motion_correction_cross_steps.py b/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/030_motion_correction_cross_steps.py deleted file mode 100644 index 56be4ee..0000000 --- a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/030_motion_correction_cross_steps.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import stia.motion_correction as mc - -input_folder = 'cell1' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -input_path_identifier = 'zstack' - -output_folder = os.path.join(input_folder, 'corrected') -if not os.path.isdir(output_folder): - os.makedirs(output_folder) - -process_num = 1 -anchor_frame_ind_chunk = 10 -anchor_frame_ind_projection = 0 -iteration_chunk = 10 -iteration_projection = 5 -max_offset_chunk = (30., 30.) -max_offset_projection = (30., 30.) -align_func = mc.phase_correlation -fill_value = 0. -avi_downsample_rate = 20 - -f_paths, _ = mc.motion_correction(input_folder=input_folder, - input_path_identifier=input_path_identifier, - process_num=process_num, - output_folder=output_folder, - anchor_frame_ind_chunk=anchor_frame_ind_chunk, - anchor_frame_ind_projection=anchor_frame_ind_projection, - iteration_chunk=iteration_chunk, - iteration_projection=iteration_projection, - max_offset_chunk=max_offset_chunk, - max_offset_projection=max_offset_projection, - align_func=align_func, - fill_value=fill_value) - -print('\n'.join(f_paths)) - -offsets_path = os.path.join(output_folder, 'correction_offsets.hdf5') -path_pairs = zip(f_paths, f_paths) -mc.apply_correction_offsets(offsets_path=offsets_path, - path_pairs=path_pairs, - process_num=process_num, - fill_value=fill_value, - output_folder=output_folder, - avi_downsample_rate=avi_downsample_rate) diff --git a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/040_get_fine_zstack.py b/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/040_get_fine_zstack.py deleted file mode 100644 index 66e69e7..0000000 --- a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/040_get_fine_zstack.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -import h5py -import numpy as np -import tifffile as tf -import stia.motion_correction as mc -import stia.utility.image_analysis as ia - -zstack_fn = 'zstack_zoom2_green.tif' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -zstack = tf.imread(zstack_fn) - -step_offsets = [[0., 0.]] # offsets between adjacent steps - -print('calculating step offsets ...') -for step_i in range(1, zstack.shape[0]): - curr_offset = mc.phase_correlation(zstack[step_i], zstack[step_i - 1]) - step_offsets.append(curr_offset) -step_offsets = np.array([np.array(so) for so in step_offsets], dtype=np.float32) -print('\nsetp offsets:') -print(step_offsets) - -print('\ncalculating final offsets ...') -final_offsets_y = np.cumsum(step_offsets[:, 0]) -final_offsets_x = np.cumsum(step_offsets[:, 1]) -final_offsets = np.array([final_offsets_x, final_offsets_y], dtype=np.float32).transpose() - -middle_frame_ind = zstack.shape[0] // 2 -middle_offsets = final_offsets[middle_frame_ind: middle_frame_ind + 1] -final_offsets = final_offsets - middle_offsets -print('\nfinal offsets:') -print(final_offsets) - -print('applying final offsets ...') - -zstack_f = [] # fine zstack - -for step_i in range(zstack.shape[0]): - - curr_offset = final_offsets[step_i] - - frame = zstack[step_i] - frame_f = ia.rigid_transform_cv2_2d(frame, offset=curr_offset, fill_value=0.).astype(np.float32) - zstack_f.append(frame_f) - -zstack_f = np.array(zstack_f, dtype=np.float32) - -tf.imsave(os.path.splitext(zstack_fn)[0] + '_aligned.tif', zstack_f) -tf.imsave(os.path.splitext(zstack_fn)[0] + '_max_projection.tif', np.max(zstack_f, axis=0)) - - diff --git a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/050_get_2p_vas_maps.py b/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/050_get_2p_vas_maps.py deleted file mode 100644 index 9a08d07..0000000 --- a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/050_get_2p_vas_maps.py +++ /dev/null @@ -1,19 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import matplotlib.pyplot as plt - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180322-M360495-2p" -file_name = "vasmap_zoom1_00001_00001.tif" -save_name = 'vasmap_2p_zoom1.tif' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -vasmap = tf.imread(os.path.join(data_folder, file_name)) -vasmap = np.mean(vasmap.transpose((0, 2, 1))[:, ::-1, :], axis=0) -vasmap = ia.array_nor(vasmap) - -tf.imsave(save_name, vasmap.astype(np.float32)) diff --git a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/060_get_wf_vas_maps.py b/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/060_get_wf_vas_maps.py deleted file mode 100644 index 4a49965..0000000 --- a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/060_get_wf_vas_maps.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -import numpy as np -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -import tifffile as tf - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180622-M391354-2p\vasmap_wf" - -vas_map_fns= ["180622JCamF100", - "180622JCamF101", - "180622JCamF102", - "180622JCamF103", - "180622JCamF104", - "180622JCamF105",] - -saveFolder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(saveFolder) - -vas_map_paths = [os.path.join(data_folder, f) for f in vas_map_fns] - -vas_maps = [] - -for vas_map_path in vas_map_paths: - - vas_map_focused, _, _ = ft.importRawJCamF(vas_map_path, column=1024, row=1024, headerLength = 116, - tailerLength=452) - vas_map_focused = vas_map_focused[2:] - vas_map_focused = vas_map_focused[:, ::-1, :] - vas_map_focused[vas_map_focused > 50000] = 400 - vas_map_focused = np.mean(vas_map_focused, axis=0) - vas_maps.append(ia.array_nor(vas_map_focused)) - -vas_map = ia.array_nor(np.mean(vas_maps, axis=0)) - -tf.imsave('vas_map_focused_wf_green.tif', vas_map.astype(np.float32)) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/old/010_motion_correction.py b/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/old/010_motion_correction.py deleted file mode 100644 index 2ad361c..0000000 --- a/corticalmapping/scripts/post_recording/00_old/zstack_single_channel_regular_2p/old/010_motion_correction.py +++ /dev/null @@ -1,49 +0,0 @@ -import os -import stia.motion_correction as mc - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180322-M360495-2p\zstack_zoom4" - -input_path_identifier = '.tif' -process_num = 1 -anchor_frame_ind_chunk = 100 -anchor_frame_ind_projection = 0 -iteration_chunk = 10 -iteration_projection = 5 -max_offset_chunk = (30., 30.) -max_offset_projection = (100., 100.) -align_func = mc.phase_correlation -fill_value = 0. -avi_downsample_rate = 20 - -sub_folder_ns = [f for f in os.listdir(data_folder) if os.path.isdir(os.path.join(data_folder, f))] -sub_folder_ns.sort() -print('\n'.join(sub_folder_ns)) - -for sub_folder_n in sub_folder_ns: - - sub_folder = os.path.join(data_folder, sub_folder_n) - - f_paths, _ = mc.motion_correction(input_folder=sub_folder, - input_path_identifier=input_path_identifier, - process_num=process_num, - output_folder=sub_folder, - anchor_frame_ind_chunk=anchor_frame_ind_chunk, - anchor_frame_ind_projection=anchor_frame_ind_projection, - iteration_chunk=iteration_chunk, - iteration_projection=iteration_projection, - max_offset_chunk=max_offset_chunk, - max_offset_projection=max_offset_projection, - align_func=align_func, - fill_value=fill_value) - - print('\n'.join(f_paths)) - - offsets_path = os.path.join(sub_folder, 'correction_offsets.hdf5') - path_pairs = zip(f_paths, f_paths) - mc.apply_correction_offsets(offsets_path=offsets_path, - path_pairs=path_pairs, - process_num=process_num, - fill_value=fill_value, - output_folder=sub_folder, - avi_downsample_rate=avi_downsample_rate) diff --git a/corticalmapping/scripts/post_recording/00_old/FlashingCircle/batch_analyzeFlashingCircle.py b/corticalmapping/scripts/post_recording/FlashingCircle/batch_analyzeFlashingCircle.py similarity index 95% rename from corticalmapping/scripts/post_recording/00_old/FlashingCircle/batch_analyzeFlashingCircle.py rename to corticalmapping/scripts/post_recording/FlashingCircle/batch_analyzeFlashingCircle.py index aacf1d8..eb931b9 100644 --- a/corticalmapping/scripts/post_recording/00_old/FlashingCircle/batch_analyzeFlashingCircle.py +++ b/corticalmapping/scripts/post_recording/FlashingCircle/batch_analyzeFlashingCircle.py @@ -68,7 +68,7 @@ vasMap = hl.getVasMap(vasMapPaths,dtype=vasMapDtype,headerLength=vasMapHeaderLength,tailerLength=vasMapTailerLength, column=vasMapColumn,row=vasMapRow,frame=vasMapFrame,crop=vasMapCrop,mergeMethod=vasMapMergeMethod) else: - print 'No vasculature map find. Taking first frame of movie as vasculature map.' + print('No vasculature map find. Taking first frame of movie as vasculature map.') firstMovPath = os.path.join(dataFolder, [f for f in fileList if (dateRecorded+'JCamF'+str(fileNumList[0]) in f) and ('.npy' in f)][0]) vasMap = BinarySlicer(firstMovPath)[0,:,:] tf.imsave(dateRecorded+'_M'+mouseID+'_vasMap.tif',vasMap.astype(np.float32)) @@ -91,10 +91,10 @@ log = ft.loadFile(logPath) refreshRate = float(log['monitor']['refreshRate']) - if 'preGapFrameNum' in log['stimulation'].keys(): + if 'preGapFrameNum' in list(log['stimulation'].keys()): preGapDur = log['stimulation']['preGapFrameNum'] / refreshRate postGapDur = log['stimulation']['postGapFrameNum'] / refreshRate - elif 'preGapDur' in log['stimulation'].keys(): + elif 'preGapDur' in list(log['stimulation'].keys()): preGapDur = log['stimulation']['preGapDur'] postGapDur = log['stimulation']['postGapDur'] diff --git a/corticalmapping/scripts/post_recording/analysis_retinotopicmapping/Retinotopic_Mapping_Analysis_Template/2015-10-30_RetinotopicMappingAnalysisTemplate.ipynb b/corticalmapping/scripts/post_recording/RetinotopicMapping/Retinotopic_Mapping_Analysis_Template/2015-10-30_RetinotopicMappingAnalysisTemplate.ipynb similarity index 100% rename from corticalmapping/scripts/post_recording/analysis_retinotopicmapping/Retinotopic_Mapping_Analysis_Template/2015-10-30_RetinotopicMappingAnalysisTemplate.ipynb rename to corticalmapping/scripts/post_recording/RetinotopicMapping/Retinotopic_Mapping_Analysis_Template/2015-10-30_RetinotopicMappingAnalysisTemplate.ipynb diff --git a/corticalmapping/scripts/post_recording/analysis_retinotopicmapping/Retinotopic_Mapping_Analysis_Template/2015-10-31_RetinotopicMappingAnalysisTemplate.ipynb b/corticalmapping/scripts/post_recording/RetinotopicMapping/Retinotopic_Mapping_Analysis_Template/2015-10-31_RetinotopicMappingAnalysisTemplate.ipynb similarity index 100% rename from corticalmapping/scripts/post_recording/analysis_retinotopicmapping/Retinotopic_Mapping_Analysis_Template/2015-10-31_RetinotopicMappingAnalysisTemplate.ipynb rename to corticalmapping/scripts/post_recording/RetinotopicMapping/Retinotopic_Mapping_Analysis_Template/2015-10-31_RetinotopicMappingAnalysisTemplate.ipynb diff --git a/corticalmapping/scripts/post_recording/analysis_retinotopicmapping/Retinotopic_Mapping_Analysis_Template/2015-11-17_RetinotopicMappingAnalysisTemplate.ipynb b/corticalmapping/scripts/post_recording/RetinotopicMapping/Retinotopic_Mapping_Analysis_Template/2015-11-17_RetinotopicMappingAnalysisTemplate.ipynb similarity index 100% rename from corticalmapping/scripts/post_recording/analysis_retinotopicmapping/Retinotopic_Mapping_Analysis_Template/2015-11-17_RetinotopicMappingAnalysisTemplate.ipynb rename to corticalmapping/scripts/post_recording/RetinotopicMapping/Retinotopic_Mapping_Analysis_Template/2015-11-17_RetinotopicMappingAnalysisTemplate.ipynb diff --git a/corticalmapping/scripts/post_recording/analysis_retinotopicmapping/Retinotopic_Mapping_Analysis_Template/2016-10-26_RetinotopicMappingAnalysisTemplate-ManualFileEntry.ipynb b/corticalmapping/scripts/post_recording/RetinotopicMapping/Retinotopic_Mapping_Analysis_Template/2016-10-26_RetinotopicMappingAnalysisTemplate-ManualFileEntry.ipynb similarity index 100% rename from corticalmapping/scripts/post_recording/analysis_retinotopicmapping/Retinotopic_Mapping_Analysis_Template/2016-10-26_RetinotopicMappingAnalysisTemplate-ManualFileEntry.ipynb rename to corticalmapping/scripts/post_recording/RetinotopicMapping/Retinotopic_Mapping_Analysis_Template/2016-10-26_RetinotopicMappingAnalysisTemplate-ManualFileEntry.ipynb diff --git a/corticalmapping/scripts/post_recording/RetinotopicMapping/Thumbs.db b/corticalmapping/scripts/post_recording/RetinotopicMapping/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..6247ede986fbf72c75b5bb86aa4022824299584e GIT binary patch literal 74240 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 skew_thr] - - subdf = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) | - (subdf['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])] - - if len(subdf) > 0: - - save_f = h5py.File(os.path.join(save_folder, save_fn)) - - nwb_fn = table_fn[0:-11] + '110_repacked.nwb' - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') - plane_n = table_fn[-11:-5] - - # S2 - s2_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s2_df) > 0: - s2_grp = save_f.create_group(table_fn[0:-5] + '_{}_ONOFF'.format(response_dir)) - s1_on_grp = save_f.create_group(table_fn[0:-5] + '_{}_ON'.format(response_dir)) - s1_off_grp = save_f.create_group(table_fn[0:-5] + '_{}_OFF'.format(response_dir)) - - for roi_i, roi_row in s2_df.iterrows(): - - print('\t s2 receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i+1, len(s2_df))) - - if response_dir == 'pos': - _, _, _, srf_on, srf_off, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - elif response_dir == 'neg': - _, _, _, _, _, srf_on, srf_off, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - else: - raise ValueError - - rf_on_mask = rf_on_new.get_weighted_mask() - rf_off_mask = rf_off_new.get_weighted_mask() - rf_onoff_new = sca.SpatialReceptiveField(mask=np.max([rf_on_mask, rf_off_mask], axis=0), - altPos=rf_on_new.altPos, - aziPos=rf_on_new.aziPos, - sign='ON_OFF', - thr=analysis_params['rf_z_thr_abs']) - - curr_s2_grp = s2_grp.create_group(roi_row['roi_n']) - rf_onoff_new.to_h5_group(curr_s2_grp) - - curr_s1_on_grp = s1_on_grp.create_group(roi_row['roi_n']) - rf_on_new.to_h5_group(curr_s1_on_grp) - - curr_s1_off_grp = s1_off_grp.create_group(roi_row['roi_n']) - rf_off_new.to_h5_group(curr_s1_off_grp) - - - # positive S1 ON - s1_on_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] < analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s1_on_df) > 0: - - s1_on_grp_n = table_fn[0:-5] + '_{}_ON'.format(response_dir) - - if s1_on_grp_n in save_f.keys(): - s1_on_grp = save_f[s1_on_grp_n] - else: - s1_on_grp = save_f.create_group(s1_on_grp_n) - - for roi_i, roi_row in s1_on_df.iterrows(): - - print('\t s1 ON receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i + 1, len(s1_on_df))) - - if response_dir == 'pos': - _, _, _, srf_on, _, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - elif response_dir == 'neg': - _, _, _, _, _, srf_on, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - else: - print(response_dir) - raise ValueError - - curr_s1_on_grp = s1_on_grp.create_group(roi_row['roi_n']) - rf_on_new.to_h5_group(curr_s1_on_grp) - - - # positive S1 OFF - s1_off_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] < analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s1_off_df) > 0: - - s1_off_grp_n = table_fn[0:-5] + '_{}_OFF'.format(response_dir) - - if s1_off_grp_n in save_f.keys(): - s1_off_grp = save_f[s1_off_grp_n] - else: - s1_off_grp = save_f.create_group(s1_off_grp_n) - - for roi_i, roi_row in s1_off_df.iterrows(): - - print('\t s1 OFF receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i + 1, len(s1_off_df))) - - if response_dir == 'pos': - _, _, _, _, srf_off, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - elif response_dir == 'neg': - - _, _, _, _, _, _, srf_off, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - else: - raise ValueError - - curr_s1_off_grp = s1_off_grp.create_group(roi_row['roi_n']) - rf_off_new.to_h5_group(curr_s1_off_grp) - - - save_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/0075_get_rf_maps_axon.py b/corticalmapping/scripts/post_recording/analysis_database/0075_get_rf_maps_axon.py deleted file mode 100644 index 3cc53f6..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0075_get_rf_maps_axon.py +++ /dev/null @@ -1,257 +0,0 @@ -import sys -sys.path.extend(['/home/junz/PycharmProjects/corticalmapping']) -import os -import numpy as np -import h5py -# import datetime -import pandas as pd -import corticalmapping.DatabaseTools as dt -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.SingleCellAnalysis as sca -from shutil import copyfile - -df_fn = 'dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30.csv' -clu_folder = r'intermediate_results\bouton_clustering\AllStimuli_DistanceThr_1.30' -nwb_folder = 'nwbs' -save_folder = "intermediate_results" - -response_dir = 'pos' -skew_thr = 0.6 -analysis_params = dt.ANALYSIS_PARAMS - -notes = ''' - zscore receptive field maps of all significant rois. Spatial temporal receptive fields - are first converted to df/f. Then 2-d zscore maps are generated. Then the zscore maps are - 2d filtered to smooth and interpolated in to high resolution. After preprocessing, if the - peak value of zscore is larger than the threshold, the receptive field will be considered - as sigificant. - ''' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(save_folder, 'rf_maps_' + os.path.splitext(df_fn)[0]) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -copyfile(os.path.realpath(__file__), - os.path.join(save_folder, - 'script_log.py')) - -df_axon = pd.read_csv(df_fn) -df_axon = df_axon[np.logical_not(df_axon['rf_{}_on_peak_z'.format(response_dir)].isnull())] -df_axon = df_axon[df_axon['skew_fil'] >= skew_thr] - -df_axon = df_axon[(df_axon['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) | - (df_axon['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])] - -plane_df = df_axon[['date', 'mouse_id', 'plane_n']].drop_duplicates().reset_index() -print('total number of planes with lsn data: {}'.format(len(plane_df))) - -for plane_i, plane_row in plane_df.iterrows(): - date = int(plane_row['date']) - mid = plane_row['mouse_id'] - plane_n = plane_row['plane_n'] - - print('processing {}_{}_{}, {} / {}'.format(date, mid, plane_n, plane_i+1, len(plane_df))) - - subdf = df_axon[(df_axon['date'] == date) & - (df_axon['mouse_id'] == mid) & - (df_axon['plane_n'] == plane_n)] - - nwb_fn = '{}_{}_110_repacked.nwb'.format(date, mid) - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') - - clu_fn = '{}_{}_{}_axon_grouping.hdf5'.format(date, mid, plane_n) - clu_f = h5py.File(os.path.join(clu_folder, clu_fn), 'r') - - save_fn = '{}_{}_{}_{}.hdf5'.format(date, mid, plane_n, response_dir) - save_f = h5py.File(os.path.join(save_folder, save_fn)) - - # S2 - s2_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s2_df) > 0: - s2_grp = save_f.create_group('{}_{}_{}_{}_ONOFF'.format(date, mid, plane_n, response_dir)) - s1_on_grp = save_f.create_group('{}_{}_{}_{}_ON'.format(date, mid, plane_n, response_dir)) - s1_off_grp = save_f.create_group('{}_{}_{}_{}_OFF'.format(date, mid, plane_n, response_dir)) - - for roi_i, roi_row in s2_df.iterrows(): - - print('\t s2 receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i + 1, len(s2_df))) - - if response_dir == 'pos': - _, _, _, srf_on, srf_off, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - elif response_dir == 'neg': - _, _, _, _, _, srf_on, srf_off, _, _, _, _, _, _, \ - _ = dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - else: - raise ValueError - - rf_on_mask = rf_on_new.get_weighted_mask() - rf_off_mask = rf_off_new.get_weighted_mask() - rf_onoff_new = sca.SpatialReceptiveField(mask=np.max([rf_on_mask, rf_off_mask], axis=0), - altPos=rf_on_new.altPos, - aziPos=rf_on_new.aziPos, - sign='ON_OFF', - thr=analysis_params['rf_z_thr_abs']) - - curr_s2_grp = s2_grp.create_group(roi_row['roi_n']) - rf_onoff_new.to_h5_group(curr_s2_grp) - - curr_s1_on_grp = s1_on_grp.create_group(roi_row['roi_n']) - rf_on_new.to_h5_group(curr_s1_on_grp) - - curr_s1_off_grp = s1_off_grp.create_group(roi_row['roi_n']) - rf_off_new.to_h5_group(curr_s1_off_grp) - - # positive S1 ON - s1_on_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] < analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s1_on_df) > 0: - - s1_on_grp_n = '{}_{}_{}_{}_ON'.format(date, mid, plane_n, response_dir) - - if s1_on_grp_n in save_f.keys(): - s1_on_grp = save_f[s1_on_grp_n] - else: - s1_on_grp = save_f.create_group(s1_on_grp_n) - - for roi_i, roi_row in s1_on_df.iterrows(): - - print('\t s1 ON receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i + 1, len(s1_on_df))) - - if response_dir == 'pos': - _, _, _, srf_on, _, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - elif response_dir == 'neg': - _, _, _, _, _, srf_on, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - else: - print(response_dir) - raise ValueError - - curr_s1_on_grp = s1_on_grp.create_group(roi_row['roi_n']) - rf_on_new.to_h5_group(curr_s1_on_grp) - - # positive S1 OFF - s1_off_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] < analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s1_off_df) > 0: - - s1_off_grp_n = '{}_{}_{}_{}_OFF'.format(date, mid, plane_n, response_dir) - - if s1_off_grp_n in save_f.keys(): - s1_off_grp = save_f[s1_off_grp_n] - else: - s1_off_grp = save_f.create_group(s1_off_grp_n) - - for roi_i, roi_row in s1_off_df.iterrows(): - - print('\t s1 OFF receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i + 1, len(s1_off_df))) - - if response_dir == 'pos': - _, _, _, _, srf_off, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - elif response_dir == 'neg': - - _, _, _, _, _, _, srf_off, _, _, _, _, _, _, \ - _ = dt.get_everthing_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - else: - raise ValueError - - curr_s1_off_grp = s1_off_grp.create_group(roi_row['roi_n']) - rf_off_new.to_h5_group(curr_s1_off_grp) - - save_f.close() - - - - diff --git a/corticalmapping/scripts/post_recording/analysis_database/0080_plot_plane_rf_center.py b/corticalmapping/scripts/post_recording/analysis_database/0080_plot_plane_rf_center.py deleted file mode 100644 index d849ca4..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0080_plot_plane_rf_center.py +++ /dev/null @@ -1,262 +0,0 @@ -import sys -sys.path.extend(['/home/junz/PycharmProjects/corticalmapping']) -import os -import numpy as np -import matplotlib.pyplot as plt -import pandas as pd -from matplotlib.backends.backend_pdf import PdfPages -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.DatabaseTools as dt - -table_name = 'big_roi_table_test.xlsx' -sheet_name = 'f_center_subtracted' - -response_dir = 'pos' -skew_thr = 0.6 -rf_peak_z_thr = 1.6 - -save_fn = 'plane_rf_centers.pdf' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -table_path = os.path.join(curr_folder, table_name) -df = pd.read_excel(table_path, sheetname=sheet_name) -subdf = df[df['skew_fil'] >= skew_thr] - -planes = subdf[['date', 'mouse_id', 'plane_n', 'depth']].drop_duplicates().reset_index() -print(planes) - -pdff = PdfPages(os.path.join('intermediate_figures', save_fn)) - -for plane_i, plane_row in planes.iterrows(): - - print('plotting {}_{}_{}, {} / {}'.format( - plane_row['date'], - plane_row['mouse_id'], - plane_row['plane_n'], - plane_i + 1, - len(planes))) - - planedf = subdf[(subdf['date'] == plane_row['date']) & \ - (subdf['mouse_id'] == plane_row['mouse_id']) & \ - (subdf['plane_n'] == plane_row['plane_n']) & \ - (subdf['depth'] == plane_row['depth'])] - - df_or = planedf[planedf['rf_{}_onoff_peak_z'.format(response_dir)] >= rf_peak_z_thr] - df_and = planedf[(planedf['rf_{}_on_peak_z'.format(response_dir)] >= rf_peak_z_thr) & \ - (planedf['rf_{}_off_peak_z'.format(response_dir)] >= rf_peak_z_thr)] - df_on = planedf[planedf['rf_{}_on_peak_z'.format(response_dir)] >= rf_peak_z_thr].drop(df_and.index) - df_off = planedf[planedf['rf_{}_off_peak_z'.format(response_dir)] >= rf_peak_z_thr].drop(df_and.index) - - df_or = df_or.reset_index() - df_and = df_and.reset_index() - df_on = df_on.reset_index() - df_off = df_off.reset_index() - - if len(df_or) == 0: - print('no any receptive fields. skip.') - else: - print('\tnumber of rois with significant rf: {}'.format(len(df_or))) - print('\tnumber of rois with S1 ON: {}'.format(len(df_on))) - print('\tnumber of rois with S1 OFF: {}'.format(len(df_off))) - print('\tnumber of rois with S2 ON/OFF: {}'.format(len(df_and))) - - f = plt.figure(figsize=(11, 8.5)) - - f.suptitle('{}_{}_{}; {} um'.format(plane_row['date'], - plane_row['mouse_id'], - plane_row['plane_n'], - plane_row['depth'])) - - #=============================RF center============================================= - # ON/OFF - alt_min = int(np.min(df_or['rf_{}_onoff_center_alt'.format(response_dir)]) - 5) - alt_max = int(np.max(df_or['rf_{}_onoff_center_alt'.format(response_dir)]) + 5) - azi_min = int(np.min(df_or['rf_{}_onoff_center_azi'.format(response_dir)]) - 5) - azi_max = int(np.max(df_or['rf_{}_onoff_center_azi'.format(response_dir)]) + 5) - ax_or_scatter = f.add_subplot(4, 5, 1) - ax_or_scatter.plot(df_or['rf_{}_onoff_center_azi'.format(response_dir)], - df_or['rf_{}_onoff_center_alt'.format(response_dir)], - '.', color='#888888') - ax_or_scatter.set_xlim([azi_min, azi_max]) - ax_or_scatter.set_ylim([alt_min, alt_max]) - ax_or_scatter.set_title('RF center') - - # ON - ax_on_scatter = f.add_subplot(4, 5, 6) - ax_on_scatter.plot(df_off['rf_{}_off_center_azi'.format(response_dir)], - df_off['rf_{}_off_center_alt'.format(response_dir)], - '.', color='#aaaaaa') - ax_on_scatter.plot(df_on['rf_{}_on_center_azi'.format(response_dir)], - df_on['rf_{}_on_center_alt'.format(response_dir)], - '.', color='#ff0000') - ax_on_scatter.set_xlim([azi_min, azi_max]) - ax_on_scatter.set_ylim([alt_min, alt_max]) - - # OFF - ax_off_scatter = f.add_subplot(4, 5, 11) - ax_off_scatter.plot(df_on['rf_{}_on_center_azi'.format(response_dir)], - df_on['rf_{}_on_center_alt'.format(response_dir)], - '.', color='#aaaaaa') - ax_off_scatter.plot(df_off['rf_{}_off_center_azi'.format(response_dir)], - df_off['rf_{}_off_center_alt'.format(response_dir)], - '.', color='#0000ff') - ax_off_scatter.set_xlim([azi_min, azi_max]) - ax_off_scatter.set_ylim([alt_min, alt_max]) - - # ON-OFF - ax_and_scatter = f.add_subplot(4, 5, 16) - ax_and_scatter.plot(df_and['rf_{}_on_center_azi'.format(response_dir)], - df_and['rf_{}_on_center_alt'.format(response_dir)], - '.', color='#ff0000') - ax_and_scatter.plot(df_and['rf_{}_off_center_azi'.format(response_dir)], - df_and['rf_{}_off_center_alt'.format(response_dir)], - '.', color='#0000ff') - ax_and_scatter.set_xlim([azi_min, azi_max]) - ax_and_scatter.set_ylim([alt_min, alt_max]) - - # =============================pairwise distance============================================= - dis_or = ia.pairwise_distance(df_or[['rf_{}_onoff_center_azi'.format(response_dir), - 'rf_{}_onoff_center_alt'.format(response_dir)]].values) - ax_or_pd = f.add_subplot(4, 5, 2) - if len(dis_or) > 0: - ax_or_pd.hist(dis_or, range=[0, 80], bins=20, facecolor='#aaaaaa', edgecolor='none') - ax_or_pd.get_yaxis().set_ticks([]) - ax_or_pd.set_title('pw RF dis') # pairwise receptive field center distance - - dis_on = ia.pairwise_distance(df_on[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values) - ax_on_pd = f.add_subplot(4, 5, 7) - if len(dis_on) > 0: - ax_on_pd.hist(dis_on, range=[0, 80], bins=20, facecolor='#ff0000', edgecolor='none') - ax_on_pd.get_yaxis().set_ticks([]) - - dis_off = ia.pairwise_distance(df_off[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values) - ax_off_pd = f.add_subplot(4, 5, 12) - if len(dis_off) > 0: - ax_off_pd.hist(dis_off, range=[0, 80], bins=20, facecolor='#0000ff', edgecolor='none') - ax_off_pd.get_yaxis().set_ticks([]) - - dis_and_on = ia.pairwise_distance(df_and[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values) - dis_and_off = ia.pairwise_distance(df_and[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values) - ax_and_pd = f.add_subplot(4, 5, 17) - if len(dis_and_on) > 0: - ax_and_pd.hist(dis_and_on, range=[0, 80], bins=20, facecolor='#ff0000', edgecolor='none', alpha=0.5) - ax_and_pd.hist(dis_and_off, range=[0, 80], bins=20, facecolor='#0000ff', edgecolor='none', alpha=0.5) - ax_and_pd.get_yaxis().set_ticks([]) - - # =============================parewise magnification============================================= - mag_or = ia.pairwise_magnification(df_or[['rf_{}_onoff_center_azi'.format(response_dir), - 'rf_{}_onoff_center_alt'.format(response_dir)]].values, - df_or[['roi_center_col', 'roi_center_row']].values) - ax_or_pm = f.add_subplot(4, 5, 3) - if len(mag_or) > 0: - mag_or = 0.00035 / mag_or # 0.35 um per pixel - ax_or_pm.hist(mag_or, range=[0, 0.2], bins=20, facecolor='#aaaaaa', edgecolor='none') - ax_or_pm.get_yaxis().set_ticks([]) - ax_or_pm.set_title('mm/deg') # pairwise magnification - # - mag_on = ia.pairwise_magnification(df_on[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values, - df_on[['roi_center_col', 'roi_center_row']].values) - ax_on_pm = f.add_subplot(4, 5, 8) - if len(mag_on) > 0: - mag_on = 0.00035 / mag_on # 0.35 um per pixel - ax_on_pm.hist(mag_on, range=[0, 0.2], bins=20, facecolor='#ff0000', edgecolor='none') - ax_on_pm.get_yaxis().set_ticks([]) - - mag_off = ia.pairwise_magnification(df_off[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values, - df_off[['roi_center_col', 'roi_center_row']].values) - ax_off_pm = f.add_subplot(4, 5, 13) - if len(mag_off) > 0: - mag_off = 0.00035 / mag_off # 0.35 um per pixel - ax_off_pm.hist(mag_off, range=[0, 0.2], bins=20, facecolor='#0000ff', edgecolor='none') - ax_off_pm.get_yaxis().set_ticks([]) - - mag_and_on = ia.pairwise_magnification(df_and[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values, - df_and[['roi_center_col', 'roi_center_row']].values) - - mag_and_off = ia.pairwise_magnification(df_and[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values, - df_and[['roi_center_col', 'roi_center_row']].values) - - ax_and_pm = f.add_subplot(4, 5, 18) - if len(mag_and_on) > 0: - mag_and_on = 0.00035 / mag_and_on # 0.35 um per pixel - mag_and_off = 0.00035 / mag_and_off # 0.35 um per pixel - ax_and_pm.hist(mag_and_on, range=[0, 0.2], bins=20, facecolor='#ff0000', edgecolor='none', alpha=0.5,) - ax_and_pm.hist(mag_and_off, range=[0, 0.2], bins=20, facecolor='#0000ff', edgecolor='none', alpha=0.5,) - ax_and_pm.get_yaxis().set_ticks([]) - - # =============================azi alt spatial distribution============================================= - ax_alt_or = f.add_subplot(4, 5, 4) - ax_alt_or.set_title('altitude') - ax_azi_or = f.add_subplot(4, 5, 5) - ax_azi_or.set_title('azimuth') - if len(df_or) > 0: - dt.plot_roi_retinotopy(coords_rf=df_or[['rf_{}_onoff_center_alt'.format(response_dir), - 'rf_{}_onoff_center_azi'.format(response_dir)]].values, - coords_roi=df_or[['roi_center_row', 'roi_center_col']].values, - ax_alt=ax_alt_or, - ax_azi=ax_azi_or, - cmap='viridis', - canvas_shape=(512, 512), - edgecolors='#000000', - linewidth=0.5) - else: - ax_alt_or.set_xticks([]) - ax_alt_or.set_yticks([]) - ax_azi_or.set_xticks([]) - ax_azi_or.set_yticks([]) - - ax_alt_on = f.add_subplot(4, 5, 9) - ax_azi_on = f.add_subplot(4, 5, 10) - if len(df_on) > 0: - dt.plot_roi_retinotopy(coords_rf=df_on[['rf_{}_on_center_alt'.format(response_dir), - 'rf_{}_on_center_azi'.format(response_dir)]].values, - coords_roi=df_on[['roi_center_row', 'roi_center_col']].values, - ax_alt=ax_alt_on, - ax_azi=ax_azi_on, - cmap='viridis', - canvas_shape=(512, 512), - edgecolors='#000000', - linewidth=0.5) - else: - ax_alt_on.set_xticks([]) - ax_alt_on.set_yticks([]) - ax_azi_on.set_xticks([]) - ax_azi_on.set_yticks([]) - - ax_alt_off = f.add_subplot(4, 5, 14) - ax_azi_off = f.add_subplot(4, 5, 15) - if len(df_off) > 0: - dt.plot_roi_retinotopy(coords_rf=df_off[['rf_{}_off_center_alt'.format(response_dir), - 'rf_{}_off_center_azi'.format(response_dir)]].values, - coords_roi=df_off[['roi_center_row', 'roi_center_col']].values, - ax_alt=ax_alt_off, - ax_azi=ax_azi_off, - cmap='viridis', - canvas_shape=(512, 512), - edgecolors='#000000', - linewidth=0.5) - else: - ax_alt_off.set_xticks([]) - ax_alt_off.set_yticks([]) - ax_azi_off.set_xticks([]) - ax_azi_off.set_yticks([]) - - # plt.tight_layout() - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - -pdff.close() - -print('for debug ...') diff --git a/corticalmapping/scripts/post_recording/analysis_database/0090_group_boutons_into_axons.py b/corticalmapping/scripts/post_recording/analysis_database/0090_group_boutons_into_axons.py deleted file mode 100644 index d34cbc9..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0090_group_boutons_into_axons.py +++ /dev/null @@ -1,70 +0,0 @@ -import os -import numpy as np -import h5py -import corticalmapping.DatabaseTools as dt -import corticalmapping.SingleCellAnalysis as sca -import matplotlib.pyplot as plt -import pandas as pd - - -nwb_folder = "nwbs" -save_folder = r"intermediate_results\bouton_clustering" -trace_type = 'f_center_subtracted' -trace_window = 'UniformContrast' # 'AllStimuli', 'UniformContrast', 'LocallySparseNoise', or 'DriftingGratingSpont' - -# BoutonClassifier parameters -skew_filter_sigma = 5. -skew_thr = 0.6 -lowpass_sigma=0.1 -detrend_sigma=3. -event_std_thr = 3. -peri_event_dur = (-1., 3.) -corr_len_thr = 30. -corr_abs_thr = 0.7 -corr_std_thr = 3. -is_cosine_similarity = False -distance_metric = 'euclidean' -linkage_method = 'weighted' -distance_thr = 1.3 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(save_folder, '{}_DistanceThr_{:.2f}'.format(trace_window, distance_thr)) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fns = [f for f in os.listdir(nwb_folder) if f[-4:] == '.nwb'] -nwb_fns.sort() - -bc = dt.BoutonClassifier(skew_filter_sigma=skew_filter_sigma, - skew_thr=skew_thr, - lowpass_sigma=lowpass_sigma, - detrend_sigma=detrend_sigma, - event_std_thr=event_std_thr, - peri_event_dur=peri_event_dur, - corr_len_thr=corr_len_thr, - corr_abs_thr=corr_abs_thr, - corr_std_thr=corr_std_thr, - is_cosine_similarity=is_cosine_similarity, - distance_metric=distance_metric, - linkage_method=linkage_method, - distance_thr=distance_thr) - -for nwb_fi, nwb_fn in enumerate(nwb_fns): - - print('processing {}, {}/{}'.format(nwb_fn, nwb_fi + 1, len(nwb_fns))) - - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') - - plane_ns = dt.get_plane_ns(nwb_f=nwb_f) - plane_ns.sort() - - for plane_i, plane_n in enumerate(plane_ns): - - print('\n\t{}, {}/{}'.format(plane_n, plane_i + 1, len(plane_ns))) - - bc.process_plane(nwb_f=nwb_f, save_folder=save_folder, plane_n=plane_n, trace_type=trace_type, - trace_window=trace_window) - - nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/analysis_database/0100_add_axon_strf.py b/corticalmapping/scripts/post_recording/analysis_database/0100_add_axon_strf.py deleted file mode 100644 index 585ea47..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0100_add_axon_strf.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -import h5py -import corticalmapping.DatabaseTools as dt - -nwb_folder = "nwbs" -clu_folder = r"intermediate_results\bouton_clustering\AllStimuli_DistanceThr_1.30" -strf_t_win = [-0.5, 2.] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -clu_fns = [f for f in os.listdir(clu_folder) if f[-5:] == '.hdf5'] -clu_fns.sort() -print('total number of planes: {}'.format(len(clu_fns))) - -for clu_fi, clu_fn in enumerate(clu_fns): - - date, mid, plane_n, _, _ = clu_fn.split('_') - - print('processing {}_{}_{}, {} / {}'.format(date, mid, plane_n, clu_fi + 1, len(clu_fns))) - - nwb_fn = '{}_{}_110_repacked.nwb'.format(date, mid) - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') - - clu_f = h5py.File(os.path.join(clu_folder, clu_fn)) - - bc = dt.BoutonClassifier() - bc.add_axon_strf(nwb_f=nwb_f, clu_f=clu_f, plane_n=plane_n, t_win=strf_t_win, verbose=False) - - nwb_f.close() - clu_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/0110_add_axon_dgcrm.py b/corticalmapping/scripts/post_recording/analysis_database/0110_add_axon_dgcrm.py deleted file mode 100644 index 68c304e..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0110_add_axon_dgcrm.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -import h5py -import corticalmapping.DatabaseTools as dt - -nwb_folder = "nwbs" -clu_folder = r"intermediate_results\bouton_clustering\AllStimuli_DistanceThr_1.30" -dgcrm_t_win = [-1., 2.5] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -clu_fns = [f for f in os.listdir(clu_folder) if f[-5:] == '.hdf5'] -clu_fns.sort() -print('total number of planes: {}'.format(len(clu_fns))) - -for clu_fi, clu_fn in enumerate(clu_fns): - - date, mid, plane_n, _, _ = clu_fn.split('_') - - print('processing {}_{}_{}, {} / {}'.format(date, mid, plane_n, clu_fi + 1, len(clu_fns))) - - nwb_fn = '{}_{}_110_repacked.nwb'.format(date, mid) - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') - - clu_f = h5py.File(os.path.join(clu_folder, clu_fn)) - - bc = dt.BoutonClassifier() - bc.add_axon_dgcrm(nwb_f=nwb_f, clu_f=clu_f, plane_n=plane_n, t_win=dgcrm_t_win, verbose=False) - - nwb_f.close() - clu_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/0120_get_axon_plane_df.py b/corticalmapping/scripts/post_recording/analysis_database/0120_get_axon_plane_df.py deleted file mode 100644 index 3df2aa4..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0120_get_axon_plane_df.py +++ /dev/null @@ -1,413 +0,0 @@ -import os -import corticalmapping.DatabaseTools as dt -import time -import pandas as pd -import numpy as np -import h5py -from multiprocessing import Pool -import shutil - -date_range = [180301, 190601] -nwb_folder = 'nwbs' -df_folder = r'other_dataframes\dataframes_190530171338' -clu_folder = r'intermediate_results\bouton_clustering\AllStimuli_DistanceThr_1.30' -process_num = 6 -is_overwrite = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -print('pandas version: {}\n'.format(pd.__version__)) - -columns = [ - 'date', - 'mouse_id', - 'plane_n', - 'roi_n', - 'depth', # microns under pia, float - - # roi mask - 'roi_area', # square micron - 'roi_center_row', # center of roi mask in field of view, row - 'roi_center_col', # center of roi mask in field of view, column - - # trace skewness - 'skew_raw', # skewness of unfiltered trace (neuropil subtracted), float - 'skew_fil', # skewness of highpassed trace, float - - # receptive fields - 'rf_pos_on_peak_z', - 'rf_pos_on_area', - 'rf_pos_on_center_alt', - 'rf_pos_on_center_azi', - - 'rf_pos_off_peak_z', - 'rf_pos_off_area', - 'rf_pos_off_center_alt', - 'rf_pos_off_center_azi', - - 'rf_pos_onoff_peak_z', - 'rf_pos_onoff_area', - 'rf_pos_onoff_center_alt', - 'rf_pos_onoff_center_azi', - - 'rf_pos_lsi', - - 'rf_neg_on_peak_z', - 'rf_neg_on_area', - 'rf_neg_on_center_alt', - 'rf_neg_on_center_azi', - - 'rf_neg_off_peak_z', - 'rf_neg_off_area', - 'rf_neg_off_center_alt', - 'rf_neg_off_center_azi', - - 'rf_neg_onoff_peak_z', - 'rf_neg_onoff_area', - 'rf_neg_onoff_center_alt', - 'rf_neg_onoff_center_azi', - - 'rf_neg_lsi', - - # drifting grating peak response - 'dgc_pos_peak_df', - 'dgc_neg_peak_df', - 'dgc_pos_p_ttest_df', - 'dgc_neg_p_ttest_df', - 'dgc_p_anova_df', - - 'dgc_pos_peak_dff', - 'dgc_neg_peak_dff', - 'dgc_pos_p_ttest_dff', - 'dgc_neg_p_ttest_dff', - 'dgc_p_anova_dff', - - 'dgc_pos_peak_z', - 'dgc_neg_peak_z', - 'dgc_pos_p_ttest_z', - 'dgc_neg_p_ttest_z', - 'dgc_p_anova_z', - - # direction / orientation tuning, pos, df - 'dgc_pos_osi_raw_df', - 'dgc_pos_dsi_raw_df', - 'dgc_pos_gosi_raw_df', - 'dgc_pos_gdsi_raw_df', - 'dgc_pos_osi_ele_df', - 'dgc_pos_dsi_ele_df', - 'dgc_pos_gosi_ele_df', - 'dgc_pos_gdsi_ele_df', - 'dgc_pos_osi_rec_df', - 'dgc_pos_dsi_rec_df', - 'dgc_pos_gosi_rec_df', - 'dgc_pos_gdsi_rec_df', - 'dgc_pos_peak_dire_raw_df', - 'dgc_pos_vs_dire_raw_df', - 'dgc_pos_vs_dire_ele_df', - 'dgc_pos_vs_dire_rec_df', - - # direction / orientation tuning, neg, df - 'dgc_neg_osi_raw_df', - 'dgc_neg_dsi_raw_df', - 'dgc_neg_gosi_raw_df', - 'dgc_neg_gdsi_raw_df', - 'dgc_neg_osi_ele_df', - 'dgc_neg_dsi_ele_df', - 'dgc_neg_gosi_ele_df', - 'dgc_neg_gdsi_ele_df', - 'dgc_neg_osi_rec_df', - 'dgc_neg_dsi_rec_df', - 'dgc_neg_gosi_rec_df', - 'dgc_neg_gdsi_rec_df', - 'dgc_neg_peak_dire_raw_df', - 'dgc_neg_vs_dire_raw_df', - 'dgc_neg_vs_dire_ele_df', - 'dgc_neg_vs_dire_rec_df', - - # direction / orientation tuning, pos, dff - 'dgc_pos_osi_raw_dff', - 'dgc_pos_dsi_raw_dff', - 'dgc_pos_gosi_raw_dff', - 'dgc_pos_gdsi_raw_dff', - 'dgc_pos_osi_ele_dff', - 'dgc_pos_dsi_ele_dff', - 'dgc_pos_gosi_ele_dff', - 'dgc_pos_gdsi_ele_dff', - 'dgc_pos_osi_rec_dff', - 'dgc_pos_dsi_rec_dff', - 'dgc_pos_gosi_rec_dff', - 'dgc_pos_gdsi_rec_dff', - 'dgc_pos_peak_dire_raw_dff', - 'dgc_pos_vs_dire_raw_dff', - 'dgc_pos_vs_dire_ele_dff', - 'dgc_pos_vs_dire_rec_dff', - - # direction / orientation tuning, neg, dff - 'dgc_neg_osi_raw_dff', - 'dgc_neg_dsi_raw_dff', - 'dgc_neg_gosi_raw_dff', - 'dgc_neg_gdsi_raw_dff', - 'dgc_neg_osi_ele_dff', - 'dgc_neg_dsi_ele_dff', - 'dgc_neg_gosi_ele_dff', - 'dgc_neg_gdsi_ele_dff', - 'dgc_neg_osi_rec_dff', - 'dgc_neg_dsi_rec_dff', - 'dgc_neg_gosi_rec_dff', - 'dgc_neg_gdsi_rec_dff', - 'dgc_neg_peak_dire_raw_dff', - 'dgc_neg_vs_dire_raw_dff', - 'dgc_neg_vs_dire_ele_dff', - 'dgc_neg_vs_dire_rec_dff', - - # direction / orientation tuning, pos, zscore - 'dgc_pos_osi_raw_z', - 'dgc_pos_dsi_raw_z', - 'dgc_pos_gosi_raw_z', - 'dgc_pos_gdsi_raw_z', - 'dgc_pos_osi_ele_z', - 'dgc_pos_dsi_ele_z', - 'dgc_pos_gosi_ele_z', - 'dgc_pos_gdsi_ele_z', - 'dgc_pos_osi_rec_z', - 'dgc_pos_dsi_rec_z', - 'dgc_pos_gosi_rec_z', - 'dgc_pos_gdsi_rec_z', - 'dgc_pos_peak_dire_raw_z', - 'dgc_pos_vs_dire_raw_z', - 'dgc_pos_vs_dire_ele_z', - 'dgc_pos_vs_dire_rec_z', - - # direction / orientation tuning, neg, zscore - 'dgc_neg_osi_raw_z', - 'dgc_neg_dsi_raw_z', - 'dgc_neg_gosi_raw_z', - 'dgc_neg_gdsi_raw_z', - 'dgc_neg_osi_ele_z', - 'dgc_neg_dsi_ele_z', - 'dgc_neg_gosi_ele_z', - 'dgc_neg_gdsi_ele_z', - 'dgc_neg_osi_rec_z', - 'dgc_neg_dsi_rec_z', - 'dgc_neg_gosi_rec_z', - 'dgc_neg_gdsi_rec_z', - 'dgc_neg_peak_dire_raw_z', - 'dgc_neg_vs_dire_raw_z', - 'dgc_neg_vs_dire_ele_z', - 'dgc_neg_vs_dire_rec_z', - - # sf tuning, pos, df - 'dgc_pos_peak_sf_raw_df', - 'dgc_pos_weighted_sf_raw_df', - 'dgc_pos_weighted_sf_log_raw_df', - 'dgc_pos_weighted_sf_ele_df', - 'dgc_pos_weighted_sf_log_ele_df', - 'dgc_pos_weighted_sf_rec_df', - 'dgc_pos_weighted_sf_log_rec_df', - - # sf tuning, neg, df - 'dgc_neg_peak_sf_raw_df', - 'dgc_neg_weighted_sf_raw_df', - 'dgc_neg_weighted_sf_log_raw_df', - 'dgc_neg_weighted_sf_ele_df', - 'dgc_neg_weighted_sf_log_ele_df', - 'dgc_neg_weighted_sf_rec_df', - 'dgc_neg_weighted_sf_log_rec_df', - - # sf tuning, pos, dff - 'dgc_pos_peak_sf_raw_dff', - 'dgc_pos_weighted_sf_raw_dff', - 'dgc_pos_weighted_sf_log_raw_dff', - 'dgc_pos_weighted_sf_ele_dff', - 'dgc_pos_weighted_sf_log_ele_dff', - 'dgc_pos_weighted_sf_rec_dff', - 'dgc_pos_weighted_sf_log_rec_dff', - - # sf tuning, neg, dff - 'dgc_neg_peak_sf_raw_dff', - 'dgc_neg_weighted_sf_raw_dff', - 'dgc_neg_weighted_sf_log_raw_dff', - 'dgc_neg_weighted_sf_ele_dff', - 'dgc_neg_weighted_sf_log_ele_dff', - 'dgc_neg_weighted_sf_rec_dff', - 'dgc_neg_weighted_sf_log_rec_dff', - - # sf tuning, pos, zscore - 'dgc_pos_peak_sf_raw_z', - 'dgc_pos_weighted_sf_raw_z', - 'dgc_pos_weighted_sf_log_raw_z', - 'dgc_pos_weighted_sf_ele_z', - 'dgc_pos_weighted_sf_log_ele_z', - 'dgc_pos_weighted_sf_rec_z', - 'dgc_pos_weighted_sf_log_rec_z', - - # sf tuning, neg, zscore - 'dgc_neg_peak_sf_raw_z', - 'dgc_neg_weighted_sf_raw_z', - 'dgc_neg_weighted_sf_log_raw_z', - 'dgc_neg_weighted_sf_ele_z', - 'dgc_neg_weighted_sf_log_ele_z', - 'dgc_neg_weighted_sf_rec_z', - 'dgc_neg_weighted_sf_log_rec_z', - - # tf tuning, pos, df - 'dgc_pos_peak_tf_raw_df', - 'dgc_pos_weighted_tf_raw_df', - 'dgc_pos_weighted_tf_log_raw_df', - 'dgc_pos_weighted_tf_ele_df', - 'dgc_pos_weighted_tf_log_ele_df', - 'dgc_pos_weighted_tf_rec_df', - 'dgc_pos_weighted_tf_log_rec_df', - - # tf tuning, neg, df - 'dgc_neg_peak_tf_raw_df', - 'dgc_neg_weighted_tf_raw_df', - 'dgc_neg_weighted_tf_log_raw_df', - 'dgc_neg_weighted_tf_ele_df', - 'dgc_neg_weighted_tf_log_ele_df', - 'dgc_neg_weighted_tf_rec_df', - 'dgc_neg_weighted_tf_log_rec_df', - - # tf tuning, pos, dff - 'dgc_pos_peak_tf_raw_dff', - 'dgc_pos_weighted_tf_raw_dff', - 'dgc_pos_weighted_tf_log_raw_dff', - 'dgc_pos_weighted_tf_ele_dff', - 'dgc_pos_weighted_tf_log_ele_dff', - 'dgc_pos_weighted_tf_rec_dff', - 'dgc_pos_weighted_tf_log_rec_dff', - - # tf tuning, neg, dff - 'dgc_neg_peak_tf_raw_dff', - 'dgc_neg_weighted_tf_raw_dff', - 'dgc_neg_weighted_tf_log_raw_dff', - 'dgc_neg_weighted_tf_ele_dff', - 'dgc_neg_weighted_tf_log_ele_dff', - 'dgc_neg_weighted_tf_rec_dff', - 'dgc_neg_weighted_tf_log_rec_dff', - - # tf tuning, pos, zscore - 'dgc_pos_peak_tf_raw_z', - 'dgc_pos_weighted_tf_raw_z', - 'dgc_pos_weighted_tf_log_raw_z', - 'dgc_pos_weighted_tf_ele_z', - 'dgc_pos_weighted_tf_log_ele_z', - 'dgc_pos_weighted_tf_rec_z', - 'dgc_pos_weighted_tf_log_rec_z', - - # tf tuning, neg, zscore - 'dgc_neg_peak_tf_raw_z', - 'dgc_neg_weighted_tf_raw_z', - 'dgc_neg_weighted_tf_log_raw_z', - 'dgc_neg_weighted_tf_ele_z', - 'dgc_neg_weighted_tf_log_ele_z', - 'dgc_neg_weighted_tf_rec_z', - 'dgc_neg_weighted_tf_log_rec_z', -] - -def process_one_nwb_for_multi_thread(inputs): - - nwb_path, df_folder, clu_folder, params, columns, save_folder, t0, nwb_i, nwb_f_num, is_overwrite = inputs - - nwb_fn = os.path.splitext(os.path.split(nwb_path)[1])[0] - - date, mid, _, _ = nwb_fn.split('_') - - nwb_f = h5py.File(nwb_path, 'r') - plane_ns = dt.get_plane_ns(nwb_f=nwb_f) - plane_ns.sort() - - for plane_n in plane_ns: - print('\tt: {:5.0f} minutes, processing {}, {} / {}, {} ...'.format((time.time() - t0) / 60., - nwb_fn, - nwb_i + 1, - nwb_f_num, - plane_n)) - - roi_df_fn = '{}_{}_{}.csv'.format(date, mid, plane_n) - roi_df = pd.read_csv(os.path.join(df_folder, roi_df_fn)) - - clu_fn = '{}_{}_{}_axon_grouping.hdf5'.format(date, mid, plane_n) - clu_f = h5py.File(os.path.join(clu_folder, clu_fn), 'r') - - axon_ns = clu_f['axons'].keys() - axon_ns.sort() - - axon_df = pd.DataFrame(np.nan, index=range(len(axon_ns)), columns=columns) - - for axon_i, axon_n in enumerate(axon_ns): - - roi_lst = clu_f['axons/{}'.format(axon_n)].value - - if len(roi_lst) == 1: - curr_roi_df = roi_df[roi_df['roi_n'] == roi_lst[0]].reset_index() - for col in columns: - axon_df.loc[axon_i, col] = curr_roi_df.loc[0, col] - axon_df.loc[axon_i, 'roi_n'] = axon_n - else: - axon_properties, _, _, _, _, _, _, _, _, _, _, _, _, _ = \ - dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=axon_n, - params=params, - verbose=False) - for rp_name, rp_value in axon_properties.items(): - axon_df.loc[axon_i, rp_name] = rp_value - - save_path = os.path.join(save_folder, '{}_{}_{}.csv'.format(date, mid, plane_n)) - - if os.path.isfile(save_path): - if is_overwrite: - os.remove(save_path) - axon_df.to_csv(save_path) - else: - raise IOError('Axon dataframe file already exists. \npath: {}'.format(save_path)) - else: - axon_df.to_csv(save_path) - -def run(): - - t0 = time.time() - - with open(os.path.join(df_folder, 'script_log.py')) as script_f: - script = script_f.readlines() - - for line in script: - if line[0:6] == 'params': - exec(line) - - nwb_fns = [] - for fn in os.listdir(os.path.realpath(nwb_folder)): - if fn[-4:] == '.nwb' and date_range[0] <= int(fn[0:6]) <= date_range[1]: - nwb_fns.append(fn) - nwb_fns.sort() - print('\nnwb file list:') - print('\n'.join(nwb_fns)) - - save_folder = df_folder + '_axon_' + os.path.split(clu_folder)[1] - - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - shutil.copyfile(os.path.realpath(__file__), os.path.join(save_folder, 'script_log.py')) - - inputs_lst = [(os.path.join(curr_folder, nwb_folder, nwb_fn), - os.path.realpath(df_folder), - os.path.realpath(clu_folder), - params, - columns, - save_folder, - t0, - nwb_i, - len(nwb_fns), - is_overwrite) for nwb_i, nwb_fn in enumerate(nwb_fns)] - - print('\nprocessing individual nwb files ...') - p = Pool(process_num) - p.map(process_one_nwb_for_multi_thread, inputs_lst) - - -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/0130_get_overall_csv.py b/corticalmapping/scripts/post_recording/analysis_database/0130_get_overall_csv.py deleted file mode 100644 index 5c70614..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0130_get_overall_csv.py +++ /dev/null @@ -1,54 +0,0 @@ -import os -import pandas as pd - -df_folder = 'other_dataframes' -# df_fn = 'dataframes_190530171338' -# df_fn = 'dataframes_190530171338_axon_AllStimuli_DistanceThr_0.50' -# df_fn = 'dataframes_190530171338_axon_AllStimuli_DistanceThr_1.00' -df_fn = 'dataframes_190530171338_axon_AllStimuli_DistanceThr_1.30' -plane_df_fn = 'plane_table_190530170648.xlsx' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -csv_fns = [fn for fn in os.listdir(os.path.join(df_folder, df_fn)) if fn[-4:] == '.csv'] -csv_fns.sort() - -plane_df = pd.read_excel(os.path.join(df_folder, plane_df_fn), sheetname='sheet1') - -df_all = [] - -for csv_fn in csv_fns: - print('reading {} ...'.format(csv_fn)) - df_all.append(pd.read_csv(os.path.join(df_folder, df_fn, csv_fn))) - -df_all = pd.concat(df_all, axis=0) - -try: - df_all.drop(['Unnamed: 0', 'Unnamed: 0.1'], axis=1, inplace=True) -except KeyError: - pass - -print(df_all.columns) - -df_all['vol_n'] = '' - -for plane_i, plane_row in plane_df.iterrows(): - plane_ind = ((df_all['date'] == plane_row['date']) & - (df_all['mouse_id'] == plane_row['mouse_id']) & - (df_all['plane_n'] == plane_row['plane_n'])) - df_all.loc[plane_ind, 'vol_n'] = plane_row['volume_n'] - -print(df_all.vol_n.drop_duplicates()) - -df_all.sort_values(by=['vol_n', 'depth', 'roi_n'], inplace=True) -df_all.reset_index(inplace=True) -df_all.drop(['index'], axis=1, inplace=True) - -print(df_all[['date', 'mouse_id', 'plane_n', 'depth', 'vol_n']].drop_duplicates()) - -df_all.to_csv(df_fn.replace('dataframes', 'dataframe') + '.csv') - - - - diff --git a/corticalmapping/scripts/post_recording/analysis_database/0140_plot_dire_retinotopy.py b/corticalmapping/scripts/post_recording/analysis_database/0140_plot_dire_retinotopy.py deleted file mode 100644 index 54e5a6d..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0140_plot_dire_retinotopy.py +++ /dev/null @@ -1,107 +0,0 @@ -import os -import numpy as np -import pandas as pd -import matplotlib.pyplot as plt -import corticalmapping.core.PlottingTools as pt -import corticalmapping.DatabaseTools as dt -from matplotlib.backends.backend_pdf import PdfPages - -# df_path = r"G:\bulk_LGN_database\dataframe_190530171338.csv" -df_path = r"G:\bulk_LGN_database\dataframe_190530171338_axon_AllStimuli_DistanceThr_1.00.csv" - -depths = [50, 100, 150, 200, 250, 300, 350, 400,] -mouse_ids = ['M360495', 'M376019', 'M386444', 'M426525', 'M439939', 'M439943'] -# mouse_ids = ['M439939'] -dire_type = 'peak_dire' # 'vs_dire' or 'peak_dire' -response_dir = 'pos' -response_type = 'dff' -post_process_type = 'ele' # 'raw', 'ele' or 'rec' -skew_thr = 0.6 -dgc_peak_z_thr = 3. -dgc_p_anova_thr = 0.01 -dsi_type = 'gdsi' -dsi_thr = 0.5 - -rf_z_thr = 1.6 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if dire_type == 'peak_dire' and (post_process_type == 'ele' or post_process_type == 'rec'): - dire_pp = 'raw' -else: - dire_pp = post_process_type - -print('loading csv file: {}'.format(df_path)) -df = pd.read_csv(df_path) -print('csv file loaded.') - -df = df[df['mouse_id'].isin(mouse_ids)] - -df = df[(df['skew_fil'] >= skew_thr) & - (df['dgc_{}_peak_z'.format(response_dir)] >= dgc_peak_z_thr) & - (df['dgc_p_anova_{}'.format(response_type)] <= dgc_p_anova_thr) & - (df['dgc_{}_{}_{}_{}'.format(response_dir, dsi_type, post_process_type, response_type)] >= dsi_thr)] - -pdff = PdfPages(os.path.join('intermediate_results', 'preferred_dire_depth.pdf')) - -f_all = plt.figure(figsize=(12, 8)) -ax_all = f_all.add_subplot(111) -ax_all.set_xlim([0, 90]) -ax_all.set_ylim([-30, 30]) -ax_all.set_aspect('equal') -ax_all.set_title('all depths') - -for depth_i, depth in enumerate(depths): - - depth_df = df[df['depth'] == depth] - print(len(depth_df)) - - f = plt.figure(figsize=(12, 8)) - ax = f.add_subplot(111) - ax.set_xlim([0, 90]) - ax.set_ylim([-30, 30]) - ax.set_aspect('equal') - ax.set_title('{} um'.format(depth)) - - for roi_i, roi_row in depth_df.iterrows(): - - if roi_row['rf_{}_on_peak_z'.format(response_dir)] >= rf_z_thr: - alt = roi_row['rf_{}_on_center_alt'.format(response_dir)] - azi = roi_row['rf_{}_on_center_azi'.format(response_dir)] - dire = roi_row['dgc_{}_{}_{}_{}'.format(response_dir, dire_type, dire_pp, response_type)] - # print('alt: {:6.2f}, azi: {:6.2f}, dire: {}'.format(alt, azi, dire)) - dire = dire * np.pi / 180. - bazi = azi - np.cos(dire) * 1. - dazi = np.cos(dire) * 2. - balt = alt - np.sin(dire) * 1. - dalt = np.sin(dire) * 2. - - ax.arrow(x=bazi, y=balt, dx=dazi, dy=dalt, length_includes_head=True, - head_width=0.5, head_length=1, ec='none', fc='r', alpha=0.5) - ax_all.arrow(x=bazi, y=balt, dx=dazi, dy=dalt, length_includes_head=True, - head_width=0.5, head_length=1, ec='none', fc='r', alpha=0.5) - - if roi_row['rf_{}_off_peak_z'.format(response_dir)] >= rf_z_thr: - alt = roi_row['rf_{}_off_center_alt'.format(response_dir)] - azi = roi_row['rf_{}_off_center_azi'.format(response_dir)] - dire = roi_row['dgc_{}_{}_{}_{}'.format(response_dir, dire_type, dire_pp, response_type)] - # print('alt: {:6.2f}, azi: {:6.2f}, dire: {}'.format(alt, azi, dire)) - dire = dire * np.pi / 180. - bazi = azi - np.sin(dire) * 1. - dazi = np.sin(dire) * 2. - balt = alt - np.cos(dire) * 1. - dalt = np.cos(dire) * 2. - - ax.arrow(x=bazi, y=balt, dx=dazi, dy=dalt, length_includes_head=True, - head_width=0.5, head_length=1, ec='none', fc='b', alpha=0.5) - ax_all.arrow(x=bazi, y=balt, dx=dazi, dy=dalt, length_includes_head=True, - head_width=0.5, head_length=1, ec='none', fc='b', alpha=0.5) - - - pdff.savefig(f) - f.clear() - plt.close(f) - -pdff.savefig(f_all) -pdff.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/0150_plot_ori_vs_rf_axis_DS.py b/corticalmapping/scripts/post_recording/analysis_database/0150_plot_ori_vs_rf_axis_DS.py deleted file mode 100644 index 8f04553..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0150_plot_ori_vs_rf_axis_DS.py +++ /dev/null @@ -1,139 +0,0 @@ -import os -import pandas as pd -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.SingleCellAnalysis as sca -import scipy.stats as stats -import h5py - -# df_path = r"G:\bulk_LGN_database\dataframe_190530171338.csv" -# rf_maps_folder = r"intermediate_results\rf_maps_dataframes_190529210731" - -df_path = r"G:\bulk_LGN_database\dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30.csv" -rf_maps_folder = r"G:\bulk_LGN_database\intermediate_results" \ - r"\rf_maps_dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30" - - -depths = [50, 100, 150, 200, 250, 300, 350, 400,] -mouse_ids = ['M360495', 'M376019', 'M386444', 'M426525', 'M439939', 'M439943'] -# mouse_ids = ['M439939'] -dire_type = 'peak_dire' # 'vs_dire' or 'peak_dire' -response_dir = 'pos' -response_type = 'dff' -post_process_type = 'ele' # 'raw', 'ele' or 'rec' -skew_thr = 0.6 -dgc_peak_z_thr = 3. -dgc_p_anova_thr = 0.01 -dsi_type = 'gdsi' -dsi_thr = 0.5 -osi_type = 'gosi' -osi_thr = 1. / 3. - -ellipse_aspect_thr = 1.0 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if dire_type == 'peak_dire' and (post_process_type == 'ele' or post_process_type == 'rec'): - dire_pp = 'raw' -else: - dire_pp = post_process_type - -print('loading csv file: {}'.format(df_path)) -df = pd.read_csv(df_path) -print('csv file loaded.') - -df = df[(df['mouse_id'].isin(mouse_ids)) & \ - (df['skew_fil'] >= skew_thr) & \ - (df['dgc_{}_peak_z'.format(response_dir)] >= dgc_peak_z_thr) & \ - (df['dgc_p_anova_{}'.format(response_type)] <= dgc_p_anova_thr) & \ - (np.isfinite(df['rf_{}_on_peak_z'.format(response_dir)]))] - -dsdf = df[(df['dgc_{}_{}_{}_{}'.format(response_dir, dsi_type, post_process_type, response_type)] >= dsi_thr)] - -ds_diff_onoff = [] -ds_diff_on = [] -ds_diff_off = [] -for roi_i, roi_row in dsdf.iterrows(): - date = int(roi_row['date']) - mid = roi_row['mouse_id'] - plane_n = roi_row['plane_n'] - roi_n = roi_row['roi_n'] - - map_fn = '{}_{}_{}_{}'.format(date, mid, plane_n, response_dir) - map_f = h5py.File(os.path.join(rf_maps_folder, map_fn + '.hdf5'), 'r') - - on_grp = map_f['{}_ON'.format(map_fn)] - off_grp = map_f['{}_OFF'.format(map_fn)] - - dire = roi_row['dgc_{}_{}_{}_{}'.format(response_dir, dire_type, dire_pp, response_type)] - ori = sca.dire2ori(dire) - - if roi_n in on_grp.keys() and roi_n in off_grp.keys(): - rf_on = sca.SpatialReceptiveField.from_h5_group(on_grp[roi_n]) - rf_off = sca.SpatialReceptiveField.from_h5_group(off_grp[roi_n]) - c_alt_on, c_azi_on = rf_on.get_weighted_rf_center() - c_alt_off, c_azi_off = rf_off.get_weighted_rf_center() - - onoff_ang = np.arctan((c_alt_on - c_alt_off) / (c_azi_on - c_azi_off)) - onoff_ang = onoff_ang * 180. / np.pi - onoff_ang = sca.dire2ori(onoff_ang) - - curr_diff = abs(onoff_ang - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - - ds_diff_onoff.append(curr_diff) - - elif roi_n in on_grp.keys(): - rf_on = sca.SpatialReceptiveField.from_h5_group(on_grp[roi_n]) - ell_on = rf_on.ellipse_fitting(is_plot=False) - if ell_on is not None and ell_on.get_aspect_ratio() >= ellipse_aspect_thr: - curr_diff = abs(ell_on.angle - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - ds_diff_on.append(curr_diff) - - elif roi_n in off_grp.keys(): - rf_off = sca.SpatialReceptiveField.from_h5_group(off_grp[roi_n]) - ell_off = rf_off.ellipse_fitting(is_plot=False) - if ell_off is not None and ell_off.get_aspect_ratio() >= ellipse_aspect_thr: - curr_diff = abs(ell_off.angle - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - ds_diff_off.append(curr_diff) - -print('\nDirection Selective ROIs:') -print('\tWith ONOFF receptive fields:') -print('\t\tn={}'.format(len(ds_diff_onoff))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(ds_diff_onoff))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(ds_diff_onoff))) -chisq_ds_onoff, p_ds_onoff = stats.chisquare(np.histogram(ds_diff_onoff, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_ds_onoff, p_ds_onoff)) - -print('\tWith only ON receptive fields:') -print('\t\tn={}'.format(len(ds_diff_on))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(ds_diff_on))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(ds_diff_on))) -chisq_ds_on, p_ds_on = stats.chisquare(np.histogram(ds_diff_on, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_ds_on, p_ds_on)) - -print('\tWith only OFF receptive fields:') -print('\t\tn={}'.format(len(ds_diff_off))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(ds_diff_off))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(ds_diff_off))) -chisq_ds_off, p_ds_off = stats.chisquare(np.histogram(ds_diff_off, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_ds_off, p_ds_off)) - -ds_diff_all = ds_diff_onoff + ds_diff_on + ds_diff_off -print('\tWith all receptive fields:') -print('\t\tn={}'.format(len(ds_diff_all))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(ds_diff_all))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(ds_diff_all))) -chisq_ds_all, p_ds_all = stats.chisquare(np.histogram(ds_diff_all, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_ds_all, p_ds_all)) - - -plt.hist([ds_diff_onoff, ds_diff_on, ds_diff_off], range=[0, 90], bins=20, stacked=True, - color=['purple', 'r', 'b'], ec='none', alpha=0.5) -plt.show() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/0160_plot_ori_vs_rf_axis_OS.py b/corticalmapping/scripts/post_recording/analysis_database/0160_plot_ori_vs_rf_axis_OS.py deleted file mode 100644 index 10cc5da..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0160_plot_ori_vs_rf_axis_OS.py +++ /dev/null @@ -1,139 +0,0 @@ -import os -import pandas as pd -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.SingleCellAnalysis as sca -import scipy.stats as stats -import h5py - -# df_path = r"G:\bulk_LGN_database\dataframe_190530171338.csv" -# rf_maps_folder = r"intermediate_results\rf_maps_dataframes_190529210731" - -df_path = r"G:\bulk_LGN_database\dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30.csv" -rf_maps_folder = r"G:\bulk_LGN_database\intermediate_results" \ - r"\rf_maps_dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30" - -depths = [50, 100, 150, 200, 250, 300, 350, 400,] -mouse_ids = ['M360495', 'M376019', 'M386444', 'M426525', 'M439939', 'M439943'] -# mouse_ids = ['M439939'] -dire_type = 'peak_dire' # 'vs_dire' or 'peak_dire' -response_dir = 'pos' -response_type = 'dff' -post_process_type = 'ele' # 'raw', 'ele' or 'rec' -skew_thr = 0.6 -dgc_peak_z_thr = 3. -dgc_p_anova_thr = 0.01 -dsi_type = 'gdsi' -dsi_thr = 0.5 -osi_type = 'gosi' -osi_thr = 1. / 3. - -ellipse_aspect_thr = 1.0 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if dire_type == 'peak_dire' and (post_process_type == 'ele' or post_process_type == 'rec'): - dire_pp = 'raw' -else: - dire_pp = post_process_type - -print('loading csv file: {}'.format(df_path)) -df = pd.read_csv(df_path) -print('csv file loaded.') - -df = df[(df['mouse_id'].isin(mouse_ids)) & \ - (df['skew_fil'] >= skew_thr) & \ - (df['dgc_{}_peak_z'.format(response_dir)] >= dgc_peak_z_thr) & \ - (df['dgc_p_anova_{}'.format(response_type)] <= dgc_p_anova_thr) & \ - (np.isfinite(df['rf_{}_on_peak_z'.format(response_dir)]))] - -osdf = df[(df['dgc_{}_{}_{}_{}'.format(response_dir, osi_type, post_process_type, response_type)] >= osi_thr) & \ - (df['dgc_{}_{}_{}_{}'.format(response_dir, dsi_type, post_process_type, response_type)] < dsi_thr)] - -os_diff_onoff = [] -os_diff_on = [] -os_diff_off = [] -for roi_i, roi_row in osdf.iterrows(): - date = int(roi_row['date']) - mid = roi_row['mouse_id'] - plane_n = roi_row['plane_n'] - roi_n = roi_row['roi_n'] - - map_fn = '{}_{}_{}_{}'.format(date, mid, plane_n, response_dir) - map_f = h5py.File(os.path.join(rf_maps_folder, map_fn + '.hdf5'), 'r') - - on_grp = map_f['{}_ON'.format(map_fn)] - off_grp = map_f['{}_OFF'.format(map_fn)] - - dire = roi_row['dgc_{}_{}_{}_{}'.format(response_dir, dire_type, dire_pp, response_type)] - ori = sca.dire2ori(dire) - - if roi_n in on_grp.keys() and roi_n in off_grp.keys(): - rf_on = sca.SpatialReceptiveField.from_h5_group(on_grp[roi_n]) - rf_off = sca.SpatialReceptiveField.from_h5_group(off_grp[roi_n]) - c_alt_on, c_azi_on = rf_on.get_weighted_rf_center() - c_alt_off, c_azi_off = rf_off.get_weighted_rf_center() - - onoff_ang = np.arctan((c_alt_on - c_alt_off) / (c_azi_on - c_azi_off)) - onoff_ang = onoff_ang * 180. / np.pi - onoff_ang = sca.dire2ori(onoff_ang) - - curr_diff = abs(onoff_ang - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - - os_diff_onoff.append(curr_diff) - - elif roi_n in on_grp.keys(): - rf_on = sca.SpatialReceptiveField.from_h5_group(on_grp[roi_n]) - ell_on = rf_on.ellipse_fitting(is_plot=False) - if ell_on is not None and ell_on.get_aspect_ratio() >= ellipse_aspect_thr: - curr_diff = abs(ell_on.angle - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - os_diff_on.append(curr_diff) - - elif roi_n in off_grp.keys(): - rf_off = sca.SpatialReceptiveField.from_h5_group(off_grp[roi_n]) - ell_off = rf_off.ellipse_fitting(is_plot=False) - if ell_off is not None and ell_off.get_aspect_ratio() >= ellipse_aspect_thr: - curr_diff = abs(ell_off.angle - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - os_diff_off.append(curr_diff) - -print('\nOrientation Selective ROIs:') -print('\tWith ONOFF receptive fields:') -print('\t\tn={}'.format(len(os_diff_onoff))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(os_diff_onoff))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(os_diff_onoff))) -chisq_os_onoff, p_os_onoff = stats.chisquare(np.histogram(os_diff_onoff, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_os_onoff, p_os_onoff)) - -print('\tWith only ON receptive fields:') -print('\t\tn={}'.format(len(os_diff_on))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(os_diff_on))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(os_diff_on))) -chisq_os_on, p_os_on = stats.chisquare(np.histogram(os_diff_on, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_os_on, p_os_on)) - -print('\tWith only OFF receptive fields:') -print('\t\tn={}'.format(len(os_diff_off))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(os_diff_off))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(os_diff_off))) -chisq_os_off, p_os_off = stats.chisquare(np.histogram(os_diff_off, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_os_off, p_os_off)) - -os_diff_all = os_diff_onoff + os_diff_on + os_diff_off -print('\tWith all receptive fields:') -print('\t\tn={}'.format(len(os_diff_all))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(os_diff_all))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(os_diff_all))) -chisq_os_all, p_os_all = stats.chisquare(np.histogram(os_diff_all, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_os_all, p_os_all)) - - -plt.hist([os_diff_onoff, os_diff_on, os_diff_off], range=[0, 90], bins=20, stacked=True, - color=['purple', 'r', 'b'], ec='none', alpha=0.5) -plt.show() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/0170_two_way_anova_dsi.py b/corticalmapping/scripts/post_recording/analysis_database/0170_two_way_anova_dsi.py deleted file mode 100644 index da6e4b5..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0170_two_way_anova_dsi.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -effects of volume and depth on dsi measurement -two way anova -""" - -import os -import numpy as np -import pandas as pd - -import statsmodels.api as sm -from statsmodels.formula.api import ols -import statsmodels.stats.multicomp - - -df_path = r"G:\bulk_LGN_database\dataframe_190530171338.csv" -# df_path = r"G:\bulk_LGN_database\dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30.csv") - -depths = [50, 100, 150, 200, 250, 300, 350, 400,] -mouse_ids = ['M360495', 'M376019', 'M386444', 'M426525', 'M439939', 'M439943'] - -# depths = [50, 100, 150, 200, 250, 300, 350, 400,] -# mouse_ids = ['M439943'] - -# depths = [50, 100, 150, 200, 250, 300, 350] -# mouse_ids = ['M439939'] - -# depths = [50, 100, 150, 200, 250, 300] -# mouse_ids = ['M426525'] - -# depths = [100, 200, 300] -# mouse_ids = ['M386444'] - -# depths = [50, 100, 150, 200, 250] -# mouse_ids = ['M376019', 'M360495'] - -# dire_type = 'peak_dire' # 'vs_dire' or 'peak_dire' -response_dir = 'pos' -response_type = 'dff' -post_process_type = 'ele' # 'raw', 'ele' or 'rec' -skew_thr = 0.6 -dgc_peak_z_thr = 3. -dgc_p_anova_thr = 0.01 -dsi_type = 'gdsi' -# dsi_thr = 0.5 - -# nti_half_span = 45. -# nti_sum_thr = 10 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -print('loading csv file: {}'.format(df_path)) -df = pd.read_csv(df_path) -print('csv file loaded.') - - -df = df[(df['mouse_id'].isin(mouse_ids)) & - (df['depth'].isin(depths)) & - (df['skew_fil'] >= skew_thr) & - (df['dgc_{}_peak_z'.format(response_dir)] >= dgc_peak_z_thr) & - (df['dgc_p_anova_{}'.format(response_type)] <= dgc_p_anova_thr)] - -model = ols(f'dgc_{response_dir}_{dsi_type}_{post_process_type}_{response_type} ~ ' \ - f'C(depth)*C(vol_n)', df).fit() - -# model = ols(f'dgc_{response_dir}_{dsi_type}_{post_process_type}_{response_type} ~ ' \ - # f'C(depth)*C(mouse_id)', df).fit() - -# model = ols(f'dgc_{response_dir}_{dsi_type}_{post_process_type}_{response_type} ~ ' \ -# f'C(depth)*C(vol_n)*C(mouse_id)', df).fit() - -print(f"Overall model F({model.df_model: .0f},{model.df_resid: .0f})" \ - f"= {model.fvalue: .3f}, p = {model.f_pvalue: .4f}") - -print(model.summary()) - -res = sm.stats.anova_lm(model, typ=2) - -def anova_table(aov): - aov['mean_sq'] = aov[:]['sum_sq']/aov[:]['df'] - - aov['eta_sq'] = aov[:-1]['sum_sq']/sum(aov['sum_sq']) - - aov['omega_sq'] = (aov[:-1]['sum_sq']-(aov[:-1]['df']*aov['mean_sq'][-1]))/(sum(aov['sum_sq'])+aov['mean_sq'][-1]) - - cols = ['sum_sq', 'mean_sq', 'df', 'F', 'PR(>F)', 'eta_sq', 'omega_sq'] - aov = aov[cols] - return aov - -print(anova_table(res)) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/0180_two_way_anova_rf_area.py b/corticalmapping/scripts/post_recording/analysis_database/0180_two_way_anova_rf_area.py deleted file mode 100644 index 58abe3a..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0180_two_way_anova_rf_area.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -effects of volume and depth on rf_area measurement -two way anova -""" - -import os -import numpy as np -import pandas as pd - -import statsmodels.api as sm -from statsmodels.formula.api import ols -import statsmodels.stats.multicomp - - -df_path = r"G:\bulk_LGN_database\dataframe_190530171338.csv" -# df_path = r"G:\bulk_LGN_database\dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30.csv" - -# depths = [50, 100, 150, 200, 250, 300, 350, 400,] -# mouse_ids = ['M360495', 'M376019', 'M386444', 'M426525', 'M439939', 'M439943'] - -# depths = [50, 100, 150, 200, 250, 300, 350, 400,] -# mouse_ids = ['M439943'] - -# depths = [50, 100, 150, 200, 250, 300, 350] -# mouse_ids = ['M439939'] - -# depths = [50, 100, 150, 200, 250, 300] -# mouse_ids = ['M426525'] - -depths = [100, 200, 300] -mouse_ids = ['M386444'] - -# depths = [50, 100, 150, 200, 250] -# mouse_ids = ['M376019', 'M360495'] - -response_dir = 'pos' -response_type = 'dff' -post_process_type = 'ele' # 'raw', 'ele' or 'rec' -skew_thr = 0.6 -rf_z_thr_abs = 1.6 -polarity = 'off' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -print('loading csv file: {}'.format(df_path)) -df = pd.read_csv(df_path) -print('csv file loaded.') - -df = df[(df['mouse_id'].isin(mouse_ids)) & - (df['depth'].isin(depths)) & - (df['skew_fil'] >= skew_thr) & - (df[f'rf_{response_dir}_{polarity}_peak_z'] >= rf_z_thr_abs)] - -model = ols(f'rf_{response_dir}_{polarity}_area ~ C(depth)*C(vol_n)', df).fit() - -# model = ols(f'rf_{response_dir}_{polarity}_area ~ C(depth)*C(mouse_id)', df).fit() - -# model = ols(f'rf_{response_dir}_{polarity}_area ~ C(depth)*C(vol_n)*C(mouse_id)', df).fit() - -print(f"Overall model F({model.df_model: .0f},{model.df_resid: .0f})" \ - f"= {model.fvalue: .3f}, p = {model.f_pvalue: .4f}") - -print(model.summary()) - -res = sm.stats.anova_lm(model, typ=2) - -def anova_table(aov): - aov['mean_sq'] = aov[:]['sum_sq']/aov[:]['df'] - - aov['eta_sq'] = aov[:-1]['sum_sq']/sum(aov['sum_sq']) - - aov['omega_sq'] = (aov[:-1]['sum_sq']-(aov[:-1]['df']*aov['mean_sq'][-1]))/(sum(aov['sum_sq'])+aov['mean_sq'][-1]) - - cols = ['sum_sq', 'mean_sq', 'df', 'F', 'PR(>F)', 'eta_sq', 'omega_sq'] - aov = aov[cols] - return aov - -print(anova_table(res)) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/0190_get_axon_morphology.py b/corticalmapping/scripts/post_recording/analysis_database/0190_get_axon_morphology.py deleted file mode 100644 index 54bcdb1..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/0190_get_axon_morphology.py +++ /dev/null @@ -1,54 +0,0 @@ -import os -import h5py -import pandas as pd -import corticalmapping.DatabaseTools as dt - -nwb_folder = r"G:\bulk_LGN_database\nwbs" -clu_folder = r"intermediate_results\bouton_clustering\AllStimuli_DistanceThr_1.30" - -dfa_fn = "dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30.csv" - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -print('reading csv file ...') -dfa = pd.read_csv(dfa_fn) -print('csv file loaded.') - -df_plane = dfa[['date', 'mouse_id', 'plane_n']].drop_duplicates().reset_index() -print(len(df_plane)) - -axon_morph_dict = {} - -ind = 0 -for plane_i, plane_row in df_plane.iterrows(): - date = int(plane_row['date']) - mid = plane_row['mouse_id'] - plane_n = plane_row['plane_n'] - - print('{}_{}_{}, {}/{}'.format(date, mid, plane_n, plane_i + 1, len(df_plane))) - - nwb_f = h5py.File(os.path.join(nwb_folder, '{}_{}_110_repacked.nwb'.format(date, mid)), 'r') - clu_f = h5py.File(os.path.join(clu_folder, '{}_{}_{}_axon_grouping.hdf5'.format(date, mid, plane_n)), 'r') - - curr_dfa = dfa[(dfa['date'] == date) & - (dfa['mouse_id'] == mid) & - (dfa['plane_n'] == plane_n)].reset_index() - - for axon_i, axon_row in curr_dfa.iterrows(): - - axon_morph = dt.get_axon_morphology(clu_f=clu_f, nwb_f=nwb_f, plane_n=plane_n, axon_n=axon_row['roi_n']) - axon_morph.update({'date': date, - 'mouse_id': mid, - 'plane_n': plane_n, - 'roi_n': axon_row['roi_n']}) - - axon_morph_dict[ind] = axon_morph - ind = ind + 1 - -dfa_morph = pd.DataFrame.from_dict(axon_morph_dict, orient='index') -print(dfa_morph) - -strs = os.path.splitext(dfa_fn)[0].split('_') -save_n = '{}_{}_axon_morphology_{}_{}.csv'.format(strs[0], strs[1], strs[-2], strs[-1]) -dfa_morph.to_csv(save_n) diff --git a/corticalmapping/scripts/post_recording/analysis_database/h5repack.exe b/corticalmapping/scripts/post_recording/analysis_database/h5repack.exe deleted file mode 100644 index 3ee5da805053a168f7b43514494c31d0c5b93321..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1877504 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

    ^X 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

    9bum?lk3JVSJ0W9eLq}eXSfvh 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νm-pAsy3MyjjBBt*N*hi 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 z zu4-<7+d92fNvrEecZs^XJw~fF>sDGdM63rJzVX=3B7(_;zKSOs?SKv1Z#W*%AkQ4D z9e?6PS^b38;&1qciUPm0+qzo|aeLltIyT+m9RS{-nS264spg|mQzyhOJcoYp+P4tx zj(56)YDvFw6xMx7e7H-Fs2P?4C!coFfmoc2_VY? zq1{UBIk42yw77oFzkjj)dyVg5grPBf7uvvJJUVa*r@s*NJN0L`7C%SYME0ufbiLqs zME2c1CbHk}Y*p1ol0!tywmpnk+-X@Sve(~?MfL*e*5W@2H2-*?^sA4i$+$gCHWnR} zmek@sZ~6VxA^K1UmFc2Vt;M^CWc7Ds?FU&WK~`&V*8oGRd9^HzBW!Ecmkl>7AcHUv z+LN#ie}9>@MLIJ!t|mO^SzlhU|zN0ZuOegdf-4z=+% zjk;d7t_jiAS=g8nr^|>8h=Kn(s*+a#+5Xo72ko%1g#J;MQqe=_@Jm;C*;2Yfj!6b) zeNjJwNyT9LL|_I0rtLWM&dYAZWa$*jfDyXf=i;%|S-YU4Y>MSC^HYB0|nHO`3|D{3L10SnhLaXYtW zy;>-vR&Q}CPjt#g^o!<3)nm4JF%m!!pKND>*iG_qO|S`OOkrWM(lLhYK3VQq^e?DU z(+`0L?LWLRS@oAx(;C@0@DH54qW(6>%u;{H@M!hN;+qsp^_RWF%)mL0hTHYyXh?%M z)%BrKj@N#HC3~3`$Q_6qebLm1#t% zGT(EC#{T2e<%32hrai2x_lA$5URdm5Svv8sZ80lk?-=0erR-kp-=N1jwGi@)Lgzfc%{P5g*Oh|o?~GJNKQMe zx{($U{?42ZN0BtESvlMFCl0}}`f+uY13QtEn3Zwz+KuN>krJd^i~r8iEB)hr(!XVx zlNmk7$@3d89*?oz4qV=mr*Z0HdV*x*#kFL*P)zDiy^}{WtfgkH;d>WXW)f2j!u5lwTcWcw*L>d~&Q#Lf*O3dGvd#X-cfGtUOP`kl+E8p0qj=VV1I5$`#clx#YN2!+tAzvjp%(6|3VL#D(?N4SnP4^> zSh^HBQzxsxFyBF~zIDiW5$3G-$&9$whXuDCFyF7XU)wnFEDq99UmH|kCz7`MI`^BX zzOKE^>g&3#jg-6fi#?=Iu0LX8XD#}%5prs+-gue(-4u^v_0wXo4{ zi~GFHs9mogN9`$4lxj{#)PA)jruI?NMq?TpWuCFoU~>{SghR3P^CxVfWBb`|ebo$G z{H`My8ICwDotac2jk^v;8!wT@X)jKC-0Qir4QEjG1kn6TuIywK%?%^ZCX=ci>?k}o zpfDUp?pGTRBZr+N@8I}YTc(CIqc6ueno-GbUk<3heCU6mUJPZ%(g0w}v;dyM^`3!e zaTtvHx1FRXl6Lj)PMJue*WRLz`v{fdU#fY>*bl97G(gQHM+4OSKA;8`AKS_*VL}hn z-YJ&zwx?10zgonf>2V;yM&0`TGtK&Y-?SeT`JjrGb6~U zizj!X;lZTc~!ZS&6}?7ZuoPy0*^&;%{BBHs^1lKi&A_74L_vxXbnr6n8UKc{kc_xfqAA zFQoV_KL!Y^4Iy-7hi&lhbGb!z2m3GN*5v$W^yf+bFqg)sfL&!3H4&A`1N-bEz`jFX zKCF`+tWI?Y=I5r#g$wvu2P9bTOvs9C*;F2KWoP|9;EY>Kvt@rG&lny}b}%z?F!8e> zX;=N((dhcEqpPpcHKik6yNRxp=sH8<9(H4{tQO%3zq`KYuw2 zyayk`>{d6b&yB87f1p(cWg74-oEz1d8ABA{F?6xqUvNZcuGkxiInHIyI5AhY2}D!1 zo3;)e!)tEhG*;S91AS&Fli*b38$4+0=`dw7A5&rTG5d`pm)+5owB55aLu)HDV^3pE z*A5W)dx^$g`m?hUS`SGOx*ZKJ)qFOTwGZ)&^}0ZRe)?I=EOpErK1R0Xk1DgP|1od; z_x0y(EAyZ$^W)Aca|UJd*#3%PO*Quoxb*=Vd>t9oPnO0Wcr&~1^)*dPb0iJ$$^^-; zg{Kefk2NR1bRXU3HoQOcUT}|~PV#F+PFjBk8BNzYycdHP7GDKh9#LstV!)y1pD!6T zxh*9E&G!W~UD5mN;DC}_XwH>A0H%Bc5RLDXbcWpXM(F4Z`g~2itg)wggHEd~UpZlO zbqLf$U%H{>*!dGn-46CR#~V@WjjCnsg6*yp>(^5La^eQr7)rkaF8a1EZbqEB?|gjD zr1+bHui9f0t^C5?=WmNZ=xM507s3QBam`4jZ#9R7I4&n+-zdX(i0Ds?j|Ew551@X}T47Nk(I zy|+-M>bjSpQk(a1m5Q)~34EA{A3{#tujsS!PDtMd|3(y5HoE>4|3fU1|Bn8=!5{9T za+K}tC`)hNfwFhzhm=hs1!Xf!MOi6LKcH-9DC^49=80}W8iyzD0O(SJWoC5+ipwmQ zV|;q*>crGv(Aac#pF`4(KhuY-)>HX6i5Z_;;;f2^mHt7Zv9JF0F+!(60kXTBLx-v6 z9#D`c!2=>XUwzRiPPV--NL(&jhK{Ar-`XMx*ucU+Y>3)J+u;=O2943WXK~~D!ul@h z0T+&}J4>w-=h@ubLu;*#j%pob#*9EnX6XEzHbB*N`ZJS1#DN4Ur2Wo>vqwkLu6G2{ zVh;>SlWEpWu%?AHBCEC2;puXJ;x;x=#^@^GdGl zk+HJpcCdNsLL)E**X?GM zwVh5^)S*GBJ@gD>d0Bg1<7d!X9ju@|Ec*ch7VFOf{&>aPi3+cH8(>3Be~8J`v&9ZM zmH7u!w1)=M#`9zOB%q=zTONYB4F)Y6w+GEqs0C~;EkJ^_2R|`Y-e@{|*nZBFS}Qq~ z|B{%|W{C?cCT>T37idiC&-VODHBSU-+lRb2CGH$4@r03eM@CBgR<5l6BvaypH&)^| z*8ys|{w(2-SG)xr!SVIRQG`fUvL5&`_P{?r@P z`7apMhRd`pl?7^hIiFw(TnnIWPAsA=FcsW>oPUJA8>m11`GXrrdCuZG5D#ujRj}8J zg^#=%IfR$g!+Pq;ED}}o%JWV`{Kj1Zt7&Q)&JJPEWg~me>b-xawEV&o8R9fT z>C{~PC9Z+3Il*Lksz2*{h`dIBuHcVX{607#uFY0cc09FP2d%lr5d>)mkb>G_rs6Na zU0_AuNd}Zg_C0LBfqegqxp(8+3hT@LN7M)|5#hF-f`Mmxz|e|B(?SW{8_{39j>(lB z^*fo55&gZDxw1czmRt59ys@HDp*h#l?D=IQgMvuVzp4u>?Sv*geU=>N_Mb zLw_#g4?fr5t+p>U{yH(9%d4p@?_4j}$EA`8tc4W<1TtVVYr-pbc4DB0G;(HMwsojk z+`yZZ3u(Q#du_Z;{Mzp-0=no=C!?aDDkHn&mjF#QZ{3lKO(H7pdd{gyTm41`qqX?R z?=(*7Fg~)W`Dc2X7&l~xwK1MC88VJ{H*OqGyJ0AJE>#ahtMvo7tj@d%o>cQ+Y2Q3E zf*i; z6fRr57P*6bLX3R0A-^|5zUtW+`JJR8De0GkT+y(A%n0)@rU%q{9f=Nl*KrF$WMd=$ zNq05aK=kEC0c5<(uhmxv2dWaJJ&#dflH*wB?+I=FzOtA|%*y@@fJD}jn5>40YSOl{qiKfd*r4t3|Q z^sh_Sy=ZIcUvK7>?f)P(!YV`I$*U1!Q?CXfNs`(?x%2imt~{kLzI8-`(R>0hm-b+XFlF+Lbo)LBFFbz z;g`13Y~FH<`ML~}m3;*hDB+`OI8v{WEBkg#Q2}ID@Sst1uIxuL+#E~EalESQ#b`+v zJ41s_uPE?91sE7>z9 z;sZEcWA?WI4uOXBQkxQ5sEAzIKF6AoWlO@A5uZu&Q1HDRCs*qyWLA6*kLQwp8K$w^ zL2@=7ue*d5McbP<*LcGc-{VqA|8Roxwq2@@#&OwoLZ6V!2``CDHod_qOwv`uXx#92^GxMMM{<8IaI3R zkiuIIghKWa76g*Yr0I>Ddaab!2KG^gF>@}4-PamS<}%fq9e`6LqdTh%`&XM7_G4r0 zKNvq(Y14w8g#A>BGmI*wv3tWP)sZo`qXU111OJ159CNoA_|Xyg$~^GB zL+~&31AL*FYwBeoDYGL^OwBET{>)`~-^!K)MKrt2W%wL=zLV$7WejT3mGRI~G5ujo zG3|{C%v`ogNp@E>Z7ZenMzOMb!y@wLl+7Fe|w>B=qzYZGIZpI()?c)7oMGfgG}(RIQ>A&A5| zZwbOnKzO%HM+irBfbhy(AT;$FiF^hWkDa5%+jpf)Y=&vjZ?^0=J!ntZ@5c_%a% zXUVS4UN&CLX3Kg6Z=X2c-ezvKkBa{>#MEqBv8mZN`f+-&!&6f7>mof!KN+i8lC-JW z(+3diG_-M(OQd7Hq|2-x5(>HdDpn{MlKHb)sKdZkvtVX6lJ+MXDcWUdwL4Y4Kzhmo z^($;pJ#nnAqHAqmN<-)eg{kHu>9V72KZhN|P~cJ+hAWs2$zKgk0GYQm(srXQY|YwS~smaPbGF z;gtNSF0mPX+PGR2Qs)(~fF3hV@b%*3#vy#wx)kyMh_)LnBM=g&(P)baQ&+b;gmESy zYM*PdxkkIX?gr@)CsL>L%ME*Uj?4wt^`W=vQ`zgH3pOd1#my}~J#h{jd_8M2iF1~t zj6*k4_2XvbH9Kak=>Hwg~y->q-m0107Pb@oW4T-1sb`Lu>l)hc`E)7diy#gxkB z-TLZoaVp~c=3jx(_17T0^;k$OqX^gFxRA;7RJZ=Nld)|Xt7pehiM^9p{GW+<#XF?~ zX6>E0@;`R(Trc>nnq{u+$>BkFSH8ZELwIbNe9u;*T;s-;_mBuUQ2K@rp0zL>uMKBz z-H?JjCV_jn?b8FMj0dV_lV3o+yP3dBRag>#o*`4$JP-71yZ(m!a<1IY~v+y-e)Xc=L2)G12_|Zok2NUb%`) zIq#(t_Q!blw1tBjP85`VfV78s-BxMIRHi%VkF{u<^lKlLex1$6S$s%_(l!Y}hjb$T zdEksz(vN4OBPj2i0Iy`R&8%R0l#7 z&zR)jJ`{JVOBmmf%l*oGhenw@P#Wj;*WE6hLEPvDMai=?KT_nBFK3!EKhbM7zlpt^ zw$ykZSC9Jw!Pe-O>va|#WmiOTObQZVyn&mu_24@%Et3 zq@L;Ayk9CynArQGQ~j6K#(DkcmI=>A$QNdi?BaA?!B)_1n1HY~YA^s<}zTI5|A~K-_J5 zq{*e*+{>kseoJrM-^Vrjh+MjWJc%CizQI`wR8^g2$%IW5=^prViV{utQHEGi=#_bq zXviv#w{XnH_aD;G<#s7;RpOaXH=l4Wg9m}c$NG++K92uBO}%tzX`}wtxul76Rs--7 z0OE+Q3!Kq|erU4(b@l0q-fy{%MX?(S&uOH#Ktc*}E;V+dx%hz&n$~T?MgL1pD+5cure!bPSktxwl{8JxQg!AJ zBbgOM49rnU?UdPmajbU37=-yB-@k^dGrh%z<%!;bM>}P~ObCq5$q|Vqm~Z|c z*jsj)hW2(%wfBqJ+Yv@RbgzPLNIV4+0~PuN@}Y5(BN{IDg=p?XU?xhH~KC}-{QKyqcT4RY4LI#+f#%WTftKS3rZ5F{Q*4z2vw zNK(%gaC=k#NNe7^J7n}hQf)mMti^4vqbcuYK3vow|4*j83}aBuAbiJrJ2bHWy(xbJ z_04Z!`&_bd1AA=Fe>dgp*a+rlDHOBVy@M%F;bfXA*U4%f$CoKjkL&(1GxFRWA^Y9t z>^E1m(A)0C(xBPzuPB!*+f|Rwes4pv-(s(KuI#1MubS_zj|k0o=Z(zw;WFRbz-*1T zP&1)P|J{4?&G*>?xYVroVh202-dc@y)*CZECNSR5skWW*Uh|#W+eXHF)t%Pze&$`O zS@$+O#lMy8(0C6ZO>WX(oB5z=-7?(oVz~cJq=BXW`jN;|H?>?KOFawa zpM#!%BH3oUYh49PZCr1lId!nq3j<3nDZFtvBLGvsBs;4JV*{M3!etEu3hH%j1#!v! zUG~iHGP?h-y@o32$XB&_7j3L+Lm@`Cd@e-DeCs#gYw#E|_`6bw(EvO8F!fADV1>q} zC;mZ4N7$B1cN=t~+2Yj)M=kRBznOs<@~v5K3}G)a(1?><2A1&tJ7kiuSfS}Tbm#xQ zH4o!S;Kf(4A0*xQV~%jec{0PSV1fEvkhpYUuspruX&C*zQ4+o?H=6DZzDHDpU}u^lb6tDBdD~BZ zZyY|fPEF3nJeNUbs`(nE>V&ct&tVIDdv?&m&e>6;%C@dXJCP=D=`}|HuWh#5(_C5Vi$>7e+Y;{3NI&)=%)!tHnsfuO09|^tL9^8jb)FNhb z)vs&KNCba+o)KK+2>w|=j^MW-D%Grz3y$DZc@7EQBP96m1l}bq+FDVWrAvKAQ-b~` z|M~?wZMSc=*L^94L3H(1*6ZJFRtS+`p!#~C-QO0Ai+nbxXo&hJ=mTG}2>964;Vg^?d+C$*M4|tVU3A#lgOI~pnkg4W7B6MYW=+@j8 zpqmalgr?Zd=5{Ew+h|}|AhtdZbe#kJrx^4R#>u@A=w|>Ia&mnLdS3^+P@uEJ*_^=8 z7GERr6eshQKFmdXyBp}xpKwS@Vk93J{SQS*j>$uEK!{|~c1FL13T(FvQos{WarfEM z%w?JfRbWXlx0u(Fcq$4704tFTtnI_b8n-ry)f;4B=74O#cP=M(=Vzz0r##oHSFPlcaMR;3xHH} zO9cFcJm3e1z?W_-);S`?MmZo8&vmp#f~fMkKSmkg|Fp)4e}W&Q8o{9E-37qZy!{qa z^J~FRhN<~{@jpS3TSPYYFKG8nb*bH_8I+jq>Gz7iLlUQN$^scDJH#5E6LPapp*?T> zbDFPQe4qxPS9=y8=v?IIblM~}e!-8DimdmJki%M@X{3qzGsvV4y2FN(^>9b``z`}p zzAG7~e@Xx8o*k_B5`W?-lZx(vY+o(NiVVMTC?F$Q^FTa9P5Ec+PK>_B5I>}KoUq*7!Ro0dw-Nx@~4}ei+60Hc4BK^ zl{u?tRc3;(NyipS)MUo3s>z(Svfq0(nF-5lG8Zly?DyTs=*dOR` z+RZG1Wc48opaGMIR{kIkH%2Y#u;E!+&dt z;6R5!QNv_1s!>?k2(2}ax8FN^O*WNbaBsidU!z}^D36sLI}IhJ*$rc@43M}CmYUhv{26m~fH*Ai z`3<|ZbZ&YW))SGY-L_#}{xZIKs#|8FW z#P;i+E?;@N_tJuk{{JBVSLlR?_TyneYgI-!%tY>#hcvlKfBcrF zI&Dp=&yX8xHR8GiPV%26h5Tp2Alh^LPVib1@VfUU@S4^3Gh=;w9wmYa_sW+h3h7FJ z3`yrmS8v}lJ?^!1;hP$`ZR0knvj8$i3m~=Hdl_%M4?5gm(_ca(TNH^le{(}(4*R!r z%y)NqDuridx^QRzOR`aKn|F9b`rWy1;$cN~Y3cW+CWj-jshdnE?qyra^mHfp)7kwL zx}PHVQ|x}axSvhbBH9Uxl^}RzPfW1HsPwj$CV|Ez!u0m-r7D(}!=@7Vw%Qxw)Mlyw z%|)w;5!pqWGrbUiUKa|@TVE1F=^TE;FW?FTteY&PKCKr2Q^Mo&lx?(C?i55|S~Vq4 zt7^Y6t=fejwCYg(pfB{8_oF#%lH?nwRrg#IYgHp@)2i)ComM3&22mx`J4d3}G$2sK zZkCtsI=xeymKI>$Y0*;uy9?D~hBz(aK0Y4XwB?8T8j@=Gsir=>ELAghu3N>G*Y$4dA+6m)^VNU)L%AjAJMsv5Ae{3`IT(z7=>X-=g(lnD!(_+t*c zvey`Y-FGr-=3)v;>*wNPc?@Mcad8vR&uk$ka2OB7I|kPU;3lPs2B{8 zmoKr2foK0|t!{ySoJd}Sq*U`;K;xPDmjh0d}vq09N0`!0r;T zUVNh3qx#$B>COFzP~IMwDuiA~=|8S;^M8~e12^kAGBA3B$p{>*VO_Cj%jnWLCTus^ zCgZ?H7jok4(+>A)|9X36)W!X8DcufFSM>#B7X|T45-+bhyTi?CyEpB~Y|)4QNfxtZ z@@~2LAxEY_5|3`8TJCe*sD-U_uv0BQ<l}za%*UkjQt=~?b2cM|C%2JNuu?!}W z0a$=DdDwn_HZ$O+KfuC`4ng*+a?RyL|C#xmC3uoQUh!6t##w@bldUBm@2cf|e)3K> z+&sg?kE$I-D%D&`B?X$cszg%~u1{!mm-UQ+bRF}^^ylELHZfwMJ9tI5pS~j9m|`mf zr`0F2tv<&#R^k9;yB9z!Hs>V&hTndh^S2b8-HpykV1~}s2psj9ryqs|!I0xeFC_30 zQnZCfd7o;Y6w}!sI@MJ|$?xX-hXNd`?co*f6Qz@>rq+65{gG#NuM*v~M+2CwXdEqq z!d(_C`{lCrol?1xrLs9kq08g`AuM?_B3n8^VeqOWigt3TBvjuoZONAHz-R2Yylaz? zq{((AW7^|Jni$xZsXE>Ov2!Z8%{W+|AQPRS4E ziHWl@HQscijj=n29m};w@Y+|3-tBdJQG2-68`oru*e2DDoi$FDRfUUlBYS(RspcZ@ zgZ9GgU)Y}jz2!fh{{UU%R2>ORHO|$rzkngC34$T(Y(2g;@gh*VEZ3t}hgiIBcoXs# z7^&{9^`?~o%RI}|rwANYF3PRcTlW^|p=8P#$0|nt5RX?4KK*@h9HW{>! zb0*$D4rkF-FjhCOQ`>;$cHL8`E`Sd+;JW?T*hC!6E6$rvRocAb zH7=EG8&6D0<`5efIL*s9rvGkF^KaONttkyK%o(z3^$AJuDyc;F+)h=hLZ-R2MC2Xl zS9wLf(uI%C=4JU8^<4N?fnj^AijW)r-&ne+(Q9vXugNv`VwryhnMAW1-?_fX`&prJ zuW^;!4VSFj7aH-VOZvR60Ib|u#>we);j&!WgkHY_sS-$)UZcn|fNbv+s6^$@gmqe9 z*{3y*yxfoPgV)-_HpYf54$;}_u>aY}e#m@G`LY4L zT;lVtG=C9kw(eEofywPPSAn)1niGwTdweX_n5Kmt-qR({zswpT=H%yTrml~D6l&)p zGB72tt%~~mV++lkwC$;NeUGj|*X3a@&QPlugAx^w(D}k;3>CcBrAKH9h-^0xf>-&Z zf7CG=f!dPEmQZvw;8V?4LQz{gd$@u|nV9yOK6T0-nyS!BdA&h((~*WM)>aN|`@0i& z6m2~kxL03qCh5};%^r3GOTh7saLj@d2q6c)i(%6mAr11)sews4yR(RVpx11uX!Eqo znr0~ZZcGa>dWTK4+FOeE=(rqi@n;j6f9cz7@oC28&9U5XhKe>$&w@q!8MEDAWKx`? zgGv8@N>BqfE#peNdk30;@Co(89V;OO*Y2uBueH1FT`JkO7{}f_t^|k`K=iLshYYll z0W{A+8*R|;>Im(prV!dvmrAx>C}_`0(NW@A{K$-Ak}HxlpV^j;S+=86AgT5DE|Izi z4U2vQc~t4%jxEU#L`U6ySRJZK$Lc#B)u8$`c6j|-vHNI9AaTkdyzLDXs$Wj=lxa&N z)oRlH)(xm*s&|@(L7_oBeI`Lwrq)0^R+JvECd941s9{aoUQ^W`0!)TIV9xa)b|dg$ zulx5-TEC32Nyr=C50uCW|CPp2T(^;;rQGuYO$T6FGkgik{XGh#MXrm|HPkP0E5(?S za?yL~3HVZ?@*Y`Igj(yyn6vEBDPE{+jLy!8Taz3K?u!}D5MKJWh!sx%XA9e?HkD{n zEqQZXl{M%gzow)LwL-@gyA?@VW`#eg%f?mql2zGQ%kMx5cy|l&F6&rz_Y+4{o#8mC zGgN3BY zP5LK+wkuWXTBV()*-{Hp-g5spN2rcn1G5*4SrgEUO$lLyHDD^jRCCIL2I@i=iOHS^ z{wd#079E(7=u*oq2=HoM`B1O>QOE(}hrm3d*k8npYNvnIqRtxsPNb00o^TcF-Z3jwhg4YOf4}02viJmn^d-!9U)#$k%?;3WwbZlxH=Uj|1GpLr=u?g|7gblM zv_KY7B-JN~IIs9zCpK*By$#l1$Mp6VVX;c20NyX{n^08Z zDQ`XhQJt!t4DiNh#TX!;vz=UtY1&Sp(=V23Ezxa?O?x9NWJ=SO-dl#U=tZ0v?L}hO zZFR2-*{D@~RK*qwzMn%9+p?S=jV}6+@QRkJTwj%QQ&gjIGOsoD%eBxsL0Qu8EWdTV zn(C8$iICp0>Kl)c+TPkkl$|t^_$zgnVB(DPoZuyTqtqn z+xJt8!pIFSkvmd>@Ooe=XSfreh1PZXf9XW0i3XzrG&4Q3>{E6GZ=K`hiIjg*a z#1$Ey(7c(vb_cY3z;qzks5aFIFamcB-lQNly{gdyf@B8UT%`1iIr*JxelAw}J1<7* zyL18JU(yBjv0(6bE4*p0qc{qocEjVnVm35{Y@AeJzN>X8PK|_E@=j-_DL0_mj%jFZ zT3QE*xk@$BW&RCB6qPrYYF;F2vc+8>jrWt@G$TCut;h%$L!#LOY)1V>VJ|S$rIP-K z>(}PmE)a|miRTHw6V_rw;)7v%II>>;O4^?ODN zfQxIg5>UUAJTMsr(5l6AWg}T6c$S|PEWSlz=E6tzlXyhy{JOmsb)XscP8R( z8%=N1?>%;SEuwha0`#tNhNX*QD6Ngt1&n`fH&Z~d-8JS7Y~_IZbp7e(#nyQ9s}=kr z@$g_S=02hXL&cb_Ge(S~0(%q4vf3M4p(a`7akimjTU_(AMA>8puC+jIp6z#Y_zA{Y zZOXGM8Jr!2x~Cl)&1(yd=D8i77q^zucKGxqgN8ZCUfd~F{!{%)))0E*0YQz~`xG#T zd)-ztq--$)vpqHyIEy^5`d4NuI|{@-mghC}a--TA_SQ{Ab^F=XqRlzSm3=-Gsw%JB zhWZ4Pq}d*u4OS5NZu8m`*&&_92^a~{)uXc$TH(vNvQEZ;nfHKqZ&_s@4mEdMl*%oN zF9&&t)e;W=gEOqLevs!HVJOMQ{!DPom;^1=H5g4NZ`5F_Q>Kv}8_YcxBGUHHNr6_s zE(IElAqo2V;eSSf9t`Uxa5E*TJj+tJMV?{tyD}VNR5cf(1B&7X!$yaYd^G)8mZ&`L z23qDH#Rx{-RU?Q}&BKMBeVEWfseNBFOS0dJ$dWvJL2OC-(EX~ZFt(i(IC9s1XZ~Z` z!A5nH*?beyPGmq*Y*o6Gv5}DW)UuhE=lII{UW%KSXMLAIN~(EuETlaks7>w1lsD~} zg_Y{L`GYF2M@v;9V-%zo^dA5GF<(fc=syYR`rn2^+9z$6>pd*!HA5=}pstW&vR0jG zwlL|>LVbeKTp3VP&9$*qdQdfOD+&0Ghn(m6MGmSqzLsb^%xiM~M%OUR0XnM|bb`S( zXcba&CiEqpkqF6YrAmw>XLOj;H=S+jRf|;@fZn`G>W%-?cZ4=)wM{3Ben5+0Vf`{w zrjHRxpx`$llT`Bz$aX?|w>};l+((++q@P+FIzr=qaxaH2 z+iu`S&pD8h=B4Vp3Z<;$*W6EaC0O-iZKE> zzy$X~B)GrU#ezGJv;iv-WlM8yD`;s0=7_q)GzF#cejZ@M*Tl3(t(AVS^&n@_RpksAMkbnc#c<5ZKY6P5UeZ`4ZSxnw zI0J2!XI+(L@)D@b4;GbHTffO~;N&~stD62u+FfBZMv`DXp|5g!>m8fpXnfR^j7|DT zk3%@QQ6s0eOy1SzZ9)Qb9LeLo6HDUKbMYK(MeuOc26^O`SbeU1C6dQd>MYg#MkJ5d z&X48sSJFc1_y5M~lW~shyX*vaU^!<3pkEw{trWz{La-b?g4*_RvLNID$K8+m zfo`;Q5*(zOa}jsno)>dBpR{pz!q;nYl-L&AcE4#Q10t4)`}&=y3CYsJmdwx+|GKI6 zpxrgqnX$d?*&9)ko2ML(TyP+FY+p`J>x$J8RMJv^ocn^;iHFCQ_@jB`0~OEB8_JIQ zlB#j9^TCMHbhc~-XO8RXcj*hd%+UUt+tVi3uhC#4OEOk!AHXpoUFZIt;;c9k- zmcMiUO2U@7mBMK5U9%x$=ZEpOj)J0hAo4jDH~c?HoZhGkp5M@72UjBbgG67pd!3#Y zMSM_&U&|potbAjBNYg+c!~1)JB@DMVT58|+^-J~`4_4H9%zg#X zDs5xZ4OYw<=qgS2?#`=f@8$-5jk}xE_7e-KAJdsFKA-jn`;WI8`)5lEPTkth5&M%O z_J7ZF$o@g3jr~`?keay}=xq0C)1mp^{UqLNjF@cqL1YEY`Ir2LH`(sfm8Ci~zWf_^ zQTdQAtI&8~oz-XDX}hJ`@iMv+t@njpUow(`3@W!qowcqkr4=d$`XjKEYHp4j&{#(` zRe9r|tSK#{XMqjs^E5K(um8j+K?7Rb=K5>;l9>`<95qVgU5Pt-EfTj5X_Zs~;(f`>*|57CSFV2od z{|spp{ZSCurfoOD>P$32!CG4yGPyEcKC4W>)de4obl1&;^RolZO{_?JokTI^3eufQ zd4ips*6--Pbk6R?fl;@x_#%mOMJ&#h$gC|X0_+AkNol4!KP^Bt0UrUGaAJ|DL)0NA zO8P4f4P}vNnlJR;ERnkr7c3V$@`2eGm>Urp(Qd>(e3*!xe6rCq^*nl0Rs}2YW^xAD z=}a|KinODqUbD%JDlh3@ZFlb_GOw#F=hvtW?thB8Jzv~DPa$4Df1zIb@yn%Dip{Fm zy`(oE#BUyU)rHROewOLn2@67XY^ z&vR&$24zP(!ge>pzK_u#2>P}XUdl#w)MRsuIqy)F`CSPI8%jCYz~P1q-R;0QLruLJ zK%;}NrvF|W5%e>ISnYi$ouS)IGtM96r@f`LJ#Q^<7I7M4wLjeLV3&ILL>3S~WxlER zmarM9cOjBUHSbyy8rH4|BD5^;of)Y2`Tw-`*FIU4Oc%r0#xv0=ma?o94eoxG$8-88 z!BqWlulwC5)#95$2l65zSEJH|tk?aof?iF7?>>PPjqW1elabU?QEv$HfBJ`}>f1Xz zn_jE+tuwS%jLo@-HG)QN-##&KMDZ*BPc1te*+=EE3E~}%^SOF0TYRFaQwx+?USQRkT=K!g~t5V!0al^91>UM-w;+S*@O)Ic5 z^GzX7jA8BsVgRC$*PdYtxn^FdkYmX}CTBoKppajE<|-it=12Q>#7vbKGfDrD0cIq` z!kv@F!YMJj$Hl^xPMXM&$P#k?8}x0Xt_3kIjQ@oGz*Tcp!d!w*m(rhJP`3M*EKFGaaij7l z{)c!NdCM3>(WzAP*P=`+67rP2fy5e-9C^7MGnKH=f%_lbiGp`joz5FPUaD<_>bU`#-hn zKJ$YuVrsXy|5 zFjdJ2UXJgZG$*cq1WNm8ROaISq%w=NzczomMw1f#@C6ueNM|P7M;f>VV!ay~8o%8gEyw`}WtIti$vV*h)`2J874;!JuZ5y!5LnyyTHTNtK|cTDDm;xzo-472fPVBv~zGQ9Uc&rnBU*lYL6S*L#qP>f~NmFc}_2nN;C@KG?e)HU` z?VHU-rdsf;WLJ?}KC%r$*^^4RPrFKf2kO!12mff-k!bJTHb&WJab;Y7T38v&$65W? zJQr5R&19(lshzO?S5g1jft%HJDQMeSBJjE@+RGT*wnUX;uLrBEc&(2nT&6cbo_R64 zWSp+y3}}ko45>>`R99}M6h9F7G1$;lb6ZT|TqtZi9uGvoD_N@Z-u9+X2ekK5D&r~9 zI=ECTI7S5Y$U(`&Zj2_;Pz&YaC_j@;|VKjEK+Rt=TjLTHq6l!wnalpR3VOH*l)0B3BKRV8_%@1CNnGK}`I+ntb^qI#`Sp~hyb;zX0~VHSlF z*mz|0;NHjxnN6+=_l6-O=P1;88S~#zu<`#UvVSn53p{hFS{KY&s(EuNBWTbWYOPH> z`PdlEjTyy*!6S9Z*xHKGz}KAIMZ7PnMuh^9D1Rs1Y?!@8#xHr4C7_rT!pizYh0` zUpo~rLPN=ynhiLH=@18e2Y@3lMi`yRNHyy$XrPx@30do4=tc~!gG;n*!~%T#7FFIc z)M!l!eOBgj$yArJxElLiUp9BMse3|&Hg=D7Tf6f+(X_XqI+JFHT1C$cn^*DDYS<3%2u zd}jy0)AdVL)auuEA)p6RLNy7^N&W^oBazqVS_jsHr=YIisg)p(Wj_{zK~mj$HjyBt z55@)^Sl#PCx-AgZ(Xw#^=TQ-!380i;AA zq#{gnfxWR6Z1={@d7{lVeFlWmuGHY8X~QZ!>ja1^ah%*P8^V zk>(3-W7&KRTdMggYNhS>VW}n?Ia4?{vy9~apHqs`5 zniXXKoJ&kArQpA*+RnR4)w-?E)3wl3>8Nj~@AdEcrhbswvA9_kGe~T^6R(SEn=Qde z7-mb4Ms{eQ+Xlt&k1Uj%k2E=7fqq@-q)6z4vlNmFqYtNide~U$IFT2K-2csnSm!jU zfD{w-uBm2SHPW^QjcmNI5}ng7F4qGX)kbhkX#h>TqC#y9OcY+{dXZqwBQ5dr0Hw|r zi|meODzMg8&tWr(Emb0O4-`S;aLhAd4@F}QIa0h(>9S)nnLSnth58gN>9PLIa=D8JaWD_;R;^lC6hi&**y`y{3`(uKx#C}6pVun{Sv9`e-iA?zYY^+enw>^Bi5 z;@s`?X!{3Q^ru~OWf%WY$R;!%0~Jh6)82)S`sclh>MaUY55spAm$)OOU)R9T_V{-V zoNd&sjy16HcrExP1y!Y*ZEy3o$LLi zL{bNB=Q~_op=^ZYxOUBMK}|g?_Ia` z;N!wu4&Jr|$oj1dJ-5h}oZ&ib+G#zoXCnle3pYwA-h9Wrfr_S}miRX_&4VMw;zV&@ zfYVM>58^;(%PxJ0A1Hyj#If=t32hsA8=9m2jOq<(&iJNa*QEU<*a}>|;j4^A8vaIZ+}*>vf-Tj7Z=3pn6ur10O&Da(S?A??F(G%;@Zv3M`4|nn$W4H3Obd!*t>y_+|pZi{=7LAx_ zAoTO9wAYpy)01&zt!5q0NMpNfoWsN9wD)Z~Giy3`si#{TX7ZzdnOU<)g>$wV<_w32 zRP!jh>p+-4ie7p))(Bk+x3r5(C6UkL_Z#a)d>TWMEb;0-{`H)8>fJEI2kjT`S^vK-U;8siaos z=~NZCvcF~Gge#}79!lX=540UOxY9$?Kh#}1egd+6+i6R~eI~NkL^?&F`Euw=HIIvE zewjJ*a0q!5X#;csG`lIcV8U4pyn=0HfVyRe7i~|e57O+MTOULEYET1iF&gWWRo;+_ z8n2R#4q3MwH0_n93vCtFpZA6gsGHFqfFjlJ>0O~gRs4-MJnK6Nr11LLyz^)9QH^&b zbE#2GIsek}Khp25x2aSq)_sp!Q)#@{n14euaLgYCgQ@0=BIcKljG4ccv@w6+(zUr% z*N@uXmtt~C^0QUyRab@DaF7GOhXXz-2EHBu#QctckIw^M5dwev6{ih*P)tK-%?w?m zOVUj_Hqi?TBTuQ6%f@WR%e*U zkzRG-1fPyZRu#@Ld+wjJLfR3m0tObA^);plgW_o`Z_sH_(RT;)U0kNrj$4WXDkCbh zC1qr-d&%R--rD(U|(lxgNb@ zd_My-)qG=wFO!F_CdBtyD+_8{fX(gL&{{}4yK#NziQ?~iXI>w6 z&csTd0{-^0y1O?*wqqW$P9d^c3qkhMwrVTDM8k3q7Gbzv@jv?f8?{qmxTx9o2-Y|Z z6Jrdw0|ny{Vfd&jmf-EAO@apt!#FS?z_U}CX$=&odq11YPxq-}@iXz%5q-1W8u&S& zGF#bc&weW!bDi~SD6i7I%5K6J?K^UjMq{p1{i=<=$&T*Sr{Cgi(H2e_yd|l^mh`yw z)!Ct&`ez*)RmrR?Obas3eR=HRNJ>kfGu8Y|#KRBin1}aB8xQBd1P{0O477lFMikFe z1O^|pE%mVP91>xiINOw98;7wX#`u9@d@jN`H4o#NA;tn>+!u`OnmN03qH#TQ?{fD$ zs6kUpo$9B~8Se+31MwD5fd=ep^|LU-w0$0?+|Yox>lc7&{x-~^5+c^cf2<7vf9nkN zXb1Q&G2ms!!pjlh7XcR9p1VW90|oex01lz%YjA+!Oow5u^9{C-F`R7}UW+i)=3%G| zF}yb)3?&ii(<^(OX-$Q|UndZ#?byRg>W)6FwC>2mk|wA3X^(UXQysF0WbCM2T-Y3P zNj1M6A^Ubn%+A}SO$W{qvYWSdE#*vF$_F>Nmh$9hQA;`6Vc5-KsE#qzLox@NA`DOF zVWpMbJM}<`_H!01Jo*AXD(hrxAm@ zI1EQP3}?j{t_31D%SRaA0BWca*M=DS2*VgKh`}?a4>>^7nrQTkX*O)@H}TXk=HgwN zF0l!4*aeq$Ix!FvPq%bk6ug+a*!J!Hf%XN@k2ojd9oZh3C<9nGSSI0w)BrTfpjL&vx z9!WceBt7{8Bz>}F*z43C{vX70QAAX%YSq0iQm3<3y75I;0+D8W2_61LNr($;@x!~F zx;(Ths=hun4@9_wqjzvj?>W#!M74=4$uYpZE zt19(gB_?iF#?Gnw|LC(iSCxHgcKDWidNj&hMz&~VZ00`kpChLxJ52+gC~ppV&-5lI zJM5iY+10BUwPak_2u9}WT=qog;br9CO!gPZvN_ow3%xG7tdsroshF2LNE$a$BwR+LG)s?Gid|nmB0_gQ65?@oV@q8XSIjV!&8qdC|YHTU>8xDvZj|ZokOmF+E z$+T5Ai_&y+t00YIcoEs%^2lEO+kouLo`dWs5+d7BTp=prbu>4Yb8auZ75Ja;7a_hA z9icLJcZiRS5w{!SV0{;EY;j8gQ_UgE6%5*I0DHxjqynQ;BW6XRyRZC zkmP%}8>>YztAB+Z7sU$JF?dyn$CBKaG`UHC!85SBMq}b^+2l1F(~&Ey?uh>s;b+b> z5C0#jLh%32;ct>1bE4kG@JkaN{>1}h{11{g{FTCg9{6dN(we;-i3!$^PeL$m*$~4Jw)^KT+r+d8pl|!h2apc5GZj5&-`zG;J2>& zZn@Ru`jYGypJ4&EHuk1QcsI+#`*nH1-c;dzjPZtg4>`Ku&2T;3EAK=-+?fu;p$@~i z7{d%8rkXE{FuVfPu&K`oG3+J`Bf;>$b#z~L_4n*8#^7f%IF7-COt4o(==aJ)-#tWs z=hL8nZ8OK99NqUg6DUWwePzU8e~00ChvCu~!~H->HP4JNv>zHXct2^=kRycQTrk9r zF4cx7A2>E?;_26>)=iU><6NBHPF31&HjcK8@r^TqULWB*E)QRMh;PMH;LF)&qW|PA z_|NZVz5i^fYUmN%M_xxCVU{bUE;K6DM4t1RFlejvrVl5OU6N6`|J_$VyZ4Ew4#kEt z&EpJe>Ac4ayY``dMW`7dVs&OzifnvZTWNtDRiySlv# zZkgkZBvCUUmiz&vP4bJLL=xX))k6m$D|-L$H~Y(1@P4Fr1LhdUPi{2kyT%y% z8pe4M#=1O=2Zk7zJORe_bR|{Uho(CmBOQ)v?8z>uCd9)}(-%SF&j{GogQgfcF-sZDteW9y97nGSTb z43-0Ztbx`pL7wSKe>pe?{RwGull}z)jUyvQ}}~P<4N(E^nE5YSomM0y-}CA~{j+ z6QdvC&<}IyC&cJW4gI$LO>Ng3 ze_O@~u7rnF^G^|iPA;$LJ2?^-8w48ikW zWXFd)DbH|No|M&dTpn#$$|5X3{wC(~L(;}&y|COt8F_kQuh}%~p%q%vcJ@t-OQ})2 zDk8N%9_@r=gAzJj3Klfj<)^3a013 zy1?}Mp=(TrZ_EBT?*0WBj`l?;cF03f5Tdw2C=}vIdVPGLx64hIA+ zT?HBHGRauU4@W)x)T>e7@jSWB8Rn2oh>_d^pj7h#5t8@&#{Aww+W74U5`q|`KBgr77NaMc z2-!X%`WRjlkrS_&CS9l_*|i~@f$P<8cLl_r%?cI!jj zqMpt>X*E%s!)$SrE6}#(%5m05XOivKM^BDcN7)a=ePU_vW&g7+&FpI%LX~To+c}oE z=P9VX0w_y0?@8N+TG>D73p@Tw(k9pU?q>;@JxGmQJa2~z(;bd53LgR55pDtD+v|?? zFJyKl*p#r^Rp_HE4#M^wvVRhbS9}@3iKrAI{(Qd}@e8~E+_9v7r>vXaA=1I8v3O=IZy!%G~ilIgjbbmfrln{Brl2Y>pr|1I&< zWnyf)>%Ob9jVUrEw2HE&b{lHd)^vy2o7JeiN;M_`*PlkkW zBp3>+HSktf7=lxNESYz?%-Jq8<1+7dnSXVeG7*%#$7SB(GUfgQ>~HR=$vxf44~R#G zhzZJZFsNj70C1oCx!;wY!w>JY&JLV=`Qgja;g{Tys3xtUQU%8<{WHsS70qR{^Ye}& zFYO(f4BWa+{RKPWq6S|uC0bo*F#LVpw?Y`c^|y6-bMy0dzrnP4PnyqT*k9sABlwACIJDGSxWLuz`@v{%iI zZ_F$Pi+2^%x3#SNM|~08BB>AZ9xJA^qZ0sW>>Nvq^L$&v4lkQi7B8?f6}&3ta-R-m zd)gc2LbxuT6ZHj9Ptz6CzrRt}&E6L?T>`7iP#X?lZ5d8L?BVenC0T56YVQA2M81cPo}}!afo{IcB*GMjgvT_lD)yd1KqxLp}U>%hkC`@Ad{L zlL6XVAx`q_i!HRb7gl=nL-W(&wV9vJ-s@(4wn!7_B!4Bov$urh_9F->$lkgP{s7kER!7aQ}Z~ksu9$@wo<*wQ=;%| z3c4|t^Z{`Tm2N!tHcj_g9%e}Xy;Ur!jTyQw=j3gzmy!6&vGN3hh{ZQ;uTXp=Nwr;z zN;JM8VRcSidZhHD@rBtu(Kc)|wFP>yiTcFU{dw)LxFl{%3X-S#l>=ehdEDg6rhZLB zq_U4)FO~f!Cg7NefXjMAz*YvsHZf{ChF)@+e{z?I{%^0-=ikPk|2g_Rt+Lr@PyyKgXZ0iaxzhpAN{34ewRdL45yvj3-#re0up6su+3jf&E&; z2M+7&F2(k%V@Tn;d^o)et*z{bY;>IL{lD1`Fm`0&fmqLm2Xe!G+5>t^ukq^XrYukw z80^!UU!k7ySo%dyWK*X2mElppIMQ+yDr+11e{Q|<*82Y!dv5|?Rdx0KXM_NWHz=t% z)!R1Kv}mP8TQR7)A(4CGMp003jzf`FY7v5>qM{cx@p$W4ht^ixT5J2X)mAIS;gLy* zD2OvS;!r&?BH#cxK>pv~+UMMJ1IXK_&-?tR&&%gS?mp9+_u6YuYd4W^AxIFTHB!gX zdxDIoAQ>_SDsX^OB8C+7CEC>_Im+9=8AS}YQs!>l9}KN5#5%#c-FABPWKgDAkk!nu z|2HLI$&w7^LG{4hF}?mtmv5mlk;k6KQO#cm4P?B#s|ATkks{ zaa8Bs+{(xPx0O?-=NaFp-eUTk?r_c*UwnXBeF)tAV;N7+!&x8VTy=*rbRam%`9DWU zVppg)GUVk6*Sf|pS~gZUi-cy6@G$lO2g6vn)0Qr#rOtD@T5S?M(y0J>Pm^1ONH zMVByxn)gEVDGhirVDg?tA8Q#vpN4*4{+NR!cl4NZB)5Lt6EPD$+eJLVtsl|nJzweh zdCx#5qC4;Np0D)$Jl+Glooi}hvxqJaMi|+c7V!DSgQA8#47*c*|9<^S>5V zuW^*EdVTOKnR=I;z~?}~2{brB*$B`#zb-)c&i*@e9(o1J>x`#BB&2;HO@`|7paekM=VfS)WjC|AP3SDM7DANovvK4Fa~;2=Ol;l zhY`Zd0ZKfcL)a@1;X8W<2rqUBl}IK*-j>?ZF92Y1!pjtZJHWxMh~OS#j6|~`e+5>^ z$j?H!`#8AQ0hiNWQ`w)C_)|#9GUR2}uhucEP?j7zK7QVrjYHl}dS)5w67XOdyKGDR zB`gf13*V~6w|wsZfKM%C+qOlB`MdKlXc4lLYDx`w_rg?WQ9M~YK0f94*aMA)u?J43 zzw=lzE*|%f6O@;4?8d=cp&&JA4Ng6oJ$5r5z0V5g(SI)N^h=Qdg6KEPG+M${V5@&I z0c)DX+3N52hy*d4vT1bBTTG*7{G5~LU_Hv35KL{ZK=Kx@=${CVJXSen9iInlXb5Y? z&4Sf+xUtAGuw-qraw^9;aBIYY?F8CRgR3 zX(4}Y#UR>0|1}M-2d*Gnv5uCEM6P}rT{LZRaqiuEI-!UE%dgDC%h&bXhZ5$qa_8w} zE=&UHr55(#UM?}Dy^&>BVU%w@E!rBq()GyL{~}=>)+15ucj1m>P*ty;63%pd&=S0~ zn{~&2a5=t!D(IE|gBxh97jiLMkNd-K3f(hj>}XPUfm`#V1Y4 zZ?`&;XPfccLDBf_T)<#?^Di@Nxe8(eLYF`dXKUn?F^KE13ihLH%J}WiLjv;$jspQH zQ9l)-t*pmjGD2m1b&R@?x470NdnkOn)ZVbdB91u@0P>y&aM%T#gq-l z$pMZg;owf)l7cZ!W9wZ2Oc1JuPvJ*en|FCU^mf-hc@3~?CT7~Rt_dU$nx*Pl5R4WD z{f}_ir#|*Tx~3)^G;)2D@EK$29I>?hXZBs;4xW|m;8~v?JnOrIXZ?2Y%*M9}>bJ*V z{~dfca0kzJ+rhK=4xW9DXBt{%=u;hqqr8Ti!PZ65yw{yu*5Y&Qf!)(n_D4SHdpvO6 zhE(_BROYc{x^~@EH)z*+IGg6>J*Cy4FQCyuc7tkE@ae}l^OPkM)~^r82Q?H`W5ay( zC=%X6jT5^aDB(~sH@V{m3DxUxqs0)ePDU{H3;-Zgv)llJ*fRV4)!T}6oDDMGq{Se= zz0|zM9v54s8hc5uQeE&B8T^Qx!GAI!GWh2y8^B4|S*7Ya)dsY>TgH)D(&WXl3BiC? z0?4?f2WJ?9kA#O$X~YIs34z8IxyFm$n1`SxM9|M6(EfONb@fhKO`H!`Uu(aAb~~4u zSk+UUai%@R`k0wQUZ?pn%A^V9#c7#KA zK!mIrfSl=;L$+e)2-&@q4cUkQ*&)29)O+~EbSj-~rcb`nzlzK&X>Y9y^fE@s(CL*i zTL+l*)^co;-K(7(k7cr`2Wl-&D1JV3NOw!p0zp~a-+Q|taBtpGl=3~`ZZ`Dq~vQ)b!Wkt|*aD(#@>{k=%mM5tIoUj~A|pwd!5RoqOi zRF$MQLX7TgX>2{I8bKOZ#T8A17Nv_H^S}Lb?Qr(Ql&Yv=b$NmD7JopSIRF#ey45n} zSle%S6&-wv@To@&E-;1v$A!6iB$<;3KBt3p|40Y>Q8r);vrgd;IAHHPVACA1QzBp) z19oB#Sa)Rv>~+cp>^ujoYm!N*a}|CL78*#-%$zbV;91G|Iy;Nq#(8Sj3<5DasIQX2< zCENu_T*41}Th*C9U3BDJLY2dZOV}9GYgI|;OtfDBae+&?^y<(hkm2WQ7k}as4!$Ah z5)!B8Cp#oQvia}4^Ut4SYRcc2Q~^QS-q9#_BGPHdj#8F_3W|Yf`WmfOL%4&iaF4%- zsxWFhma=K$eUBJ_o=Z=ku`|zM_y?o~Ze=WoI8zB;?S2pd6F=6d2Y_D`C6BQC%)-o? zAh07BAhRLN4b~4%x&z=3=9j_2`Z|6G+L`WQEUjPQjZby}>sp{Rp+-P1)7Y5~;V0O~Dx~czqN7nd-K;RZ1mTe2?tUNfw zw+H9Q2u{d9sY%N6^wGeqk$*FL^#gtAMBtU^9bMe&to84awVuC|taSsdLJJz%h3ilw z3o8PRoN(24H1d10Ol(&pY1aX1(k>k)=~DCGC{4BL&|Fk-y@Wclt%PVIQ8u!zp)bQE ziAojFM`EA~+bG24r%rp1r#$j8tmHkqeGAU3Of0D@$1!qjC*}}8ap6?EqrP5vC+VeF zld&M>J?&4!pxcI*hfjLwJ5PQmF=ys|2CjzM;bpPeZvlR0`@Q^yc80+d@o8nr%)H*o z%<4@vEQ3YIu1kg+8FNKTo^?*u15vZ}gUqF61uZ{hUQ-7IL)x(4+w|NR)O7qyw#Mu4 zs>Ru=g;tr(sf;!?$!j__4J(h-v?pcm#{K(RtuE)$U__6rNlRqN!vE>@CWWmjLMnN+ zDp$V|#VQX?ea4o-y@!sfA*;HKedzGIX$|`1w(6N;Im0Op3p1Y)9h;WyHpnQN+`)kOt2M!T77+QvSV=$XhjILp_>FkJs zRM8hTwrx__+_puYd)QXrYJ|*x<3f$E^MSXA#<6&Ryf;FXkyJUQ9|I!IYj_FO&f(v~$EC&0V*RSpAfAi!)@Y08mo4gfbgHl6VU(GcH zS8eqr;gy?-=#zg#l=QeUkgG=Q;izy1qn-m5B zD49q&EItJ6mA>LwfUWPl5^VXs99vztCo>OKLVI`1F{KrQ|_2wfj-gx z#|YllIe70lD8RcVfY+JE;cIB5Ia%0f$!Z!5;&T6;gDhg>Q2V?29=H_-S8Yq40B#f< z6TlI!6z^mr-pwS^L9_f=3I3rr3uuV0SWIaEow>?a~O$IUuRfg*$|sEzbSjwOx-65K8`Q$k8CsP zz6#ZQf5ifcI;WGAew^_vhATEq@fp|1`cI`%3w0r7?#BK8m%A29h;mJ^s|pObH4~PS z9*&&5gOlEe)_7-US*Ai8pYWf3WQ{uD$3w=ZouuSE+wF2FK-}MzP)c!t78`1Hw);3H zkL_M{S!9KRr=Kzd=P|OvDudkP`312*I87qJovQdqre_WYZ#;@+V99o>!gxMT4~BA zt+!^nDs~ol0rra3m~a&u^>w3r5uW#QC3ubnA9waPLllV*S5J2&4)puaT?2{Sylnmo zQRr>*zjN{z^i@|TGu_?OYLm4~VzVA~v^N^MKj^|~xHX6Fuf;|o=DYb=AWN9MksocsFp@auz8wF@oF&2;kUupvDVi_MiD&V>Hfq-F=)^^!k8z)`n|8Z9>hmmkDW4VtaNXBBEltP7WWg0na zrGMa&ygV_GqYJ@tx#XB?LzI`Vv{D^T`tsGTSV_@$#k{CC%pY}}Tz`|do>jw@LpHEX zcgQp3M+s`klbtbV;m_RYoJRX+h-@{A4+jXEw_yL>7M{}&H=hV4+P z;^qDuvp@yFAMs9l&^zhk4W#1~uS??MZT4`CTFMe1IvYqsXwaw_w9X~wX8;T9yA`v* zI{q}-tmD~pbMK*cB15y$#s6 za=<*>^0NNB%{XL zk=4S^wJwY4z5q+K?*oLOzFqG=!#SMfO4Qa5yDdwa8r5rlYNDA7DO8*b1iFLmFAms) z+)cE9BLdc#0~X5x`+Wec>u=U_MDk~T-twcFeW$(SE1BX9tlKwpUx1Tu;yi(W(ZFAp zfm1V@2EEAwj@T@>K6d*Cu0x|R@T3{|kIn4`2A(QjWCmVlH!<)Bi!NhEZSDhb*`!o{^^pJ=x^_GgZ4R9jkxCTh7YK?y74w zX?I2kmOrgtAK$`t)1dd8ia*wTZ)nkLVzZA4EIK^xYFmpY@f@b}15|?A{GXWG3Z*^g zm4CgZH@dnSs+~oj!*ytcMU=Tqe!&Z!MJr4K*hoGH*e@Nh`_IV%YcgP&9I&tD0sF`1 z09ZNz<^ny$JO8axopF)wSJqv17E+>()+qc*4$DCf%aIY5g@)yl9F~WG9Flxgh~=FN zjAW}fQ}`BoyCxa)B@v_x4ARUTq;vBim4=XRbC70lpx&U0ircK-&`VTq>9tUoXeZhW z<#~epjCxb;O4OSNzsS{_vm6aG`HXs#5NPVnFb8ZTlrg#%0jtOXd(nXa*iQmrJ?l-B zUeNZ>ki(o{jcuTVyA5p-dpn^JK7|qe1w@@a^)a7{fzq$94F67?io%0W< zzL$4X9*0h^4sC&JMxJvt=7~)~6{1MaTvp(=%^C!*FYB1OLV_gix zJenBee4vKvi3OBYn5-jP_?=$E6Mko4cm*V$oykv7oJFyqlGRctU7TBbCEoX%qOOX9 z!$7okjxX%z?`+~nkT`|$t`tWXRnFFbhREjq|uTeU> z#;@ams9?1MJ=ZG$&gb8V$<2KhBGtf%SMkn9c(I=A)vgrpT&p*-ak=Tkt~I&wpaf#QjMj>@_WJ@M4+&Ee9JD z_yJ#l;oj?Ick!MAeM=C`1ln9F-nk!Rs&2A*HM&YNv-H|@BBRR|shS2yY2~HXH7^;} z=W!Yo5DwN3vV170&vo1(ifYmss14qN1cPnmO&T`nOnF~uz5jUqY1S?nRvWzHzrW7b zJE<=I5ox2Mm(DW543g}fPUk`}M~LJEGmz_0Fb{tm2xiQ=t}e1Ei_x|FtH<*?A>*vc z{HDC;+qFd7KZS8I+7XQ$J@l-E^ej&UEXPPJrqsC(CN=+*0D9-&Y_j)~ahY^=U6ID2 zCnjvsx4Kfx{Cz(Z6S1~nMc!OgOCH7u^hO5;dpS7=x;O-V9H7-$v+(oebYRhX82MBzcvSd^GC)6r2o#9;+-4V?xG3bi5daq6gQ)|@ggcB zn8wqVPt%;>)m>(lCr>cp|5ZA0!oLC79PJA-C!AjbHx&Ni0S^C#pPI#|y^WnO;*^`b z5pjp-RaT#B{|?_K4V70>4?qy^;Sj$6!vK?WREG+pS?OCJ>b0tBDB(t2o1s5|2>)z{ zs>%Dfa}uaFAh@EI^VF|Rl2CjuS_XR!vUCIts|o#mZ1yyKsaLT86yU$%Wn*|!PyCO7 z)!{$G;lC4sg!l*Wz29|?vD&$op$XL0#NrxnSt2{S-0#l-N3g0_5zl*ZHxJ)mIiK3_HExA3!RwqBVI(9Ow{&*0ST+(IcCyQl<)LG3 z!HL#shrY?7|8*!*uk?IFAIqWlKpC=oK!|?vBr_6mxNj3vebQdaySQ1Vsxe<1 z^kMjax-EdsJl<^%z-_NgdY3Zn`{7@c8<{;nnoVF$&5W9r`6GYBGA{hZWbXx_C^A=q ztGZUV2bPM^>?M=oAsG6-msiNwdIvvRCNZ+VHAR|uKC8FMc;e|M<96}tO!Ruw={`9o z?^+u%Ig+x;_>Hq8op!DUdcvXwEsCZJGO!R-!S-rZ!PT;F`<0;lg)A9`|qlv6+i<_Z!To{Mk8}XXL?rv^j)X z*!>0nFhJrm|5S=e;1s7w%p)A= za&B5;*Wvpk4Z4Oc*5Kk2?S}y*P-%lB3NaVC67sz}fQA;bry7T^K@IWV0fNqnqAan~ z;vR(vT34f(MbXddpIY>zpk~{H1%f^zXI3WL3KK4k7^_nlFSdhq2!`SYuJkjlqj5`; zHFq`S!gc>D_)l47BzrHW3n5ubNB zp5SwLSAx%ZT~2E7`N%28=LXn?&k+-i&rZ&^fWZ;qNwO@uygE(G=;i@MnQaz{QBD0_ zzb032`%#2^H5o>L`6CDOw-O6)QR?6aw@zA0z&H;5)k7rJm4aek_7X}sfJc#B=w;_Zf!}L_)8T$XqqMvo{znEsw`~1Mw-}~Tkh4{nNc2wT zj{-8${xDFuMmA@29ok4th^4%{e(2yoDRG;PV2382v}Z>o#rqWRsw9m`o>h>dCzoTrp4sp1;&M=i8#W66;n%F;Y@y1~ja(?R}hor?J`AaA< zuk@S7&{H`izsN&!Scv5LGmRmc4lZlCf6x)mI=u?f0S^bhF30sEP)trv5l88BQ^Y6l zS}6{jAD&=r4uS%z<_rOLE=#$7WI1wKs=sns`=4ztORmT5u#ZO4oWoMhVFkpb>Pjs$ z7^rO!ad#5fakh+j1sY(NM9^K!jwxqb9-Xzc*Om6(V%LB_>aAP{tqhV9?L~4bPS<-o z_R;ce=!fAFch7jmG`b2-2T^d5^VgJ?oyEYH!KPPA7>G`$7l&PL=BbwbA5CuX|(l@9d(>Z{n61Iu+I#g2#T<_qQ z#vGS9Bj*lp3t~xXmVBgqLU2aeBQZ>jh5)>an+fW_rE-PJQy$0M6y;! zs^%qZ7{XS_^}`ta#TN9?O zU*<0~v@;VXS&ue&2#wTB%Ohfpha5L(@w=1|#v#Xrma*bZ zQ)_>u6PU&+r#g;ViD*`T*&>mBD$(7jgA-mK&Jdn;1vfICg)QG<5oC)tzx7=tjAJ8= z7a+^7tCQZCI;P1um#q8fvaYkO=vKCL{%xIFB3Z_3XUQ@Si@x0p8atcBFn(%s+8c8) zsDXprN7Yya!%Eb$ME5aeq}Xw<46oyn-f&NjE>k9+$<8n~`DuE<$?oCt+1(d3g7Akf zQ2+k1W)Qm~ia<>FgE-NCI+P$bgjIrULW8*bjldudKZSLWEFIrUIzcFFKiW@+w&x)w z_KUF8Xe!I}E*dyw4Vy*QrM&qKG&bva*f?a3_ZjQ1H&JE}3DNBOMwT_SojsHPty<{U zpA=-%u!r$AT^!Z$M7OMBe5LY|&HknYmie4iK6_GPZ$g}N-*H7g!2AaZl>&#(iI}Cu ztE_8s=2+u+A2IF>qc0qC&{2-#o#8U%_<_{{$4w^(Zs?%57>MfZq^;pp(FZkxre^t) zSFDi{o?0MdU0;kbJdX&S!!r`NiS~PPc>VUL zcKW=QL;7pbfb>d-G#Md%9>9tAIXR>Ur}nTR1W|n33d;T2iJ;Y;h(IW}=#2yi7QMIjMSj18rcdU5rXmx{mHa|DRtAg>(!hgmeS^g+h8(^uKh1;`pnp%bw~sDS5nZ`o5Cj)T%&8d;f`>~XmqQEjKD;VZY8LIjkkEg!CvBS5Ux$*Mxwnl!cyX} zX#1OLsjcm3fWHR@SehTSeJ?@ zLk;U6j(1k36nL8dqp_N&*0P4C14kjNK5lKUXZ%a#0WS>>S80Undc*Z<4%dgTM7VCE zY`FFha9u83f%&BQHRa*nWPGY3IA~{WMo^=Apdxk4GhYN|1J{qU!x%~)Jj%GfNCfc` zP1rzWPLhZmB@^=~DGw?6-FVD~gT+Quhwpd68S%XZX9S3=jlTO`HdOAba%V<^&azYRBLE1cgu&Is-NCmxS{d4W5B%D;yr*=2?=KnX}D@&!&b zLUO4CeTxGPuGbvsr*gP1=0<>IXCZltWhV#S{d7rzwv;XNx14B|mU{Gxz6|P7G6(Nx z4&J3Xc>8mMsUJuPpgw$@gQ~Sr)sWAM3*esD$JL*mxdY*I7Dzg8M}(gPn9f%jLWs@E zz+0N0zAPZHMC&Z&p;e)!oaRdL&i4VP<9@AgWZ>S}Dc8y{?Ks(iOBfeV^aM942X5yO+^+zq z{*VH~I%T`qLMTi(HTmIR)q7%ah<+5kqcp$GZs>ctKlUm&>RqQ{Wy!Fil|%WkrHKuS zD|Mo5ZcU{w;{M;i*xc=H!o{m|^GRAFdO4(xy}!`R*Vx3;l~%$DPIjd@*7!r-$Ccg1 z4;5wO*g%OJm<(4p00LMKc|1bM9&{lcLmA;xJt(~&7@?iS`P$)_pu5?saqb4FO1Y6} zUkyEho!sb9bj}i#CF*6Udgb*pSQYaYqmrjsH$Q)UHY0~x%ttq~^wKXtqoGH{;>I0W zs@*cf)}Qa(kR4tw2U6!J-~FuHJC3+>CyQ0o5jnQMW*1{^_0FMR(j;;9)yyQYaA}{s z7>etct^^MWSCY7n9A)A3 zbzT`2^j|vG>YT>J{;*b|`fd>!zs`{{AV<#HUgF@vz6tkDpm> z^QSU~))UQf-#XHWUZ5Yx<@o?jwC@c*t}%^!0B!n#+c5ze|Gi`2?gwS0(d}P^Q_~yYb0D&ic7!;LD13T43I@a zDlhUw{j3>n7Qo?aONPgVBjGJl+K79_P2_oMQr_8v<#l@OXE9UN5@I*5K`$`Li^^fm z3NCi!(iM!&`VdHo%zK3`rzA3;6}F@sXp&}fuR(1L^#3)`ehv2(o8t*uE~cVnt6KEL zTP|zst~#6v874<^8*4c0`4raMR{emNI&HB9wWpcdW$}HsbX*8lP0Pe!`8lu$I@msj z0BkyhdmMSvr(CW$5NI~{P&y8cHEX!PYpG6pSsW+UY%|`aWFNYtv5cU z?MfS<_KF*ZmYugBI|#BP%Gk2gAeTJ6GQlRD%mUa-7i~&q<}v7y+?3?q+OjFDGg}H< z#-$PObo_Q=iI^!!EXfF*{^_-$1?L?EB4u~aECfowMEAT@WnaJSO`Pz<_@3XsOS_uwvVH1OLz2*w> zLb`QdZ5q@O+q+OsnEXWol@B*_3_>v#^9W|5P^U99=f`^aR0?U8cPgDwzsn{GLW4 z!G^Eel`TWHIKM%oaR)%vpp^G=a?r{se^1gIP@MJ(x*t!~u9&hHf6iy@*+~4S!y_R8 zOUO`)Ag&R_!vyiIMzXNKmd=Nl@|1!Ul`Umo)s%t!IsXv>Fy&Ly(Uk z%?KAxVpXGN8u~U4-R->Zq%>lf9fRQKqqxFkaZz$;;neS^yrP7Xqs=cd3qg|&wF9TT z*2JKr*Uxm3Xd7AWd9nveHKL%5)|S6c13`PrRL3}5b7~tn!(eKiLSwbWobjIa)?3~P zHm-l_Gh3q}J~gf<;zOt&2Re`Wh+KxN9>=Uu_1KG6(^Zcnmq*p(`;@JEEF0+>*9L1` z$5f`Xr=mTk1d<-ATIe+uG}S)aa(L71XJWH7-cLRiSIWLEzr>)r zx*Lw0YDY2E{EB7tHGTvMav*sy{K7S#64Bgf69~z+B0J4VunG;9)(8Z!o&gt)?T2jb!Dstas+o-{vU*UqazHL(ndkZ=x2t<_5HkKd@ zR;OEPG=KcwA1OGU^;^D^qhYW`4xgMTZG||SMB8P7w;5V_-u_A{fZ2rktmJRHxr3MS z(gnB#d_!AG?xwbK%9~rWywm$)`Rah>LStF!wKp6g@tqX0yz0r218mx({idizs1ePc&d**{ZQ%2!fKfWZ+`WY?vRH%i@eE+6SWl zt@C}bUY`2X1vgk7@n-P6J z!cn7Lp*AQ!tHme_>sP4yA+lXn| zTJ7eV+Nv=a8lcmArL!j#CbK6LC9@Y6C$m$?4gKR%yi9KyM{Yl9Q=4tIR~_QSyhQh7 zdYbIlA$OF@EV8zWUF0RzdTjR}!@(}L`=o3v$lYGTZ8bm1O|b`h1$V7M5+J+VJG^UG z#d%jC*y^$M)D=lPt@U=lSRA+3f6jfAa%=q;D(c+i*80sV%73-iZ&6VTuxYPb z>pA`AagsJ>M^XW2Vj$G>LYij>Ym*&9K@DuJ|5U}+`p?|Y=kDhV_p{mkY;ixdg!5o` z$^(&qH;+oE2`4Wd$K5`CYC*!I-}Huz8MA+Bc&`HQGirq@1amPJJ5RL;^H1D2L-E%8 zzkXs}cO&tm`HyVrr985@FZ|^7DfY=zb)|SmCQ_E`Lw_P)rL%h_cw1Yr1^WT_;$qf_ zQ3y1!wx+9XG=o&Hq({xSI1C-)KPWjukK9b{MD-Ap@-14 zW43DOI8BFj_~#y?!TaHHVUZJk{5!%@9bvgKhvl1;x^8jQ_gulC{Xpz?il^J^25Q%n z4(Vy|V7`z4HS9lVw6{PFv@JQ@Xe;9?VB}l?=bt3>$l}PwT}Ihl+`t?I+inU1KmIo{ z&^sE%F?b@lbs|VdUr!yHK`6H656a;u`3cS&bWSG8gvvq#mqvJN; zGs3I*3-~d|?>YKDIy4ZB8~&Uu0+@U7Do&?_;2wg0Zp1YywkzZ5{(E(d8EB-u89Y6W z6&Gvssf@-+M@nEiyo3KFR2lB`pZU_^vE&V=IRJ?V!e{wxzT$;Cnk7Y(+=`WBhuZ-%+$K4AR*+3XP!7jdXashM9fc7@}w19=aHu0EaW~y&xbQMXL~5swH95~%gy}7f!d1v(I(R%ZHyy@S3kg=<`78$#DMQ;t#TS|R}w+*=ngExM| z_ClVP>71Oiz2EI{)gqNXhM37O;}maJ4oEmcf~U;45~8c7ggiDMV%Bk9ZBBy!acHP` zwH6YKH%2;>nvQZvv<7UF(ftfax^87#s^8)?sQ$(En;mnhF{>eaGWW>ZMTX z718gfX42JxeV{7Q{&xi8q>zB3&`j4n6x4xP2Rmw9ke?f8Whl5=W1Pe{Wt;#%;abw1 z9a7SNT0asW&6+oDy|<*RbdPzNmEDX-Fk~{@T2f2ydjNs`0^HfH0&K|EvBPD# z|NSR)mYRI=k=?(11MYZHgja04Jrt6f8U{&)v(#Hy$fhbQ$b2+=Ou;xRh5A3Q_%S`3nsFME&r%1|TyH$O4nm zkcR`8Ha@CcaI`)BhGXesxWP*d_C}UB)Xtx3gYwMRF!pgXC#|}F|FV1opxD8GMSlJ2 z%MoxMLHOUTSsH0+2f%eakf0aP9_ISAfRm6deV3iYc0$}56S6T7%Qbgy&gn5)|jjTqw6Yq)`m;*rza>%crWWAzR3 zcBmDxz3Ui;D?6mTQvPM$tjDLV=g_0jztqsJA7V~}nFA7wDvEomdj+0Cap#jyi7ZS0 z?8tfx+?{9A_Ay!BE{^=uSa4(Y-i1o8%6s*3(g1t)b=3Je^$io+5@emMKcY3!D_7qq zs``5MWen@(Rr!f|RemCYWg)eldirNtX$eM^o|Gvz7^EscI5gcP7Y9?t=|=?f;Df3E z;~~%lN`5_Z0}_X5qKghV#E>MEzkOO0`sv%6pf=X`YfTh;MIv@ieF;RXs`sLf=UI-k zSNt{o7Ajt$RYsLG3fjl+6jQI>`ET52YR=-msXZC@&`xo{9Q>oe+z+R`IyTAhc zAzNF(uOA33AaTAdph3g#oJ55F$%_){|0t(_LCw=Y-~Jo=S3H%M5Bo`GM*J(;)1Q|Q zyW^`CIX(S-;j6kzW(NnjwzKY2l<_Kx5=Z2aNkv_Y?-=k$+}}mm%45y|si5^m+7`Ir_ir=wGTINBZ8D&pq7=8&VUc!I<_Wp9kR7&$ku)P zZ`;xw#iT8>fV4Ln$_|xU|?A1c0na4P!gt?97AI?J)^Oe=PsZjfRRkVhJzJd8asTq%`BUzDPGc;Dz2FZAm| z@(|!Yvo8#8&(Fe7uIfDuE-r_QVI?i;VWdG=KQN#l7|;(4 z=%+W*NAl`G#ZO%bX2A%^&PyX5-csV~fD_)`rHcSoU+|atKrv`Zt0RD}K2hJdqaTz* z9PRY8+byOaZG&L}$vmegO)g1Kv02NU#^6*r57TYKnOJ*|WyT!}+L2o5Lz65~XhWI# zce%yXo$drrao5P_4SDbX`P)#FM;_B@qOa>j-P3W}FcQ$JCV@MB^6!umd#gJGyLQ|5 z(+PYVn={Y8b!T)Bb|zAdu!bQ%trC!#eAfxDa!0~LQF$WWT6lC9wkN#*q1(8HNO+N( z$Qf%O!yL&u8Rp#yWSDnnAcC?~_jzW&HKa(SJ>Lx5hw(`kMY#SEsmLoHI*!)S{1km6 z=eTIB)Gx_aeT6ZAu=V}%ey$vLjLg06bx)7zU-6OpCtLe(t{FZK#rVZ8U-Qn z{Nd5Vc@5U?A%64rdzk0v3%=XUf$2$a`1Y5!Rq=m=-mTjz!3Ze+T8J>Z_4Vi$RDQ!K z<_pSSv#++>R;hw3KEhvY9Z%%9S3kP$|Fl>CJKHPQsI@Wd(4#rTQAaoA$$Q-3fC$~7 zYUm@?i=Bc7?+Mk9C|Ve}qXrMBx`PI9;PxB5|DoG$qX3)>W?Zx|FfUrj(VA=N^6msJ zUEZDiXd!nyYT6_}V}6Ix0<$h(9%{vRD7^Ugh4(*nyKNMJL*ZE?6z>t*25o$f{jFcLnhSLsRzezuLdM`iWdTFcf6;?_Sy$JlU#U z0@U>HHvLZVKw)md#?@Sh{kvh5jgn{g-cCI5zv|ybaZDhtFY&)Ctv~mf>n{xT@j)r5b!+sZ3P@6q<`gY@A|JwM= z`%7~5^Hv-H_45Szfo#>(z#ULO$KRp)`DU(uzJGgEKX0RK^>fIc+o_-1#_MiA#dTw% z@fYW&wsRbx`YY7URPDSeRp$kpB^XRciN|oD_tJ3AhGH;hvweH-F#cR1}T&KlxdeG@#4W)ZdhA{EUd+S4FJ zViwV!!fP;#sHZ^`r-w$oMi7Ch8Rldkm5P{Qwock>#}Mv1dwlDr$R6*bZ1y;Em+jc&@Bj2=_Q=`9 z%$d)BFV6bBaaa<|rdq0*;5is}VxVSGT3>HIJANuN^A1L29TfX#<_%uRJ2gD)&wKAe zY4}bJ?=0osv)ps9#(C98WL5LH1@yi6a$r8#ff3kBRxUt|U&ecVy?G988PAOZDniu1 zbMud{Fu&&S+f#m%-TdPl5?r=wBv*m__C-dC_RW~5li%-t9?5SiWs_guowt==vTg3& zeB~ecM>h?bd0tnexr#mDsm#Rr%H)#FOp03H$WhSs*|EEQrJ?ng&v7f+c7sc7lbhzt z*+Z~CH`6|k_b{zh)5iG+lPQvABwB0yJUkHde&4ZjseT|5*EHE!IW1=jD{qWgxtp@F za(I6i^JZ*;V0T71FnzlWC@}lZT&5@8(~k4kkZu#y+BSEgL;Zxa!jcH}r${Q%-j+js zavtjaL)42ZL)1O@FC@D!>o6+9{(5(tt~tC%Kjhz|zWKe$P8d+Jr=#LnN5v)jaRNBs zsL&w;T;rQxyde_6A1Ipu4*L=+a^lae_h!XqTbRM>EKV}jf=>2K1Zr3eUexgV9%fOS z^y8@54So~t(V;s>=TY&skczpnP!?LJxg;Yz`j_->@byE@+l8t0q4!|QGi1tb8N2V@ zurBX$y&gAd%}`s$?q?p_WKdX~Ia=#r#=}Iii{op#MxGoL& z9B*Ij9)0oW+!yb;_xR%B;0v^qP-Kv;I)NWneh=R%pAfe!QsVEqo;96q6SqUUy$@5F zp-{f6Wiqz7ntPlzdYObNa%;G^GAPIWl^jNcVJNA`W{`pUh20GG>-uqqu^+T1+K+*F zuHo@u=nO-LvVdm)_6m}Els?K~sx+$Jy+Yu5ck=_tF8a4*xVP2i+I07ps}o9!IME?J z%_049gmj!CJt2p5;k6Oc8z@5x`ACsj*KxoW*A0}i#dmOrs6wmoV)rcd zcMXVkucq6^$HU5Mn*b!32DIMe;NL5+frH_yEKBNYz!ri<#%0V|dX@gyoNIAD#fi5U z%j3xPsJESX3k79!L=@{0lyBk?GCG9$D@<1juS45fz0u-F5Vf%!+MiCfjV;_1P}@*j zFy$!>Tg;P<)W3FPclUh1c+0%j%w61?Fq@4{qrnxMv-UsvDd>Kz_Ji0Jhf#cVFqgr4 zEPJ;;_CPvDk`>c=?13As%^ftqxTAP!!=QP^-W|N(FlYf=;I>dLae7!5JgoNy^v;ef92vVO-FtPicWwC`E{PW2;|)>~ce7gC+8#Ef#%4=` z*wLb|$d2s7S-_*&&@Iot+Lq5=H6UC*JDM9f*iX4(t}O1qRwiHag#UosuOsI)3V^t; zvAo2)%k<*_|DCT%K?Yi`@rd8#I`oJeyudkS0f?{e8V02KRU&P<=yVY7v~6+)1$L(7%Ps!B=cCde0~?-FTZ~fkgPNR)={NU*Q=gcAoleR*HB2 z&5W5Y!4CAf!~P2_=65u{wEqGx+w@6NWRhJ z`QC?QXYpPqF8ra=In-%sbF=fm%22a!cF)6}?joPZs|Bg-fbt~gFLfU`xWUuf^Ahi) zuHx)Pg|%CzU}6p0!3Kb~feBWdAx8SLVQi*VZ!Vo40_H_~r|YFCG`14)_{6k#oq`e? z_xa@6z~Dv)Q*c@*Ann&^V@MsAa}#m09n3x!qRleDP?Sikt6*vFEw&w8^&0anvJSL$ zW`2pn*?-?VbPfGlBiHcqm4R!RQW6Z6gj0Pxpf5Hj$e9}uI2MA{c0=Mqg7+NB&}w9+ z>B!glqw{E;;oMlGqxCqh^dcRvwa}Jmr}q`Ou~VThbYlmEv@R~*9<3tS{EwYN0Lsc^ zkJHf90y-B_!l#tg=f>9I;mY9$y<%K6%lQ> zfteQ|>N~CkH6PD5de=D5`3__Uv|UsLZFshR1ZPRqC`(LwH;c~LoVqfa%3IRek~&tX z7yFHUiVM09M^16c$**INQsBjvCUodc@;YqvEa-eujwM_AZV)>6WGm!)YwqIy?W5Fy z8+YTyouFO>spKvd)V)k6SGbriNTS2kSyGB6>33y1cF+8j_qhM-G7Ta*R+PZHIqj9P zq)rBk$i*N^ud%HS{`<$8$JM^y<9{ytyCOMG%*pYkS@_=j%v(CJ-scl)$Ct%2N65!| zPsoar7EjH7IW~J9HTTwyEyGAzB zdGBM6-Biy*q05A85l)M(b2(leHVCEL0r*A(*sba2~%+Ksz_( zT^U+%2luFa9+jy$E6NkLKTPr7`>+YqRWY-G4f=5w@DUP8v=2jUTqCRQnUV26MVY&C zf5PVVgbT~NJ0Ah)qut#=ez=6IrYdUC#|&ib5Ss8VFUSuoaFFF zb(PuS>=!7RPawgmU}U?SpDJG3T~$RgoC%4-i_5xBNj$9aiPVxt8`*R>b^7%Q=ob#g z>|`7q7;!KZ?UHLP$HAd_98`oH-1&v$KnWy9Biy^84ZZ3Iphx0VWmGspHFockuv50) ztmtK;?Dh`cQ%DIN7lJv_J`Qw&1dnz$h3pnyYVyL)e(p-~&KqpsDx+HYc4e0D{+RtY zAmo8+pJg{?V_Yjee#jb97^ZIw_rrF(V za)|S$$76G@5E&t=g$xj`oN}kuiPUvE$fX(BhsP(&%{;2$EKtuOh#}G5gj@iLFun@! zVF2~P#esp({%n1BCuU96>TM>_Vm&h3T&`C4C+zm;5EFoO{@R+on*y{0tQmj~VN0rF zih3$$Y}S3E_|*xuKc!v#1LdT*S>vN_8Zqh3bhWnpU^@7!?h=L7*XjG~CGi`AGe#}? zSjg~b8NAO`>SMjqH5%&9N?FUaqMz};n?wLLxc9?QJUMW4ynh{jL*74}5%Avjsoc8* zmsv*iyrJdjkW8h^lJ`k^t)eebq$0zjGAT(}THBj)owRReC8o0@8$1YMcb>1d`!TA3 zGp#)9Cw~w26wfn#x&P5kq_qvA(=Z4%t8em(hPHO;T(5!S}ar3T=P%sWN>Iu$T$6NzMt7t!6eC#;(wY-=H#33u3eW zlk4oU$ZZmvJI7Wxg!WFrs&6p$6J_`;WOAl%CuWeJn2-&F-YqugN;7D_>~_|?nBG!d z*H2;QanBsx?vBP$`f=Kv3{wnF=CnEXqDY(7lzA)eKeQ2MIIiM5Vbv9dM6G9Zc(XNt z$s${!~xm275mC-8~if#gK{ zZzK61>ST@l@3}DaiI=%jypyw#6%J$;nYeqHLvDWLx>n&MF$Mk4{TUh8%6rA#42%me z_PB|krs8>whs}@s)4mH$Z5j8`4Wy`7`;*L{r6n4@!GL}wP6C)z*G(fPGJI@eDR=xqI@C!H&p z>JptZ_lxKZwPgKCRYq&CXsL2xn>*BK2nXk`C*5nK*OB(#$R&oyaKWHwQs zbYdLdL>;HJXH7z5_5$^hZZPkK74k04vCE8*%$6LZa|hzd?4FOIBNAbfeDL`rNxpkT zY)-nkj2G(C*#qsF&SaDQrh{ON6vDl1C%W^z{!sSOYW5r0ckr5Z8#yWvN@5TTZft)MytTyHIo5)86pF&GURmjKjb4)-tA_crMgIRu&`a>1;=-;(jD(6-5`vV zEaBjCwE~gcQfjfMr@a9Sn6OAMV3T@@lVGAS&07+bgoFGbFBDnY8%EL%Cr-#u2S+JO z1O5pgY$HBWDz5{1=fl=C8uLxwlCAiT`*FSuSmuil^~F_u5wYv|w7=f#u{p&4H(QOy z+?p8;YDA@D=?1{ih+x~1#;Do2&o4}U!%v%6#Lgot6Ck$FTK|zWwB_+3i7J6PHd|4ny>x<(4-5u#O2xfyb zlN8MwRdj*q#PQ{1dX4_z5KIUlq{#q@76F4L8r6pRo07$Jp;+(Xi20fL_1A1FLhTMs z6Vaz=R)-!1iX{aa*0nDoOw&OR2qRV8ue<80{>25Ehb_A%RZPhsr1R=}>vyKnNaoX` z2Ib#h!jY3RzLm%(wj{HoYM%DH=Xom{vI7c|#jtsSpDrd9?|in}zE?PHEyI0{wNFp& zBNIs$Pg+hknc5RP1#-cfzRF~lBXAEK%tcNA-DNC~PA7@VAip;68c2%A& zrLql&V%n}E%c#7~$&Igf^_tKCBXvSmkK!ARx#kHCg}gg2v~b2CSUIKfkaHA1-OD2>Q!X{_E%u&O*rZ;vDtp|&o(!LaOT)d=_*7aCdKo%BW5yO+ z!9^o;#ktm|oLCSxGDmYG(LM{45Bg}U*U-k0r>%sp>BJ_^LrDnjOfYO0tvi-y4~OdrXhC-MZhxl_2@QB>jJhsI;o=I^MzAB>|NvVkcCya|LT52*KAgZEh~ z=%-;CiltYW2(Gpf3tdZT!QjdLZV-~25}Iy~=l@v#Pga=kLWNg=OS^UjVIk-?pWbqf%3+_K~J8o!u{N0C8<8Z$_2-j%Yz?iNj`3t&4`g z6~ai{3|f38p<@*9w=)@qvICTU*Sj=_T+9{zV7mVvl$vN?gyw=!;@haQ^ZW`yaWzWL z*n3f7K`G|COgxDZ|5t)WQ~*FVVepYr`mzMGI)TQuS6wG$c!Etx<_*+60bgha+KQomyKghcJ9u`6uhmY02>rmne{GR5Eb6$ik^hy6>=E6G+69-Sk`7MV zL8XORtC)&dK|_7X35|o^BN*u&I{OKU?q%??OtHf#Fa0!4cZWX~`06?HegGyQjyIWP zFK51zI3YVAMtN1L_-YfA|J8u1dyN_6iQpClkLj~P9U)<7%(oHJ5SAzd6b*6j?y8S_ zTWB|v`a)>;I*>%DX^QIPZ2@4zp!K7&CFk)?&cB_310Jh@fXUdgjASZgqiV0v=h^c4 z{;Us*I5%huM+cBrx50ajW-I9}R(pQLmQ*&CiwyJI^W*(w@zDJC{598}b7I|mno7`t zrZ!zXFX1gt6+fLSo;NC6-YdD04i{|%n34gWzAn!8yeP=KB?V${@*C8;FI^sTcA9}=lfqf9qFRauc7yX9=oO#GFu#e=oWDQuy-4IPZ z+>&{s1Z7W_vZeUY(y6j7cngnJfVJylnRoKlPRMRvB2YGBUp{CReZCe-q)&SmIL)C2 zaGctWcFL+OAyr(ys=MmvC0kp9Mnt6`DJ44!FFt?{K?$dRbV{$ud`md23!Qwv9YeUW z`VJt3wvdDrYQ$Kbmei*ycF$woRbK_|HjZSm9Eo_=$ALp(?4bP9&<^}%@8ui8Qo~^r z6|)>$9=cI~{)IfFkRf)@8<{O15=~%tSN*EExB%t1p!xiue+3|=eU9bun?WWcLHn7{ z@f7AEzLg!0gE(?)2eBRfH`KnI^AC$FoeBdB;HNnGEjQF{p9XeoBCX}Hm9EtOsJ!s}CMty-hD6|#bcK|R$56jWM5@4Lgl zlx8!ijd?~yo%pm&)p9x=e^YrPdvZ4>w2*JQtIjNv!St-cB`oTu0~x4{zLlG$Q_nu9 zx}6PKV5-6Ab(M5dJJnMkZ6->W*nEFE-Qh0^^ZmZ`GRl@gnA1l^=IW)}H1>Kek5OY_ z-SYakr0DJkv+TJ4@(%wxUzK@tRGAySWizPHjp*~J?dfwHe;@SU@b^n->q=^wlchRC zR%wB>sY>iFq)r(ZB%QGW?|zJ?IeZ?cieGe&{;O~&JvVp^QcJs`rcrz%v#g}6)Sl4e zGlv(=in^vxhoxIU(a<)SmBD=#w#G3FdVM5mqM>nvW$Fa>;YLw-LvR$^TW?VYCA6DF zSl$<9W#=6h#&&8nt?4RE7ZNc9z7>CkxX>d3T%-OBJRID!UF2p@9MXbS3-nrXz_v zL3cB}qnFC`QknHPuQltwvwAgDq1owNX39!rQggYJ&K~sgM~IuQ=h;v(R*WmsZSodj z?KQywKB}h~iTeXdI!e!zQ+4k;lYvYEZS_2NUl;Z~%g>-=$hnQ{?FKU?FP^TRXDR+S z=y^_fou21%YD}(M(sGt?S~obHEm-8eNM)sOCY*N;32}Ze59eKLL!7sO0-Qg|;XK1| z_Kt9V_*xF+bVXzv3^NTP>b4GI|lnj-=} z*PdoX_(&pP^kRopPVTnq$wGVCqQU%Z-7OA>!}Hcx*`RpO0ddtw0g`DAS;MF}b|;Z( zWZVt)Y8YT5|I-$yx&H3HLQ8(%Pmoo>8cVK9j-h8eAJ@iMW7tP^qu|!Rg2SYoemXnM zxrA&Ti~AO&ykW$mPx7=*9obe0G4&j@N*xoM-fT*=*C8S>ZbC2;Bol`h{9o+7d7M<$ z)$dKy-O@5x7$u@+C=*PGs6mMd*hagck<(Bp3Qloqhak=b8q|o|*q~h=OK~>FD8_k+ zvp}ga&D?+};Dj?8HK%N6qoOg^`~9tbhN8hd&vXB{_kP~@_4AQFb%r(Vwbx#I?X}lN zR9qpn4;xKrRUbsk@hCTx|H8|m(7ab&?E7I$nQ9T&HotvNvH8I@m;lD2C&i25F06Hv zB4(jPzK4Rl<9yG**Myb7RNc(W+j6gmF4uYfrJ`Eb6S=mxxSmj4YA;q_@lO*|T7-vU zs`?f&F{9z74a5}6Cz4HY&I_?zwCp=|qNY`cL1OXm7Tey4ImcK!sJ9O-oUadmZ69*( zZI|L*`Y`VZ&tDMHzX4ye0Uw#vEWQ^90p(2FXad<|7|K@5R7I083tnF#*(iAZ=$o4k z8EfPM7vHDmZqDZ#(I#q?A)Rv}>i>aQ@>QWfyRrKBSt5y@^1%6erA>Xs2)Mw>aU2%I2 zQr7>Yggsy}Ugi%8%+fjX!zmKyCK-ITu+--UUXP4gL1``HD{Rk2Xlet#A79~_nAAS9 zL6v%z-{lDUgI3Car>WDKqWBJ8NFsg;W@x{QR!{sT!BrF)#RPkP^^cFH`_LC;g714b zGKaY%ty3NKUewFRaU)Z>0R;*>+23EJld-P5xq)TS%<4pBLbh&_8BqW-P>K#^(YOWM zz79Ddz}t=rk+WDU-P~45V#Iu}cT+-lKFJ2Q&fq5Fxgjol!g&Er{d zs%yor8eUl_)HiyW7S!czE=|&9qu76JJl8;3f9D?1*Qt@hW(}K^(5xCOS`V~FiyAVx zd-Vp5cgsm?yvl0FHjQ`b;mGDy{dSEv+F#tM-H@-rh?}g@U{ohCMhV6r5*S*#N}9~I zemdB}pv0}Tok8iAK&cj#vHYf$B)FuNBAJ)BlDp?x2`yt^`dn1s)=Mo^r*IG5(Zn83 zt+Bt#Nt_tYn({|fu!eeNzh-c0lv&-88StXN&}Q7ch8>TmP((^eDRe8PtfK|lS7bNC zuv|Qgk>Llav;0~vU18HDw>mn*@Mx4t*~j*kZ0~H)$MZP8^(wb!9u=4Q}1lV0j9X@xI9t(RHiWcaz#S%%83%G?OKReb#m zzQ%ztK`Sn%C2`)Dc_{NCfJPuptB>I-qj_%3O+R~4$ggyRkv^6sZF3)6;-G~k_&OR@IEHTvwHKz&@hc? zq_N_BMB#x>zQ>Es_c-7a`5xxEX8jwie};qefQ6-YLe%Dh4C~~AY|mHf2900|U64zz zh2_u%@wgqiAY-_WT@VN}6!|??zg3%krXZ?xlI%#a&?i!l(df- z&Toyuxg-z)-+Nw4et`tp>7`22)k94tO_CLlG^9abUAMLwYJ=1x)s6pK_iFvKuFr6efe$zO&i`sEMOn&u)p#;L^!Q@ zo2Jhc`ar)U`t31Ej^iU-b2lAiMdr#<@ipMjY&*9{m={`F($OuvWI85#Yd@O2SCqPw z^=;ldV2SMwD4Wo+V#LUw-#6MK&>hR#wbbVe79iwqimzyL%q}7JIS}LaDs7g?28#a7 zutp;I013j;bi6v`**)QCfN@04z>uS@$~gM_{vk)df6_SOlz`HR-s4LnYQ~qsSwE2! zH_Ze1_uis$-9!sN8<8D>Fz!n$GnRIN5N#FMAJm+_muSFGTx(;Bu65|pfRjSR#V=i& zv_i-mGJK7MvlpyMg zpo`y_oE~%EUAo>rC$sg@t)q$wt-6I)YV+A!Cb~Bg5HCQN)58ClVoL90U`15K~wd!*>OZn>kOD*@=ZTN_GDhHRIs%^@{-5zBfnY_1Q?MSd56Jz~! zG`p2OB2Z>=^3~g8QrMfv6wmD&QTxBXty!FDKzf$$lqr0eKsqFb^d}&}2~B@GSLjmB z%O>k%5P2x;&zC7Be?{;b6FasNDF=!M^tdYS$!i&Zk!%Le?FJ`R zn?{BE(HW{viI4IQ_XF2C_4&(*-I%l6p5S*mOFlSq^9?lUG~@%p7rtZ!-!Ho1gjv)O z&=bpJ*6P}5F%wwh1uiLGpu6OM!WEmOimSSApg|g@8-q`uc4~C^TvH?UqZoxz3Hs|KfPB@^jqTkJG{4)Lydfqb_qC-6J|! z-4{&Yz3D~ca=FkuF2@2pQ$UwPE?*xNwdAetDjn=4te4p+7;0s`U-c0Q7P7@QJqb29 zPQwn6{kVTHkFrUwW{7lEJwV*A0WVZ+@2IFxs8|UuHXy}R9LDcR*58llcz7vvY+g=> z-}9pqB~_~$8vGS)z2tM|YzK-{@& zO+>egMkk_yMjX-OP(+W9Oo(gd+7w|Mh-0fm+?Q@tL(`X@t49?G{irWp@m=+$RPK)w zNxu4eItK`QIfTDV5S9R%DU6R1?o);^6(PKBiR(`{JZ;LQx)IwufRDdm9K0>Kj)UJD zz*Ay?AE0NEc%R_fI5-G^sB%XoNe;63I1oq3fnXU+*&$^H+_LBr6K~T_WYVN2Soq~CBzuCOQE=b%vi~UUN*T`Q5wbQ|I^W}U5QPf3R zG!8cV!5A)ZjkoeD3~y#OL}E#^=Y}2>HBV5qz@gdpsWE^lZPzt^yavx}ZvHHh8s4kH~2{ zt!^qGaJiwr-WP&&3aa;ars`6j&UzW#R+t6Wmv8 zWq3S3HP_Cz?60X{TIk>w`}73y*(3TL9|>LA8WZcSCc*@^5p^Yh+P_NFFx8u`u>DhR$azvkFpkmF zGKJ-d{=AMfi#IdSXtrs(TGnc6Ee9)Xtr@Nr8E?WRx+iVZN-B3M;4_7%fm^Hx82d-1 zO|@%NGj9v&JfJ>5tVHa-E@EZsJ|V$kFdb7JoVe0ayq%-?M@I2;3B_vv;>S2wohSx8 zfCih2CHfUojC+|7y-i58IsrTSMf9b>lQhS(pH)F5rdpEaC% zCwqn)niaRZ*lBO&bK201rovgWr#S?KiVF}snz3Z}SQx{AYE#)d9spCStrZ0{In;1d zebl_vzLAbk=^OQr;#SBiQ+Njn7T^Du4C2Mcv`%jB2iPfRgtJh~=;=vJ)B2-Wlx&&~ zg*0_Z=Ic|kQMW5C?)MTXE^OQ6+*Bad91bP zKPT!#6Ef&!hU50gfJnaTPqdRrU{PyEKLsGBvcwc}K{Mmm%?R_76?5Z9WG(6t)-;VQ zgEc7_s0HQzaSyj4Ll$M)mqzUgXxr*>qPEB8YGNB2f?-hGPK_WT6xdh-3=c70r*7$B zXj{zpheWM2g;WCR20=Pk)rMZ4LTKJG=9RqOWcM^yVZ_daSnRqR(IPZqEeeofu6<;w zE|?Au+bW*NPhBHK=>*YpLbMA!A>gt>Dj9D(tzRJ&6+tQ6v?lAR(MIiG@84p!o`EEqb_58L&^}qt^K!)YrZ-`Sfsud$g%NjJXeZGLt`|ABP^3E!y)9mKVF5w?`aW z;Xx*`yT7LJ;Dzc^S+}JtGhh)4P=~cRT+5M}e0{&J78d0u->^RU&8sBqaGT9;&y!{& zyOJdNT`1DW=bE&>WY-W`**i+QCbx<-AHv-#BQ-ofx)fBfXEs#S)1H+aRmm%C`a%Xd z-m4X(aEYDz2+!ehF}InhSG-aADn=ofj}m3jdw$innbMOD{C=|gcRqruqGD$1eg##&xcSgTUt1=E3> zWZ>fElv)G#Y66avMY|MVu2dvsjwIFKk_sB~E|tflyEW@J+G#ofmvzN2xf5K=2EkBJ zJ3b>K&3$owWL6zeX96`5)RqWyLqTQze%nSua&kzs)?Fqf%iv~>NS_@V)G*~K6HLVz z;gmjxf_YCk*%k#(6HF+R(f*=xv802xIf4^Qq_Dr>e3ihtMR1nk4j>9A5tob}k4HH;h(oO{>6O&?#l8+BfW>_0z{OVvl0+pl0HYiHu)#t|$#Iae=$jYr( zYsemh?Y2IlGYnW`(HNk}dGO8rvb2MrddHcS%d)g0b*FjsqEz4YjjfjiEBPSog<(++ z6<~5+nx%7R<_4ZceVWinv!y z`BZCk{6B8H8s2T%K}#xibR5R!`MoJ`sPmtdY{>f!EdlTEpUd`%IyADcwCzfkNdlC5 zx?8Tk7tieuH+s-O^*xALnURs?Y%FtWV*x~tFHvxY+aF1wQ7(g$*_n2zmwEX;-g{Du z37;#~%JLdqxWiwX>}GNS#6?u~q>qqOcHrK4({xKEG+eUGd~7Bc^Y$_yJHH}wx?8y6 zYDtGa_jd~T+$&i~c*`MUL3vZij%NfoBu4i3uR~;+!a?X7S_Q5wph3Q+L(W8I8;86% z$U|he&NF2GVzIhPtZpLLL8C_9SOy2;yp7{aR*f|*%4%`^%dAiOFS2fw?=|*NriG>K zac$w=*x+&s{A^hHQ&l;(?xl*C=Rj-NnN!f_$iAu8uAuh>ca?^TBid~~deCx_9(*v@ z&A(a}H4^(hoY=3`+pYJlx+!%jX+RnLU5!YZR-bxDd9)Xh`D(b1L5hEct;{fjrKO*t$#uDvRG zUo(mS@_q6xjaFj45}N?CA^>U2yvj8r{HoFMzoZg^OsCO}@+Qv^~AaC*F{0?dd5N zzg;8?s>;)0<6XXI)Oc?Vi5l-Nca<*g%{+#6a%l&=s+i{AC@S=y@b^#6y+Z6lw5e9U z=Yen`{QV98&ld{6pWy#ajQ{u;|4tG9C-8lN)GUB`EK9wL&EZB`Y%8005A2X%i)Ux3 z32F6Vsn+E-<&DohCQ}=%?XdChFw}QA<^Zi+ve>lpZ>$-Gl4u1h)9$zrwhvCCB-e6{ zil>90KccWpSVe>CNS)Ox)cSMUIe+9m4e|_xb?lHq3TtvGout7jWLF}>607buW z6z!5ww8SX7CZ_0@WfW}|QS_&W=}=FXiZ`3oZ7YCILUA}zR6ybqs>Way6+}E7;{%-+qe3h~%v=&n?mDAWQFLbpF8Ng=xudu?$$j>a z(dn_udNu~T8FE?Q(I0Z>?%)q^$xHq%Sd2V5$^$883U&n#AHja`cngQUTY~)-B#|lH z9AocYhJD>HL+ob=JJGGKS_XB^5H(}mhM|j{q=w10makAV_7HFSd)FGNUutqAb35o| zq}NcRSyMQBM=_t~XI*gDz4BVxrB|LB`9R1&jn-wbNCMqOCm4+V(T+&0=58Um@OdvC z1u6fK&#;>F9&}c7`XW(**<^^+kQc5OFm{F&p55Hj^Y4(_4Yf z{|U2Q3cNIPn>DVc-8Pu+uGPzKx{K_SxvoCPc^JbicZJ4M5BP9*5Zq0qIUxGW1xEBq zY9kO4MV}0pnZl8<&2M;}yhoxM!?@;dI(X>;SFY!N5L=-_UT5Wv2{S#dP1G=((ZdG+ zvRU@i0@3-?N>PX?dn%K7NMm+&Xc(-k(gd|3%Br1`Z}n{?YZ*tsRpy)j*EP8)&vnVN%TGCBI0RJc?^$ z^0WJCLIQHT5|E2?*07p-Uqi4o^6Z&T& z%hA)_=s6>%=bJ$ZJx_6Mz3a#uUlRQ7QAd)h zPa~o;ogRGLW+WY(kaRX}jnfd5baELx`) zk#npg=NSpgNhqLIGKKSFa$eazk49qhty<#SP{UOcd&Hu1%pnxyLow z00*p=_NMh0{ndc&kpSD>+q(?d77?&p0T%HqyYjqKspm0E06pCST_-{vg`e#v z3NML)7Jr^lSm4?y91~KgFqE6)|RM6UAxpE$y?h)Ey zERy?@Qn0~#tM3_(dtpEpI^_XZBj-%v0mumjbG(x%gVXc7niv>}?{HV?;v~wlTOckR zccb}~tuar3&lYt2>V~n*;RY>I)L|X8Q{=0`-`omOf5aA<;G%1#nzoZ73k8!4CaG84 zIMN3j5K^)nteJx9vcVwq?8|SP?LpCZLyCgG-1RXjKE+d6&67{u_-L~*;#w+L5PX>X zcjQ`T{a%mo5eR?(Lo23ta%7GfU#J!y>;kupG=8r~xx&}*8Me8R9<(e|co3k$P@#Ic zI83tguhHmeE6WetBs}PMkx*IiOI-c?2Zm%b$JIWG3-A%pGX<6Iaa>L0cf?hlXwcC1 z!kva{6@z0~d|fi>KRT(vZaMS8)BpOn6ix^qcba~c8~&b3hU1eB;iECa(#{FOr@1zS z;~m1ILFgi(o7Gky3yda#9^ydn=|E3TK&P#R=EtCKDFfXUf$rfzR|$0fU}`(#jdNBq z=Xv0ZaXq8WkUWf}(BJ3W;Iu`8va3D^i!F$aL(;6LOXu*3pL^y zz7n^C+xPy26NA_4$hMXAGacD>nErBP+X()8OD&AaCv*{kYY8|uR~KO@YYt_;->C5r zP|od}a!75C@c-GDGysRiqU(zC?=5^-ye*Or(C7CY1&r!T5=BnCC{*kCV}KyPafBwfu>CF-H8Tf6{$F`DxgF zbGOJibezM6=6*Fq_8NYvL7ANh?0P5?o_(6JTk7cr`(I-NxKQ4M%K*yZ4&}KC%F{vN zRty}<`#=*J@be;+-*qUvXLEC1OTMn{+h#KG&=j@R%>N*Ejs%C#G#f6Qjc(9b{g^oo z-4mR`;UfJGXKfkI9udwz-)wAFd#uR;`RU+yS2%9`UJH2#_>vzy{Z8-!G?=DfqX5+}1%}J~M4Mv})eJPMSIB$>7U7$?TfGNTa_y|A85K z7(GzDRY|oizxPn*mzqevKVKtf)*o3@?^o~tvu<510ZHVfgE9Nr*~}`;IT{)^h>R_Q zHfVqQQHwXz)W34Xz6DQwj8D*&8(}o~nm^+|Sv*RGIsFyE_{&Wc)LN-t!3UYbEQsy6 z1yya)QFbx4Z=oT!6RbmlG~_r)PjuLNrXo(gwTda(xx~GHvf9-kPwFFFsN3IA z+|d?-_J^felx;&cU*btY*P5t#{puEQvtAw3l`5m_nSLQ%C*4HrzVG>qi=!E%%QHXg z?oi6!FwBmfR_OVoYJ%%8HA!kvJ#3C7=`!#vyz#YTmBzWw0jE_?;R@Q0bhP~v+QK&a zbhgkI&ty;)4)!&gCNzZ0c7|88>QorT&i+k~1LrYtgeb7*9<2cZ| zlKsF@(PxwGvu%^lw$o?Te8#J+NKIGo=0nW2eYWRn`|NQ9z>y@$XNTysrxa2iwsnJ_ zGuxn=ij^EdmR+3=T8bZ&k{2$H5m-{v?9mSj$PNjRKmXVo5sCkTt76W9QykvO&x+`V zAo~XcLy)s1$`EAMK<=1;+&>2S#*YB`OH1Av1!HdhFz9`j$lMBRYVNSDDS4fNNGrmt z1*P$|#mQ7FKxed|y^La0631x2S^juwOdZqxRt1%%OSS@4TIx@qtp=)WSET1mO5>V& ztemLYn{J`5QHJGGfjgixHFpGNU_@`j7Po~R6V_L!Ii}8UK~W?M zjsJeD^|=AG|LWkbXTRnF62WQhiZ5EP(!3@^xzctiXOIUyIF!}-zqC}>`ESTs#wFms zy;a|%Z8!;UGj5h#^VOHn6|olmbN#*-#BxPv*gkDWCwMp-8{YY$jcjwVSfTl8x}NUk zKk50e1}ENXCT*nO>uA0Ma&I7Gv+m?`Gbsq4adHVTxMGHQt%G(V(70jaEcd$#SF_Aa{kOV91Dg zSUk;mwg{}_`7e+Hse3z4VLk0=Tlq-DddCmqVX^s-orcBJZ;6M+_wJW8-+_${hsFOi z9tI!*`5O<}xbqu+g%)@lh#>Q*G8zy4hz16P!?_lB!6(;6)NC*w5+3Z!(07mxefeJf zSURQRbc4Uv|0=leC5?NwRCFlqbS-t)z(3vLe<^A%zgqZMK#uWmQ-=TL?}zv&fuF2f zpCu5IE-iolNE)bY0Msx{aPRnj6M{W%(onb`ExVbQo30+AU%@4kjLzs&@ zY&;Yp+o6_7Ro$}u40uz;A5&%T!svY2aYl_O$8&X5`K4+iUX>dk&-ZS_Mm zag%Ud;99<$!_k=F=w-~F6yvz84977Mj`i0@%x(pa)SO{ejI9Eja$xU&z+||20(Op( zetHac&oZ#xBd|BcU>|2tNX;2VA;0r&Flkx1*`76qVGk%qIQuyaw~Kx!oNa*Sc^ z_K9%r;M#<T?!LV_fH#w~?Bsg?ALDD+PtMNDnqEAIle@{WRtI=UQ!}MS zZk+Wh*TzHa1P!qcT3CNbdNfYrf!Pkqgq)T5{qIj1r;XDsKP*UW#j#!U67 zu02-If3tU{$Ew;@sTu#2lCp1#Mnk~w(;HxUU&+}C_;i|y1?8CKGqy_vJc4Tz@M~8~ zjITE^FIqTTT`QH-GK1f1F>e6ek z4z12z4oy&~{tAV+sBIjDFIuBak12c(U{OcCm20DLFGpeVFdV+n%#(8>JK0s7X zk%bMXL8sps+xsk1$P~5%@_*mn_jQsx4CF}eq^J>?|3E~hun)kX0iymG(EvRsb8V9A z8dI}@y>|nkbMptaHRNi_XJecn;=jU~cGKT;$zRp)??nT<*zzhAqak-V5rgY(LwZB* zQjoCg|B6{)=)(Dj!p4Q$T zd;^Yf;wBs~)EJKScN>mv6CBHdlPM^()8QCdhGV-3#{*Y59H$vOgx8jK89L6Z4?}Kk zgj%^**E(3vS6!4h<2)`Rdo``yEx<%_kj^~ zboa(h-krV4yEkp}?k$_Vd+R3eUc1S=N?l4QBZ;ZmZXgK7T5kO213E1~NQ!FZK`Vos z<5NpK$#*5pd=gYg$QlvOitjpGxQV}x_K?2s3|r_kf2DjnuMau^kgF$U%tt^wtGBzSJZd>EMoLOf_03Veq#6K5EFb0 zzG(lN;$llfkCXuefbxU-*SFH$kXVLDP-*`Bphg?I(&akio@C~C^mUQGptsbzNM0ZFj{8jsr-h&=@0lVXFke?OYpz zy&VDt^VUIrmPxVAbcl+5WWR3-gM9b6ARnelDgG)Xbuf|5;I!X4PW|dv0O{p>o!Z?j zz~{G%jw}P3+0VlrKCI_1_FA8B?v%CGvTHbfdE`svTxbZ%JxjEqjhehvPx{vKhI25!~2QW`bEEoG@bxiu=*dS#U3Qbbh#T-86sUB2Dw%r&McDuXkefwG76jAJgcO6)sja^prhx*23MGU+k@? zE@$5ikgSF(b-7z+fs6vL6yDi5L}*qi_}%N)*^Z~zUv2&8L1_*>40CeimhATK%abGQ z$yLddXL#az;d3eTwHDkGxmL5efiLj8+u|s~t{1y*xJSKNoVS{=x@K|uxGe@c7kdG( zX9|S`!u~S?JMW6y5r75)anG?56uKM=}grqq-G-K&gr$W9hw< zQRz@CwYjCd^LqX~*(?s4+idBGuZE7x?%R6EH<1P>y>tYvdAGXg_*y$}Yx3CC59_6y zPWWte@}KTJ<*@#Fr+~X$#?q2#(y_j5(t%=032Hx@^k9tkg+!k;>zKMg1CWadw|4W} zHLUJm!QY^-6lDLdts_{CFiJL@5o-ja^};ilPJcDlfJInX;v1o>ga#b0R*(k##-2Q% zJXy^XrvcCOG7T7fbEE+`a03lE9hC?T@y-`H4HzV3WI-KtJ3}ggd-F6(Q!0WdP zxw7p-_4^z+9e-p^rK``zOobo*vlhsbyZ=IcXzJHnC+XGcSm%7NpRx=_gv296a>{aM zP)t7$0qvRWNMfwpR)b5v&#^YfnD(Cz`b?pTMsR%g;CD3Bd8}tR)*YB*`bpQbS_4!% zlx`l)2HAx5Y18|f(8yE3A!oO#P zfAxjmf`3eB{K$K{(WdkC+M0Ismevh~RK%9E+btEPDb>L-vffLr)7Zm&uYDj`qCMk@ zO^MbNF5<5YzB{)V9AnCH_zeSt67f0mQM7>96J}xkReB1nzoS2n^&TdARZwuEzqEV8 z`aWE9Hyu2Gf%PiKXJ|}3$HgC&1-}6o2WWK@E*|)6A}`lj_}7-w%ye#0O64rlb>E`% zH2Cj%EhW|wH0@X48spWT+n8$EQ9tv&mJkpQhFEa{Rar^RxXa8qJ1{5o&gL3%hWAC! z5AIDwACLYk@*!+m((+YR(+T`$s-Q`c){r*ipEJ98^Z!xf^;oYP%m?CYrU>yhvxfMZ zi9~$O+#R#aR3|jV6?$)TcWqp|YmYD$Y_sYEfgRiP1)1w(-4~Ac zcCQDUCwM_CI+c}P?Y@m??5Q<*!n;TI?O^VF6fSCq>Bh{WHXXZY{umkRL&J&?+WwCv z$Q(g#b@km05-9wYe>R1m$*)l1CvC32eh(6K3V%blMB(@2+7$ln78IU%fr&g{{Syal zgafAWCj|C019p21?0_<06%nv&9k33~cbe9k3HtXUUj&_Mk{G4%(1)gRP2sPS4Ba9Ju|#pkIx zZ0=i7)jIO)2EVQ^x?j;c^55ZQ*Vd8WgqM}ABTF32G($q`y5{hgNo^I=#TY%f4!jnUiR!Pg&1mttN9Y5xO&F z8vW`~8m?W0ZVY#rQj#vAcH^-3wOPLyyh{-!Xf91r4#8C<)uB66Z#4DXe|D^%TUSeW zlz#7YXF^q?JAJvfUUOeF7Qa-is{!)>T$6kHO$6m%yNHt7#aclTYA-c3@bc`3AgnCq zN1s-@8N#J&%s*5j>|3BF9x5xyEA)>+4hhlB_bUw@v+*05B7xM)+L%A4w)$!y zH0=(k-x;VMCZL+&h4%pU=NQzu5L8E|d0M}5j5YQB4l-bt4Y`?t=$C-lEe3IP3}VX= z1oHdk4JN-7Vq|~Jwda{wcIakPNd~DyuygcZk^r^+Wdh<+`P@c{N%RM|+hN!?!p)SS z@_UU2Hmn>BK)}E7r?6dIy#1A~@MH1zOSocqzXtSCyj|(s!FP=U%tmU4zZqCa!TGyw zol51-)^E;XO-tSr6ApjqxIswg2)xCzf9|f*kT6apJcl_SDT@jyUEhG3Io8#$FcHRCBBlgd<= ztsFMEfdMNuLm7VA+F^&LX5Ot&^Sz!TXuNb-njc+)V;{H8z}N}Ak~SONzMujdCg|F0 zjmqDNSV!dtP?9M$@SUUbB7R4n(Vh{NtIskjTL4Zb8+~Auj@H`Ts|`};KQ1GaAJ>xs z2)dzCk0%6IHY-hGMX=eYZt=8-ky$+g%+S%>(NWYNN5`Lyjw53_x|Y%L;MbwF8jKEN z*r^#iQSwD`Y^GPrNk*%i4~O|XOdAdA`OW-k zIWrBEF?CHF7pg)3}1|%dU>X zPCw-agWo{pU|gnyN6y$_g1jR5r>jz=Xj8@@e3!}^~D(>QayY&X%&6%^Ddv?RRGaO6PbPuvxnxR#?5 zL?SXi@+#x)dHɔ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 z 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

    Z*J}Hpf$(7nzpd>*8{uq( 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

      1@s#VH})K(=lRa&heS`_`gpL6dd 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 skew_thr] - - subdf = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) | - (subdf['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])] - - if len(subdf) > 0: - - save_f = h5py.File(os.path.join(save_folder, save_fn)) - - nwb_fn = table_fn[0:-11] + '110_repacked.nwb' - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') - plane_n = table_fn[-11:-5] - - # S2 - s2_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s2_df) > 0: - s2_grp = save_f.create_group(table_fn[0:-5] + '_{}_ONOFF'.format(response_dir)) - s1_on_grp = save_f.create_group(table_fn[0:-5] + '_{}_ON'.format(response_dir)) - s1_off_grp = save_f.create_group(table_fn[0:-5] + '_{}_OFF'.format(response_dir)) - - for roi_i, roi_row in s2_df.iterrows(): - - print('\t s2 receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i+1, len(s2_df))) - - if response_dir == 'pos': - _, _, _, srf_on, srf_off, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - elif response_dir == 'neg': - _, _, _, _, _, srf_on, srf_off, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - else: - raise ValueError - - rf_on_mask = rf_on_new.get_weighted_mask() - rf_off_mask = rf_off_new.get_weighted_mask() - rf_onoff_new = sca.SpatialReceptiveField(mask=np.max([rf_on_mask, rf_off_mask], axis=0), - altPos=rf_on_new.altPos, - aziPos=rf_on_new.aziPos, - sign='ON_OFF', - thr=analysis_params['rf_z_thr_abs']) - - curr_s2_grp = s2_grp.create_group(roi_row['roi_n']) - rf_onoff_new.to_h5_group(curr_s2_grp) - - curr_s1_on_grp = s1_on_grp.create_group(roi_row['roi_n']) - rf_on_new.to_h5_group(curr_s1_on_grp) - - curr_s1_off_grp = s1_off_grp.create_group(roi_row['roi_n']) - rf_off_new.to_h5_group(curr_s1_off_grp) - - - # positive S1 ON - s1_on_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] < analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s1_on_df) > 0: - - s1_on_grp_n = table_fn[0:-5] + '_{}_ON'.format(response_dir) - - if s1_on_grp_n in save_f.keys(): - s1_on_grp = save_f[s1_on_grp_n] - else: - s1_on_grp = save_f.create_group(s1_on_grp_n) - - for roi_i, roi_row in s1_on_df.iterrows(): - - print('\t s1 ON receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i + 1, len(s1_on_df))) - - if response_dir == 'pos': - _, _, _, srf_on, _, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - elif response_dir == 'neg': - _, _, _, _, _, srf_on, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - else: - print(response_dir) - raise ValueError - - curr_s1_on_grp = s1_on_grp.create_group(roi_row['roi_n']) - rf_on_new.to_h5_group(curr_s1_on_grp) - - - # positive S1 OFF - s1_off_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] < analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s1_off_df) > 0: - - s1_off_grp_n = table_fn[0:-5] + '_{}_OFF'.format(response_dir) - - if s1_off_grp_n in save_f.keys(): - s1_off_grp = save_f[s1_off_grp_n] - else: - s1_off_grp = save_f.create_group(s1_off_grp_n) - - for roi_i, roi_row in s1_off_df.iterrows(): - - print('\t s1 OFF receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i + 1, len(s1_off_df))) - - if response_dir == 'pos': - _, _, _, _, srf_off, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - elif response_dir == 'neg': - - _, _, _, _, _, _, srf_off, _, _, _, _, _, _, \ - _ = dt.get_everything_from_roi(nwb_f=nwb_f, - plane_n=plane_n, - roi_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - else: - raise ValueError - - curr_s1_off_grp = s1_off_grp.create_group(roi_row['roi_n']) - rf_off_new.to_h5_group(curr_s1_off_grp) - - - save_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/old/0075_get_rf_maps_axon.py b/corticalmapping/scripts/post_recording/analysis_database/old/0075_get_rf_maps_axon.py deleted file mode 100644 index 3cc53f6..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/old/0075_get_rf_maps_axon.py +++ /dev/null @@ -1,257 +0,0 @@ -import sys -sys.path.extend(['/home/junz/PycharmProjects/corticalmapping']) -import os -import numpy as np -import h5py -# import datetime -import pandas as pd -import corticalmapping.DatabaseTools as dt -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.SingleCellAnalysis as sca -from shutil import copyfile - -df_fn = 'dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30.csv' -clu_folder = r'intermediate_results\bouton_clustering\AllStimuli_DistanceThr_1.30' -nwb_folder = 'nwbs' -save_folder = "intermediate_results" - -response_dir = 'pos' -skew_thr = 0.6 -analysis_params = dt.ANALYSIS_PARAMS - -notes = ''' - zscore receptive field maps of all significant rois. Spatial temporal receptive fields - are first converted to df/f. Then 2-d zscore maps are generated. Then the zscore maps are - 2d filtered to smooth and interpolated in to high resolution. After preprocessing, if the - peak value of zscore is larger than the threshold, the receptive field will be considered - as sigificant. - ''' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(save_folder, 'rf_maps_' + os.path.splitext(df_fn)[0]) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -copyfile(os.path.realpath(__file__), - os.path.join(save_folder, - 'script_log.py')) - -df_axon = pd.read_csv(df_fn) -df_axon = df_axon[np.logical_not(df_axon['rf_{}_on_peak_z'.format(response_dir)].isnull())] -df_axon = df_axon[df_axon['skew_fil'] >= skew_thr] - -df_axon = df_axon[(df_axon['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) | - (df_axon['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])] - -plane_df = df_axon[['date', 'mouse_id', 'plane_n']].drop_duplicates().reset_index() -print('total number of planes with lsn data: {}'.format(len(plane_df))) - -for plane_i, plane_row in plane_df.iterrows(): - date = int(plane_row['date']) - mid = plane_row['mouse_id'] - plane_n = plane_row['plane_n'] - - print('processing {}_{}_{}, {} / {}'.format(date, mid, plane_n, plane_i+1, len(plane_df))) - - subdf = df_axon[(df_axon['date'] == date) & - (df_axon['mouse_id'] == mid) & - (df_axon['plane_n'] == plane_n)] - - nwb_fn = '{}_{}_110_repacked.nwb'.format(date, mid) - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') - - clu_fn = '{}_{}_{}_axon_grouping.hdf5'.format(date, mid, plane_n) - clu_f = h5py.File(os.path.join(clu_folder, clu_fn), 'r') - - save_fn = '{}_{}_{}_{}.hdf5'.format(date, mid, plane_n, response_dir) - save_f = h5py.File(os.path.join(save_folder, save_fn)) - - # S2 - s2_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s2_df) > 0: - s2_grp = save_f.create_group('{}_{}_{}_{}_ONOFF'.format(date, mid, plane_n, response_dir)) - s1_on_grp = save_f.create_group('{}_{}_{}_{}_ON'.format(date, mid, plane_n, response_dir)) - s1_off_grp = save_f.create_group('{}_{}_{}_{}_OFF'.format(date, mid, plane_n, response_dir)) - - for roi_i, roi_row in s2_df.iterrows(): - - print('\t s2 receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i + 1, len(s2_df))) - - if response_dir == 'pos': - _, _, _, srf_on, srf_off, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - elif response_dir == 'neg': - _, _, _, _, _, srf_on, srf_off, _, _, _, _, _, _, \ - _ = dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - else: - raise ValueError - - rf_on_mask = rf_on_new.get_weighted_mask() - rf_off_mask = rf_off_new.get_weighted_mask() - rf_onoff_new = sca.SpatialReceptiveField(mask=np.max([rf_on_mask, rf_off_mask], axis=0), - altPos=rf_on_new.altPos, - aziPos=rf_on_new.aziPos, - sign='ON_OFF', - thr=analysis_params['rf_z_thr_abs']) - - curr_s2_grp = s2_grp.create_group(roi_row['roi_n']) - rf_onoff_new.to_h5_group(curr_s2_grp) - - curr_s1_on_grp = s1_on_grp.create_group(roi_row['roi_n']) - rf_on_new.to_h5_group(curr_s1_on_grp) - - curr_s1_off_grp = s1_off_grp.create_group(roi_row['roi_n']) - rf_off_new.to_h5_group(curr_s1_off_grp) - - # positive S1 ON - s1_on_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] < analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s1_on_df) > 0: - - s1_on_grp_n = '{}_{}_{}_{}_ON'.format(date, mid, plane_n, response_dir) - - if s1_on_grp_n in save_f.keys(): - s1_on_grp = save_f[s1_on_grp_n] - else: - s1_on_grp = save_f.create_group(s1_on_grp_n) - - for roi_i, roi_row in s1_on_df.iterrows(): - - print('\t s1 ON receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i + 1, len(s1_on_df))) - - if response_dir == 'pos': - _, _, _, srf_on, _, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - elif response_dir == 'neg': - _, _, _, _, _, srf_on, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_on_new = dt.get_rf_properties(srf=srf_on, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - else: - print(response_dir) - raise ValueError - - curr_s1_on_grp = s1_on_grp.create_group(roi_row['roi_n']) - rf_on_new.to_h5_group(curr_s1_on_grp) - - # positive S1 OFF - s1_off_df = subdf[(subdf['rf_{}_on_peak_z'.format(response_dir)] < analysis_params['rf_z_thr_abs']) & - (subdf['rf_{}_off_peak_z'.format(response_dir)] >= analysis_params['rf_z_thr_abs'])].reset_index() - - if len(s1_off_df) > 0: - - s1_off_grp_n = '{}_{}_{}_{}_OFF'.format(date, mid, plane_n, response_dir) - - if s1_off_grp_n in save_f.keys(): - s1_off_grp = save_f[s1_off_grp_n] - else: - s1_off_grp = save_f.create_group(s1_off_grp_n) - - for roi_i, roi_row in s1_off_df.iterrows(): - - print('\t s1 OFF receptive fields, {}, {} / {} ...'.format(roi_row['roi_n'], roi_i + 1, len(s1_off_df))) - - if response_dir == 'pos': - _, _, _, _, srf_off, _, _, _, _, _, _, _, _, \ - _ = dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='positive', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - elif response_dir == 'neg': - - _, _, _, _, _, _, srf_off, _, _, _, _, _, _, \ - _ = dt.get_everthing_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=roi_row['roi_n'], - params=analysis_params) - - _, rf_off_new = dt.get_rf_properties(srf=srf_off, - polarity='negative', - sigma=analysis_params['gaussian_filter_sigma_rf'], - interpolate_rate=analysis_params['interpolate_rate_rf'], - z_thr_abs=analysis_params['rf_z_thr_abs'], - z_thr_rel=analysis_params['rf_z_thr_rel']) - - else: - raise ValueError - - curr_s1_off_grp = s1_off_grp.create_group(roi_row['roi_n']) - rf_off_new.to_h5_group(curr_s1_off_grp) - - save_f.close() - - - - diff --git a/corticalmapping/scripts/post_recording/analysis_database/old/0080_plot_plane_rf_center.py b/corticalmapping/scripts/post_recording/analysis_database/old/0080_plot_plane_rf_center.py deleted file mode 100644 index d849ca4..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/old/0080_plot_plane_rf_center.py +++ /dev/null @@ -1,262 +0,0 @@ -import sys -sys.path.extend(['/home/junz/PycharmProjects/corticalmapping']) -import os -import numpy as np -import matplotlib.pyplot as plt -import pandas as pd -from matplotlib.backends.backend_pdf import PdfPages -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.DatabaseTools as dt - -table_name = 'big_roi_table_test.xlsx' -sheet_name = 'f_center_subtracted' - -response_dir = 'pos' -skew_thr = 0.6 -rf_peak_z_thr = 1.6 - -save_fn = 'plane_rf_centers.pdf' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -table_path = os.path.join(curr_folder, table_name) -df = pd.read_excel(table_path, sheetname=sheet_name) -subdf = df[df['skew_fil'] >= skew_thr] - -planes = subdf[['date', 'mouse_id', 'plane_n', 'depth']].drop_duplicates().reset_index() -print(planes) - -pdff = PdfPages(os.path.join('intermediate_figures', save_fn)) - -for plane_i, plane_row in planes.iterrows(): - - print('plotting {}_{}_{}, {} / {}'.format( - plane_row['date'], - plane_row['mouse_id'], - plane_row['plane_n'], - plane_i + 1, - len(planes))) - - planedf = subdf[(subdf['date'] == plane_row['date']) & \ - (subdf['mouse_id'] == plane_row['mouse_id']) & \ - (subdf['plane_n'] == plane_row['plane_n']) & \ - (subdf['depth'] == plane_row['depth'])] - - df_or = planedf[planedf['rf_{}_onoff_peak_z'.format(response_dir)] >= rf_peak_z_thr] - df_and = planedf[(planedf['rf_{}_on_peak_z'.format(response_dir)] >= rf_peak_z_thr) & \ - (planedf['rf_{}_off_peak_z'.format(response_dir)] >= rf_peak_z_thr)] - df_on = planedf[planedf['rf_{}_on_peak_z'.format(response_dir)] >= rf_peak_z_thr].drop(df_and.index) - df_off = planedf[planedf['rf_{}_off_peak_z'.format(response_dir)] >= rf_peak_z_thr].drop(df_and.index) - - df_or = df_or.reset_index() - df_and = df_and.reset_index() - df_on = df_on.reset_index() - df_off = df_off.reset_index() - - if len(df_or) == 0: - print('no any receptive fields. skip.') - else: - print('\tnumber of rois with significant rf: {}'.format(len(df_or))) - print('\tnumber of rois with S1 ON: {}'.format(len(df_on))) - print('\tnumber of rois with S1 OFF: {}'.format(len(df_off))) - print('\tnumber of rois with S2 ON/OFF: {}'.format(len(df_and))) - - f = plt.figure(figsize=(11, 8.5)) - - f.suptitle('{}_{}_{}; {} um'.format(plane_row['date'], - plane_row['mouse_id'], - plane_row['plane_n'], - plane_row['depth'])) - - #=============================RF center============================================= - # ON/OFF - alt_min = int(np.min(df_or['rf_{}_onoff_center_alt'.format(response_dir)]) - 5) - alt_max = int(np.max(df_or['rf_{}_onoff_center_alt'.format(response_dir)]) + 5) - azi_min = int(np.min(df_or['rf_{}_onoff_center_azi'.format(response_dir)]) - 5) - azi_max = int(np.max(df_or['rf_{}_onoff_center_azi'.format(response_dir)]) + 5) - ax_or_scatter = f.add_subplot(4, 5, 1) - ax_or_scatter.plot(df_or['rf_{}_onoff_center_azi'.format(response_dir)], - df_or['rf_{}_onoff_center_alt'.format(response_dir)], - '.', color='#888888') - ax_or_scatter.set_xlim([azi_min, azi_max]) - ax_or_scatter.set_ylim([alt_min, alt_max]) - ax_or_scatter.set_title('RF center') - - # ON - ax_on_scatter = f.add_subplot(4, 5, 6) - ax_on_scatter.plot(df_off['rf_{}_off_center_azi'.format(response_dir)], - df_off['rf_{}_off_center_alt'.format(response_dir)], - '.', color='#aaaaaa') - ax_on_scatter.plot(df_on['rf_{}_on_center_azi'.format(response_dir)], - df_on['rf_{}_on_center_alt'.format(response_dir)], - '.', color='#ff0000') - ax_on_scatter.set_xlim([azi_min, azi_max]) - ax_on_scatter.set_ylim([alt_min, alt_max]) - - # OFF - ax_off_scatter = f.add_subplot(4, 5, 11) - ax_off_scatter.plot(df_on['rf_{}_on_center_azi'.format(response_dir)], - df_on['rf_{}_on_center_alt'.format(response_dir)], - '.', color='#aaaaaa') - ax_off_scatter.plot(df_off['rf_{}_off_center_azi'.format(response_dir)], - df_off['rf_{}_off_center_alt'.format(response_dir)], - '.', color='#0000ff') - ax_off_scatter.set_xlim([azi_min, azi_max]) - ax_off_scatter.set_ylim([alt_min, alt_max]) - - # ON-OFF - ax_and_scatter = f.add_subplot(4, 5, 16) - ax_and_scatter.plot(df_and['rf_{}_on_center_azi'.format(response_dir)], - df_and['rf_{}_on_center_alt'.format(response_dir)], - '.', color='#ff0000') - ax_and_scatter.plot(df_and['rf_{}_off_center_azi'.format(response_dir)], - df_and['rf_{}_off_center_alt'.format(response_dir)], - '.', color='#0000ff') - ax_and_scatter.set_xlim([azi_min, azi_max]) - ax_and_scatter.set_ylim([alt_min, alt_max]) - - # =============================pairwise distance============================================= - dis_or = ia.pairwise_distance(df_or[['rf_{}_onoff_center_azi'.format(response_dir), - 'rf_{}_onoff_center_alt'.format(response_dir)]].values) - ax_or_pd = f.add_subplot(4, 5, 2) - if len(dis_or) > 0: - ax_or_pd.hist(dis_or, range=[0, 80], bins=20, facecolor='#aaaaaa', edgecolor='none') - ax_or_pd.get_yaxis().set_ticks([]) - ax_or_pd.set_title('pw RF dis') # pairwise receptive field center distance - - dis_on = ia.pairwise_distance(df_on[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values) - ax_on_pd = f.add_subplot(4, 5, 7) - if len(dis_on) > 0: - ax_on_pd.hist(dis_on, range=[0, 80], bins=20, facecolor='#ff0000', edgecolor='none') - ax_on_pd.get_yaxis().set_ticks([]) - - dis_off = ia.pairwise_distance(df_off[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values) - ax_off_pd = f.add_subplot(4, 5, 12) - if len(dis_off) > 0: - ax_off_pd.hist(dis_off, range=[0, 80], bins=20, facecolor='#0000ff', edgecolor='none') - ax_off_pd.get_yaxis().set_ticks([]) - - dis_and_on = ia.pairwise_distance(df_and[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values) - dis_and_off = ia.pairwise_distance(df_and[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values) - ax_and_pd = f.add_subplot(4, 5, 17) - if len(dis_and_on) > 0: - ax_and_pd.hist(dis_and_on, range=[0, 80], bins=20, facecolor='#ff0000', edgecolor='none', alpha=0.5) - ax_and_pd.hist(dis_and_off, range=[0, 80], bins=20, facecolor='#0000ff', edgecolor='none', alpha=0.5) - ax_and_pd.get_yaxis().set_ticks([]) - - # =============================parewise magnification============================================= - mag_or = ia.pairwise_magnification(df_or[['rf_{}_onoff_center_azi'.format(response_dir), - 'rf_{}_onoff_center_alt'.format(response_dir)]].values, - df_or[['roi_center_col', 'roi_center_row']].values) - ax_or_pm = f.add_subplot(4, 5, 3) - if len(mag_or) > 0: - mag_or = 0.00035 / mag_or # 0.35 um per pixel - ax_or_pm.hist(mag_or, range=[0, 0.2], bins=20, facecolor='#aaaaaa', edgecolor='none') - ax_or_pm.get_yaxis().set_ticks([]) - ax_or_pm.set_title('mm/deg') # pairwise magnification - # - mag_on = ia.pairwise_magnification(df_on[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values, - df_on[['roi_center_col', 'roi_center_row']].values) - ax_on_pm = f.add_subplot(4, 5, 8) - if len(mag_on) > 0: - mag_on = 0.00035 / mag_on # 0.35 um per pixel - ax_on_pm.hist(mag_on, range=[0, 0.2], bins=20, facecolor='#ff0000', edgecolor='none') - ax_on_pm.get_yaxis().set_ticks([]) - - mag_off = ia.pairwise_magnification(df_off[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values, - df_off[['roi_center_col', 'roi_center_row']].values) - ax_off_pm = f.add_subplot(4, 5, 13) - if len(mag_off) > 0: - mag_off = 0.00035 / mag_off # 0.35 um per pixel - ax_off_pm.hist(mag_off, range=[0, 0.2], bins=20, facecolor='#0000ff', edgecolor='none') - ax_off_pm.get_yaxis().set_ticks([]) - - mag_and_on = ia.pairwise_magnification(df_and[['rf_{}_on_center_azi'.format(response_dir), - 'rf_{}_on_center_alt'.format(response_dir)]].values, - df_and[['roi_center_col', 'roi_center_row']].values) - - mag_and_off = ia.pairwise_magnification(df_and[['rf_{}_off_center_azi'.format(response_dir), - 'rf_{}_off_center_alt'.format(response_dir)]].values, - df_and[['roi_center_col', 'roi_center_row']].values) - - ax_and_pm = f.add_subplot(4, 5, 18) - if len(mag_and_on) > 0: - mag_and_on = 0.00035 / mag_and_on # 0.35 um per pixel - mag_and_off = 0.00035 / mag_and_off # 0.35 um per pixel - ax_and_pm.hist(mag_and_on, range=[0, 0.2], bins=20, facecolor='#ff0000', edgecolor='none', alpha=0.5,) - ax_and_pm.hist(mag_and_off, range=[0, 0.2], bins=20, facecolor='#0000ff', edgecolor='none', alpha=0.5,) - ax_and_pm.get_yaxis().set_ticks([]) - - # =============================azi alt spatial distribution============================================= - ax_alt_or = f.add_subplot(4, 5, 4) - ax_alt_or.set_title('altitude') - ax_azi_or = f.add_subplot(4, 5, 5) - ax_azi_or.set_title('azimuth') - if len(df_or) > 0: - dt.plot_roi_retinotopy(coords_rf=df_or[['rf_{}_onoff_center_alt'.format(response_dir), - 'rf_{}_onoff_center_azi'.format(response_dir)]].values, - coords_roi=df_or[['roi_center_row', 'roi_center_col']].values, - ax_alt=ax_alt_or, - ax_azi=ax_azi_or, - cmap='viridis', - canvas_shape=(512, 512), - edgecolors='#000000', - linewidth=0.5) - else: - ax_alt_or.set_xticks([]) - ax_alt_or.set_yticks([]) - ax_azi_or.set_xticks([]) - ax_azi_or.set_yticks([]) - - ax_alt_on = f.add_subplot(4, 5, 9) - ax_azi_on = f.add_subplot(4, 5, 10) - if len(df_on) > 0: - dt.plot_roi_retinotopy(coords_rf=df_on[['rf_{}_on_center_alt'.format(response_dir), - 'rf_{}_on_center_azi'.format(response_dir)]].values, - coords_roi=df_on[['roi_center_row', 'roi_center_col']].values, - ax_alt=ax_alt_on, - ax_azi=ax_azi_on, - cmap='viridis', - canvas_shape=(512, 512), - edgecolors='#000000', - linewidth=0.5) - else: - ax_alt_on.set_xticks([]) - ax_alt_on.set_yticks([]) - ax_azi_on.set_xticks([]) - ax_azi_on.set_yticks([]) - - ax_alt_off = f.add_subplot(4, 5, 14) - ax_azi_off = f.add_subplot(4, 5, 15) - if len(df_off) > 0: - dt.plot_roi_retinotopy(coords_rf=df_off[['rf_{}_off_center_alt'.format(response_dir), - 'rf_{}_off_center_azi'.format(response_dir)]].values, - coords_roi=df_off[['roi_center_row', 'roi_center_col']].values, - ax_alt=ax_alt_off, - ax_azi=ax_azi_off, - cmap='viridis', - canvas_shape=(512, 512), - edgecolors='#000000', - linewidth=0.5) - else: - ax_alt_off.set_xticks([]) - ax_alt_off.set_yticks([]) - ax_azi_off.set_xticks([]) - ax_azi_off.set_yticks([]) - - # plt.tight_layout() - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - -pdff.close() - -print('for debug ...') diff --git a/corticalmapping/scripts/post_recording/analysis_database/old/0090_group_boutons_into_axons.py b/corticalmapping/scripts/post_recording/analysis_database/old/0090_group_boutons_into_axons.py deleted file mode 100644 index d34cbc9..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/old/0090_group_boutons_into_axons.py +++ /dev/null @@ -1,70 +0,0 @@ -import os -import numpy as np -import h5py -import corticalmapping.DatabaseTools as dt -import corticalmapping.SingleCellAnalysis as sca -import matplotlib.pyplot as plt -import pandas as pd - - -nwb_folder = "nwbs" -save_folder = r"intermediate_results\bouton_clustering" -trace_type = 'f_center_subtracted' -trace_window = 'UniformContrast' # 'AllStimuli', 'UniformContrast', 'LocallySparseNoise', or 'DriftingGratingSpont' - -# BoutonClassifier parameters -skew_filter_sigma = 5. -skew_thr = 0.6 -lowpass_sigma=0.1 -detrend_sigma=3. -event_std_thr = 3. -peri_event_dur = (-1., 3.) -corr_len_thr = 30. -corr_abs_thr = 0.7 -corr_std_thr = 3. -is_cosine_similarity = False -distance_metric = 'euclidean' -linkage_method = 'weighted' -distance_thr = 1.3 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(save_folder, '{}_DistanceThr_{:.2f}'.format(trace_window, distance_thr)) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fns = [f for f in os.listdir(nwb_folder) if f[-4:] == '.nwb'] -nwb_fns.sort() - -bc = dt.BoutonClassifier(skew_filter_sigma=skew_filter_sigma, - skew_thr=skew_thr, - lowpass_sigma=lowpass_sigma, - detrend_sigma=detrend_sigma, - event_std_thr=event_std_thr, - peri_event_dur=peri_event_dur, - corr_len_thr=corr_len_thr, - corr_abs_thr=corr_abs_thr, - corr_std_thr=corr_std_thr, - is_cosine_similarity=is_cosine_similarity, - distance_metric=distance_metric, - linkage_method=linkage_method, - distance_thr=distance_thr) - -for nwb_fi, nwb_fn in enumerate(nwb_fns): - - print('processing {}, {}/{}'.format(nwb_fn, nwb_fi + 1, len(nwb_fns))) - - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') - - plane_ns = dt.get_plane_ns(nwb_f=nwb_f) - plane_ns.sort() - - for plane_i, plane_n in enumerate(plane_ns): - - print('\n\t{}, {}/{}'.format(plane_n, plane_i + 1, len(plane_ns))) - - bc.process_plane(nwb_f=nwb_f, save_folder=save_folder, plane_n=plane_n, trace_type=trace_type, - trace_window=trace_window) - - nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/analysis_database/old/0100_add_axon_strf.py b/corticalmapping/scripts/post_recording/analysis_database/old/0100_add_axon_strf.py deleted file mode 100644 index 585ea47..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/old/0100_add_axon_strf.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -import h5py -import corticalmapping.DatabaseTools as dt - -nwb_folder = "nwbs" -clu_folder = r"intermediate_results\bouton_clustering\AllStimuli_DistanceThr_1.30" -strf_t_win = [-0.5, 2.] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -clu_fns = [f for f in os.listdir(clu_folder) if f[-5:] == '.hdf5'] -clu_fns.sort() -print('total number of planes: {}'.format(len(clu_fns))) - -for clu_fi, clu_fn in enumerate(clu_fns): - - date, mid, plane_n, _, _ = clu_fn.split('_') - - print('processing {}_{}_{}, {} / {}'.format(date, mid, plane_n, clu_fi + 1, len(clu_fns))) - - nwb_fn = '{}_{}_110_repacked.nwb'.format(date, mid) - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') - - clu_f = h5py.File(os.path.join(clu_folder, clu_fn)) - - bc = dt.BoutonClassifier() - bc.add_axon_strf(nwb_f=nwb_f, clu_f=clu_f, plane_n=plane_n, t_win=strf_t_win, verbose=False) - - nwb_f.close() - clu_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/old/0110_add_axon_dgcrm.py b/corticalmapping/scripts/post_recording/analysis_database/old/0110_add_axon_dgcrm.py deleted file mode 100644 index 68c304e..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/old/0110_add_axon_dgcrm.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -import h5py -import corticalmapping.DatabaseTools as dt - -nwb_folder = "nwbs" -clu_folder = r"intermediate_results\bouton_clustering\AllStimuli_DistanceThr_1.30" -dgcrm_t_win = [-1., 2.5] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -clu_fns = [f for f in os.listdir(clu_folder) if f[-5:] == '.hdf5'] -clu_fns.sort() -print('total number of planes: {}'.format(len(clu_fns))) - -for clu_fi, clu_fn in enumerate(clu_fns): - - date, mid, plane_n, _, _ = clu_fn.split('_') - - print('processing {}_{}_{}, {} / {}'.format(date, mid, plane_n, clu_fi + 1, len(clu_fns))) - - nwb_fn = '{}_{}_110_repacked.nwb'.format(date, mid) - nwb_f = h5py.File(os.path.join(nwb_folder, nwb_fn), 'r') - - clu_f = h5py.File(os.path.join(clu_folder, clu_fn)) - - bc = dt.BoutonClassifier() - bc.add_axon_dgcrm(nwb_f=nwb_f, clu_f=clu_f, plane_n=plane_n, t_win=dgcrm_t_win, verbose=False) - - nwb_f.close() - clu_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/old/0120_get_axon_plane_df.py b/corticalmapping/scripts/post_recording/analysis_database/old/0120_get_axon_plane_df.py deleted file mode 100644 index 3df2aa4..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/old/0120_get_axon_plane_df.py +++ /dev/null @@ -1,413 +0,0 @@ -import os -import corticalmapping.DatabaseTools as dt -import time -import pandas as pd -import numpy as np -import h5py -from multiprocessing import Pool -import shutil - -date_range = [180301, 190601] -nwb_folder = 'nwbs' -df_folder = r'other_dataframes\dataframes_190530171338' -clu_folder = r'intermediate_results\bouton_clustering\AllStimuli_DistanceThr_1.30' -process_num = 6 -is_overwrite = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -print('pandas version: {}\n'.format(pd.__version__)) - -columns = [ - 'date', - 'mouse_id', - 'plane_n', - 'roi_n', - 'depth', # microns under pia, float - - # roi mask - 'roi_area', # square micron - 'roi_center_row', # center of roi mask in field of view, row - 'roi_center_col', # center of roi mask in field of view, column - - # trace skewness - 'skew_raw', # skewness of unfiltered trace (neuropil subtracted), float - 'skew_fil', # skewness of highpassed trace, float - - # receptive fields - 'rf_pos_on_peak_z', - 'rf_pos_on_area', - 'rf_pos_on_center_alt', - 'rf_pos_on_center_azi', - - 'rf_pos_off_peak_z', - 'rf_pos_off_area', - 'rf_pos_off_center_alt', - 'rf_pos_off_center_azi', - - 'rf_pos_onoff_peak_z', - 'rf_pos_onoff_area', - 'rf_pos_onoff_center_alt', - 'rf_pos_onoff_center_azi', - - 'rf_pos_lsi', - - 'rf_neg_on_peak_z', - 'rf_neg_on_area', - 'rf_neg_on_center_alt', - 'rf_neg_on_center_azi', - - 'rf_neg_off_peak_z', - 'rf_neg_off_area', - 'rf_neg_off_center_alt', - 'rf_neg_off_center_azi', - - 'rf_neg_onoff_peak_z', - 'rf_neg_onoff_area', - 'rf_neg_onoff_center_alt', - 'rf_neg_onoff_center_azi', - - 'rf_neg_lsi', - - # drifting grating peak response - 'dgc_pos_peak_df', - 'dgc_neg_peak_df', - 'dgc_pos_p_ttest_df', - 'dgc_neg_p_ttest_df', - 'dgc_p_anova_df', - - 'dgc_pos_peak_dff', - 'dgc_neg_peak_dff', - 'dgc_pos_p_ttest_dff', - 'dgc_neg_p_ttest_dff', - 'dgc_p_anova_dff', - - 'dgc_pos_peak_z', - 'dgc_neg_peak_z', - 'dgc_pos_p_ttest_z', - 'dgc_neg_p_ttest_z', - 'dgc_p_anova_z', - - # direction / orientation tuning, pos, df - 'dgc_pos_osi_raw_df', - 'dgc_pos_dsi_raw_df', - 'dgc_pos_gosi_raw_df', - 'dgc_pos_gdsi_raw_df', - 'dgc_pos_osi_ele_df', - 'dgc_pos_dsi_ele_df', - 'dgc_pos_gosi_ele_df', - 'dgc_pos_gdsi_ele_df', - 'dgc_pos_osi_rec_df', - 'dgc_pos_dsi_rec_df', - 'dgc_pos_gosi_rec_df', - 'dgc_pos_gdsi_rec_df', - 'dgc_pos_peak_dire_raw_df', - 'dgc_pos_vs_dire_raw_df', - 'dgc_pos_vs_dire_ele_df', - 'dgc_pos_vs_dire_rec_df', - - # direction / orientation tuning, neg, df - 'dgc_neg_osi_raw_df', - 'dgc_neg_dsi_raw_df', - 'dgc_neg_gosi_raw_df', - 'dgc_neg_gdsi_raw_df', - 'dgc_neg_osi_ele_df', - 'dgc_neg_dsi_ele_df', - 'dgc_neg_gosi_ele_df', - 'dgc_neg_gdsi_ele_df', - 'dgc_neg_osi_rec_df', - 'dgc_neg_dsi_rec_df', - 'dgc_neg_gosi_rec_df', - 'dgc_neg_gdsi_rec_df', - 'dgc_neg_peak_dire_raw_df', - 'dgc_neg_vs_dire_raw_df', - 'dgc_neg_vs_dire_ele_df', - 'dgc_neg_vs_dire_rec_df', - - # direction / orientation tuning, pos, dff - 'dgc_pos_osi_raw_dff', - 'dgc_pos_dsi_raw_dff', - 'dgc_pos_gosi_raw_dff', - 'dgc_pos_gdsi_raw_dff', - 'dgc_pos_osi_ele_dff', - 'dgc_pos_dsi_ele_dff', - 'dgc_pos_gosi_ele_dff', - 'dgc_pos_gdsi_ele_dff', - 'dgc_pos_osi_rec_dff', - 'dgc_pos_dsi_rec_dff', - 'dgc_pos_gosi_rec_dff', - 'dgc_pos_gdsi_rec_dff', - 'dgc_pos_peak_dire_raw_dff', - 'dgc_pos_vs_dire_raw_dff', - 'dgc_pos_vs_dire_ele_dff', - 'dgc_pos_vs_dire_rec_dff', - - # direction / orientation tuning, neg, dff - 'dgc_neg_osi_raw_dff', - 'dgc_neg_dsi_raw_dff', - 'dgc_neg_gosi_raw_dff', - 'dgc_neg_gdsi_raw_dff', - 'dgc_neg_osi_ele_dff', - 'dgc_neg_dsi_ele_dff', - 'dgc_neg_gosi_ele_dff', - 'dgc_neg_gdsi_ele_dff', - 'dgc_neg_osi_rec_dff', - 'dgc_neg_dsi_rec_dff', - 'dgc_neg_gosi_rec_dff', - 'dgc_neg_gdsi_rec_dff', - 'dgc_neg_peak_dire_raw_dff', - 'dgc_neg_vs_dire_raw_dff', - 'dgc_neg_vs_dire_ele_dff', - 'dgc_neg_vs_dire_rec_dff', - - # direction / orientation tuning, pos, zscore - 'dgc_pos_osi_raw_z', - 'dgc_pos_dsi_raw_z', - 'dgc_pos_gosi_raw_z', - 'dgc_pos_gdsi_raw_z', - 'dgc_pos_osi_ele_z', - 'dgc_pos_dsi_ele_z', - 'dgc_pos_gosi_ele_z', - 'dgc_pos_gdsi_ele_z', - 'dgc_pos_osi_rec_z', - 'dgc_pos_dsi_rec_z', - 'dgc_pos_gosi_rec_z', - 'dgc_pos_gdsi_rec_z', - 'dgc_pos_peak_dire_raw_z', - 'dgc_pos_vs_dire_raw_z', - 'dgc_pos_vs_dire_ele_z', - 'dgc_pos_vs_dire_rec_z', - - # direction / orientation tuning, neg, zscore - 'dgc_neg_osi_raw_z', - 'dgc_neg_dsi_raw_z', - 'dgc_neg_gosi_raw_z', - 'dgc_neg_gdsi_raw_z', - 'dgc_neg_osi_ele_z', - 'dgc_neg_dsi_ele_z', - 'dgc_neg_gosi_ele_z', - 'dgc_neg_gdsi_ele_z', - 'dgc_neg_osi_rec_z', - 'dgc_neg_dsi_rec_z', - 'dgc_neg_gosi_rec_z', - 'dgc_neg_gdsi_rec_z', - 'dgc_neg_peak_dire_raw_z', - 'dgc_neg_vs_dire_raw_z', - 'dgc_neg_vs_dire_ele_z', - 'dgc_neg_vs_dire_rec_z', - - # sf tuning, pos, df - 'dgc_pos_peak_sf_raw_df', - 'dgc_pos_weighted_sf_raw_df', - 'dgc_pos_weighted_sf_log_raw_df', - 'dgc_pos_weighted_sf_ele_df', - 'dgc_pos_weighted_sf_log_ele_df', - 'dgc_pos_weighted_sf_rec_df', - 'dgc_pos_weighted_sf_log_rec_df', - - # sf tuning, neg, df - 'dgc_neg_peak_sf_raw_df', - 'dgc_neg_weighted_sf_raw_df', - 'dgc_neg_weighted_sf_log_raw_df', - 'dgc_neg_weighted_sf_ele_df', - 'dgc_neg_weighted_sf_log_ele_df', - 'dgc_neg_weighted_sf_rec_df', - 'dgc_neg_weighted_sf_log_rec_df', - - # sf tuning, pos, dff - 'dgc_pos_peak_sf_raw_dff', - 'dgc_pos_weighted_sf_raw_dff', - 'dgc_pos_weighted_sf_log_raw_dff', - 'dgc_pos_weighted_sf_ele_dff', - 'dgc_pos_weighted_sf_log_ele_dff', - 'dgc_pos_weighted_sf_rec_dff', - 'dgc_pos_weighted_sf_log_rec_dff', - - # sf tuning, neg, dff - 'dgc_neg_peak_sf_raw_dff', - 'dgc_neg_weighted_sf_raw_dff', - 'dgc_neg_weighted_sf_log_raw_dff', - 'dgc_neg_weighted_sf_ele_dff', - 'dgc_neg_weighted_sf_log_ele_dff', - 'dgc_neg_weighted_sf_rec_dff', - 'dgc_neg_weighted_sf_log_rec_dff', - - # sf tuning, pos, zscore - 'dgc_pos_peak_sf_raw_z', - 'dgc_pos_weighted_sf_raw_z', - 'dgc_pos_weighted_sf_log_raw_z', - 'dgc_pos_weighted_sf_ele_z', - 'dgc_pos_weighted_sf_log_ele_z', - 'dgc_pos_weighted_sf_rec_z', - 'dgc_pos_weighted_sf_log_rec_z', - - # sf tuning, neg, zscore - 'dgc_neg_peak_sf_raw_z', - 'dgc_neg_weighted_sf_raw_z', - 'dgc_neg_weighted_sf_log_raw_z', - 'dgc_neg_weighted_sf_ele_z', - 'dgc_neg_weighted_sf_log_ele_z', - 'dgc_neg_weighted_sf_rec_z', - 'dgc_neg_weighted_sf_log_rec_z', - - # tf tuning, pos, df - 'dgc_pos_peak_tf_raw_df', - 'dgc_pos_weighted_tf_raw_df', - 'dgc_pos_weighted_tf_log_raw_df', - 'dgc_pos_weighted_tf_ele_df', - 'dgc_pos_weighted_tf_log_ele_df', - 'dgc_pos_weighted_tf_rec_df', - 'dgc_pos_weighted_tf_log_rec_df', - - # tf tuning, neg, df - 'dgc_neg_peak_tf_raw_df', - 'dgc_neg_weighted_tf_raw_df', - 'dgc_neg_weighted_tf_log_raw_df', - 'dgc_neg_weighted_tf_ele_df', - 'dgc_neg_weighted_tf_log_ele_df', - 'dgc_neg_weighted_tf_rec_df', - 'dgc_neg_weighted_tf_log_rec_df', - - # tf tuning, pos, dff - 'dgc_pos_peak_tf_raw_dff', - 'dgc_pos_weighted_tf_raw_dff', - 'dgc_pos_weighted_tf_log_raw_dff', - 'dgc_pos_weighted_tf_ele_dff', - 'dgc_pos_weighted_tf_log_ele_dff', - 'dgc_pos_weighted_tf_rec_dff', - 'dgc_pos_weighted_tf_log_rec_dff', - - # tf tuning, neg, dff - 'dgc_neg_peak_tf_raw_dff', - 'dgc_neg_weighted_tf_raw_dff', - 'dgc_neg_weighted_tf_log_raw_dff', - 'dgc_neg_weighted_tf_ele_dff', - 'dgc_neg_weighted_tf_log_ele_dff', - 'dgc_neg_weighted_tf_rec_dff', - 'dgc_neg_weighted_tf_log_rec_dff', - - # tf tuning, pos, zscore - 'dgc_pos_peak_tf_raw_z', - 'dgc_pos_weighted_tf_raw_z', - 'dgc_pos_weighted_tf_log_raw_z', - 'dgc_pos_weighted_tf_ele_z', - 'dgc_pos_weighted_tf_log_ele_z', - 'dgc_pos_weighted_tf_rec_z', - 'dgc_pos_weighted_tf_log_rec_z', - - # tf tuning, neg, zscore - 'dgc_neg_peak_tf_raw_z', - 'dgc_neg_weighted_tf_raw_z', - 'dgc_neg_weighted_tf_log_raw_z', - 'dgc_neg_weighted_tf_ele_z', - 'dgc_neg_weighted_tf_log_ele_z', - 'dgc_neg_weighted_tf_rec_z', - 'dgc_neg_weighted_tf_log_rec_z', -] - -def process_one_nwb_for_multi_thread(inputs): - - nwb_path, df_folder, clu_folder, params, columns, save_folder, t0, nwb_i, nwb_f_num, is_overwrite = inputs - - nwb_fn = os.path.splitext(os.path.split(nwb_path)[1])[0] - - date, mid, _, _ = nwb_fn.split('_') - - nwb_f = h5py.File(nwb_path, 'r') - plane_ns = dt.get_plane_ns(nwb_f=nwb_f) - plane_ns.sort() - - for plane_n in plane_ns: - print('\tt: {:5.0f} minutes, processing {}, {} / {}, {} ...'.format((time.time() - t0) / 60., - nwb_fn, - nwb_i + 1, - nwb_f_num, - plane_n)) - - roi_df_fn = '{}_{}_{}.csv'.format(date, mid, plane_n) - roi_df = pd.read_csv(os.path.join(df_folder, roi_df_fn)) - - clu_fn = '{}_{}_{}_axon_grouping.hdf5'.format(date, mid, plane_n) - clu_f = h5py.File(os.path.join(clu_folder, clu_fn), 'r') - - axon_ns = clu_f['axons'].keys() - axon_ns.sort() - - axon_df = pd.DataFrame(np.nan, index=range(len(axon_ns)), columns=columns) - - for axon_i, axon_n in enumerate(axon_ns): - - roi_lst = clu_f['axons/{}'.format(axon_n)].value - - if len(roi_lst) == 1: - curr_roi_df = roi_df[roi_df['roi_n'] == roi_lst[0]].reset_index() - for col in columns: - axon_df.loc[axon_i, col] = curr_roi_df.loc[0, col] - axon_df.loc[axon_i, 'roi_n'] = axon_n - else: - axon_properties, _, _, _, _, _, _, _, _, _, _, _, _, _ = \ - dt.get_everything_from_axon(nwb_f=nwb_f, - clu_f=clu_f, - plane_n=plane_n, - axon_n=axon_n, - params=params, - verbose=False) - for rp_name, rp_value in axon_properties.items(): - axon_df.loc[axon_i, rp_name] = rp_value - - save_path = os.path.join(save_folder, '{}_{}_{}.csv'.format(date, mid, plane_n)) - - if os.path.isfile(save_path): - if is_overwrite: - os.remove(save_path) - axon_df.to_csv(save_path) - else: - raise IOError('Axon dataframe file already exists. \npath: {}'.format(save_path)) - else: - axon_df.to_csv(save_path) - -def run(): - - t0 = time.time() - - with open(os.path.join(df_folder, 'script_log.py')) as script_f: - script = script_f.readlines() - - for line in script: - if line[0:6] == 'params': - exec(line) - - nwb_fns = [] - for fn in os.listdir(os.path.realpath(nwb_folder)): - if fn[-4:] == '.nwb' and date_range[0] <= int(fn[0:6]) <= date_range[1]: - nwb_fns.append(fn) - nwb_fns.sort() - print('\nnwb file list:') - print('\n'.join(nwb_fns)) - - save_folder = df_folder + '_axon_' + os.path.split(clu_folder)[1] - - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - shutil.copyfile(os.path.realpath(__file__), os.path.join(save_folder, 'script_log.py')) - - inputs_lst = [(os.path.join(curr_folder, nwb_folder, nwb_fn), - os.path.realpath(df_folder), - os.path.realpath(clu_folder), - params, - columns, - save_folder, - t0, - nwb_i, - len(nwb_fns), - is_overwrite) for nwb_i, nwb_fn in enumerate(nwb_fns)] - - print('\nprocessing individual nwb files ...') - p = Pool(process_num) - p.map(process_one_nwb_for_multi_thread, inputs_lst) - - -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/old/0130_get_overall_csv.py b/corticalmapping/scripts/post_recording/analysis_database/old/0130_get_overall_csv.py deleted file mode 100644 index 5c70614..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/old/0130_get_overall_csv.py +++ /dev/null @@ -1,54 +0,0 @@ -import os -import pandas as pd - -df_folder = 'other_dataframes' -# df_fn = 'dataframes_190530171338' -# df_fn = 'dataframes_190530171338_axon_AllStimuli_DistanceThr_0.50' -# df_fn = 'dataframes_190530171338_axon_AllStimuli_DistanceThr_1.00' -df_fn = 'dataframes_190530171338_axon_AllStimuli_DistanceThr_1.30' -plane_df_fn = 'plane_table_190530170648.xlsx' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -csv_fns = [fn for fn in os.listdir(os.path.join(df_folder, df_fn)) if fn[-4:] == '.csv'] -csv_fns.sort() - -plane_df = pd.read_excel(os.path.join(df_folder, plane_df_fn), sheetname='sheet1') - -df_all = [] - -for csv_fn in csv_fns: - print('reading {} ...'.format(csv_fn)) - df_all.append(pd.read_csv(os.path.join(df_folder, df_fn, csv_fn))) - -df_all = pd.concat(df_all, axis=0) - -try: - df_all.drop(['Unnamed: 0', 'Unnamed: 0.1'], axis=1, inplace=True) -except KeyError: - pass - -print(df_all.columns) - -df_all['vol_n'] = '' - -for plane_i, plane_row in plane_df.iterrows(): - plane_ind = ((df_all['date'] == plane_row['date']) & - (df_all['mouse_id'] == plane_row['mouse_id']) & - (df_all['plane_n'] == plane_row['plane_n'])) - df_all.loc[plane_ind, 'vol_n'] = plane_row['volume_n'] - -print(df_all.vol_n.drop_duplicates()) - -df_all.sort_values(by=['vol_n', 'depth', 'roi_n'], inplace=True) -df_all.reset_index(inplace=True) -df_all.drop(['index'], axis=1, inplace=True) - -print(df_all[['date', 'mouse_id', 'plane_n', 'depth', 'vol_n']].drop_duplicates()) - -df_all.to_csv(df_fn.replace('dataframes', 'dataframe') + '.csv') - - - - diff --git a/corticalmapping/scripts/post_recording/analysis_database/old/0140_plot_dire_retinotopy.py b/corticalmapping/scripts/post_recording/analysis_database/old/0140_plot_dire_retinotopy.py deleted file mode 100644 index 54e5a6d..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/old/0140_plot_dire_retinotopy.py +++ /dev/null @@ -1,107 +0,0 @@ -import os -import numpy as np -import pandas as pd -import matplotlib.pyplot as plt -import corticalmapping.core.PlottingTools as pt -import corticalmapping.DatabaseTools as dt -from matplotlib.backends.backend_pdf import PdfPages - -# df_path = r"G:\bulk_LGN_database\dataframe_190530171338.csv" -df_path = r"G:\bulk_LGN_database\dataframe_190530171338_axon_AllStimuli_DistanceThr_1.00.csv" - -depths = [50, 100, 150, 200, 250, 300, 350, 400,] -mouse_ids = ['M360495', 'M376019', 'M386444', 'M426525', 'M439939', 'M439943'] -# mouse_ids = ['M439939'] -dire_type = 'peak_dire' # 'vs_dire' or 'peak_dire' -response_dir = 'pos' -response_type = 'dff' -post_process_type = 'ele' # 'raw', 'ele' or 'rec' -skew_thr = 0.6 -dgc_peak_z_thr = 3. -dgc_p_anova_thr = 0.01 -dsi_type = 'gdsi' -dsi_thr = 0.5 - -rf_z_thr = 1.6 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if dire_type == 'peak_dire' and (post_process_type == 'ele' or post_process_type == 'rec'): - dire_pp = 'raw' -else: - dire_pp = post_process_type - -print('loading csv file: {}'.format(df_path)) -df = pd.read_csv(df_path) -print('csv file loaded.') - -df = df[df['mouse_id'].isin(mouse_ids)] - -df = df[(df['skew_fil'] >= skew_thr) & - (df['dgc_{}_peak_z'.format(response_dir)] >= dgc_peak_z_thr) & - (df['dgc_p_anova_{}'.format(response_type)] <= dgc_p_anova_thr) & - (df['dgc_{}_{}_{}_{}'.format(response_dir, dsi_type, post_process_type, response_type)] >= dsi_thr)] - -pdff = PdfPages(os.path.join('intermediate_results', 'preferred_dire_depth.pdf')) - -f_all = plt.figure(figsize=(12, 8)) -ax_all = f_all.add_subplot(111) -ax_all.set_xlim([0, 90]) -ax_all.set_ylim([-30, 30]) -ax_all.set_aspect('equal') -ax_all.set_title('all depths') - -for depth_i, depth in enumerate(depths): - - depth_df = df[df['depth'] == depth] - print(len(depth_df)) - - f = plt.figure(figsize=(12, 8)) - ax = f.add_subplot(111) - ax.set_xlim([0, 90]) - ax.set_ylim([-30, 30]) - ax.set_aspect('equal') - ax.set_title('{} um'.format(depth)) - - for roi_i, roi_row in depth_df.iterrows(): - - if roi_row['rf_{}_on_peak_z'.format(response_dir)] >= rf_z_thr: - alt = roi_row['rf_{}_on_center_alt'.format(response_dir)] - azi = roi_row['rf_{}_on_center_azi'.format(response_dir)] - dire = roi_row['dgc_{}_{}_{}_{}'.format(response_dir, dire_type, dire_pp, response_type)] - # print('alt: {:6.2f}, azi: {:6.2f}, dire: {}'.format(alt, azi, dire)) - dire = dire * np.pi / 180. - bazi = azi - np.cos(dire) * 1. - dazi = np.cos(dire) * 2. - balt = alt - np.sin(dire) * 1. - dalt = np.sin(dire) * 2. - - ax.arrow(x=bazi, y=balt, dx=dazi, dy=dalt, length_includes_head=True, - head_width=0.5, head_length=1, ec='none', fc='r', alpha=0.5) - ax_all.arrow(x=bazi, y=balt, dx=dazi, dy=dalt, length_includes_head=True, - head_width=0.5, head_length=1, ec='none', fc='r', alpha=0.5) - - if roi_row['rf_{}_off_peak_z'.format(response_dir)] >= rf_z_thr: - alt = roi_row['rf_{}_off_center_alt'.format(response_dir)] - azi = roi_row['rf_{}_off_center_azi'.format(response_dir)] - dire = roi_row['dgc_{}_{}_{}_{}'.format(response_dir, dire_type, dire_pp, response_type)] - # print('alt: {:6.2f}, azi: {:6.2f}, dire: {}'.format(alt, azi, dire)) - dire = dire * np.pi / 180. - bazi = azi - np.sin(dire) * 1. - dazi = np.sin(dire) * 2. - balt = alt - np.cos(dire) * 1. - dalt = np.cos(dire) * 2. - - ax.arrow(x=bazi, y=balt, dx=dazi, dy=dalt, length_includes_head=True, - head_width=0.5, head_length=1, ec='none', fc='b', alpha=0.5) - ax_all.arrow(x=bazi, y=balt, dx=dazi, dy=dalt, length_includes_head=True, - head_width=0.5, head_length=1, ec='none', fc='b', alpha=0.5) - - - pdff.savefig(f) - f.clear() - plt.close(f) - -pdff.savefig(f_all) -pdff.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/old/0150_plot_ori_vs_rf_axis_DS.py b/corticalmapping/scripts/post_recording/analysis_database/old/0150_plot_ori_vs_rf_axis_DS.py deleted file mode 100644 index 8f04553..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/old/0150_plot_ori_vs_rf_axis_DS.py +++ /dev/null @@ -1,139 +0,0 @@ -import os -import pandas as pd -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.SingleCellAnalysis as sca -import scipy.stats as stats -import h5py - -# df_path = r"G:\bulk_LGN_database\dataframe_190530171338.csv" -# rf_maps_folder = r"intermediate_results\rf_maps_dataframes_190529210731" - -df_path = r"G:\bulk_LGN_database\dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30.csv" -rf_maps_folder = r"G:\bulk_LGN_database\intermediate_results" \ - r"\rf_maps_dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30" - - -depths = [50, 100, 150, 200, 250, 300, 350, 400,] -mouse_ids = ['M360495', 'M376019', 'M386444', 'M426525', 'M439939', 'M439943'] -# mouse_ids = ['M439939'] -dire_type = 'peak_dire' # 'vs_dire' or 'peak_dire' -response_dir = 'pos' -response_type = 'dff' -post_process_type = 'ele' # 'raw', 'ele' or 'rec' -skew_thr = 0.6 -dgc_peak_z_thr = 3. -dgc_p_anova_thr = 0.01 -dsi_type = 'gdsi' -dsi_thr = 0.5 -osi_type = 'gosi' -osi_thr = 1. / 3. - -ellipse_aspect_thr = 1.0 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if dire_type == 'peak_dire' and (post_process_type == 'ele' or post_process_type == 'rec'): - dire_pp = 'raw' -else: - dire_pp = post_process_type - -print('loading csv file: {}'.format(df_path)) -df = pd.read_csv(df_path) -print('csv file loaded.') - -df = df[(df['mouse_id'].isin(mouse_ids)) & \ - (df['skew_fil'] >= skew_thr) & \ - (df['dgc_{}_peak_z'.format(response_dir)] >= dgc_peak_z_thr) & \ - (df['dgc_p_anova_{}'.format(response_type)] <= dgc_p_anova_thr) & \ - (np.isfinite(df['rf_{}_on_peak_z'.format(response_dir)]))] - -dsdf = df[(df['dgc_{}_{}_{}_{}'.format(response_dir, dsi_type, post_process_type, response_type)] >= dsi_thr)] - -ds_diff_onoff = [] -ds_diff_on = [] -ds_diff_off = [] -for roi_i, roi_row in dsdf.iterrows(): - date = int(roi_row['date']) - mid = roi_row['mouse_id'] - plane_n = roi_row['plane_n'] - roi_n = roi_row['roi_n'] - - map_fn = '{}_{}_{}_{}'.format(date, mid, plane_n, response_dir) - map_f = h5py.File(os.path.join(rf_maps_folder, map_fn + '.hdf5'), 'r') - - on_grp = map_f['{}_ON'.format(map_fn)] - off_grp = map_f['{}_OFF'.format(map_fn)] - - dire = roi_row['dgc_{}_{}_{}_{}'.format(response_dir, dire_type, dire_pp, response_type)] - ori = sca.dire2ori(dire) - - if roi_n in on_grp.keys() and roi_n in off_grp.keys(): - rf_on = sca.SpatialReceptiveField.from_h5_group(on_grp[roi_n]) - rf_off = sca.SpatialReceptiveField.from_h5_group(off_grp[roi_n]) - c_alt_on, c_azi_on = rf_on.get_weighted_rf_center() - c_alt_off, c_azi_off = rf_off.get_weighted_rf_center() - - onoff_ang = np.arctan((c_alt_on - c_alt_off) / (c_azi_on - c_azi_off)) - onoff_ang = onoff_ang * 180. / np.pi - onoff_ang = sca.dire2ori(onoff_ang) - - curr_diff = abs(onoff_ang - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - - ds_diff_onoff.append(curr_diff) - - elif roi_n in on_grp.keys(): - rf_on = sca.SpatialReceptiveField.from_h5_group(on_grp[roi_n]) - ell_on = rf_on.ellipse_fitting(is_plot=False) - if ell_on is not None and ell_on.get_aspect_ratio() >= ellipse_aspect_thr: - curr_diff = abs(ell_on.angle - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - ds_diff_on.append(curr_diff) - - elif roi_n in off_grp.keys(): - rf_off = sca.SpatialReceptiveField.from_h5_group(off_grp[roi_n]) - ell_off = rf_off.ellipse_fitting(is_plot=False) - if ell_off is not None and ell_off.get_aspect_ratio() >= ellipse_aspect_thr: - curr_diff = abs(ell_off.angle - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - ds_diff_off.append(curr_diff) - -print('\nDirection Selective ROIs:') -print('\tWith ONOFF receptive fields:') -print('\t\tn={}'.format(len(ds_diff_onoff))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(ds_diff_onoff))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(ds_diff_onoff))) -chisq_ds_onoff, p_ds_onoff = stats.chisquare(np.histogram(ds_diff_onoff, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_ds_onoff, p_ds_onoff)) - -print('\tWith only ON receptive fields:') -print('\t\tn={}'.format(len(ds_diff_on))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(ds_diff_on))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(ds_diff_on))) -chisq_ds_on, p_ds_on = stats.chisquare(np.histogram(ds_diff_on, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_ds_on, p_ds_on)) - -print('\tWith only OFF receptive fields:') -print('\t\tn={}'.format(len(ds_diff_off))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(ds_diff_off))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(ds_diff_off))) -chisq_ds_off, p_ds_off = stats.chisquare(np.histogram(ds_diff_off, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_ds_off, p_ds_off)) - -ds_diff_all = ds_diff_onoff + ds_diff_on + ds_diff_off -print('\tWith all receptive fields:') -print('\t\tn={}'.format(len(ds_diff_all))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(ds_diff_all))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(ds_diff_all))) -chisq_ds_all, p_ds_all = stats.chisquare(np.histogram(ds_diff_all, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_ds_all, p_ds_all)) - - -plt.hist([ds_diff_onoff, ds_diff_on, ds_diff_off], range=[0, 90], bins=20, stacked=True, - color=['purple', 'r', 'b'], ec='none', alpha=0.5) -plt.show() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_database/old/0160_plot_ori_vs_rf_axis_OS.py b/corticalmapping/scripts/post_recording/analysis_database/old/0160_plot_ori_vs_rf_axis_OS.py deleted file mode 100644 index 10cc5da..0000000 --- a/corticalmapping/scripts/post_recording/analysis_database/old/0160_plot_ori_vs_rf_axis_OS.py +++ /dev/null @@ -1,139 +0,0 @@ -import os -import pandas as pd -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.SingleCellAnalysis as sca -import scipy.stats as stats -import h5py - -# df_path = r"G:\bulk_LGN_database\dataframe_190530171338.csv" -# rf_maps_folder = r"intermediate_results\rf_maps_dataframes_190529210731" - -df_path = r"G:\bulk_LGN_database\dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30.csv" -rf_maps_folder = r"G:\bulk_LGN_database\intermediate_results" \ - r"\rf_maps_dataframe_190530171338_axon_AllStimuli_DistanceThr_1.30" - -depths = [50, 100, 150, 200, 250, 300, 350, 400,] -mouse_ids = ['M360495', 'M376019', 'M386444', 'M426525', 'M439939', 'M439943'] -# mouse_ids = ['M439939'] -dire_type = 'peak_dire' # 'vs_dire' or 'peak_dire' -response_dir = 'pos' -response_type = 'dff' -post_process_type = 'ele' # 'raw', 'ele' or 'rec' -skew_thr = 0.6 -dgc_peak_z_thr = 3. -dgc_p_anova_thr = 0.01 -dsi_type = 'gdsi' -dsi_thr = 0.5 -osi_type = 'gosi' -osi_thr = 1. / 3. - -ellipse_aspect_thr = 1.0 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if dire_type == 'peak_dire' and (post_process_type == 'ele' or post_process_type == 'rec'): - dire_pp = 'raw' -else: - dire_pp = post_process_type - -print('loading csv file: {}'.format(df_path)) -df = pd.read_csv(df_path) -print('csv file loaded.') - -df = df[(df['mouse_id'].isin(mouse_ids)) & \ - (df['skew_fil'] >= skew_thr) & \ - (df['dgc_{}_peak_z'.format(response_dir)] >= dgc_peak_z_thr) & \ - (df['dgc_p_anova_{}'.format(response_type)] <= dgc_p_anova_thr) & \ - (np.isfinite(df['rf_{}_on_peak_z'.format(response_dir)]))] - -osdf = df[(df['dgc_{}_{}_{}_{}'.format(response_dir, osi_type, post_process_type, response_type)] >= osi_thr) & \ - (df['dgc_{}_{}_{}_{}'.format(response_dir, dsi_type, post_process_type, response_type)] < dsi_thr)] - -os_diff_onoff = [] -os_diff_on = [] -os_diff_off = [] -for roi_i, roi_row in osdf.iterrows(): - date = int(roi_row['date']) - mid = roi_row['mouse_id'] - plane_n = roi_row['plane_n'] - roi_n = roi_row['roi_n'] - - map_fn = '{}_{}_{}_{}'.format(date, mid, plane_n, response_dir) - map_f = h5py.File(os.path.join(rf_maps_folder, map_fn + '.hdf5'), 'r') - - on_grp = map_f['{}_ON'.format(map_fn)] - off_grp = map_f['{}_OFF'.format(map_fn)] - - dire = roi_row['dgc_{}_{}_{}_{}'.format(response_dir, dire_type, dire_pp, response_type)] - ori = sca.dire2ori(dire) - - if roi_n in on_grp.keys() and roi_n in off_grp.keys(): - rf_on = sca.SpatialReceptiveField.from_h5_group(on_grp[roi_n]) - rf_off = sca.SpatialReceptiveField.from_h5_group(off_grp[roi_n]) - c_alt_on, c_azi_on = rf_on.get_weighted_rf_center() - c_alt_off, c_azi_off = rf_off.get_weighted_rf_center() - - onoff_ang = np.arctan((c_alt_on - c_alt_off) / (c_azi_on - c_azi_off)) - onoff_ang = onoff_ang * 180. / np.pi - onoff_ang = sca.dire2ori(onoff_ang) - - curr_diff = abs(onoff_ang - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - - os_diff_onoff.append(curr_diff) - - elif roi_n in on_grp.keys(): - rf_on = sca.SpatialReceptiveField.from_h5_group(on_grp[roi_n]) - ell_on = rf_on.ellipse_fitting(is_plot=False) - if ell_on is not None and ell_on.get_aspect_ratio() >= ellipse_aspect_thr: - curr_diff = abs(ell_on.angle - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - os_diff_on.append(curr_diff) - - elif roi_n in off_grp.keys(): - rf_off = sca.SpatialReceptiveField.from_h5_group(off_grp[roi_n]) - ell_off = rf_off.ellipse_fitting(is_plot=False) - if ell_off is not None and ell_off.get_aspect_ratio() >= ellipse_aspect_thr: - curr_diff = abs(ell_off.angle - ori) - if curr_diff > 90.: - curr_diff = 180 - curr_diff - os_diff_off.append(curr_diff) - -print('\nOrientation Selective ROIs:') -print('\tWith ONOFF receptive fields:') -print('\t\tn={}'.format(len(os_diff_onoff))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(os_diff_onoff))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(os_diff_onoff))) -chisq_os_onoff, p_os_onoff = stats.chisquare(np.histogram(os_diff_onoff, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_os_onoff, p_os_onoff)) - -print('\tWith only ON receptive fields:') -print('\t\tn={}'.format(len(os_diff_on))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(os_diff_on))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(os_diff_on))) -chisq_os_on, p_os_on = stats.chisquare(np.histogram(os_diff_on, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_os_on, p_os_on)) - -print('\tWith only OFF receptive fields:') -print('\t\tn={}'.format(len(os_diff_off))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(os_diff_off))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(os_diff_off))) -chisq_os_off, p_os_off = stats.chisquare(np.histogram(os_diff_off, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_os_off, p_os_off)) - -os_diff_all = os_diff_onoff + os_diff_on + os_diff_off -print('\tWith all receptive fields:') -print('\t\tn={}'.format(len(os_diff_all))) -print('\t\torie difference predicted vs. measured, mean={}'.format(np.mean(os_diff_all))) -print('\t\torie difference predicted vs. measured, std={}'.format(np.std(os_diff_all))) -chisq_os_all, p_os_all = stats.chisquare(np.histogram(os_diff_all, range=[0., 90.], bins=20)[0]) -print('\t\tagainst uniform distribution: chi-squared={}, p={}'.format(chisq_os_all, p_os_all)) - - -plt.hist([os_diff_onoff, os_diff_on, os_diff_off], range=[0, 90], bins=20, stacked=True, - color=['purple', 'r', 'b'], ec='none', alpha=0.5) -plt.show() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/010_get_vasmap_2p.py b/corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/010_get_vasmap_2p.py deleted file mode 100644 index 9267e9c..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/010_get_vasmap_2p.py +++ /dev/null @@ -1,67 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import matplotlib.pyplot as plt -import cv2 - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\181213-M421211-2p" -scope = 'sutter' # 'sutter', 'deepscope' or 'scientifica' -identifier = "vasmap_2p" -channels = ['green', 'red'] -is_equalize = False # equalize histogram - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -vasmaps = {} -for chn in channels: - vasmaps.update({chn: []}) - -file_ns = [f for f in os.listdir(data_folder) if identifier in f] -file_ns.sort() -print('\n'.join(file_ns)) - -for file_n in file_ns: - print(file_n) - - curr_vasmap = tf.imread(os.path.join(data_folder, file_n)) - - if len(curr_vasmap.shape) == 2: - if len(channels) == 1: - vasmaps[channels[0]].append(np.array([curr_vasmap])) - else: - raise ValueError('recorded file is 2d, cannot be deinterleved into {} channels.'.format(len(channels))) - else: - if len(curr_vasmap.shape) != 3: - raise ValueError('shape of recorded file: {}. should be either 2d or 3d.'.format(curr_vasmap.shape)) - - for ch_i, ch_n in enumerate(channels): - curr_vasmap_ch = curr_vasmap[ch_i::len(channels)] - curr_vasmap_ch = ia.array_nor(np.mean(curr_vasmap_ch, axis=0)) - if is_equalize: - curr_vasmap_ch = (curr_vasmap_ch * 255).astype(np.uint8) - curr_vasmap_ch = cv2.equalizeHist(curr_vasmap_ch).astype(np.float32) - vasmaps[ch_n].append(curr_vasmap_ch) - -for ch_n, ch_vasmap in vasmaps.items(): - # save_vasmap = np.concatenate(ch_vasmap, axis=0) - # print(save_vasmap.shape) - # save_vasmap = ia.array_nor(np.mean(save_vasmap, axis=0)) - # print(save_vasmap.shape) - - save_vasmap = ia.array_nor(np.mean(ch_vasmap, axis=0)) - - if scope == 'scientifica': - save_vasmap_r = save_vasmap[::-1, :] - save_vasmap_r = ia.rigid_transform_cv2_2d(save_vasmap_r, rotation=135) - elif scope == 'sutter': - save_vasmap_r = save_vasmap.transpose()[::-1, :] - elif scope == 'deepscope': - save_vasmap_r = ia.rigid_transform_cv2(save_vasmap, rotation=140)[:, ::-1] - else: - raise LookupError("Do not understand scope type. Should be 'sutter' or 'deepscope' or 'scientifica'.") - - tf.imsave('{}_{}.tif'.format(identifier, ch_n), save_vasmap.astype(np.float32)) - tf.imsave('{}_{}_rotated.tif'.format(identifier, ch_n), save_vasmap_r.astype(np.float32)) diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/020_get_vasmap_wf_deepscope.py b/corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/020_get_vasmap_wf_deepscope.py deleted file mode 100644 index 97cf956..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/020_get_vasmap_wf_deepscope.py +++ /dev/null @@ -1,40 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import skimage.io as io -import skimage.color as color -import matplotlib.pyplot as plt -import corticalmapping.core.ImageAnalysis as ia - -vasmap_wf_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\181102-M412052-deepscope\vasmap_wf" - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -map_fns = [f for f in os.listdir(vasmap_wf_folder) if f[-4:]=='.tif'] -map_fns.sort() -print('\n'.join(map_fns)) - -map_wf = [] -for map_fn in map_fns: - curr_map = tf.imread(os.path.join(vasmap_wf_folder, map_fn)).astype(np.float32) - map_wf.append(ia.array_nor(curr_map)) - -map_wf = ia.array_nor(np.mean(map_wf, axis=0)) -map_wf_r = ia.array_nor(ia.rigid_transform_cv2(map_wf, rotation=140)[:, ::-1]) - -f = plt.figure(figsize=(12, 6)) -ax_wf = f.add_subplot(121) -ax_wf.imshow(map_wf, vmin=0., vmax=1., cmap='gray', interpolation='nearest') -ax_wf.set_axis_off() -ax_wf.set_title('vasmap widefield') - -ax_wf_r = f.add_subplot(122) -ax_wf_r.imshow(map_wf_r, vmin=0., vmax=1., cmap='gray', interpolation='nearest') -ax_wf_r.set_axis_off() -ax_wf_r.set_title('vasmap widefield rotated') - -plt.show() - -tf.imsave('vasmap_wf.tif', map_wf.astype(np.float32)) -tf.imsave('vasmap_wf_rotated.tif', map_wf_r.astype(np.float32)) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/030_get_vasmap_wf_sutter.py b/corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/030_get_vasmap_wf_sutter.py deleted file mode 100644 index a6fd568..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_get_vasmap/030_get_vasmap_wf_sutter.py +++ /dev/null @@ -1,32 +0,0 @@ -import os -import numpy as np -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -import tifffile as tf - -save_name = 'vasmap_wf' -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\190228-M426525-2p\vasmap_wf" - -saveFolder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(saveFolder) - -vasmap_fns = [f for f in os.listdir(data_folder) if 'JCam' in f] -vasmap_fns.sort() -print('\n'.join(vasmap_fns)) - -vasmaps = [] - -for vasmap_fn in vasmap_fns: - - vasmap_focused, _, _ = ft.importRawJCamF(os.path.join(data_folder, vasmap_fn), column=1024, row=1024, - headerLength=116, tailerLength=452) # try 452 if 218 does not work - vasmap_focused = vasmap_focused[2:] - vasmap_focused[vasmap_focused > 50000] = 400 - vasmap_focused = np.mean(vasmap_focused, axis=0) - vasmaps.append(ia.array_nor(vasmap_focused)) - -vasmap = ia.array_nor(np.mean(vasmaps, axis=0)) -vasmap_r = vasmap[::-1, :] - -tf.imsave('{}.tif'.format(save_name), vasmap.astype(np.float32)) -tf.imsave('{}_rotated.tif'.format(save_name), vasmap_r.astype(np.float32)) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_histology_big_tiffs/01_get_thumbnail.py b/corticalmapping/scripts/post_recording/analysis_pipeline_histology_big_tiffs/01_get_thumbnail.py deleted file mode 100644 index a167681..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_histology_big_tiffs/01_get_thumbnail.py +++ /dev/null @@ -1,32 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia - -channels = ['DAPI', 'GCaMP', 'mRuby', 'NeuN'] -downsample_rate = 0.1 - -curr_folder = os.path.realpath(os.path.dirname(__file__)) -os.chdir(curr_folder) - -fns = [f for f in os.listdir(curr_folder) if f[-4:] == '.btf'] -fns.sort() -print('\n'.join(fns)) - -for fn in fns: - print('\nprocessing {} ...'.format(fn)) - big_img = tf.imread(fn) - fname = os.path.splitext(fn)[0] - print('shape: {}'.format(big_img.shape)) - print('dtype: {}'.format(big_img.dtype)) - - # comb_img = [] - - for chi, chn in enumerate(channels): - print('\tchannel: {}'.format(chn)) - down_img_ch = ia.rigid_transform_cv2(big_img[chi], zoom=downsample_rate).astype(np.uint16)[::-1, :] - tf.imsave('thumbnail_{}_{:02d}_{}.tif'.format(fname, chi, chn), down_img_ch) - # comb_img.append(down_img_ch) - - # comb_img = np.array(comb_img) - # tf.imsave('{}_downsampled.tif'.format(fname), comb_img) diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_histology_big_tiffs/02_get_section.py b/corticalmapping/scripts/post_recording/analysis_pipeline_histology_big_tiffs/02_get_section.py deleted file mode 100644 index 5b499a7..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_histology_big_tiffs/02_get_section.py +++ /dev/null @@ -1,44 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia - -base_name = '363669_1_01' -save_name = '363669_1_section02.tif' -thumbnail_region = [100, 1125, 1673, 3176] - -channels = ['mRuby', 'GCaMP', 'DAPI', 'NeuN'] -d_rate = 0.5 - -thumbnail_d_rate = 0.1 # down sample rate of thumbnail - -curr_folder = os.path.realpath(os.path.dirname(__file__)) -os.chdir(curr_folder) - -big_region = (np.array(thumbnail_region) / thumbnail_d_rate).astype(np.uint64) -print('region in big image: {}'.format(big_region)) - -thumbnail_fns = [f for f in os.listdir(curr_folder) if base_name in f and f[-4:] == '.tif'] -ch_lst = [] -for chn in channels: - curr_chi = [int(f.split('_')[-2]) for f in thumbnail_fns if chn in f] - if len(curr_chi) != 1: - raise LookupError - ch_lst.append(curr_chi[0]) - -print('channel index list: {}'.format(ch_lst)) - -big_img = tf.imread(base_name + '.btf') -print('reading the big image: {}.btf ...'.format(base_name)) - -section_img = [] - -for ch_i in ch_lst: - curr_img = big_img[ch_i][::-1, :][big_region[0]: big_region[1], big_region[2]: big_region[3]] - curr_img = ia.rigid_transform_cv2(curr_img, zoom=d_rate).astype(np.uint16) - section_img.append(curr_img) - -section_img = np.array(section_img) - -print('saving {} ...'.format(save_name)) -tf.imsave(save_name, section_img) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_check_deepscope_file_creation_time.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_check_deepscope_file_creation_time.py deleted file mode 100644 index 87f5cbe..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_check_deepscope_file_creation_time.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\190822-M471944-deepscope\movie" -identifier = '110_LSNDGCUC' - -fns = np.array([f for f in os.listdir(data_folder) if f[-4:] == '.tif' and identifier in f]) -f_nums = [int(os.path.splitext(fn)[0].split('_')[-2]) for fn in fns] -fns = fns[np.argsort(f_nums)] -print('total file number: {}'.format(len(fns))) - -ctimes = [] - -for fn in fns: - ctimes.append(os.path.getctime(os.path.join(data_folder, fn))) - -ctime_diff = np.diff(ctimes) -max_ind = np.argmax(ctime_diff) -print('maximum creation gap: {}'.format(ctime_diff[max_ind])) - -fis = np.arange(21, dtype=np.int) - 10 + max_ind - -for fi in fis: - print('{}, ctime: {}s, duration: {}s'.format(fns[fi], ctimes[fi], ctime_diff[fi])) - -plt.plot(ctime_diff) -plt.show() diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_check_deepscope_filename.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_check_deepscope_filename.py deleted file mode 100644 index 4a47cf1..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_check_deepscope_filename.py +++ /dev/null @@ -1,25 +0,0 @@ -import os -import numpy as np - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\190822-M471944-deepscope\movie" -identifier = '110_LSNDGCUC' - -fns = np.array([f for f in os.listdir(data_folder) if f[-4:] == '.tif' and identifier in f]) -f_nums = [int(os.path.splitext(fn)[0].split('_')[-2]) for fn in fns] -fns = fns[np.argsort(f_nums)] -print('total file number: {}'.format(len(fns))) - -for i in range(1, len(fns) + 1): - - if i < 100000: - if fns[i-1] != '{}_{:05d}_00001.tif'.format(identifier, i): - print('{}th file, name: {}, do not match!'.format(i, fns[i])) - break - elif i < 1000000: - if fns[i - 1] != '{}_{:06d}_00001.tif'.format(identifier, i): - print('{}th file, name: {}, do not match!'.format(i, fns[i])) - break - elif i < 10000000: - if fns[i - 1] != '{}_{:07d}_00001.tif'.format(identifier, i): - print('{}th file, name: {}, do not match!'.format(i, fns[i])) - break diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_plot_volume.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_plot_volume.py deleted file mode 100644 index 5324453..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_(optional)_plot_volume.py +++ /dev/null @@ -1,32 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.core.ImageAnalysis as ia - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\190822-M471944-deepscope\movie" -identifier = '110_LSNDGCUC' -start_ind = 121228 -frame_num = 3 - -fns = [] - -for ind in np.arange(frame_num, dtype=np.int) + start_ind: - - if ind < 100000: - fns.append('{}_{:05d}_00001.tif'.format(identifier, ind)) - elif ind < 1000000: - fns.append('{}_{:06d}_00001.tif'.format(identifier, ind)) - elif ind < 10000000: - fns.append('{}_{:07d}_00001.tif'.format(identifier, ind)) - -f = plt.figure(figsize=(5, 12)) -for frame_i in range(frame_num): - ax = f.add_subplot(frame_num, 1, frame_i+1) - ax.imshow(ia.array_nor(tf.imread(os.path.join(data_folder, fns[frame_i]))), cmap='gray', - vmin=0, vmax=0.5, interpolation='nearest') - ax.set_title(fns[frame_i]) - ax.set_axis_off() - -plt.tight_layout() -plt.show() diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_reorganize_movie_2p_deepscope.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_reorganize_movie_2p_deepscope.py deleted file mode 100644 index c0c1501..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_reorganize_movie_2p_deepscope.py +++ /dev/null @@ -1,97 +0,0 @@ -import os -import numpy as np -import tifffile as tf - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180928-M393857-deepscope\movie_mp" -identifier = '110_LSNDGC' -channels = ['green', 'red'] -plane_num = 3 -temporal_downsample_rate = 4 -frame_each_file = 500 -low_thr = -500 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -fns = np.array([f for f in os.listdir(data_folder) if f[-4:] == '.tif' and identifier in f]) -f_nums = [int(os.path.splitext(fn)[0].split('_')[-2]) for fn in fns] -fns = fns[np.argsort(f_nums)] -print('total file number: {}'.format(len(fns))) - -# print('\n'.join(fns)) - -save_folders = [] -for i in range(plane_num): - curr_save_folder = os.path.join(data_folder, identifier + '_reorged', 'plane{}'.format(i)) - if not os.path.isdir(curr_save_folder): - os.makedirs(curr_save_folder) - save_folders.append(curr_save_folder) - -# frame_per_plane = len(fns) // plane_num -for plane_ind in range(plane_num): - print('\nprocessing plane: {}'.format(plane_ind)) - curr_fns = fns[plane_ind::plane_num] - - total_frames_down = len(curr_fns) // temporal_downsample_rate - curr_fns = curr_fns[: total_frames_down * temporal_downsample_rate].reshape((total_frames_down, temporal_downsample_rate)) - - print(curr_fns.shape) - - print('current file ind: 000') - curr_file_ind = 0 - curr_frame_ind = 0 - curr_mov = {} - for ch_n in channels: - curr_mov.update({ch_n : []}) - - for fgs in curr_fns: - - frame_grp = [] - - for fn in fgs: - cf = tf.imread(os.path.join(data_folder, fn)) - # remove extreme negative pixels - cf[cf < low_thr] = low_thr - if len(cf.shape) == 2: - cf = np.array([cf]) - frame_grp.append(cf) - - curr_frame = {} - - for ch_i, ch_n in enumerate(channels): - ch_frame_grp = np.array([f[ch_i::len(channels)][0] for f in frame_grp]) - # print ch_frame_grp.shape - ch_frame = np.mean(ch_frame_grp, axis=0).astype(np.int16) - # ch_frame = ch_frame.transpose()[::-1, ::-1] - curr_frame.update({ch_n: ch_frame}) - - if curr_frame_ind < frame_each_file: - - for ch_n in channels: - curr_mov[ch_n].append(curr_frame[ch_n]) - - curr_frame_ind = curr_frame_ind + 1 - - else: - for ch_n in channels: - curr_mov_ch = np.array(curr_mov[ch_n], dtype=np.int16) - save_name = '{}_{:05d}_reorged.tif'.format(identifier, curr_file_ind) - save_folder_ch = os.path.join(save_folders[plane_ind], ch_n) - if not os.path.isdir(save_folder_ch): - os.makedirs(save_folder_ch) - tf.imsave(os.path.join(save_folder_ch, save_name), curr_mov_ch) - curr_mov[ch_n] = [curr_frame[ch_n]] - print('current file ind: {:05d}; channel: {}'.format(curr_file_ind, ch_n)) - curr_file_ind += 1 - curr_frame_ind = 1 - - for ch_n in channels: - curr_mov_ch = np.array(curr_mov[ch_n], dtype=np.int16) - save_name = '{}_{:05d}_reorged.tif'.format(identifier, curr_file_ind) - save_folder_ch = os.path.join(save_folders[plane_ind], ch_n) - if not os.path.isdir(save_folder_ch): - os.makedirs(save_folder_ch) - tf.imsave(os.path.join(save_folder_ch, save_name), curr_mov_ch) - print('current file ind: {:05d}; channel: {}'.format(curr_file_ind, ch_n)) - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_reorganize_movie_2p_sutter.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_reorganize_movie_2p_sutter.py deleted file mode 100644 index 48233e7..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/000_reorganize_movie_2p_sutter.py +++ /dev/null @@ -1,98 +0,0 @@ -import os -import numpy as np -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -import tifffile as tf -import warnings -data_folder = r'\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\190503-M439939-2p\movie' -file_identifier = '110_LSNDGC' -ch_ns = ['green', 'red'] -frames_per_file = 500 -td_rate = 1 # temporal downsample rate - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -file_list = [f for f in os.listdir(data_folder) if file_identifier in f and f[-4:] == '.tif'] -file_list.sort() -print '\n'.join(file_list) - -file_paths = [os.path.join(data_folder, f) for f in file_list] - -save_folders = [] -save_ids = [0 for ch in ch_ns] -total_movs = [None for ch in ch_ns] -for ch_n in ch_ns: - curr_save_folder = os.path.join(data_folder, file_identifier + '_reorged', 'plane0', ch_n) - if not os.path.isdir(curr_save_folder): - os.makedirs(curr_save_folder) - save_folders.append(curr_save_folder) - -for file_path in file_paths: - print('\nprocessing {} ...'.format(os.path.split(file_path)[1])) - - curr_mov = tf.imread(file_path) - - if curr_mov.shape[0] % len(ch_ns) != 0: - raise ValueError('\ttotal frame number of current movie ({}) cannot be divided by number of ' - 'channels ({})!'.format(curr_mov.shape[0], len(ch_ns))) - - # curr_mov = curr_mov.transpose((0, 2, 1))[:, ::-1, :] - - for ch_i, ch_n in enumerate(ch_ns): - print('\n\tprocessing channel: {}'.format(ch_n)) - - curr_mov_ch = curr_mov[ch_i::len(ch_ns)] - - if total_movs[ch_i] is None: - total_movs[ch_i] = curr_mov_ch - else: - total_movs[ch_i] = np.concatenate((total_movs[ch_i], curr_mov_ch), axis=0) - - while (total_movs[ch_i] is not None) and \ - (total_movs[ch_i].shape[0] >= frames_per_file * td_rate): - - num_file_to_save = total_movs[ch_i].shape[0] // (frames_per_file * td_rate) - - for save_file_id in range(num_file_to_save): - save_chunk = total_movs[ch_i][save_file_id * (frames_per_file * td_rate) : - (save_file_id + 1) * (frames_per_file * td_rate)] - save_path = os.path.join(save_folders[ch_i], '{}_{:05d}_reorged.tif'.format(file_identifier, - save_ids[ch_i])) - if td_rate != 1: - print('\tdown sampling for {} ...'.format(os.path.split(save_path)[1])) - save_chunk = ia.z_downsample(save_chunk, downSampleRate=td_rate, is_verbose=False) - - print('\tsaving {} ...'.format(os.path.split(save_path)[1])) - tf.imsave(save_path, save_chunk) - save_ids[ch_i] = save_ids[ch_i] + 1 - - if total_movs[ch_i].shape[0] % (frames_per_file * td_rate) == 0: - total_movs[ch_i] = None - else: - frame_num_left = total_movs[ch_i].shape[0] % (frames_per_file * td_rate) - total_movs[ch_i] = total_movs[ch_i][-frame_num_left:] - -print('\nprocessing residual frames ...') - -for ch_i, ch_n in enumerate(ch_ns): - - if total_movs[ch_i] is not None: - print('\n\tprocessing channel: {}'.format(ch_n)) - - save_path = os.path.join(save_folders[ch_i], '{}_{:05d}_reorged.tif'.format(file_identifier, save_ids[ch_i])) - - curr_mov_ch = total_movs[ch_i] - - if td_rate != 1: - if curr_mov_ch.shape[0] % td_rate !=0: - warning_msg = '\tthe residual frame number ({}) cannot be divided by temporal down sample rate ({}).' \ - ' Drop last few frames.'.format(curr_mov_ch.shape[0], td_rate) - print(warning_msg) - print('\tdown sampling for {} ...'.format(os.path.split(save_path)[1])) - curr_mov_ch = ia.z_downsample(curr_mov_ch, downSampleRate=td_rate, is_verbose=False) - - print('\tsaving {} ...'.format(os.path.split(save_path)[1])) - tf.imsave(save_path, curr_mov_ch) - -print('\nDone!') \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/050_motion_correction.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/050_motion_correction.py deleted file mode 100644 index d85047f..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/050_motion_correction.py +++ /dev/null @@ -1,71 +0,0 @@ -import os -import stia.motion_correction as mc - -date_recorded = '190503' -mouse_id = 'M439939' -data_folder_n = '110_LSNDGC_reorged' -imaging_mode = '2p' # '2p' or 'deepscope' - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\{}-{}-{}" \ - r"\{}".format(date_recorded, mouse_id, imaging_mode, data_folder_n) - -def correct(data_folder): - - ref_ch_n = 'red' - apply_ch_ns = ['green', 'red'] - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - ref_data_folder = os.path.join(data_folder, ref_ch_n) - - mc.motion_correction(input_folder=ref_data_folder, - input_path_identifier='.tif', - process_num=6, - output_folder=os.path.join(ref_data_folder, 'corrected'), - anchor_frame_ind_chunk=10, - anchor_frame_ind_projection=0, - iteration_chunk=10, - iteration_projection=10, - max_offset_chunk=(100., 100.), - max_offset_projection=(100., 100.), - align_func=mc.phase_correlation, - preprocessing_type=6, - fill_value=0.) - - offsets_path = os.path.join(ref_data_folder, 'corrected', 'correction_offsets.hdf5') - ref_fns = [f for f in os.listdir(ref_data_folder) if f[-4:] == '.tif'] - ref_fns.sort() - ref_paths = [os.path.join(ref_data_folder, f) for f in ref_fns] - print('\nreference paths:') - print('\n'.join(ref_paths)) - - for apply_ch_i, apply_ch_n in enumerate(apply_ch_ns): - apply_data_folder = os.path.join(data_folder, apply_ch_n) - apply_fns = [f for f in os.listdir(apply_data_folder) if f[-4:] == '.tif'] - apply_fns.sort() - apply_paths = [os.path.join(apply_data_folder, f) for f in apply_fns] - print('\napply paths:') - print('\n'.join(apply_paths)) - - mc.apply_correction_offsets(offsets_path=offsets_path, - path_pairs=zip(ref_paths, apply_paths), - output_folder=os.path.join(apply_data_folder, 'corrected'), - process_num=6, - fill_value=0., - avi_downsample_rate=10, - is_equalizing_histogram=False) - -def run(): - - plane_folders = [f for f in os.listdir(data_folder) if f[0:5] == 'plane' and - os.path.isdir(os.path.join(data_folder, f))] - plane_folders.sort() - print('folders to be corrected:') - print('\n'.join(plane_folders)) - - for plane_folder in plane_folders: - correct(os.path.join(data_folder, plane_folder)) - -if __name__ == "__main__": - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/051_(optional)_reapply_motion_correction_deepscope.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/051_(optional)_reapply_motion_correction_deepscope.py deleted file mode 100644 index 8ae7049..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/051_(optional)_reapply_motion_correction_deepscope.py +++ /dev/null @@ -1,44 +0,0 @@ -import os -import stia.motion_correction as mc - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\190118-M417949-deepscope\movie\110_LSNDGC" - -# apply the correction offsets from one plane to other planes -reference_plane = 'plane1' -apply_plane_ns = ['plane0', 'plane2'] -ref_ch_n = 'red' -apply_ch_ns = ['green', 'red'] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -def run(): - ref_folder = os.path.join(data_folder, reference_plane, ref_ch_n) - offsets_path = os.path.join(ref_folder, 'corrected', 'correction_offsets.hdf5') - ref_paths = [f for f in os.listdir(ref_folder) if f[-4:] == '.tif'] - ref_paths.sort() - ref_paths = [os.path.join(ref_folder, f) for f in ref_paths] - print('\nreference paths:') - print('\n'.join(ref_paths)) - - - for apply_plane_n in apply_plane_ns: - for apply_ch_n in apply_ch_ns: - print('\n\tapply to {}, channel: {}'.format(apply_plane_n, apply_ch_n)) - working_folder = os.path.join(data_folder, apply_plane_n, apply_ch_n) - apply_paths = [f for f in os.listdir(working_folder) if f[-4:] == '.tif'] - apply_paths.sort() - apply_paths = [os.path.join(working_folder, f) for f in apply_paths] - print('\n\tapply paths:') - print('\t'+'\n\t'.join(apply_paths)) - - mc.apply_correction_offsets(offsets_path=offsets_path, - path_pairs=zip(ref_paths, apply_paths), - output_folder=os.path.join(working_folder, 'corrected'), - process_num=6, - fill_value=0., - avi_downsample_rate=10, - is_equalizing_histogram=False) - -if __name__ == "__main__": - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/052_(optional)_downsample_corrected_files.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/052_(optional)_downsample_corrected_files.py deleted file mode 100644 index 103e305..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/052_(optional)_downsample_corrected_files.py +++ /dev/null @@ -1,105 +0,0 @@ -import os -import numpy as np -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -import tifffile as tf -import warnings -import shutil - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\190104-M417949-2p\110_LSNDGC_reorged" -td_rate = 5 -file_identifier = '110_LSNDGC' -frames_per_file = 500 - -def downsample_folder(working_folder, - td_rate, - file_identifier, - frames_per_file=500): - - file_list = [f for f in os.listdir(working_folder) if file_identifier in f and f[-14:] == '_corrected.tif'] - file_list.sort() - print('\t\tall files:') - print '\n'.join(['\t\t' + f for f in file_list]) - - print('\n\t\tmoving files to "not_downsampled" folder:') - file_paths = [os.path.join(working_folder, f) for f in file_list] - print - - not_downsampled_folder = os.path.join(working_folder, 'not_downsampled') - os.mkdir(not_downsampled_folder) - for file_path in file_paths: - fn = os.path.split(file_path)[1] - shutil.move(file_path, os.path.join(not_downsampled_folder, fn)) - - file_paths_original = [os.path.join(not_downsampled_folder, fn) for fn in file_list] - file_paths_original.sort() - - - save_id = 0 - total_mov = None - for file_path_o in file_paths_original: - print('\t\tprocessing {} ...'.format(os.path.split(file_path_o)[1])) - curr_mov = tf.imread(file_path_o) - - if total_mov is None: - total_mov = curr_mov - else: - total_mov = np.concatenate((total_mov, curr_mov), axis=0) - - while total_mov is not None and \ - (total_mov.shape[0] >= frames_per_file * td_rate): - - num_file_to_save = total_mov.shape[0] // (frames_per_file * td_rate) - - for save_file_id in range(num_file_to_save): - save_chunk = total_mov[save_file_id * (frames_per_file * td_rate) : - (save_file_id + 1) * (frames_per_file * td_rate)] - save_path = os.path.join(working_folder, '{}_{:05d}_corrected_downsampled.tif'.format(file_identifier, - save_id)) - save_chunk = ia.z_downsample(save_chunk, downSampleRate=td_rate, is_verbose=False) - - print('\t\t\tsaving {} ...'.format(os.path.split(save_path)[1])) - tf.imsave(save_path, save_chunk) - save_id = save_id + 1 - - if total_mov.shape[0] % (frames_per_file * td_rate) == 0: - total_mov = None - else: - frame_num_left = total_mov.shape[0] % (frames_per_file * td_rate) - total_mov = total_mov[-frame_num_left:] - - if total_mov is not None: - save_path = os.path.join(working_folder, '{}_{:05d}_corrected_downsampled.tif'.format(file_identifier, save_id)) - save_chunk = ia.z_downsample(total_mov, downSampleRate=td_rate, is_verbose=False) - print('\t\t\tsaving {} ...'.format(os.path.split(save_path)[1])) - tf.imsave(save_path, save_chunk) - - return - -if td_rate == 1: - raise ValueError('Downsample rate shold not be 1!') - -plane_ns = [f for f in os.listdir(data_folder) if f[0:5]=='plane'] -plane_ns.sort() -print('all planes:') -print('\n'.join(plane_ns)) -print - -for plane_n in plane_ns: - print('current plane: {}'.format(plane_n)) - plane_folder = os.path.join(data_folder, plane_n) - ch_ns = [f for f in os.listdir(plane_folder)] - ch_ns.sort() - print('\tall channels: {}'.format(ch_ns)) - - for ch_n in ch_ns: - print - print('\tcurrent channel: {}'.format(ch_n)) - ch_folder = os.path.join(plane_folder, ch_n) - - downsample_folder(working_folder=os.path.join(ch_folder, 'corrected'), - td_rate=td_rate, - file_identifier=file_identifier, - frames_per_file=frames_per_file) - -print('\nDone!') \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/055_downsample_from_server.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/055_downsample_from_server.py deleted file mode 100644 index 9e3a003..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/055_downsample_from_server.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import numpy as np -import tifffile as tf - -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia - -date_recorded = '190503' -mouse_id = 'M439939' -xy_downsample_rate = 2 -t_downsample_rate = 10 -ch_ns = ['green', 'red'] -data_folder_n = '110_LSNDGC_reorged' -imaging_mode = '2p' # '2p' or 'deepscope' -identifier = '110_LSNDGC' - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\{}-{}-{}" \ - r"\{}".format(date_recorded, mouse_id, imaging_mode, data_folder_n) - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -plane_ns = [f for f in os.listdir(data_folder) if os.path.isdir(os.path.join(data_folder, f)) and f[:5] == 'plane'] -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('\nprocessing plane: {}'.format(plane_n)) - - save_folder = os.path.join(curr_folder, plane_n) - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - for ch_n in ch_ns: - print('\n\tprocessing channel: {}'.format(ch_n)) - plane_folder = os.path.join(data_folder, plane_n, ch_n, 'corrected') - - # f_ns = [f for f in os.listdir(plane_folder) if f[-14:] == '_corrected.tif'] - f_ns= [f for f in os.listdir(plane_folder) if f[-4:] == '.tif' and identifier in f] - f_ns.sort() - print('\t\t'+'\n\t\t'.join(f_ns) + '\n') - - mov_d = [] - - for f_n in f_ns: - print('\t\tprocessing {} ...'.format(f_n)) - curr_mov = tf.imread(os.path.join(plane_folder, f_n)) - curr_mov_d = ia.rigid_transform_cv2(img=curr_mov, zoom=(1. / xy_downsample_rate)) - curr_mov_d = ia.z_downsample(curr_mov_d, downSampleRate=t_downsample_rate, is_verbose=False) - mov_d.append(curr_mov_d) - - mov_d = np.concatenate(mov_d, axis=0) - save_n = '{}_{}_{}_downsampled.tif'.format(os.path.split(data_folder)[1], plane_n, ch_n) - tf.imsave(os.path.join(save_folder, save_n), mov_d) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/060_get_image_data.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/060_get_image_data.py deleted file mode 100644 index 88ce9b1..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/060_get_image_data.py +++ /dev/null @@ -1,78 +0,0 @@ -import os -import h5py -import numpy as np -import skimage.external.tifffile as tf - -date_recorded = '190503' -mouse_id = 'M439939' -sess_id = '110' -channel = 'green' -data_folder_n = '110_LSNDGC_reorged' -imaging_mode = '2p' # '2p' or 'deepscope' -identifier = '110_LSNDGC' - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\{}-{}-{}" \ - r"\{}".format(date_recorded, mouse_id, imaging_mode, data_folder_n) - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -file_prefix = '{}_{}_{}'.format(date_recorded, mouse_id, sess_id) - -plane_fns = [f for f in os.listdir(data_folder) if f[:5] == 'plane'] -plane_fns.sort() -print('\n'.join(plane_fns)) - -data_f = h5py.File(file_prefix + '_2p_movies.hdf5') - -for plane_fn in plane_fns: - print('\nprocessing {} ...'.format(plane_fn)) - plane_folder = os.path.join(data_folder, plane_fn, channel, 'corrected') - # mov_fns = [f for f in os.listdir(plane_folder) if f[-14:] == '_corrected.tif'] - mov_fns = [f for f in os.listdir(plane_folder) if f[-4:] == '.tif' and identifier in f] - mov_fns.sort() - print('\n'.join(mov_fns)) - - # get shape of concatenated movie - z1, y, x = tf.imread(os.path.join(plane_folder, mov_fns[0])).shape - z0, _, _ = tf.imread(os.path.join(plane_folder, mov_fns[-1])).shape - z = z0 + z1 * (len(mov_fns) - 1) - - # for mov_fn in mov_fns: - # print('reading {} ...'.format(mov_fn)) - # curr_z, curr_y, curr_x = tf.imread(os.path.join(plane_folder, mov_fn)).shape - # - # if y is None: - # y = curr_y - # else: - # if y != curr_y: - # raise ValueError('y dimension ({}) of file "{}" does not agree with previous file(s) ({}).' - # .format(curr_y, mov_fn, y)) - # - # if x is None: - # x = curr_x - # else: - # if x != curr_x: - # raise ValueError('x dimension ({}) of file "{}" does not agree with previous file(s) ({}).' - # .format(curr_x, mov_fn, x)) - # - # z = z + curr_z - - print((z,y,x)) - dset = data_f.create_dataset(plane_fn, (z, y, x), dtype=np.int16, compression='lzf') - - start_frame = 0 - end_frame = 0 - for mov_fn in mov_fns: - print('reading {} ...'.format(mov_fn)) - curr_mov = tf.imread(os.path.join(plane_folder, mov_fn)) - end_frame = start_frame + curr_mov.shape[0] - dset[start_frame : end_frame] = curr_mov - start_frame = end_frame - - dset.attrs['conversion'] = 1. - dset.attrs['resolution'] = 1. - dset.attrs['unit'] = 'arbiturary_unit' - -data_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/070_get_hdf5_files_for_caiman_soma.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/070_get_hdf5_files_for_caiman_soma.py deleted file mode 100644 index 15442fe..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/070_get_hdf5_files_for_caiman_soma.py +++ /dev/null @@ -1,65 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import h5py - -date_recorded = '190809' -mouse_id = 'M471944' -sess_id = '110' -t_downsample_rate = 3 -channel = 'green' -data_folder_n = '110_LSNDGC_reorged' -imaging_mode = '2p' # '2p' or 'deepscope' -identifier = '110_LSNDGC' - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\{}-{}-{}" \ - r"\{}".format(date_recorded, mouse_id, imaging_mode, data_folder_n) - -plane_ns = [p for p in os.listdir(data_folder) if os.path.isdir(os.path.join(data_folder, p))] -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('\nprocessing {} ...'.format(plane_n)) - - plane_folder = os.path.join(data_folder, plane_n, channel, 'corrected') - os.chdir(plane_folder) - - # f_ns = [f for f in os.listdir(plane_folder) if f[-14:] == '_corrected.tif'] - f_ns = [f for f in os.listdir(plane_folder) if f[-4:] == '.tif' and identifier in f] - f_ns.sort() - print('\n'.join(f_ns)) - - mov_join = [] - for f_n in f_ns: - print('processing plane: {}; file: {} ...'.format(plane_n, f_n)) - - curr_mov = tf.imread(os.path.join(plane_folder, f_n)) - - if curr_mov.shape[0] % t_downsample_rate != 0: - print('the frame number of {} ({}) is not divisible by t_downsample_rate ({}).' - .format(f_n, curr_mov.shape[0], t_downsample_rate)) - - curr_mov_d = ia.z_downsample(curr_mov, downSampleRate=t_downsample_rate, is_verbose=False) - mov_join.append(curr_mov_d) - - mov_join = np.concatenate(mov_join, axis=0) - - save_name = '{}_{}_{}_{}_downsampled_for_caiman.hdf5'.format(date_recorded, mouse_id, sess_id, plane_n) - save_f = h5py.File(os.path.join(plane_folder, save_name)) - save_f.create_dataset('mov', data=mov_join) - save_f.close() - - # save_name = '{}_d1_{}_d2_{}_d3_1_order_C_frames_{}_.mmap'\ - # .format(base_name, mov_join.shape[2], mov_join.shape[1], mov_join.shape[0]) - # - # mov_join = mov_join.reshape((mov_join.shape[0], mov_join.shape[1] * mov_join.shape[2]), order='F').transpose() - # mov_join_mmap = np.memmap(os.path.join(plane_folder, save_name), shape=mov_join.shape, order='C', - # dtype=np.float32, mode='w+') - # mov_join_mmap[:] = mov_join + add_to_mov - # mov_join_mmap.flush() - # del mov_join_mmap - -print('done!') \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/070_get_mmap_files_for_caiman_bouton.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/070_get_mmap_files_for_caiman_bouton.py deleted file mode 100644 index 963e2b3..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/070_get_mmap_files_for_caiman_bouton.py +++ /dev/null @@ -1,66 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import h5py - -date_recorded = '190503' -mouse_id = 'M439939' -sess_id = '110' -t_downsample_rate = 5 -channel = 'green' -data_folder_n = '110_LSNDGC_reorged' -imaging_mode = '2p' # '2p' or 'deepscope' -identifier = '110_LSNDGC' - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\{}-{}-{}" \ - r"\{}".format(date_recorded, mouse_id, imaging_mode, data_folder_n) -base_name = '{}_{}_{}'.format(date_recorded, mouse_id, sess_id) - -plane_ns = [p for p in os.listdir(data_folder) if os.path.isdir(os.path.join(data_folder, p))] -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('\nprocessing {} ...'.format(plane_n)) - - plane_folder = os.path.join(data_folder, plane_n, channel, 'corrected') - os.chdir(plane_folder) - - # f_ns = [f for f in os.listdir(plane_folder) if f[-14:] == '_corrected.tif'] - f_ns = [f for f in os.listdir(plane_folder) if f[-4:] == '.tif' and identifier in f] - f_ns.sort() - print('\n'.join(f_ns)) - - mov_join = [] - for f_n in f_ns: - print('processing plane: {}; file: {} ...'.format(plane_n, f_n)) - - curr_mov = tf.imread(os.path.join(plane_folder, f_n)) - - if curr_mov.shape[0] % t_downsample_rate != 0: - print('the frame number of {} ({}) is not divisible by t_downsample_rate ({}).' - .format(f_n, curr_mov.shape[0], t_downsample_rate)) - - curr_mov_d = ia.z_downsample(curr_mov, downSampleRate=t_downsample_rate, is_verbose=False) - mov_join.append(curr_mov_d) - - mov_join = np.concatenate(mov_join, axis=0) - add_to_mov = 10 - np.amin(mov_join) - - save_name = '{}_d1_{}_d2_{}_d3_1_order_C_frames_{}_.mmap'\ - .format(base_name, mov_join.shape[2], mov_join.shape[1], mov_join.shape[0]) - - mov_join = mov_join.reshape((mov_join.shape[0], mov_join.shape[1] * mov_join.shape[2]), order='F').transpose() - mov_join_mmap = np.memmap(os.path.join(plane_folder, save_name), shape=mov_join.shape, order='C', - dtype=np.float32, mode='w+') - mov_join_mmap[:] = mov_join + add_to_mov - mov_join_mmap.flush() - del mov_join_mmap - - save_file = h5py.File(os.path.join(plane_folder, 'caiman_segmentation_results.hdf5')) - save_file['bias_added_to_movie'] = add_to_mov - save_file.close() - -print('done!') \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/110_caiman_segmentation_bouton.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/110_caiman_segmentation_bouton.py deleted file mode 100644 index 6ba8a0f..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/110_caiman_segmentation_bouton.py +++ /dev/null @@ -1,126 +0,0 @@ -""" -run it in command line with old caiman v1.0 - ->>> activate ciaman -""" - -import sys; print('Python %s on %s' % (sys.version, sys.platform)) -sys.path.extend([r"E:\data\github_packages\CaImAn"]) - -import os -import numpy as np -import caiman as cm -import matplotlib.pyplot as plt -from caiman.source_extraction.cnmf import cnmf as cnmf -import h5py -from shutil import copyfile - -def run(): - date_recorded = '190503' - mouse_id = 'M439939' - play_movie = False - resolution = 512 - channel = 'green' - - data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\{}-{}-2p" \ - r"\110_LSNDGC_reorged".format(date_recorded, mouse_id) - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - - plane_ns = [f for f in os.listdir(data_folder) if os.path.isdir(f) and f[:5] == 'plane'] - plane_ns.sort() - print('planes:') - print('\n'.join(plane_ns)) - - # %% start cluster - c, dview, n_processes = cm.cluster.setup_cluster(backend='local', n_processes=6, single_thread=False) - - for plane_n in plane_ns: - - print('\nsegmenting plane: {}'.format(plane_n)) - - plane_folder = os.path.join(data_folder, plane_n, channel, 'corrected') - os.chdir(plane_folder) - - fn = [f for f in os.listdir(plane_folder) if f[-5:] == '.mmap'] - if len(fn) > 1: - print('\n'.join(fn)) - raise LookupError('more than one file found.') - elif len(fn) == 0: - raise LookupError('no file found.') - else: - fn = fn[0] - - fn_parts = fn.split('_') - d1 = int(fn_parts[fn_parts.index('d1') + 1]) # column, x - d2 = int(fn_parts[fn_parts.index('d2') + 1]) # row, y - d3 = int(fn_parts[fn_parts.index('d3') + 1]) # channel - d4 = int(fn_parts[fn_parts.index('frames') + 1]) # frame, T - order = fn_parts[fn_parts.index('order') + 1] - - print('playing {} ...'.format(fn)) - - mov = np.memmap(filename=fn, shape=(d1, d2, d4), order=order, dtype=np.float32, mode='r') - mov = mov.transpose((2, 1, 0)) - - print('shape of joined movie: {}.'.format(mov.shape)) - - #%% play movie, press q to quit - if play_movie: - cm.movie(mov).play(fr=50,magnification=1,gain=2.) - - #%% movie cannot be negative! - mov_min = float(np.amin(mov)) - print('minimum pixel value: {}.'.format(mov_min)) - if mov_min < 0: - raise Exception('Movie too negative, add_to_movie should be larger') - - #%% correlation image. From here infer neuron size and density - Cn = cm.movie(mov).local_correlations(swap_dim=False) - # plt.imshow(Cn, cmap='gray') - # plt.show() - - K = 100 # number of neurons expected per patch - gSig = [5, 5] # expected half size of neurons - merge_thresh = 0.9 # merging threshold, max correlation allowed - p = 2 # order of the autoregressive system - cnm = cnmf.CNMF(n_processes, - k=10, # number of neurons expected per patch - gSig=[5, 5] , # expected half size of neurons - merge_thresh=0.9, # merging threshold, max correlation allowed - p=2, # order of the autoregressive system - dview=dview, - Ain=None, - method_deconvolution='oasis', - rolling_sum = False, - method_init='sparse_nmf', - alpha_snmf=10e1, - ssub=1, - tsub=1, - p_ssub=1, - p_tsub=1, - rf=int(resolution / 2), # half-size of the patches in pixels - border_pix=0, - do_merge=False) - cnm = cnm.fit(mov) - A, C, b, f, YrA, sn = cnm.A, cnm.C, cnm.b, cnm.f, cnm.YrA, cnm.sn - #%% - # crd = cm.utils.visualization.plot_contours(cnm.A, Cn) - # plt.show() - # input("Press enter to continue ...") - - roi_num = cnm.A.shape[1] - save_fn = h5py.File('caiman_segmentation_results.hdf5') - bias = save_fn['bias_added_to_movie'].value - save_fn['masks'] = np.array(cnm.A.todense()).T.reshape((roi_num, 512, 512), order='F') - save_fn['traces'] = cnm.C - bias - save_fn.close() - - copyfile(os.path.join(plane_folder, 'caiman_segmentation_results.hdf5'), - os.path.join(curr_folder, plane_n, 'caiman_segmentation_results.hdf5')) - - plt.close('all') - - -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/110_caiman_segmentation_soma.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/110_caiman_segmentation_soma.py deleted file mode 100644 index e5e4c8f..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/110_caiman_segmentation_soma.py +++ /dev/null @@ -1,109 +0,0 @@ -""" -run it in command line with new caiman v1.5.3 - ->>> activate caiman_new -""" - -import os -import numpy as np -from caiman.source_extraction import cnmf as cnmf -import h5py -from shutil import copyfile - - -date_recorded = '190814' -mouse_id = 'M471944' -resolution = (512, 512) -channel = 'green' -data_folder_n = '110_LSNDGCUC_reorg' -imaging_mode = 'deepscope' # '2p' or 'deepscope' - - -# caiman parameters -fr = 2 # frame rate (Hz) -decay_time = 0.5 # approximate length of transient event in seconds -gSig = (8, 8) # expected half size of neurons -p = 1 # order of AR indicator dynamics -min_SNR = 1 # minimum SNR for accepting new components -rval_thr = 0.80 # correlation threshold for new component inclusion -ds_factor = 1 # spatial downsampling factor (increases speed but may lose some fine structure) -gnb = 2 # number of background components -gSig = tuple(np.ceil(np.array(gSig) / ds_factor).astype('int')) # recompute gSig if downsampling is involved -mot_corr = False # flag for online motion correction -pw_rigid = False # flag for pw-rigid motion correction (slower but potentially more accurate) -max_shifts_online = np.ceil(10. / ds_factor).astype('int') # maximum allowed shift during motion correction -sniper_mode = True # flag using a CNN to detect new neurons (o/w space correlation is used) -init_batch = 200 # number of frames for initialization (presumably from the first file) -expected_comps = 500 # maximum number of expected components used for memory pre-allocation (exaggerate here) -dist_shape_update = True # flag for updating shapes in a distributed way -min_num_trial = 10 # number of candidate components per frame -K = 2 # initial number of components -epochs = 2 # number of passes over the data -show_movie = False # show the movie with the results as the data gets processed - -curr_folder = os.path.dirname(os.path.realpath(__file__)) - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\{}-{}-{}" \ - r"\{}".format(date_recorded, mouse_id, imaging_mode, data_folder_n) - - -plane_ns = [f for f in os.listdir(data_folder) if os.path.isdir(f) and f[:5] == 'plane'] -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - - print('\nsegmenting plane: {}'.format(plane_n)) - - plane_folder = os.path.join(data_folder, plane_n, channel, 'corrected') - os.chdir(plane_folder) - - fn = [f for f in os.listdir(plane_folder) if len(f) > 16 and f[-16:] == '_for_caiman.hdf5'] - if len(fn) > 1: - print('\n'.join(fn)) - raise LookupError('more than one file found.') - elif len(fn) == 0: - raise LookupError('no file found.') - else: - fn = fn[0] - - fp = os.path.join(os.path.realpath(plane_folder), fn) - - params_dict = {'fnames': [fp], - 'fr': fr, - 'decay_time': decay_time, - 'gSig': gSig, - 'p': p, - 'min_SNR': min_SNR, - 'rval_thr': rval_thr, - 'ds_factor': ds_factor, - 'nb': gnb, - 'motion_correct': mot_corr, - 'init_batch': init_batch, - 'init_method': 'bare', - 'normalize': True, - 'expected_comps': expected_comps, - 'sniper_mode': sniper_mode, - 'dist_shape_update': dist_shape_update, - 'min_num_trial': min_num_trial, - 'K': K, - 'epochs': epochs, - 'max_shifts_online': max_shifts_online, - 'pw_rigid': pw_rigid, - 'show_movie': show_movie} - - opts = cnmf.params.CNMFParams(params_dict=params_dict) - - cnm = cnmf.online_cnmf.OnACID(params=opts) - cnm.fit_online() - - roi_num = cnm.estimates.A.shape[1] - print('saving ...') - save_f = h5py.File('caiman_segmentation_results.hdf5') - save_f['masks'] = np.array(cnm.estimates.A.todense()).T.reshape((roi_num, resolution[0], resolution[1]), order='F') - save_f['traces'] = cnm.estimates.C - save_f.close() - - copyfile(os.path.join(plane_folder, 'caiman_segmentation_results.hdf5'), - os.path.join(curr_folder, plane_n, 'caiman_segmentation_results.hdf5')) diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/200_generate_nwb.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/200_generate_nwb.py deleted file mode 100644 index 7105a41..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/200_generate_nwb.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import corticalmapping.NwbTools as nt - -date_recorded = '190503' -mouse_id = '439939' -sess_num = '110' - -experimenter = 'Jun' -genotype = 'Vipr2-IRES2-Cre-neo' -sex = 'Male' -age = '147' -indicator = 'GCaMP6s' -imaging_rate = 30. # deepscope 37. -imaging_depth = '150 microns' # deepscope [150, 100, 50] or [300, 250, 200] -imaging_location = 'visual cortex' -imaging_device = 'Sutter' # or DeepScope -imaging_excitation_lambda = '920 nanometers' # deepscope 940 nanometers - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -notebook_path = os.path.join(curr_folder, 'notebook.txt') -with open(notebook_path, 'r') as ff: - notes = ff.read() - -general = nt.DEFAULT_GENERAL -general['experimenter'] = experimenter -general['subject']['subject_id'] = mouse_id -general['subject']['genotype'] = genotype -general['subject']['sex'] = sex -general['subject']['age'] = age -general['optophysiology'].update({'imaging_plane_0': {}}) -general['optophysiology']['imaging_plane_0'].update({'indicator': indicator}) -general['optophysiology']['imaging_plane_0'].update({'imaging_rate': imaging_rate}) -general['optophysiology']['imaging_plane_0'].update({'imaging_depth': imaging_depth}) -general['optophysiology']['imaging_plane_0'].update({'location': imaging_location}) -general['optophysiology']['imaging_plane_0'].update({'device': imaging_device}) -general['optophysiology']['imaging_plane_0'].update({'excitation_lambda': imaging_excitation_lambda}) -general['notes'] = notes - -file_name = date_recorded + '_M' + mouse_id + '_' + sess_num + '.nwb' - -rf = nt.RecordedFile(os.path.join(curr_folder, file_name), identifier=file_name[:-4], description='') -rf.add_general(general=general) -rf.close() - - - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/210_add_vasmap.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/210_add_vasmap.py deleted file mode 100644 index 25a86fa..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/210_add_vasmap.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import corticalmapping.NwbTools as nt -import corticalmapping.core.ImageAnalysis as ia -import matplotlib.pyplot as plt -import tifffile as tf - - -import os -import corticalmapping.NwbTools as nt -import corticalmapping.core.ImageAnalysis as ia -import matplotlib.pyplot as plt -import tifffile as tf - -is_plot = False -vasmap_dict = { - 'vasmap_wf': 'wide field surface vasculature map through cranial window original', - 'vasmap_wf_rotated': 'wide field surface vasculature map through cranial window rotated', - 'vasmap_2p_green': '2p surface vasculature map through cranial window green original, zoom1', - 'vasmap_2p_green_rotated': '2p surface vasculature map through cranial window green rotated, zoom1', - 'vasmap_2p_red': '2p surface vasculature map through cranial window red original, zoom1', - 'vasmap_2p_red_rotated': '2p surface vasculature map through cranial window red rotated, zoom1' - } - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -for mn, des in vasmap_dict.items(): - try: - curr_m = ia.array_nor(tf.imread(mn + '.tif')) - - if is_plot: - f = plt.figure(figsize=(10, 10)) - ax = f.add_subplot(111) - ax.imshow(curr_m, vmin=0., vmax=1., cmap='gray', interpolation='nearest') - ax.set_axis_off() - ax.set_title(mn) - plt.show() - - print('adding {} to nwb file.'.format(mn)) - nwb_f.add_acquisition_image(mn, curr_m, description=des) - - except Exception as e: - print(e) - -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/220_add_sync_data.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/220_add_sync_data.py deleted file mode 100644 index 2e26ef3..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/220_add_sync_data.py +++ /dev/null @@ -1,23 +0,0 @@ -import os -import corticalmapping.NwbTools as nt - -record_date = '190503' -mouse_id = '439939' -session_id = '110' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = record_date + '_M' + mouse_id + '_' + session_id + '.nwb' - -sync_fn = [f for f in os.listdir(curr_folder) if f[-3:] == '.h5' and record_date in f and 'M' + mouse_id in f] -if len(sync_fn) == 0: - raise LookupError('Did not find sync .h5 file.') -elif len(sync_fn) > 1: - raise LookupError('More than one sync .h5 files found.') -else: - sync_fn = sync_fn[0] - -nwb_f = nt.RecordedFile(nwb_fn) -nwb_f.add_sync_data(sync_fn) -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/221_(optional)_check_2p_timestamps_deepscope.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/221_(optional)_check_2p_timestamps_deepscope.py deleted file mode 100644 index e211042..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/221_(optional)_check_2p_timestamps_deepscope.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -import numpy as np -import h5py -import matplotlib.pyplot as plt - -ts_2p_key = 'digital_vsync_2p_rise' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -ts_2p = nwb_f['acquisition/timeseries/{}/timestamps'.format(ts_2p_key)].value -print('number of 2p frame timestamps: {}'.format(len(ts_2p))) - -dur_2p = np.diff(ts_2p) -max_ind = np.argmax(dur_2p) -print('maximum 2p frame duration: {}'.format(dur_2p[max_ind])) - -# fis = np.arange(21, dtype=np.int) - 10 + max_ind -# -# for fi in fis: -# print('{}, ctime: {}s, duration: {}s'.format(fns[fi], ctimes[fi], ctime_diff[fi])) - -plt.plot(dur_2p) -plt.show() diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/230_add_image_data.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/230_add_image_data.py deleted file mode 100644 index a913bff..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/230_add_image_data.py +++ /dev/null @@ -1,69 +0,0 @@ -import os -import h5py -import corticalmapping.NwbTools as nt - -dset_ns = ['plane0'] # ['plane0', 'plane1', 'plane2'] -imaging_depths = [150] -temporal_downsample_rate = 5 # down sample rate before motion correction times down sample rate after motion correction -scope = 'sutter' # 'sutter' or 'DeepScope' -zoom = 4 - -description = '2-photon imaging data' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if scope == 'DeepScope': - pixel_size = 0.000000967 / zoom # meter -elif scope == 'sutter': - pixel_size = 0.0000014 / zoom # meter -else: - raise LookupError('do not understand scope type') - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -ts_2p_tot = nwb_f.file_pointer['/acquisition/timeseries/digital_vsync_2p_rise/timestamps'].value - -# if scope == 'sutter': -# ts_2p_tot = nwb_f.file_pointer['/acquisition/timeseries/digital_vsync_2p_rise/timestamps'].value -# elif scope == 'DeepScope': -# ts_2p_tot = nwb_f.file_pointer['/acquisition/timeseries/digital_2p_vsync_rise/timestamps'].value -# else: -# raise LookupError('do not understand scope type') -# print('total 2p timestamps count: {}'.format(len(ts_2p_tot))) - -mov_fn = os.path.splitext(nwb_fn)[0] + '_2p_movies.hdf5' -mov_f = h5py.File(mov_fn, 'r') - -for mov_i, mov_dn in enumerate(dset_ns): - - if mov_dn is not None: - - curr_dset = mov_f[mov_dn] - if mov_dn is not None: - mov_ts = ts_2p_tot[mov_i::len(dset_ns)] - print('\n{}: total 2p timestamps count: {}'.format(mov_dn, len(mov_ts))) - - mov_ts_d = mov_ts[::temporal_downsample_rate] - print('{}: downsampled 2p timestamps count: {}'.format(mov_dn, len(mov_ts_d))) - print('{}: downsampled 2p movie frame num: {}'.format(mov_dn, curr_dset.shape[0])) - - # if len(mov_ts_d) == curr_dset.shape[0]: - # pass - # elif len(mov_ts_d) == curr_dset.shape[0] + 1: - # mov_ts_d = mov_ts_d[0: -1] - # else: - # raise ValueError('the timestamp count of {} movie ({}) does not equal (or is not greater by one) ' - # 'the frame cound in the movie ({})'.format(mov_dn, len(mov_ts_d), curr_dset.shape[0])) - mov_ts_d = mov_ts_d[:curr_dset.shape[0]] - - curr_description = '{}. Imaging depth: {} micron.'.format(description, imaging_depths[mov_i]) - nwb_f.add_acquired_image_series_as_remote_link('2p_movie_' + mov_dn, image_file_path=mov_fn, - dataset_path=mov_dn, timestamps=mov_ts_d, - description=curr_description, comments='', - data_format='zyx', pixel_size=[pixel_size, pixel_size], - pixel_size_unit='meter') - -mov_f.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/240_add_motion_correction_module.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/240_add_motion_correction_module.py deleted file mode 100644 index 8532270..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/240_add_motion_correction_module.py +++ /dev/null @@ -1,61 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import h5py -import corticalmapping.NwbTools as nt -import corticalmapping.core.ImageAnalysis as ia - -movie_2p_fn = '190503_M439939_110_2p_movies.hdf5' -plane_num = 1 -temporal_downsample_rate = 1 # downsample rate after motion correction - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -input_parameters = [] - -for i in range(plane_num): - - plane_n = 'plane{}'.format(i) - - offsets_path = os.path.join(plane_n, 'correction_offsets.hdf5') - offsets_f = h5py.File(offsets_path, 'r') - offsets_keys = offsets_f.keys() - if 'path_list' in offsets_keys: - offsets_keys.remove('path_list') - - offsets_keys.sort() - offsets = [] - for offsets_key in offsets_keys: - offsets.append(offsets_f[offsets_key].value) - offsets = np.concatenate(offsets, axis=0) - offsets = np.array(zip(offsets[:, 1], offsets[:, 0])) - offsets_f.close() - - mean_projection = tf.imread(os.path.join(plane_n, 'corrected_mean_projection.tif')) - max_projection = tf.imread(os.path.join(plane_n, 'corrected_max_projections.tif')) - max_projection = ia.array_nor(np.max(max_projection, axis=0)) - - input_dict = {'field_name': plane_n, - 'original_timeseries_path': '/acquisition/timeseries/2p_movie_plane' + str(i), - 'corrected_file_path': movie_2p_fn, - 'corrected_dataset_path': plane_n, - 'xy_translation_offsets': offsets, - 'mean_projection': mean_projection, - 'max_projection': max_projection, - 'description': '', - 'comments': '', - 'source': ''} - - input_parameters.append(input_dict) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -nwb_f.add_muliple_dataset_to_motion_correction_module(input_parameters=input_parameters, - module_name='motion_correction', - temporal_downsample_rate=temporal_downsample_rate) -nwb_f.close() - - - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/250_get_photodiode_onset.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/250_get_photodiode_onset.py deleted file mode 100644 index 7344453..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/250_get_photodiode_onset.py +++ /dev/null @@ -1,45 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.HighLevel as hl - -# photodiode -digitizeThr = 0.15 # deepscope: 0.15 or 0.055, sutter: -0.15 -filterSize = 0.01 # deepscope: 0.01, sutter: 0.01 -segmentThr = 0.02 # deepscope: 0.02, sutter: 0.01 -smallestInterval = 0.05 # deepscope: 0.05, sutter: 0.05 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] - -nwb_f = nt.RecordedFile(nwb_fn) -pd, pd_t = nwb_f.get_analog_data(ch_n='analog_photodiode') -fs = 1. / np.mean(np.diff(pd_t)) -# print fs - -pd_onsets = hl.segmentPhotodiodeSignal(pd, digitizeThr=digitizeThr, filterSize=filterSize, - segmentThr=segmentThr, Fs=fs, smallestInterval=smallestInterval) - -raw_input('press enter to continue ...') - -pdo_ts = nwb_f.create_timeseries('TimeSeries', 'digital_photodiode_rise', modality='other') -pdo_ts.set_time(pd_onsets) -pdo_ts.set_data([], unit='', conversion=np.nan, resolution=np.nan) -pdo_ts.set_value('digitize_threshold', digitizeThr) -pdo_ts.set_value('filter_size', filterSize) -pdo_ts.set_value('segment_threshold', segmentThr) -pdo_ts.set_value('smallest_interval', smallestInterval) -pdo_ts.set_description('Real Timestamps (master acquisition clock) of photodiode onset. ' - 'Extracted from analog photodiode signal by the function:' - 'corticalmapping.HighLevel.segmentPhotodiodeSignal() using parameters saved in the' - 'current timeseries.') -pdo_ts.set_path('/analysis') -pdo_ts.finalize() - -nwb_f.close() - - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/260_add_visual_stimuli_retinotopic_mapping.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/260_add_visual_stimuli_retinotopic_mapping.py deleted file mode 100644 index 6f021ad..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/260_add_visual_stimuli_retinotopic_mapping.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -import retinotopic_mapping.DisplayLogAnalysis as dla -import corticalmapping.NwbTools as nt - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -stim_pkl_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.pkl'][0] -stim_log = dla.DisplayLogAnalyzer(stim_pkl_fn) - -nwb_f.add_visual_display_log_retinotopic_mapping(stim_log=stim_log) -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/270_analyze_photodiode_onsets.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/270_analyze_photodiode_onsets.py deleted file mode 100644 index af32220..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/270_analyze_photodiode_onsets.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import retinotopic_mapping.DisplayLogAnalysis as dla -import corticalmapping.core.TimingAnalysis as ta - -# for deepscope -vsync_frame_path='acquisition/timeseries/digital_vsync_stim_rise' - -# for sutter -# vsync_frame_path='acquisition/timeseries/digital_vsync_visual_rise' - -pd_ts_pd_path = 'analysis/digital_photodiode_rise' -pd_thr = -0.5 # this is color threshold, not analog photodiode threshold -ccg_t_range = (0., 0.1) -ccg_bins = 100 -is_plot = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -stim_pkl_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.pkl'][0] -stim_log = dla.DisplayLogAnalyzer(stim_pkl_fn) - -# get display lag -display_delay = nwb_f.get_display_delay_retinotopic_mapping(stim_log=stim_log, indicator_color_thr=pd_thr, - ccg_t_range=ccg_t_range, ccg_bins=ccg_bins, - is_plot=is_plot, pd_onset_ts_path=pd_ts_pd_path, - vsync_frame_ts_path=vsync_frame_path) - -# analyze photodiode onset -stim_dict = stim_log.get_stim_dict() -pd_onsets_seq = stim_log.analyze_photodiode_onsets_sequential(stim_dict=stim_dict, pd_thr=pd_thr) -pd_onsets_com = stim_log.analyze_photodiode_onsets_combined(pd_onsets_seq=pd_onsets_seq, - is_dgc_blocked=True) -nwb_f.add_photodiode_onsets_combined_retinotopic_mapping(pd_onsets_com=pd_onsets_com, - display_delay=display_delay, - vsync_frame_path=vsync_frame_path) -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/275_add_eyetracking.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/275_add_eyetracking.py deleted file mode 100644 index 228502f..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/275_add_eyetracking.py +++ /dev/null @@ -1,68 +0,0 @@ -import os -import numpy as np -import h5py -import corticalmapping.NwbTools as nt - -diagonal_length = 9.0 # mm, the length of diagonal line of eyetracking field of view -side = 'right' # right eye or left eye -scope = 'deepscope' # 'sutter' or 'deepscope' - -comments = 'small_x=temporal; big_x=nasal; small_y=dorsal; big_y=ventral' - -if scope == 'sutter': - eyetracking_ts_name = 'digital_vsync_right_eye_mon_rise' - frame_shape = (658, 492) -elif scope == 'deepscope': - eyetracking_ts_name = 'digital_cam_eye_rise' - frame_shape = (640, 480) -else: - raise LookupError('do not understand scope type.') - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -eye_folder = os.path.join(curr_folder, 'videomon') -fn = [f for f in os.listdir(eye_folder) if f[-12:] == '_output.hdf5'] -if len(fn) != 1: - print('could not find processed eyetracking data.') -else: - fn = fn[0] - -pixel_size = diagonal_length / np.sqrt(658. ** 2 + 492. ** 2) -print('eyetracking pixel size: {:5.3f} mm/pix'.format(pixel_size)) - -eye_file = h5py.File(os.path.join(eye_folder, fn), 'r') -led_pos = eye_file['led_positions'].value -pup_pos = eye_file['pupil_positions'].value -pup_shape = eye_file['pupil_shapes'].value -pup_shape[:, 0] = pup_shape[:, 0] * pixel_size -pup_shape[:, 1] = pup_shape[:, 1] * pixel_size -pup_shape_meta = 'format: {}; unit: [millimeter, millimeter, degree]'.format(eye_file['pupil_shapes'].attrs['format']) - -if scope == 'sutter': - pup_x = frame_shape[0] - pup_pos[:, 1] -elif scope == 'deepscope': - pup_x = pup_pos[:, 1] -else: - raise LookupError('do not understand scope type.') - -pup_y = pup_pos[:,0] - -pup_pos = (pup_pos - led_pos) * pixel_size -pup_area = pup_shape[:, 0] * pup_shape[:, 1] * np.pi * pixel_size * pixel_size - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) -nwb_f.add_eyetracking_data(ts_path=eyetracking_ts_name, - pupil_x=pup_x, - pupil_y=pup_y, - pupil_area=pup_area, - module_name='eye_tracking', - unit='millimeter', - side=side, - comments=comments, - description='', - source="Jun's eyetracker with adaptive thresholding", - pupil_shape=pup_shape, - pupil_shape_meta=pup_shape_meta) -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/280_add_rois_and_traces_caiman_segmentation.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/280_add_rois_and_traces_caiman_segmentation.py deleted file mode 100644 index 2044bf7..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/280_add_rois_and_traces_caiman_segmentation.py +++ /dev/null @@ -1,173 +0,0 @@ -import os -import h5py -import numpy as np -import matplotlib.pyplot as plt -import tifffile as tf -import corticalmapping.NwbTools as nt -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia - -# for deepscope -# plane_ns = ['plane0', 'plane1', 'plane2'] -# plane_depths = [150, 100, 50] # or [300, 250, 200] - -# for single plane -plane_ns = ['plane0'] -plane_depths = [150] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -def add_rois_and_traces(data_folder, nwb_f, plane_n, imaging_depth, - mov_path='/processing/motion_correction/MotionCorrection'): - - mov_grp = nwb_f.file_pointer[mov_path + '/' + plane_n + '/corrected'] - - data_f = h5py.File(os.path.join(data_folder, 'rois_and_traces.hdf5'), 'r') - mask_arr_c = data_f['masks_center'].value - mask_arr_s = data_f['masks_surround'].value - traces_center_raw = data_f['traces_center_raw'].value - # traces_center_demixed = data_f['traces_center_demixed'].value - traces_center_subtracted = data_f['traces_center_subtracted'].value - # traces_center_dff = data_f['traces_center_dff'].value - traces_surround_raw = data_f['traces_surround_raw'].value - neuropil_r = data_f['neuropil_r'].value - neuropil_err = data_f['neuropil_err'].value - data_f.close() - - - if traces_center_raw.shape[1] != mov_grp['num_samples'].value: - raise ValueError('number of trace time points ({}) does not match frame number of ' - 'corresponding movie ({}).'.format(traces_center_raw.shape[1], mov_grp['num_samples'].value)) - - # traces_center_raw = traces_center_raw[:, :mov_grp['num_samples'].value] - # traces_center_subtracted = traces_center_subtracted[:, :mov_grp['num_samples'].value] - # traces_surround_raw = traces_surround_raw[:, :mov_grp['num_samples'].value] - - rf_img_max = tf.imread(os.path.join(data_folder, 'corrected_max_projection.tif')) - rf_img_mean = tf.imread(os.path.join(data_folder, 'corrected_mean_projection.tif')) - - print 'adding segmentation results ...' - rt_mo = nwb_f.create_module('rois_and_traces_' + plane_n) - rt_mo.set_value('imaging_depth_micron', imaging_depth) - is_if = rt_mo.create_interface('ImageSegmentation') - is_if.create_imaging_plane('imaging_plane', description='') - is_if.add_reference_image('imaging_plane', 'max_projection', rf_img_max) - is_if.add_reference_image('imaging_plane', 'mean_projection', rf_img_mean) - - for i in range(mask_arr_c.shape[0]): - curr_cen = mask_arr_c[i] - curr_cen_n = 'roi_' + ft.int2str(i, 4) - curr_cen_roi = ia.WeightedROI(curr_cen) - curr_cen_pixels_yx = curr_cen_roi.get_pixel_array() - curr_cen_pixels_xy = np.array([curr_cen_pixels_yx[:, 1], curr_cen_pixels_yx[:, 0]]).transpose() - is_if.add_roi_mask_pixels(image_plane='imaging_plane', roi_name=curr_cen_n, desc='', - pixel_list=curr_cen_pixels_xy, weights=curr_cen_roi.weights, width=512, height=512) - - curr_sur = mask_arr_s[i] - curr_sur_n = 'surround_' + ft.int2str(i, 4) - curr_sur_roi = ia.ROI(curr_sur) - curr_sur_pixels_yx = curr_sur_roi.get_pixel_array() - curr_sur_pixels_xy = np.array([curr_sur_pixels_yx[:, 1], curr_sur_pixels_yx[:, 0]]).transpose() - is_if.add_roi_mask_pixels(image_plane='imaging_plane', roi_name=curr_sur_n, desc='', - pixel_list=curr_sur_pixels_xy, weights=None, width=512, height=512) - is_if.finalize() - - - - trace_f_if = rt_mo.create_interface('Fluorescence') - seg_if_path = '/processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane' - # print seg_if_path - ts_path = mov_path + '/' + plane_n + '/corrected' - - print 'adding center fluorescence raw' - trace_raw_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_raw') - trace_raw_ts.set_data(traces_center_raw, unit='au', conversion=np.nan, resolution=np.nan) - trace_raw_ts.set_value('data_format', 'roi (row) x time (column)') - trace_raw_ts.set_value('data_range', '[-8192, 8191]') - trace_raw_ts.set_description('fluorescence traces extracted from the center region of each roi') - trace_raw_ts.set_time_as_link(ts_path) - trace_raw_ts.set_value_as_link('segmentation_interface', seg_if_path) - roi_names = ['roi_' + ft.int2str(ind, 4) for ind in range(traces_center_raw.shape[0])] - trace_raw_ts.set_value('roi_names', roi_names) - trace_raw_ts.set_value('num_samples', traces_center_raw.shape[1]) - trace_f_if.add_timeseries(trace_raw_ts) - trace_raw_ts.finalize() - - print 'adding neuropil fluorescence raw' - trace_sur_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_surround_raw') - trace_sur_ts.set_data(traces_surround_raw, unit='au', conversion=np.nan, resolution=np.nan) - trace_sur_ts.set_value('data_format', 'roi (row) x time (column)') - trace_sur_ts.set_value('data_range', '[-8192, 8191]') - trace_sur_ts.set_description('neuropil traces extracted from the surroud region of each roi') - trace_sur_ts.set_time_as_link(ts_path) - trace_sur_ts.set_value_as_link('segmentation_interface', seg_if_path) - sur_names = ['surround_' + ft.int2str(ind, 4) for ind in range(traces_center_raw.shape[0])] - trace_sur_ts.set_value('roi_names', sur_names) - trace_sur_ts.set_value('num_samples', traces_surround_raw.shape[1]) - trace_f_if.add_timeseries(trace_sur_ts) - trace_sur_ts.finalize() - - roi_center_n_path = '/processing/rois_and_traces_' + plane_n + '/Fluorescence/f_center_raw/roi_names' - # print 'adding center fluorescence demixed' - # trace_demix_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_demixed') - # trace_demix_ts.set_data(traces_center_demixed, unit='au', conversion=np.nan, resolution=np.nan) - # trace_demix_ts.set_value('data_format', 'roi (row) x time (column)') - # trace_demix_ts.set_description('center traces after overlapping demixing for each roi') - # trace_demix_ts.set_time_as_link(mov_path + '/' + plane_n + '/corrected') - # trace_demix_ts.set_value_as_link('segmentation_interface', seg_if_path) - # trace_demix_ts.set_value('roi_names', roi_names) - # trace_demix_ts.set_value('num_samples', traces_center_demixed.shape[1]) - # trace_f_if.add_timeseries(trace_demix_ts) - # trace_demix_ts.finalize() - - print 'adding center fluorescence after neuropil subtraction' - trace_sub_ts = nwb_f.create_timeseries('RoiResponseSeries', 'f_center_subtracted') - trace_sub_ts.set_data(traces_center_subtracted, unit='au', conversion=np.nan, resolution=np.nan) - trace_sub_ts.set_value('data_format', 'roi (row) x time (column)') - trace_sub_ts.set_description('center traces after overlap demixing and neuropil subtraction for each roi') - trace_sub_ts.set_time_as_link(mov_path + '/' + plane_n + '/corrected') - trace_sub_ts.set_value_as_link('segmentation_interface', seg_if_path) - trace_sub_ts.set_value_as_link('roi_names', roi_center_n_path) - trace_sub_ts.set_value('num_samples', traces_center_subtracted.shape[1]) - trace_sub_ts.set_value('r', neuropil_r, dtype='float32') - trace_sub_ts.set_value('rmse', neuropil_err, dtype='float32') - trace_sub_ts.set_comments('value "r": neuropil contribution ratio for each roi. ' - 'value "rmse": RMS error of neuropil subtraction for each roi') - trace_f_if.add_timeseries(trace_sub_ts) - trace_sub_ts.finalize() - - trace_f_if.finalize() - - # print 'adding global dF/F traces for each roi' - # trace_dff_if = rt_mo.create_interface('DfOverF') - # - # trace_dff_ts = nwb_f.create_timeseries('RoiResponseSeries', 'dff_center') - # trace_dff_ts.set_data(traces_center_dff, unit='au', conversion=np.nan, resolution=np.nan) - # trace_dff_ts.set_value('data_format', 'roi (row) x time (column)') - # trace_dff_ts.set_description('global df/f traces for each roi center, input fluorescence is the trace after demixing' - # ' and neuropil subtraction. global df/f is calculated by ' - # 'allensdk.brain_observatory.dff.compute_dff() function.') - # trace_dff_ts.set_time_as_link(ts_path) - # trace_dff_ts.set_value_as_link('segmentation_interface', seg_if_path) - # trace_dff_ts.set_value('roi_names', roi_names) - # trace_dff_ts.set_value('num_samples', traces_center_dff.shape[1]) - # trace_dff_if.add_timeseries(trace_dff_ts) - # trace_dff_ts.finalize() - # trace_dff_if.finalize() - - rt_mo.finalize() - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -for plane_i, plane_n in enumerate(plane_ns): - - print('\n\n' + plane_n) - - data_folder = os.path.join(curr_folder, plane_n) - add_rois_and_traces(data_folder, nwb_f, plane_n, imaging_depth=plane_depths[plane_i]) - -nwb_f.close() - - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/300_get_response_tables.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/300_get_response_tables.py deleted file mode 100644 index 198aedf..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/300_get_response_tables.py +++ /dev/null @@ -1,24 +0,0 @@ -import os -import corticalmapping.NwbTools as nt -import corticalmapping.core.TimingAnalysis as ta - - -strf_t_win = [-0.5, 2.] -dgc_t_win = [-1., 2.5] - -lsn_stim_name = '001_LocallySparseNoiseRetinotopicMapping' -dgc_stim_name = '003_DriftingGratingCircleRetinotopicMapping' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -nwb_f.get_drifting_grating_response_table_retinotopic_mapping(stim_name=dgc_stim_name, time_window=dgc_t_win) -nwb_f.get_spatial_temporal_receptive_field_retinotopic_mapping(stim_name=lsn_stim_name, time_window=strf_t_win) - -nwb_f.close() - - - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/310_plot_STRFs.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/310_plot_STRFs.py deleted file mode 100644 index 7fc800d..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/310_plot_STRFs.py +++ /dev/null @@ -1,65 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import h5py -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages -import corticalmapping.DatabaseTools as dt - -trace_type = 'sta_f_center_subtracted' -save_folder = 'figures' -bias = 1. - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -strf_grp = nwb_f['analysis/strf_001_LocallySparseNoiseRetinotopicMapping'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - plane_grp = strf_grp[plane_n] - pdff = PdfPages(os.path.join(save_folder, 'STRFs_' + plane_n + '.pdf')) - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - for roi_ind, roi_n in enumerate(roi_lst): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_lst))) - - curr_trace, _ = dt.get_single_trace(nwb_f=nwb_f, plane_n=plane_n, roi_n=roi_n) - if np.min(curr_trace) < bias: - add_to_trace = -np.min(curr_trace) + bias - else: - add_to_trace = 0. - - curr_strf = sca.get_strf_from_nwb(h5_grp=strf_grp[plane_n], roi_ind=roi_ind, trace_type=trace_type) - - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - - v_min, v_max = curr_strf_dff.get_data_range() - f = curr_strf_dff.plot_traces(yRange=(v_min, v_max * 1.1), figSize=(16, 10), - columnSpacing=0.002, rowSpacing=0.002) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/320_plot_zscore_RFs.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/320_plot_zscore_RFs.py deleted file mode 100644 index fece3db..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/320_plot_zscore_RFs.py +++ /dev/null @@ -1,76 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import h5py -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages -import corticalmapping.DatabaseTools as dt - -trace_type = 'sta_f_center_subtracted' -save_folder = 'figures' -is_local_dff = True -zscore_range = [-4., 4.] -t_window = [0., 0.5] -bias = 1. - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -strf_grp = nwb_f['analysis/strf_001_LocallySparseNoiseRetinotopicMapping'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - plane_grp = strf_grp[plane_n] - pdff = PdfPages(os.path.join(save_folder, 'zscore_RFs_' + plane_n + '.pdf')) - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - for roi_ind, roi_n in enumerate(roi_lst): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_lst))) - - curr_trace, _ = dt.get_single_trace(nwb_f=nwb_f, plane_n=plane_n, roi_n=roi_n) - if np.min(curr_trace) < bias: - add_to_trace = -np.min(curr_trace) + bias - else: - add_to_trace = 0. - - curr_strf = sca.get_strf_from_nwb(h5_grp=strf_grp[plane_n], roi_ind=roi_ind, trace_type=trace_type) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - # v_min, v_max = curr_strf_dff.get_data_range() - - rf_on, rf_off = curr_strf_dff.get_zscore_receptive_field(timeWindow=t_window) - f = plt.figure(figsize=(15, 4)) - f.suptitle('{}: t_window: {}'.format(roi_n, t_window)) - ax_on = f.add_subplot(121) - rf_on.plot_rf(plot_axis=ax_on, is_colorbar=True, cmap='RdBu_r', vmin=zscore_range[0], vmax=zscore_range[1]) - ax_on.set_title('ON zscore RF') - ax_off = f.add_subplot(122) - rf_off.plot_rf(plot_axis=ax_off, is_colorbar=True, cmap='RdBu_r', vmin=zscore_range[0], vmax=zscore_range[1]) - ax_off.set_title('OFF zscore RF') - plt.close() - - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/330_plot_RF_contours.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/330_plot_RF_contours.py deleted file mode 100644 index 3233965..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/330_plot_RF_contours.py +++ /dev/null @@ -1,113 +0,0 @@ -import os -import numpy as np -import h5py -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages -import corticalmapping.DatabaseTools as dt - -trace_type = 'sta_f_center_subtracted' -roi_t_window = [0., 0.5] -zscore_range = [0., 4.] -save_folder = 'figures' -bias = 1. - -# plot control -thr_ratio = 0.4 -filter_sigma = 1. -interpolate_rate = 10 -absolute_thr = 1.6 -level_num = 1 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -strf_grp = nwb_f['analysis/strf_001_LocallySparseNoiseRetinotopicMapping'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -X = None -Y = None - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - plane_grp = strf_grp[plane_n] - - f_all = plt.figure(figsize=(10, 10)) - f_all.suptitle('t window: {}; z threshold: {}'.format(roi_t_window, absolute_thr / thr_ratio)) - ax_all = f_all.add_subplot(111) - - pdff = PdfPages(os.path.join(save_folder, 'RF_contours_' + plane_n + '.pdf')) - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - for roi_ind, roi_n in enumerate(roi_lst): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_lst))) - - curr_trace, _ = dt.get_single_trace(nwb_f=nwb_f, plane_n=plane_n, roi_n=roi_n) - if np.min(curr_trace) < bias: - add_to_trace = -np.min(curr_trace) + bias - else: - add_to_trace = 0. - - curr_strf = sca.get_strf_from_nwb(h5_grp=strf_grp[plane_n], roi_ind=roi_ind, trace_type=trace_type) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - rf_on, rf_off, _ = curr_strf_dff.get_zscore_thresholded_receptive_fields(timeWindow=roi_t_window, - thr_ratio=thr_ratio, - filter_sigma=filter_sigma, - interpolate_rate=interpolate_rate, - absolute_thr=absolute_thr) - - if X is None and Y is None: - X, Y = np.meshgrid(np.arange(len(rf_on.aziPos)), - np.arange(len(rf_on.altPos))) - - levels_on = [np.max(rf_on.get_weighted_mask().flat) * thr_ratio] - levels_off = [np.max(rf_off.get_weighted_mask().flat) * thr_ratio] - ax_all.contour(X, Y, rf_on.get_weighted_mask(), levels=levels_on, colors='r', lw=5) - ax_all.contour(X, Y, rf_off.get_weighted_mask(), levels=levels_off, colors='b', lw=5) - - f_single = plt.figure(figsize=(10, 10)) - ax_single = f_single.add_subplot(111) - ax_single.contour(X, Y, rf_on.get_weighted_mask(), levels=levels_on, colors='r', lw=5) - ax_single.contour(X, Y, rf_off.get_weighted_mask(), levels=levels_off, colors='b', lw=5) - ax_single.set_xticks(range(len(rf_on.aziPos))[::20]) - ax_single.set_xticklabels(['{:3d}'.format(int(round(l))) for l in rf_on.aziPos[::20]]) - ax_single.set_yticks(range(len(rf_on.altPos))[::20]) - ax_single.set_yticklabels(['{:3d}'.format(int(round(l))) for l in rf_on.altPos[::-1][::20]]) - ax_single.set_aspect('equal') - ax_single.set_title('{}: {}. t_window: {}; ON thr:{}; OFF thr:{}.'.format(plane_n, roi_n, roi_t_window, - rf_on.thr, rf_off.thr)) - pdff.savefig(f_single) - f_single.clear() - plt.close(f_single) - - pdff.close() - - ax_all.set_xticks(range(len(rf_on.aziPos))[::20]) - ax_all.set_xticklabels(['{:3d}'.format(int(round(l))) for l in rf_on.aziPos[::20]]) - ax_all.set_yticks(range(len(rf_on.altPos))[::20]) - ax_all.set_yticklabels(['{:3d}'.format(int(round(l))) for l in rf_on.altPos[::-1][::20]]) - ax_all.set_aspect('equal') - ax_all.set_title('{}, abs_zscore_thr:{}'.format(plane_n, absolute_thr)) - - f_all.savefig(os.path.join(save_folder, 'RF_contours_' + plane_n + '_all.pdf'), dpi=300) - -nwb_f.close() - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/340_plot_dgc_response_all.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/340_plot_dgc_response_all.py deleted file mode 100644 index 21275e9..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/340_plot_dgc_response_all.py +++ /dev/null @@ -1,169 +0,0 @@ -import os -import h5py -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.core.PlottingTools as pt -from matplotlib.backends.backend_pdf import PdfPages -import matplotlib.gridspec as gridspec -import corticalmapping.DatabaseTools as dt - -trace_type = 'f_center_subtracted' -response_table_path = 'analysis/response_table_003_DriftingGratingCircleRetinotopicMapping' - -baseline_span = [-0.5, 0.] -response_span = [0., 1.] -bias = 1. - -face_cmap = 'RdBu_r' - -def get_dff(traces, t_axis, response_span, baseline_span): - """ - - :param traces: dimension, trial x timepoint - :param t_axis: - :return: - """ - - baseline_ind = np.logical_and(t_axis > baseline_span[0], t_axis <= baseline_span[1]) - response_ind = np.logical_and(t_axis > response_span[0], t_axis <= response_span[1]) - baseline = np.mean(traces[:, baseline_ind], axis=1, keepdims=True) - dff_traces = (traces - baseline) / baseline - - trace_mean = np.mean(traces, axis=0) - baseline_mean = np.mean(trace_mean[baseline_ind]) - dff_trace_mean = (trace_mean - baseline_mean) / baseline_mean - dff_mean = np.mean(dff_trace_mean[response_ind]) - - return dff_traces, dff_trace_mean, dff_mean - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, 'figures') -if not os.path.isdir(save_folder): - os.mkdir(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -print(nwb_fn) -nwb_f = h5py.File(nwb_fn, 'r') - -plane_ns = nwb_f[response_table_path].keys() -plane_ns.sort() - -for plane_n in plane_ns: - - print('\nprocessing {} ...'.format(plane_n)) - - res_grp = nwb_f['{}/{}'.format(response_table_path, plane_n)] - t_axis = res_grp.attrs['sta_timestamps'] - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - grating_ns = res_grp.keys() - - # remove blank sweep - grating_ns = [gn for gn in grating_ns if gn[-37:] != '_sf0.00_tf00.0_dire000_con0.00_rad000'] - - dire_lst = np.array(list(set([str(gn[38:41]) for gn in grating_ns]))) - dire_lst.sort() - tf_lst = np.array(list(set([str(gn[29:33]) for gn in grating_ns]))) - tf_lst.sort() - sf_lst = np.array(list(set([str(gn[22:26]) for gn in grating_ns]))) - sf_lst.sort() - - print('\nall directions (deg): {}'.format(dire_lst)) - print('all temporal frequencies (Hz): {}'.format(tf_lst)) - print('all spatial frequencies (dpd): {}\n'.format(sf_lst)) - - pdff = PdfPages(os.path.join(save_folder, 'STA_DriftingGrating_' + plane_n + '_all.pdf')) - - for roi_i, roi_n in enumerate(roi_lst): - print('plotting: {} ...'.format(roi_n)) - - curr_trace, _ = dt.get_single_trace(nwb_f=nwb_f, plane_n=plane_n, roi_n=roi_n) - if np.min(curr_trace) < bias: - add_to_trace = -np.min(curr_trace) + bias - else: - add_to_trace = 0. - - f = plt.figure(figsize=(8.5, 11)) - gs_out = gridspec.GridSpec(len(tf_lst), 1) - gs_in_dict = {} - for gs_ind, gs_o in enumerate(gs_out): - curr_gs_in = gridspec.GridSpecFromSubplotSpec(len(sf_lst), len(dire_lst), subplot_spec=gs_o, - wspace=0.0, hspace=0.0) - gs_in_dict[gs_ind] = curr_gs_in - - v_max = 0 - v_min = 0 - dff_mean_max=0 - dff_mean_min=0 - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - dff_traces, dff_trace_mean, dff_mean = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, - baseline_span=baseline_span) - v_max = max([np.amax(dff_traces), v_max]) - v_min = min([np.amin(dff_traces), v_min]) - dff_mean_max = max([dff_mean, dff_mean_max]) - dff_mean_min = min([dff_mean, dff_mean_min]) - - dff_mean_max = max([abs(dff_mean_max), abs(dff_mean_min)]) - dff_mean_min = - dff_mean_max - - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - dff_traces, dff_trace_mean, dff_mean = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, - baseline_span=baseline_span) - - curr_tf = grating_n[29:33] - tf_i = np.where(tf_lst == curr_tf)[0][0] - curr_sf = grating_n[22:26] - sf_i = np.where(sf_lst == curr_sf)[0][0] - curr_dire = grating_n[38:41] - dire_i = np.where(dire_lst == curr_dire)[0][0] - ax = plt.Subplot(f, gs_in_dict[tf_i][sf_i * len(dire_lst) + dire_i]) - f_color = pt.value_2_rgb(value=(dff_mean - dff_mean_min) / (dff_mean_max - dff_mean_min), - cmap=face_cmap) - - # f_color = pt.value_2_rgb(value=dff_mean / dff_mean_max, cmap=face_cmap) - - # print f_color - ax.set_axis_bgcolor(f_color) - ax.set_xticks([]) - ax.set_yticks([]) - for sp in ax.spines.values(): - sp.set_visible(False) - ax.axhline(y=0, ls='--', color='#888888', lw=1) - ax.axvspan(response_span[0], response_span[1], alpha=0.5, color='#888888', ec='none') - for t in dff_traces: - ax.plot(t_axis, t, '-', color='#888888', lw=0.5) - ax.plot(t_axis, dff_trace_mean, '-r', lw=1) - f.add_subplot(ax) - - all_axes = f.get_axes() - for ax in all_axes: - ax.set_ylim([v_min, v_max]) - ax.set_xlim([t_axis[0], t_axis[-1]]) - - f.suptitle('roi:{:04d}; trace type:{}; baseline:{}; response:{}; \ntrace range:{}; color range:{}' - .format(roi_i, trace_type, baseline_span, response_span, [v_min, v_max], - [dff_mean_min, dff_mean_max]), fontsize=8) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() -nwb_f.close() - -print('done!') \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/350_plot_dgc_response_mean.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/350_plot_dgc_response_mean.py deleted file mode 100644 index f8967cc..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/350_plot_dgc_response_mean.py +++ /dev/null @@ -1,158 +0,0 @@ -import os -import h5py -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.core.PlottingTools as pt -from matplotlib.backends.backend_pdf import PdfPages -import matplotlib.gridspec as gridspec -import corticalmapping.DatabaseTools as dt - -trace_type = 'f_center_subtracted' -response_table_path = 'analysis/response_table_003_DriftingGratingCircleRetinotopicMapping' - -baseline_span = [-0.5, 0.] -response_span = [0., 1.] -bias = 1. - -face_cmap = 'RdBu_r' - -def get_dff(traces, t_axis, response_span, baseline_span): - """ - - :param traces: dimension, trial x timepoint - :param t_axis: - :return: - """ - - trace_mean = np.mean(traces, axis=0) - trace_std = np.std(traces, axis=0) - trace_sem = trace_std / np.sqrt(traces.shape[0]) - - baseline_ind = np.logical_and(t_axis > baseline_span[0], t_axis <= baseline_span[1]) - response_ind = np.logical_and(t_axis > response_span[0], t_axis <= response_span[1]) - baseline = np.mean(trace_mean[baseline_ind]) - dff_trace_mean = (trace_mean - baseline) / baseline - dff_trace_std = trace_std / baseline - dff_trace_sem = trace_sem / baseline - dff_mean = np.mean(dff_trace_mean[response_ind]) - - return dff_trace_mean, dff_trace_std, dff_trace_sem, dff_mean - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, 'figures') -if not os.path.isdir(save_folder): - os.mkdir(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -print(nwb_fn) -nwb_f = h5py.File(nwb_fn, 'r') - -plane_ns = nwb_f[response_table_path].keys() -plane_ns.sort() - -for plane_n in plane_ns: - - print('\nprocessing {} ...'.format(plane_n)) - - res_grp = nwb_f['{}/{}'.format(response_table_path, plane_n)] - t_axis = res_grp.attrs['sta_timestamps'] - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - grating_ns = res_grp.keys() - # remove blank sweep - grating_ns = [gn for gn in grating_ns if gn[-37:] != '_sf0.00_tf00.0_dire000_con0.00_rad000'] - - dire_lst = np.array(list(set([str(gn[38:41]) for gn in grating_ns]))) - dire_lst.sort() - tf_lst = np.array(list(set([str(gn[29:33]) for gn in grating_ns]))) - tf_lst.sort() - sf_lst = np.array(list(set([str(gn[22:26]) for gn in grating_ns]))) - sf_lst.sort() - - pdff = PdfPages(os.path.join(save_folder, 'STA_DriftingGrating_' + plane_n + '_mean.pdf')) - - for roi_i, roi_n in enumerate(roi_lst): - print(roi_n) - - curr_trace, _ = dt.get_single_trace(nwb_f=nwb_f, plane_n=plane_n, roi_n=roi_n) - if np.min(curr_trace) < bias: - add_to_trace = -np.min(curr_trace) + bias - else: - add_to_trace = 0. - - f = plt.figure(figsize=(8.5, 11)) - gs_out = gridspec.GridSpec(len(tf_lst), 1) - gs_in_dict = {} - for gs_ind, gs_o in enumerate(gs_out): - curr_gs_in = gridspec.GridSpecFromSubplotSpec(len(sf_lst), len(dire_lst), subplot_spec=gs_o, - wspace=0.05, hspace=0.05) - gs_in_dict[gs_ind] = curr_gs_in - - v_max = 0 - v_min = 0 - dff_mean_max=0 - dff_mean_min=0 - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - _ = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, baseline_span=baseline_span) - dff_trace_mean, dff_trace_std, dff_trace_sem, dff_mean = _ - v_max = max([np.amax(dff_trace_mean + dff_trace_sem), v_max]) - v_min = min([np.amin(dff_trace_mean - dff_trace_sem), v_min]) - dff_mean_max = max([dff_mean, dff_mean_max]) - dff_mean_min = min([dff_mean, dff_mean_min]) - dff_mean_max = max([abs(dff_mean_max), abs(dff_mean_min)]) - dff_mean_min = - dff_mean_max - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - _ = get_dff(traces=curr_sta, t_axis=t_axis, response_span=response_span, baseline_span=baseline_span) - dff_trace_mean, dff_trace_std, dff_trace_sem, dff_mean = _ - curr_tf = grating_n[29:33] - tf_i = np.where(tf_lst == curr_tf)[0][0] - curr_sf = grating_n[22:26] - sf_i = np.where(sf_lst == curr_sf)[0][0] - curr_dire = grating_n[38:41] - dire_i = np.where(dire_lst == curr_dire)[0][0] - ax = plt.Subplot(f, gs_in_dict[tf_i][sf_i * len(dire_lst) + dire_i]) - f_color = pt.value_2_rgb(value=(dff_mean - dff_mean_min) / (dff_mean_max - dff_mean_min), - cmap=face_cmap) - - # f_color = pt.value_2_rgb(value=dff_mean / dff_mean_max, cmap=face_cmap) - - # print f_color - ax.set_axis_bgcolor(f_color) - ax.set_xticks([]) - ax.set_yticks([]) - for sp in ax.spines.values(): - sp.set_visible(False) - ax.axhline(y=0, ls='--', color='#888888', lw=1) - ax.axvspan(response_span[0], response_span[1], alpha=0.5, color='#888888', ec='none') - ax.fill_between(t_axis, dff_trace_mean - dff_trace_sem, dff_trace_mean + dff_trace_sem, edgecolor='none', - facecolor='#880000', alpha=0.5) - ax.plot(t_axis, dff_trace_mean, '-r', lw=1) - f.add_subplot(ax) - - all_axes = f.get_axes() - for ax in all_axes: - ax.set_ylim([v_min, v_max]) - ax.set_xlim([t_axis[0], t_axis[-1]]) - - f.suptitle('roi:{:04d}; trace type:{}; baseline:{}; response:{}; \ntrace range:{}; color range:{}' - .format(roi_i, trace_type, baseline_span, response_span, [v_min, v_max], - [dff_mean_min, dff_mean_max]), fontsize=8) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/360_plot_dgc_tuning_curves.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/360_plot_dgc_tuning_curves.py deleted file mode 100644 index 1efaf2f..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/360_plot_dgc_tuning_curves.py +++ /dev/null @@ -1,198 +0,0 @@ -import os -import h5py -import numpy as np -import pandas as pd -import matplotlib.pyplot as plt -from matplotlib.backends.backend_pdf import PdfPages -import corticalmapping.DatabaseTools as dt - -trace_type = 'f_center_subtracted' -response_table_path = 'analysis/response_table_003_DriftingGratingCircleRetinotopicMapping' - -baseline_span = [-0.5, 0.] -response_span = [0., 1.5] -bias = 1. - -def get_response(traces, t_axis, response_span, baseline_span): - """ - - :param traces: dimension, trial x timepoint - :param t_axis: - :return: - """ - - baseline_ind = np.logical_and(t_axis > baseline_span[0], t_axis <= baseline_span[1]) - response_ind = np.logical_and(t_axis > response_span[0], t_axis <= response_span[1]) - - trace_mean = np.mean(traces, axis=0) - baseline_mean = np.mean(trace_mean[baseline_ind]) - dff_trace_mean = (trace_mean - baseline_mean) / baseline_mean - dff_mean = np.mean(dff_trace_mean[response_ind]) - - baselines = np.mean(traces[:, baseline_ind], axis=1, keepdims=True) - dff_traces = (traces - baselines) / baselines - dffs = np.mean(dff_traces[:, response_ind], axis=1) - dff_std = np.std(dffs) - dff_sem = dff_std / np.sqrt(traces.shape[0]) - - return dff_mean, dff_std, dff_sem - - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, 'figures') -if not os.path.isdir(save_folder): - os.mkdir(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -print(nwb_fn) -nwb_f = h5py.File(nwb_fn, 'r') - -plane_ns = nwb_f[response_table_path].keys() -plane_ns.sort() - -for plane_n in plane_ns: - - print('\nprocessing {} ...'.format(plane_n)) - - res_grp = nwb_f['{}/{}'.format(response_table_path, plane_n)] - t_axis = res_grp.attrs['sta_timestamps'] - - roi_lst = nwb_f['processing/rois_and_traces_' + plane_n + '/ImageSegmentation/imaging_plane/roi_list'].value - roi_lst = [r for r in roi_lst if r[:4] == 'roi_'] - roi_lst.sort() - - grating_ns = res_grp.keys() - - # remove blank sweep - grating_ns = [gn for gn in grating_ns if gn[-37:] != '_sf0.00_tf00.0_dire000_con0.00_rad000'] - - dire_lst = np.array(list(set([str(gn[38:41]) for gn in grating_ns]))) - dire_lst.sort() - tf_lst = np.array(list(set([str(gn[29:33]) for gn in grating_ns]))) - tf_lst.sort() - sf_lst = np.array(list(set([str(gn[22:26]) for gn in grating_ns]))) - sf_lst.sort() - - pdff = PdfPages(os.path.join(save_folder, 'tuning_curve_DriftingGrating_' + plane_n + '_mean.pdf')) - - for roi_i, roi_n in enumerate(roi_lst): - print(roi_n) - - curr_trace, _ = dt.get_single_trace(nwb_f=nwb_f, plane_n=plane_n, roi_n=roi_n) - if np.min(curr_trace) < bias: - add_to_trace = -np.min(curr_trace) + bias - else: - add_to_trace = 0. - - # get response table - res_tab = pd.DataFrame(columns=['con', 'tf', 'sf', 'dire', 'dff_mean', 'dff_std', 'dff_sem']) - row_ind = 0 - - for grating_n in grating_ns: - grating_grp = res_grp[grating_n] - curr_sta = grating_grp['sta_' + trace_type].value[roi_i] + add_to_trace - _ = get_response(traces=curr_sta, t_axis=t_axis, response_span=response_span, baseline_span=baseline_span) - dff_mean, dff_std, dff_sem = _ - - con = float(grating_n.split('_')[5][3:]) - tf = float(grating_n.split('_')[3][2:]) - sf = float(grating_n.split('_')[2][2:]) - dire = int(grating_n.split('_')[4][4:]) - - res_tab.loc[row_ind] = [con, tf, sf, dire, dff_mean, dff_std, dff_sem] - row_ind += 1 - - # find the preferred condition - top_condition = res_tab[res_tab.dff_mean == max(res_tab.dff_mean)] - - # make figure - f = plt.figure(figsize=(8.5, 11)) - - # get tf plot - tf_conditions = res_tab[(res_tab.sf == float(top_condition.sf)) & \ - (res_tab.dire == int(top_condition.dire))] - tf_conditions = tf_conditions.sort_values(by='tf') - - tf_log = np.log(tf_conditions.tf) - - ax_tf = f.add_subplot(311) - ax_tf.fill_between(x=tf_log, y1=tf_conditions.dff_mean + tf_conditions.dff_sem, - y2=tf_conditions.dff_mean - tf_conditions.dff_sem, edgecolor='none', - facecolor='#888888', alpha=0.5) - ax_tf.axhline(y=0, ls='--', color='k', lw=1) - ax_tf.plot(tf_log, tf_conditions.dff_mean, 'r-', lw=2) - ax_tf.set_title('temporal frequency tuning', rotation='vertical', x=-0.4, y=0.5, va='center', ha='center', - size=10) - ax_tf.set_xticks(tf_log) - ax_tf.set_xticklabels(list(tf_conditions.tf)) - ax_tf.set_xlim(np.log([0.9, 16])) - ax_tf_xrange = ax_tf.get_xlim()[1] - ax_tf.get_xlim()[0] - ax_tf_yrange = ax_tf.get_ylim()[1] - ax_tf.get_ylim()[0] - ax_tf.set_aspect(aspect=(ax_tf_xrange / ax_tf_yrange)) - ax_tf.set_ylabel('mean df/f', size=10) - ax_tf.set_xlabel('temporal freqency (Hz)', size=10) - ax_tf.tick_params(axis='both', which='major', labelsize=10) - - # get sf plot - sf_conditions = res_tab[(res_tab.tf == float(top_condition.tf)) & \ - (res_tab.dire == int(top_condition.dire))] - sf_conditions = sf_conditions.sort_values(by='sf') - - sf_log = np.log(sf_conditions.sf) - - ax_sf = f.add_subplot(312) - ax_sf.fill_between(x=sf_log, y1=sf_conditions.dff_mean + sf_conditions.dff_sem, - y2=sf_conditions.dff_mean - sf_conditions.dff_sem, edgecolor='none', - facecolor='#888888', alpha=0.5) - ax_sf.axhline(y=0, ls='--', color='k', lw=1) - ax_sf.plot(sf_log, sf_conditions.dff_mean, '-r', lw=2) - ax_sf.set_title('spatial frequency tuning', rotation='vertical', x=-0.4, y=0.5, va='center', ha='center', - size=10) - ax_sf.set_xticks(sf_log) - ax_sf.set_xticklabels(['{:04.2f}'.format(s) for s in list(sf_conditions.sf)]) - ax_sf.set_xlim(np.log([0.008, 0.4])) - ax_sf_xrange = ax_sf.get_xlim()[1] - ax_sf.get_xlim()[0] - ax_sf_yrange = ax_sf.get_ylim()[1] - ax_sf.get_ylim()[0] - ax_sf.set_aspect(aspect=(ax_sf_xrange / ax_sf_yrange)) - ax_sf.set_ylabel('mean df/f', size=10) - ax_sf.set_xlabel('spatial freqency (cpd)', size=10) - ax_sf.tick_params(axis='both', which='major', labelsize=10) - - # get dire plot - dire_conditions = res_tab[(res_tab.tf == float(top_condition.tf)) & \ - (res_tab.sf == float(top_condition.sf))] - dire_conditions = dire_conditions.sort_values(by='dire') - dire_arc = list(dire_conditions.dire * np.pi / 180.) - dire_arc.append(dire_arc[0]) - dire_dff = np.array(dire_conditions.dff_mean) - dire_dff[dire_dff < 0.] = 0. - dire_dff = list(dire_dff) - dire_dff.append(dire_dff[0]) - dire_dff_sem = list(dire_conditions.dff_sem) - dire_dff_sem.append(dire_dff_sem[0]) - dire_dff_low = np.array(dire_dff) - np.array(dire_dff_sem) - dire_dff_low[dire_dff_low < 0.] = 0. - dire_dff_high = np.array(dire_dff) + np.array(dire_dff_sem) - - r_ticks = [0, round(max(dire_dff) * 10000.) / 10000.] - - ax_dire = f.add_subplot(313, projection='polar') - ax_dire.fill_between(x=dire_arc, y1=dire_dff_low, y2=dire_dff_high, edgecolor='none', facecolor='#888888', - alpha=0.5) - ax_dire.plot(dire_arc, dire_dff, '-r', lw=2) - ax_dire.set_title('orientation tuning', rotation='vertical', x=-0.4, y=0.5, va='center', ha='center', size=10) - ax_dire.set_rticks(r_ticks) - ax_dire.set_xticks(dire_arc) - ax_dire.tick_params(axis='both', which='major', labelsize=10) - - f.suptitle('roi:{:04d}; trace type:{}; baseline:{}; response:{}' - .format(roi_i, trace_type, baseline_span, response_span), fontsize=10) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/batch_generate_nwb.bat b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/batch_generate_nwb.bat deleted file mode 100644 index 878d0e8..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/batch_generate_nwb.bat +++ /dev/null @@ -1,16 +0,0 @@ -call activate bigmess - -set PYTHONPATH=%PYTHONPATH%;E:\data\python_packages\corticalmapping;E:\data\python_packages\allensdk_internal;E:\data\python_packages\ainwb\ainwb;E:\data\github_packages\retinotopic_mapping; - -python 200_generate_nwb.py -python 210_add_vasmap.py -python 220_add_sync_data.py -python 230_add_image_data.py -python 240_add_motion_correction_module.py -python 250_get_photodiode_onset.py -python 260_add_visual_stimuli_retinotopic_mapping.py -python 270_analyze_photodiode_onsets.py -python 275_add_eyetracking.py -python 280_add_rois_and_traces_caiman_segmentation.py - -PAUSE \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/090_(optional)_show_mmap_movie.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/090_(optional)_show_mmap_movie.py deleted file mode 100644 index e38d1c4..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/090_(optional)_show_mmap_movie.py +++ /dev/null @@ -1,35 +0,0 @@ -import sys; print('Python %s on %s' % (sys.version, sys.platform)) -sys.path.extend([r"E:\data\github_packages\CaImAn"]) - -import os -import numpy as np -import caiman as cm -import matplotlib.pyplot as plt - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180813-M386444-2p\movie_2p\110_LSN_reorged\plane0\green\corrected" -fn = '180813_M386444_110_d1_512_d2_512_d3_1_order_C_frames_3300_.mmap' - -fn_parts = fn.split('_') -d1 = int(fn_parts[fn_parts.index('d1') + 1]) # column, x -d2 = int(fn_parts[fn_parts.index('d2') + 1]) # row, y -d3 = int(fn_parts[fn_parts.index('d3') + 1]) # channel -d4 = int(fn_parts[fn_parts.index('frames') + 1]) # frame, T -order = fn_parts[fn_parts.index('order') + 1] - -mov = np.memmap(filename=os.path.join(data_folder, fn), shape=(d1, d2, d4), order=order, dtype=np.float32, mode='r') -mov = mov.transpose((2, 1, 0)) - -print('movie shape: {}'.format(mov.shape)) - -f = plt.figure(figsize=(8, 5)) -ax = f.add_subplot(111) -fig = ax.imshow(np.mean(mov, axis=0), vmin=300, vmax=1500, cmap='inferno', interpolation='nearest') -f.colorbar(fig) -plt.show() - -input("Press enter to continue ...") - -print('playing {} ...'.format(fn)) -cm.movie(mov).play(fr=30,magnification=1,gain=2.) - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/290_get_drifting_grating_response_tables.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/290_get_drifting_grating_response_tables.py deleted file mode 100644 index 6d8f02e..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/290_get_drifting_grating_response_tables.py +++ /dev/null @@ -1,18 +0,0 @@ -import os -import h5py -import numpy as np -import corticalmapping.NwbTools as nt - -# plane_ns = ['plane0', 'plane1', 'plane2', 'plane3', 'plane4'] -stim_name = '003_DriftingGratingCircleRetinotopicMapping' -t_win = [-1., 2.5] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -nwb_f.get_drifting_grating_response_table_retinotopic_mapping(stim_name=stim_name, time_window=t_win) - -nwb_f.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/300_get_STRFs.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/300_get_STRFs.py deleted file mode 100644 index 3abfef7..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/300_get_STRFs.py +++ /dev/null @@ -1,101 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -stim_name = '001_LocallySparseNoiseRetinotopicMapping' -trace_source = 'f_center_subtracted' -start_time = -0.5 -end_time = 2. - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = nt.RecordedFile(nwb_fn) - -# deleting existing strf group -if 'STRFs' in nwb_f.file_pointer['analysis']: - del nwb_f.file_pointer['analysis/STRFs'] - -probe_grp = nwb_f.file_pointer['analysis/photodiode_onsets/' + stim_name] -probe_ns = probe_grp.keys() -probe_ns.sort() - -probe_locations = [[float(pn[3: 9]), float(pn[13: 19])] for pn in probe_ns] -probe_signs = [float(pn[-2:]) for pn in probe_ns] -# print(probe_locations) - -plane_ns = nwb_f.file_pointer['processing'].keys() -plane_ns = [pn.split('_')[-1] for pn in plane_ns if 'rois_and_traces_plane' in pn] -plane_ns.sort() -print('\n'.join(plane_ns)) - -strf_grp = nwb_f.file_pointer['analysis'].create_group('STRFs') - -for plane_n in plane_ns: - print('\ngetting STRFs for {} ...'.format(plane_n)) - - roi_ns = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + - '/ImageSegmentation/imaging_plane/roi_list'].value - roi_ns = [rn for rn in roi_ns if rn[0: 4] == 'roi_'] - roi_ns.sort() - roi_num = len(roi_ns) - - plane_strf_grp = strf_grp.create_group(plane_n) - plane_traces = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + '/Fluorescence/' + - trace_source + '/data'].value - plane_trace_ts = nwb_f.file_pointer['processing/rois_and_traces_' + plane_n + '/Fluorescence/' + - trace_source + '/timestamps'].value - - plane_mean_frame_dur = np.mean(np.diff(plane_trace_ts)) - plane_chunk_frame_dur = int(np.ceil((end_time - start_time) / plane_mean_frame_dur)) - plane_chunk_frame_start = int(np.floor(start_time / plane_mean_frame_dur)) - plane_t = (np.arange(plane_chunk_frame_dur) + plane_chunk_frame_start) * plane_mean_frame_dur - print '{}: STRF time axis: \n{}'.format(plane_n, plane_t) - - plane_roi_traces = [] - trigger_ts_lst = [] - - for probe_ind, probe_n in enumerate(probe_ns): - - probe_ts = probe_grp[probe_n]['pd_onset_ts_sec'].value - trigger_ts_lst.append(probe_ts) - probe_traces = [] - for curr_probe_ts in probe_ts: - curr_frame_start = ta.find_nearest(plane_trace_ts, curr_probe_ts) + plane_chunk_frame_start - curr_frame_end = curr_frame_start + plane_chunk_frame_dur - if curr_frame_start >= 0 and curr_frame_end <= len(plane_trace_ts): - probe_traces.append(plane_traces[:, curr_frame_start: curr_frame_end]) - - plane_roi_traces.append(np.array(probe_traces)) - print('probe: {} / {}; shape: {}'.format(probe_ind + 1, len(probe_ns), np.array(probe_traces).shape)) - - # plane_roi_traces = np.array(plane_roi_traces) - - print('saving ...') - for roi_ind in range(roi_num): - - print "roi: {} / {}".format(roi_ind + 1, roi_num) - curr_unit_traces = [pt[:, roi_ind, :] for pt in plane_roi_traces] - curr_unit_traces = [list(t) for t in curr_unit_traces] - curr_strf = sca.SpatialTemporalReceptiveField(locations=probe_locations, - signs=probe_signs, - traces=curr_unit_traces, - time=plane_t, - # trigger_ts=trigger_ts_lst, - trigger_ts=None, - name='roi_{:04d}'.format(roi_ind), - trace_data_type=trace_source) - - curr_strf_grp = plane_strf_grp.create_group('strf_roi_{:04d}'.format(roi_ind)) - curr_strf.to_h5_group(curr_strf_grp) - -nwb_f.close() - - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/310_plot_STRFs.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/310_plot_STRFs.py deleted file mode 100644 index b227207..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/310_plot_STRFs.py +++ /dev/null @@ -1,61 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import h5py -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -save_folder = 'figures' -is_add_to_traces = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -strf_grp = nwb_f['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File(os.path.join(plane_n, "caiman_segmentation_results.hdf5"), - 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - pdff = PdfPages(os.path.join(save_folder, 'STRFs_' + plane_n + '.pdf')) - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - - v_min, v_max = curr_strf_dff.get_data_range() - f = curr_strf_dff.plot_traces(yRange=(v_min, v_max * 1.1), figSize=(16, 10), - columnSpacing=0.002, rowSpacing=0.002) - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/320_plot_zscore_RFs.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/320_plot_zscore_RFs.py deleted file mode 100644 index f870f5f..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/320_plot_zscore_RFs.py +++ /dev/null @@ -1,72 +0,0 @@ -import os -import numpy as np -import matplotlib.pyplot as plt -import h5py -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -save_folder = 'figures' -is_local_dff = True -zscore_range = [-4., 4.] -t_window = [0., 0.5] -is_add_to_traces = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'][0] -nwb_f = h5py.File(nwb_fn, 'r') - -strf_grp = nwb_f['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File(os.path.join(plane_n, "caiman_segmentation_results.hdf5"), - 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - pdff = PdfPages(os.path.join(save_folder, 'zscore_RFs_' + plane_n + '.pdf')) - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - # v_min, v_max = curr_strf_dff.get_data_range() - - rf_on, rf_off = curr_strf_dff.get_zscore_receptive_field(timeWindow=t_window) - f = plt.figure(figsize=(15, 4)) - f.suptitle('{}: t_window: {}'.format(roi_n, t_window)) - ax_on = f.add_subplot(121) - rf_on.plot_rf(plot_axis=ax_on, is_colorbar=True, cmap='RdBu_r', vmin=zscore_range[0], vmax=zscore_range[1]) - ax_on.set_title('ON zscore RF') - ax_off = f.add_subplot(122) - rf_off.plot_rf(plot_axis=ax_off, is_colorbar=True, cmap='RdBu_r', vmin=zscore_range[0], vmax=zscore_range[1]) - ax_off.set_title('OFF zscore RF') - plt.close() - - # plt.show() - pdff.savefig(f) - f.clear() - plt.close(f) - - pdff.close() - -nwb_f.close() diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/330_plot_RF_contours.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/330_plot_RF_contours.py deleted file mode 100644 index d45003b..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/old/330_plot_RF_contours.py +++ /dev/null @@ -1,115 +0,0 @@ -import os -import numpy as np -import h5py -import matplotlib.pyplot as plt -import corticalmapping.NwbTools as nt -import corticalmapping.core.TimingAnalysis as ta -import corticalmapping.SingleCellAnalysis as sca -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -from matplotlib.backends.backend_pdf import PdfPages - -roi_t_window = [0., 0.5] -zscore_range = [0., 4.] -save_folder = 'figures' -is_add_to_traces = True - -# plot control -thr_ratio = 0.4 -filter_sigma = 1. -interpolate_rate = 10 -absolute_thr = 1.6 -level_num = 1 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -save_folder = os.path.join(curr_folder, save_folder) -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -nwb_fn = [f for f in os.listdir(curr_folder) if f[-4:] == '.nwb'] -print('\n'.join(nwb_fn)) - -if len(nwb_fn) != 1: - raise LookupError - -nwb_fn = nwb_fn[0] -rff = h5py.File(nwb_fn, 'r') - -strf_grp = rff['analysis/STRFs'] -plane_ns = strf_grp.keys() -plane_ns.sort() -print('planes:') -print('\n'.join(plane_ns)) - -X = None -Y = None - -for plane_n in plane_ns: - print('plotting rois in {} ...'.format(plane_n)) - - if is_add_to_traces: - add_to_trace = h5py.File(os.path.join(plane_n, "caiman_segmentation_results.hdf5"), - 'r')['bias_added_to_movie'].value - else: - add_to_trace = 0. - - plane_grp = strf_grp[plane_n] - - roi_ns = [rn[-8:] for rn in plane_grp.keys()] - roi_ns.sort() - - f_all = plt.figure(figsize=(10, 10)) - f_all.suptitle('t window: {}; z threshold: {}'.format(roi_t_window, absolute_thr/thr_ratio)) - ax_all = f_all.add_subplot(111) - - pdff = PdfPages(os.path.join(save_folder, 'RF_contours_' + plane_n + '.pdf')) - - for roi_ind, roi_n in enumerate(roi_ns): - print('roi: {} / {}'.format(roi_ind + 1, len(roi_ns))) - curr_strf = sca.SpatialTemporalReceptiveField.from_h5_group(plane_grp['strf_' + roi_n]) - curr_strf_dff = curr_strf.get_local_dff_strf(is_collaps_before_normalize=True, add_to_trace=add_to_trace) - rf_on, rf_off, _ = curr_strf_dff.get_zscore_thresholded_receptive_fields(timeWindow=roi_t_window, - thr_ratio=thr_ratio, - filter_sigma=filter_sigma, - interpolate_rate=interpolate_rate, - absolute_thr=absolute_thr) - - if X is None and Y is None: - X, Y = np.meshgrid(np.arange(len(rf_on.aziPos)), - np.arange(len(rf_on.altPos))) - - levels_on = [np.max(rf_on.get_weighted_mask().flat) * thr_ratio] - levels_off = [np.max(rf_off.get_weighted_mask().flat) * thr_ratio] - ax_all.contour(X, Y, rf_on.get_weighted_mask(), levels=levels_on, colors='r', lw=5) - ax_all.contour(X, Y, rf_off.get_weighted_mask(), levels=levels_off, colors='b', lw=5) - - f_single = plt.figure(figsize=(10, 10)) - ax_single = f_single.add_subplot(111) - ax_single.contour(X, Y, rf_on.get_weighted_mask(), levels=levels_on, colors='r', lw=5) - ax_single.contour(X, Y, rf_off.get_weighted_mask(), levels=levels_off, colors='b', lw=5) - ax_single.set_xticks(range(len(rf_on.aziPos))[::20]) - ax_single.set_xticklabels(['{:3d}'.format(int(round(l))) for l in rf_on.aziPos[::20]]) - ax_single.set_yticks(range(len(rf_on.altPos))[::20]) - ax_single.set_yticklabels(['{:3d}'.format(int(round(l))) for l in rf_on.altPos[::-1][::20]]) - ax_single.set_aspect('equal') - ax_single.set_title('{}: {}. t_window: {}; ON thr:{}; OFF thr:{}.'.format(plane_n, roi_n, roi_t_window, - rf_on.thr, rf_off.thr)) - pdff.savefig(f_single) - f_single.clear() - plt.close(f_single) - - pdff.close() - - ax_all.set_xticks(range(len(rf_on.aziPos))[::20]) - ax_all.set_xticklabels(['{:3d}'.format(int(round(l))) for l in rf_on.aziPos[::20]]) - ax_all.set_yticks(range(len(rf_on.altPos))[::20]) - ax_all.set_yticklabels(['{:3d}'.format(int(round(l))) for l in rf_on.altPos[::-1][::20]]) - ax_all.set_aspect('equal') - ax_all.set_title('{}, abs_zscore_thr:{}'.format(plane_n, absolute_thr)) - - f_all.savefig(os.path.join(save_folder, 'RF_contours_' + plane_n + '_all.pdf'), dpi=300) - -rff.close() - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/120_get_cells_file_bouton.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/120_get_cells_file_bouton.py deleted file mode 100644 index 7366ec2..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/120_get_cells_file_bouton.py +++ /dev/null @@ -1,87 +0,0 @@ -import os -import numpy as np -import h5py -import tifffile as tf -import allensdk_internal.brain_observatory.mask_set as mask_set -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import scipy.ndimage as ni -import matplotlib.pyplot as plt - - -isSave = True -is_filter = True - -filter_sigma = 0. # parameters only used if filter the rois -# dilation_iterations = 1. # parameters only used if filter the rois -cut_thr = 2.5 # low for more rois, high for less rois - -bg_fn = "corrected_mean_projections.tif" -save_folder = 'figures' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -data_f = h5py.File('caiman_segmentation_results.hdf5') -masks = data_f['masks'].value -data_f.close() - -bg = ia.array_nor(np.max(tf.imread(bg_fn), axis=0)) - -final_roi_dict = {} - -for i, mask in enumerate(masks): - - if is_filter: - mask_nor = (mask - np.mean(mask.flatten())) / np.abs(np.std(mask.flatten())) - mask_nor_f = ni.filters.gaussian_filter(mask_nor, filter_sigma) - mask_bin = np.zeros(mask_nor_f.shape, dtype=np.uint8) - mask_bin[mask_nor_f > cut_thr] = 1 - - else: - mask_bin = np.zeros(mask.shape, dtype=np.uint8) - mask_bin[mask > 0] = 1 - - mask_labeled, mask_num = ni.label(mask_bin) - curr_mask_dict = ia.get_masks(labeled=mask_labeled, keyPrefix='caiman_mask_{:03d}'.format(i), labelLength=5) - for roi_key, roi_mask in curr_mask_dict.items(): - final_roi_dict.update({roi_key: ia.WeightedROI(roi_mask * mask)}) - -print 'Total number of ROIs:',len(final_roi_dict) - -f = plt.figure(figsize=(15, 8)) -ax1 = f.add_subplot(121) -ax1.imshow(bg, vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') -colors1 = pt.random_color(masks.shape[0]) -for i, mask in enumerate(masks): - pt.plot_mask_borders(mask, plotAxis=ax1, color=colors1[i]) -ax1.set_title('original ROIs') -ax1.set_axis_off() -ax2 = f.add_subplot(122) -ax2.imshow(ia.array_nor(bg), vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') -colors2 = pt.random_color(len(final_roi_dict)) -i = 0 -for roi in final_roi_dict.values(): - pt.plot_mask_borders(roi.get_binary_mask(), plotAxis=ax2, color=colors2[i]) - i = i + 1 -ax2.set_title('filtered ROIs') -ax2.set_axis_off() -plt.show() - -if isSave: - - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - f.savefig(os.path.join(save_folder, 'caiman_segmentation_filtering.pdf'), dpi=300) - - cell_file = h5py.File('cells.hdf5', 'w') - - i = 0 - for key, value in sorted(final_roi_dict.iteritems()): - curr_grp = cell_file.create_group('cell{:04d}'.format(i)) - curr_grp.attrs['name'] = key - value.to_h5_group(curr_grp) - i += 1 - - cell_file.close() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/120_get_cells_file_soma.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/120_get_cells_file_soma.py deleted file mode 100644 index e97e5d5..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/120_get_cells_file_soma.py +++ /dev/null @@ -1,81 +0,0 @@ -import os -import numpy as np -import h5py -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import scipy.ndimage as ni -import matplotlib.pyplot as plt -import corticalmapping.HighLevel as hl - -plt.ioff() - -def run(): - isSave = True - - filter_sigma = 2. # parameters only used if filter the rois - thr_high = 0.0 - thr_low = 0.1 - - bg_fn = "corrected_mean_projections.tif" - save_folder = 'figures' - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - data_f = h5py.File('caiman_segmentation_results.hdf5') - masks = data_f['masks'].value - data_f.close() - - bg = ia.array_nor(np.max(tf.imread(bg_fn), axis=0)) - - final_roi_dict = {} - - roi_ind = 0 - for i, mask in enumerate(masks): - mask_dict = hl.threshold_mask_by_energy(mask, sigma=filter_sigma, thr_high=thr_high, thr_low=thr_low) - for mask_roi in mask_dict.values(): - final_roi_dict.update({'roi_{:04d}'.format(roi_ind): mask_roi}) - roi_ind += 1 - - print 'Total number of ROIs:',len(final_roi_dict) - - f = plt.figure(figsize=(15, 8)) - ax1 = f.add_subplot(121) - ax1.imshow(bg, vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') - colors1 = pt.random_color(masks.shape[0]) - for i, mask in enumerate(masks): - pt.plot_mask_borders(mask, plotAxis=ax1, color=colors1[i]) - ax1.set_title('original ROIs') - ax1.set_axis_off() - ax2 = f.add_subplot(122) - ax2.imshow(ia.array_nor(bg), vmin=0, vmax=0.5, cmap='gray', interpolation='nearest') - colors2 = pt.random_color(len(final_roi_dict)) - i = 0 - for roi in final_roi_dict.values(): - pt.plot_mask_borders(roi.get_binary_mask(), plotAxis=ax2, color=colors2[i]) - i = i + 1 - ax2.set_title('filtered ROIs') - ax2.set_axis_off() - # plt.show() - - if isSave: - - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - f.savefig(os.path.join(save_folder, 'caiman_segmentation_filtering.pdf'), dpi=300) - - cell_file = h5py.File('cells.hdf5', 'w') - - i = 0 - for key, value in sorted(final_roi_dict.iteritems()): - curr_grp = cell_file.create_group('cell{:04d}'.format(i)) - curr_grp.attrs['name'] = key - value.to_h5_group(curr_grp) - i += 1 - - cell_file.close() - -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/130_refine_cells_bouton.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/130_refine_cells_bouton.py deleted file mode 100644 index 925d1dd..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/130_refine_cells_bouton.py +++ /dev/null @@ -1,173 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Jun 30 17:44:42 2015 - -@author: junz -""" -import os -import h5py -import numpy as np -import operator -import matplotlib.pyplot as plt -import scipy.ndimage as ni -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.FileTools as ft -import corticalmapping.core.PlottingTools as pt -import corticalmapping.SingleCellAnalysis as sca - -plt.ioff() - -# pixels, masks with center location within this pixel region at the image border will be discarded -center_margin = [10, 10, 10, 20] # [top margin, bottom margin, left margin, right margin] - -# area range, range of number of pixels of a valid roi -area_range = [10, 100] - -# for the two masks that are overlapping, if the ratio between overlap and the area of the smaller mask is larger than -# this value, the smaller mask will be discarded. -overlap_thr = 0.2 - -save_folder = 'figures' - -data_file_name = 'cells.hdf5' -save_file_name = 'cells_refined.hdf5' -background_file_name = "corrected_mean_projections.tif" - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -if not os.path.isdir(save_folder): - os.makedirs(save_folder) - -# read cells -dfile = h5py.File(data_file_name) -cells = {} -for cellname in dfile.iterkeys(): - cells.update({cellname:ia.WeightedROI.from_h5_group(dfile[cellname])}) - -print 'total number of cells:', len(cells) - -# get the names of cells which are on the edge -edge_cells = [] -for cellname, cellmask in cells.iteritems(): - dimension = cellmask.dimension - center = cellmask.get_center() - if center[0] < center_margin[0] or \ - center[0] > dimension[0] - center_margin[1] or \ - center[1] < center_margin[2] or \ - center[1] > dimension[1] - center_margin[3]: - - # cellmask.plot_binary_mask_border(color='#ff0000', borderWidth=1) - # plt.title(cellname) - # plt.show() - - edge_cells.append(cellname) - -print '\ncells to be removed because they are on the edges:' -print '\n'.join(edge_cells) - -# remove edge cells -for edge_cell in edge_cells: - _ = cells.pop(edge_cell) - -# get dictionary of cell areas -cell_areas = {} -for cellname, cellmask in cells.iteritems(): - cell_areas.update({cellname: cellmask.get_binary_area()}) - - -# remove cellnames that have area outside of the area_range -invalid_cell_ns = [] -for cellname, cellarea in cell_areas.items(): - if cellarea < area_range[0] or cellarea > area_range[1]: - invalid_cell_ns.append(cellname) -print "cells to be removed because they do not meet area criterion:" -print "\n".join(invalid_cell_ns) -for invalid_cell_n in invalid_cell_ns: - cell_areas.pop(invalid_cell_n) - - -# sort cells with their binary area -cell_areas_sorted = sorted(cell_areas.items(), key=operator.itemgetter(1)) -cell_areas_sorted.reverse() -cell_names_sorted = [c[0] for c in cell_areas_sorted] -# print '\n'.join([str(c) for c in cell_areas_sorted]) - -# get the name of cells that needs to be removed because of overlapping -retain_cells = [] -remove_cells = [] -for cell1_name in cell_names_sorted: - cell1_mask = cells[cell1_name] - is_remove = 0 - cell1_area = cell1_mask.get_binary_area() - for cell2_name in retain_cells: - cell2_mask = cells[cell2_name] - cell2_area = cell2_mask.get_binary_area() - curr_overlap = cell1_mask.binary_overlap(cell2_mask) - - if float(curr_overlap) / cell1_area > overlap_thr: - remove_cells.append(cell1_name) - is_remove = 1 - print cell1_name, ':', cell1_mask.get_binary_area(), ': removed' - - # f = plt.figure(figsize=(10,10)) - # ax = f.add_subplot(111) - # cell1_mask.plot_binary_mask_border(plotAxis=ax, color='#ff0000', borderWidth=1) - # cell2_mask.plot_binary_mask_border(plotAxis=ax, color='#0000ff', borderWidth=1) - # ax.set_title('red:'+cell1_name+'; blue:'+cell2_name) - # plt.show() - break - - if is_remove == 0: - retain_cells.append(cell1_name) - print cell1_name, ':', cell1_mask.get_binary_area(), ': retained' - -print '\ncells to be removed because of overlapping:' -print '\n'.join(remove_cells) - -print '\ntotal number of reatined cells:', len(retain_cells) - -# plotting -colors = pt.random_color(len(cells.keys())) -bgImg = ia.array_nor(np.max(tf.imread(background_file_name), axis=0)) - -f = plt.figure(figsize=(10, 10)) -ax = f.add_subplot(111) -ax.imshow(ia.array_nor(bgImg), cmap='gray', vmin=0, vmax=0.5, interpolation='nearest') - -f2 = plt.figure(figsize=(10, 10)) -ax2 = f2.add_subplot(111) -ax2.imshow(np.zeros(bgImg.shape, dtype=np.uint8), vmin=0, vmax=1, cmap='gray', interpolation='nearest') - -i = 0 -for retain_cell in retain_cells: - cells[retain_cell].plot_binary_mask_border(plotAxis=ax, color=colors[i], borderWidth=1) - cells[retain_cell].plot_binary_mask_border(plotAxis=ax2, color=colors[i], borderWidth=1) - i += 1 -plt.show() - -# save figures -pt.save_figure_without_borders(f, os.path.join(save_folder, '2P_refined_ROIs_with_background.png'), dpi=300) -pt.save_figure_without_borders(f2, os.path.join(save_folder, '2P_refined_ROIs_without_background.png'), dpi=300) - -# save h5 file -save_file = h5py.File(save_file_name, 'w') -i = 0 -for retain_cell in retain_cells: - print retain_cell, ':', cells[retain_cell].get_binary_area() - - currGroup = save_file.create_group('cell' + ft.int2str(i, 4)) - currGroup.attrs['name'] = retain_cell - roiGroup = currGroup.create_group('roi') - cells[retain_cell].to_h5_group(roiGroup) - i += 1 - -for attr, value in dfile.attrs.iteritems(): - save_file.attrs[attr] = value - -save_file.close() -dfile.close() - - - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/130_refine_cells_soma.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/130_refine_cells_soma.py deleted file mode 100644 index f25d873..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/130_refine_cells_soma.py +++ /dev/null @@ -1,175 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Jun 30 17:44:42 2015 - -@author: junz -""" -import os -import h5py -import numpy as np -import operator -import matplotlib.pyplot as plt -import scipy.ndimage as ni -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.FileTools as ft -import corticalmapping.core.PlottingTools as pt -import corticalmapping.SingleCellAnalysis as sca - -plt.ioff() - -def run(): - # pixels, masks with center location within this pixel region at the image border will be discarded - center_margin = [10, 20, 25, 10] # [top margin, bottom margin, left margin, right margin] - - # area range, range of number of pixels of a valid roi - area_range = [150, 1000] - - # for the two masks that are overlapping, if the ratio between overlap and the area of the smaller mask is larger than - # this value, the smaller mask will be discarded. - overlap_thr = 0.2 - - save_folder = 'figures' - - data_file_name = 'cells.hdf5' - save_file_name = 'cells_refined.hdf5' - background_file_name = "corrected_mean_projections.tif" - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - # read cells - dfile = h5py.File(data_file_name) - cells = {} - for cellname in dfile.iterkeys(): - cells.update({cellname:ia.WeightedROI.from_h5_group(dfile[cellname])}) - - print 'total number of cells:', len(cells) - - # get the names of cells which are on the edge - edge_cells = [] - for cellname, cellmask in cells.iteritems(): - dimension = cellmask.dimension - center = cellmask.get_center() - if center[0] < center_margin[0] or \ - center[0] > dimension[0] - center_margin[1] or \ - center[1] < center_margin[2] or \ - center[1] > dimension[1] - center_margin[3]: - - # cellmask.plot_binary_mask_border(color='#ff0000', borderWidth=1) - # plt.title(cellname) - # plt.show() - - edge_cells.append(cellname) - - print '\ncells to be removed because they are on the edges:' - print '\n'.join(edge_cells) - - # remove edge cells - for edge_cell in edge_cells: - _ = cells.pop(edge_cell) - - # get dictionary of cell areas - cell_areas = {} - for cellname, cellmask in cells.iteritems(): - cell_areas.update({cellname: cellmask.get_binary_area()}) - - - # remove cellnames that have area outside of the area_range - invalid_cell_ns = [] - for cellname, cellarea in cell_areas.items(): - if cellarea < area_range[0] or cellarea > area_range[1]: - invalid_cell_ns.append(cellname) - print "cells to be removed because they do not meet area criterion:" - print "\n".join(invalid_cell_ns) - for invalid_cell_n in invalid_cell_ns: - cell_areas.pop(invalid_cell_n) - - - # sort cells with their binary area - cell_areas_sorted = sorted(cell_areas.items(), key=operator.itemgetter(1)) - cell_areas_sorted.reverse() - cell_names_sorted = [c[0] for c in cell_areas_sorted] - # print '\n'.join([str(c) for c in cell_areas_sorted]) - - # get the name of cells that needs to be removed because of overlapping - retain_cells = [] - remove_cells = [] - for cell1_name in cell_names_sorted: - cell1_mask = cells[cell1_name] - is_remove = 0 - cell1_area = cell1_mask.get_binary_area() - for cell2_name in retain_cells: - cell2_mask = cells[cell2_name] - cell2_area = cell2_mask.get_binary_area() - curr_overlap = cell1_mask.binary_overlap(cell2_mask) - - if float(curr_overlap) / cell1_area > overlap_thr: - remove_cells.append(cell1_name) - is_remove = 1 - print cell1_name, ':', cell1_mask.get_binary_area(), ': removed' - - # f = plt.figure(figsize=(10,10)) - # ax = f.add_subplot(111) - # cell1_mask.plot_binary_mask_border(plotAxis=ax, color='#ff0000', borderWidth=1) - # cell2_mask.plot_binary_mask_border(plotAxis=ax, color='#0000ff', borderWidth=1) - # ax.set_title('red:'+cell1_name+'; blue:'+cell2_name) - # plt.show() - break - - if is_remove == 0: - retain_cells.append(cell1_name) - print cell1_name, ':', cell1_mask.get_binary_area(), ': retained' - - print '\ncells to be removed because of overlapping:' - print '\n'.join(remove_cells) - - print '\ntotal number of reatined cells:', len(retain_cells) - - # plotting - colors = pt.random_color(len(cells.keys())) - bgImg = ia.array_nor(np.max(tf.imread(background_file_name), axis=0)) - - f = plt.figure(figsize=(10, 10)) - ax = f.add_subplot(111) - ax.imshow(ia.array_nor(bgImg), cmap='gray', vmin=0, vmax=0.5, interpolation='nearest') - - f2 = plt.figure(figsize=(10, 10)) - ax2 = f2.add_subplot(111) - ax2.imshow(np.zeros(bgImg.shape, dtype=np.uint8), vmin=0, vmax=1, cmap='gray', interpolation='nearest') - - i = 0 - for retain_cell in retain_cells: - cells[retain_cell].plot_binary_mask_border(plotAxis=ax, color=colors[i], borderWidth=1) - cells[retain_cell].plot_binary_mask_border(plotAxis=ax2, color=colors[i], borderWidth=1) - i += 1 - # plt.show() - - # save figures - pt.save_figure_without_borders(f, os.path.join(save_folder, '2P_refined_ROIs_with_background.png'), dpi=300) - pt.save_figure_without_borders(f2, os.path.join(save_folder, '2P_refined_ROIs_without_background.png'), dpi=300) - - # save h5 file - save_file = h5py.File(save_file_name, 'w') - i = 0 - for retain_cell in retain_cells: - print retain_cell, ':', cells[retain_cell].get_binary_area() - - currGroup = save_file.create_group('cell' + ft.int2str(i, 4)) - currGroup.attrs['name'] = retain_cell - roiGroup = currGroup.create_group('roi') - cells[retain_cell].to_h5_group(roiGroup) - i += 1 - - for attr, value in dfile.attrs.iteritems(): - save_file.attrs[attr] = value - - save_file.close() - dfile.close() - -if __name__ == '__main__': - run() - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/135_generate_marked_avi.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/135_generate_marked_avi.py deleted file mode 100644 index 5122b93..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/135_generate_marked_avi.py +++ /dev/null @@ -1,132 +0,0 @@ -import os -import numpy as np -import h5py -import scipy.ndimage as ni -import matplotlib.pyplot as plt -from multiprocessing import Pool -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import cv2 -import PIL -from cStringIO import StringIO - -plt.ioff() - -chunk_size = 1000 -process_num = 5 -downsample_r = 10 -frame_size = 8 # inch - -def downsample_for_multiprocessing(params): - - nwb_path, dset_path, frame_start_i, frame_end_i, dr = params - - print('\tdownsampling frame {} - {}'.format(frame_start_i, frame_end_i)) - - ff = h5py.File(nwb_path, 'r') - chunk = ff[dset_path][frame_start_i:frame_end_i, :, :] - ff.close() - chunk_d = ia.z_downsample(chunk, downSampleRate=dr, is_verbose=False) - return chunk_d - -def downsample_mov(nwb_path, dset_path, dr): - - ff = h5py.File(nwb_path, 'r') - frame_num = ff[dset_path].shape[0] - print('\tshape of movie: {}'.format(ff[dset_path].shape)) - chunk_starts = np.array(range(0, frame_num, chunk_size)) - chunk_ends = chunk_starts + chunk_size - chunk_ends[-1] = frame_num - - params = [] - for i, chunk_start in enumerate(chunk_starts): - params.append((nwb_path, dset_path, chunk_start, chunk_ends[i], dr)) - - p = Pool(process_num) - mov_d = p.map(downsample_for_multiprocessing, params) - - return np.concatenate(mov_d, axis=0) - -def run(): - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - print('getting total mask ...') - cell_f = h5py.File('cells_refined.hdf5', 'r') - h, w = cell_f['cell0000']['roi'].attrs['dimension'] - total_mask = np.zeros((h, w), dtype=np.uint8) - for cell_n, cell_grp in cell_f.items(): - curr_roi = ia.WeightedROI.from_h5_group(cell_grp['roi']) - curr_mask = curr_roi.get_binary_mask() - total_mask = np.logical_or(total_mask, curr_mask) - cell_f.close() - total_mask = ni.binary_dilation(total_mask, iterations=1) - # plt.imshow(total_mask) - # plt.title('total_mask') - # plt.show() - - nwb_folder = os.path.dirname(curr_folder) - nwb_fn = [f for f in os.listdir(nwb_folder) if f[-4:] == '.nwb'][0] - nwb_path = os.path.join(nwb_folder, nwb_fn) - - plane_n = os.path.split(curr_folder)[1] - dset_path = 'processing/motion_correction/MotionCorrection/{}/corrected/data'.format(plane_n) - - print('downsampling movie ...') - print('\tnwb_path: {}'.format(nwb_path)) - print('\tdset_path: {}'.format(dset_path)) - - nwb_f = h5py.File(nwb_path, 'r') - dset = nwb_f[dset_path] - print('\ttotal shape: {}'.format(dset.shape)) - nwb_f.close() - - mov_d = downsample_mov(nwb_path=nwb_path, dset_path=dset_path, dr=downsample_r) - v_min = np.amin(mov_d) - v_max = np.amax(mov_d) - print('\tshape of downsampled movie: {}'.format(mov_d.shape)) - - print('\n\tgenerating avi ...') - - if cv2.__version__[0:3] == '3.1': - codex = 'XVID' - fourcc = cv2.VideoWriter_fourcc(*codex) - out = cv2.VideoWriter('marked_mov.avi', fourcc, 30, (frame_size * 100, frame_size * 100), isColor=True) - elif cv2.__version__[0:6] == '2.4.11': - out = cv2.VideoWriter('marked_mov.avi', -1, 30, (frame_size * 100, frame_size * 100), isColor=True) - elif cv2.__version__[0:3] == '2.4': - codex = 'XVID' - fourcc = cv2.cv.CV_FOURCC(*codex) - out = cv2.VideoWriter('marked_mov.avi', fourcc, 30, (frame_size * 100, frame_size * 100), isColor=True) - else: - raise EnvironmentError('Do not understand opencv cv2 version: {}.'.format(cv2.__version__)) - - f = plt.figure(figsize=(frame_size, frame_size)) - for frame_i, frame in enumerate(mov_d): - print('\tframe: {} / {}'.format(frame_i, mov_d.shape[0])) - f.clear() - ax = f.add_subplot(111) - ax.imshow(frame, vmin=v_min, vmax=v_max*0.5, cmap='gray', interpolation='nearest') - pt.plot_mask_borders(total_mask, plotAxis=ax, color='#ff0000', zoom=1, borderWidth=1) - ax.set_aspect('equal') - # plt.show() - - buffer_ = StringIO() - pt.save_figure_without_borders(f, buffer_, dpi=100) - buffer_.seek(0) - image = PIL.Image.open(buffer_) - curr_frame = np.asarray(image) - r, g, b, a = np.rollaxis(curr_frame, axis=-1) - curr_frame = (np.dstack((b, g, r))) - # print(r.dtype) - # print(curr_frame.shape) - - out.write(curr_frame) - - out.release() - cv2.destroyAllWindows() - print('\t.avi movie generated.') - -if __name__ == '__main__': - run() diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/140_get_weighted_rois_and_surrounds.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/140_get_weighted_rois_and_surrounds.py deleted file mode 100644 index 787c122..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/140_get_weighted_rois_and_surrounds.py +++ /dev/null @@ -1,127 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Jun 30 17:44:42 2015 - -@author: junz -""" - -import os -import numpy as np -import h5py -import tifffile as tf -import allensdk_internal.brain_observatory.mask_set as mask_set -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import scipy.ndimage as ni -import matplotlib.pyplot as plt - -plt.ioff() - - -def run(): - data_file_name = 'cells_refined.hdf5' - background_file_name = "corrected_mean_projections.tif" - save_folder = 'figures' - - overlap_threshold = 0.9 - surround_limit = [1, 8] - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - if not os.path.isdir(save_folder): - os.makedirs(save_folder) - - print 'reading cells file ...' - data_f = h5py.File(data_file_name, 'r') - - cell_ns = data_f.keys() - cell_ns.sort() - - binary_mask_array = [] - weight_mask_array = [] - - for cell_n in cell_ns: - curr_roi = ia.ROI.from_h5_group(data_f[cell_n]['roi']) - binary_mask_array.append(curr_roi.get_binary_mask()) - weight_mask_array.append(curr_roi.get_weighted_mask()) - - data_f.close() - binary_mask_array = np.array(binary_mask_array) - weight_mask_array = np.array(weight_mask_array) - print 'starting mask_array shape:', weight_mask_array.shape - - print 'getting total mask ...' - total_mask = np.zeros((binary_mask_array.shape[1], binary_mask_array.shape[2]), dtype=np.uint8) - for curr_mask in binary_mask_array: - total_mask = np.logical_or(total_mask, curr_mask) - total_mask = np.logical_not(total_mask) - - plt.imshow(total_mask, interpolation='nearest') - plt.title('total_mask') - # plt.show() - - print 'getting and surround masks ...' - binary_surround_array = [] - for binary_center in binary_mask_array: - curr_surround = np.logical_xor(ni.binary_dilation(binary_center, iterations=surround_limit[1]), - ni.binary_dilation(binary_center, iterations=surround_limit[0])) - curr_surround = np.logical_and(curr_surround, total_mask).astype(np.uint8) - binary_surround_array.append(curr_surround) - # plt.imshow(curr_surround) - # plt.show() - binary_surround_array = np.array(binary_surround_array) - - print "saving rois ..." - center_areas = [] - surround_areas = [] - for mask_ind in range(binary_mask_array.shape[0]): - center_areas.append(np.sum(binary_mask_array[mask_ind].flat)) - surround_areas.append(np.sum(binary_surround_array[mask_ind].flat)) - roi_f = h5py.File('rois_and_traces.hdf5') - roi_f['masks_center'] = weight_mask_array - roi_f['masks_surround'] = binary_surround_array - - roi_f.close() - print 'minimum surround area:', min(surround_areas), 'pixels.' - - f = plt.figure(figsize=(10, 10)) - ax_center = f.add_subplot(211) - ax_center.hist(center_areas, bins=30) - ax_center.set_title('roi center area distribution') - ax_surround = f.add_subplot(212) - ax_surround.hist(surround_areas, bins=30) - ax_surround.set_title('roi surround area distribution') - # plt.show() - - print 'plotting ...' - colors = pt.random_color(weight_mask_array.shape[0]) - bg = ia.array_nor(np.max(tf.imread(background_file_name), axis=0)) - - f_c_bg = plt.figure(figsize=(10, 10)) - ax_c_bg = f_c_bg.add_subplot(111) - ax_c_bg.imshow(bg, cmap='gray', vmin=0, vmax=0.5, interpolation='nearest') - f_c_nbg = plt.figure(figsize=(10, 10)) - ax_c_nbg = f_c_nbg.add_subplot(111) - ax_c_nbg.imshow(np.zeros(bg.shape,dtype=np.uint8),vmin=0,vmax=1,cmap='gray',interpolation='nearest') - f_s_nbg = plt.figure(figsize=(10, 10)) - ax_s_nbg = f_s_nbg.add_subplot(111) - ax_s_nbg.imshow(np.zeros(bg.shape,dtype=np.uint8),vmin=0,vmax=1,cmap='gray',interpolation='nearest') - - i = 0 - for mask_ind in range(binary_mask_array.shape[0]): - pt.plot_mask_borders(binary_mask_array[mask_ind], plotAxis=ax_c_bg, color=colors[i], borderWidth=1) - pt.plot_mask_borders(binary_mask_array[mask_ind], plotAxis=ax_c_nbg, color=colors[i], borderWidth=1) - pt.plot_mask_borders(binary_surround_array[mask_ind], plotAxis=ax_s_nbg, color=colors[i], borderWidth=1) - i += 1 - - # plt.show() - - print 'saving figures ...' - pt.save_figure_without_borders(f_c_bg, os.path.join(save_folder, '2P_ROIs_with_background.png'), dpi=300) - pt.save_figure_without_borders(f_c_nbg, os.path.join(save_folder, '2P_ROIs_without_background.png'), dpi=300) - pt.save_figure_without_borders(f_s_nbg, os.path.join(save_folder, '2P_ROI_surrounds_background.png'), dpi=300) - f.savefig(os.path.join(save_folder, 'roi_area_distribution.pdf'), dpi=300) - -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/150_get_raw_center_and_surround_traces.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/150_get_raw_center_and_surround_traces.py deleted file mode 100644 index 0e57582..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/150_get_raw_center_and_surround_traces.py +++ /dev/null @@ -1,129 +0,0 @@ -import os -import numpy as np -import h5py -import time -import corticalmapping.core.ImageAnalysis as ia -import corticalmapping.core.PlottingTools as pt -import corticalmapping.core.FileTools as ft -import corticalmapping.NwbTools as nt -import matplotlib.pyplot as plt -from multiprocessing import Pool - -CHUNK_SIZE = 2000 -PROCESS_NUM = 5 - -def get_chunk_frames(frame_num, chunk_size): - chunk_num = frame_num // chunk_size - if frame_num % chunk_size > 0: - chunk_num = chunk_num + 1 - - print("total number of frames:", frame_num) - print("total number of chunks:", chunk_num) - - chunk_ind = [] - chunk_starts = [] - chunk_ends = [] - - for chunk_i in range(chunk_num): - chunk_ind.append(chunk_i) - chunk_starts.append(chunk_i * chunk_size) - - if chunk_i < chunk_num - 1: - chunk_ends.append((chunk_i + 1) * chunk_size) - else: - chunk_ends.append(frame_num) - - return zip(chunk_ind, chunk_starts, chunk_ends) - -def get_traces(params): - t0 = time.time() - - chunk_ind, chunk_start, chunk_end, nwb_path, data_path, curr_folder, center_array, surround_array = params - - nwb_f = h5py.File(nwb_path, 'r') - print('\nstart analyzing chunk: {}'.format(chunk_ind)) - curr_mov = nwb_f[data_path][chunk_start: chunk_end] - nwb_f.close() - - # print 'extracting traces' - curr_traces_center = np.empty((center_array.shape[0], curr_mov.shape[0]), dtype=np.float32) - curr_traces_surround = np.empty((center_array.shape[0], curr_mov.shape[0]), dtype=np.float32) - for i in range(center_array.shape[0]): - curr_center = ia.WeightedROI(center_array[i]) - curr_surround = ia.ROI(surround_array[i]) - curr_traces_center[i, :] = curr_center.get_weighted_trace_pixelwise(curr_mov) - - # scale surround trace to be similar as center trace - mean_center_weight = curr_center.get_mean_weight() - curr_traces_surround[i, :] = curr_surround.get_binary_trace_pixelwise(curr_mov) * mean_center_weight - - # print 'saveing chunk {} ...'.format(chunk_ind) - chunk_folder = os.path.join(curr_folder, 'chunks') - if not os.path.isdir(chunk_folder): - os.mkdir(chunk_folder) - chunk_f = h5py.File(os.path.join(chunk_folder, 'chunk_temp_' + ft.int2str(chunk_ind, 4) + '.hdf5')) - chunk_f['traces_center'] = curr_traces_center - chunk_f['traces_surround'] = curr_traces_surround - chunk_f.close() - - print('\n\t{:06d} seconds: chunk: {}; demixing finished.'.format(int(time.time() - t0), chunk_ind)) - - return None - -def run(): - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - plane_n = os.path.split(curr_folder)[1] - print(plane_n) - - print('getting masks ...') - rois_f = h5py.File('rois_and_traces.hdf5') - center_array = rois_f['masks_center'].value - surround_array = rois_f['masks_surround'].value - - print('\nanalyzing movie in chunks of size:', CHUNK_SIZE , 'frames.') - - nwb_folder = os.path.dirname(curr_folder) - nwb_fn = [f for f in os.listdir(nwb_folder) if f[-4:] == '.nwb'][0] - nwb_path = os.path.join(nwb_folder, nwb_fn) - print('\n' + nwb_path) - data_path = '/processing/motion_correction/MotionCorrection/' + plane_n + '/corrected/data' - - nwb_f = h5py.File(nwb_path, 'r') - total_frame = nwb_f[data_path].shape[0] - nwb_f.close() - - chunk_frames = get_chunk_frames(total_frame, CHUNK_SIZE) - chunk_params = [(cf[0], cf[1], cf[2], nwb_path, data_path, - curr_folder, center_array, surround_array) for cf in chunk_frames] - - p = Pool(PROCESS_NUM) - p.map(get_traces, chunk_params) - - chunk_folder = os.path.join(curr_folder, 'chunks') - chunk_fns = [f for f in os.listdir(chunk_folder) if f[0:11] == 'chunk_temp_'] - chunk_fns.sort() - print('\nreading chunks files ...') - print('\n'.join(chunk_fns)) - - traces_raw = [] - traces_surround = [] - - for chunk_fn in chunk_fns: - curr_chunk_f = h5py.File(os.path.join(chunk_folder, chunk_fn)) - traces_raw.append(curr_chunk_f['traces_center'].value) - traces_surround.append(curr_chunk_f['traces_surround'].value) - - print("saving ...") - traces_raw = np.concatenate(traces_raw, axis=1) - traces_surround = np.concatenate(traces_surround, axis=1) - rois_f['traces_center_raw'] = traces_raw - rois_f['traces_surround_raw'] = traces_surround - print('done.') - - -if __name__ == '__main__': - run() - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/160_get_neuropil_subtracted_traces.py b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/160_get_neuropil_subtracted_traces.py deleted file mode 100644 index 3605fdf..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/160_get_neuropil_subtracted_traces.py +++ /dev/null @@ -1,120 +0,0 @@ -import sys -import os -import h5py -import numpy as np -import corticalmapping.HighLevel as hl -import corticalmapping.core.FileTools as ft -import matplotlib.pyplot as plt -from multiprocessing import Pool - - -lam = 0.05 -plot_chunk_size = 5000 -process_num = 5 - - -def plot_traces_chunks(traces, labels, chunk_size, roi_ind): - """ - - :param traces: np.array, shape=[trace_type, t_num] - :param labels: - :param chunk_size: - :param figures_folder: - :param roi_ind: - :return: - """ - - t_num = traces.shape[1] - chunk_num = t_num // chunk_size - - chunks = [] - for chunk_ind in range(chunk_num): - chunks.append([chunk_ind * chunk_size, (chunk_ind + 1) * chunk_size]) - - if t_num % chunk_size != 0: - chunks.append([chunk_num * chunk_size, t_num]) - - v_max = np.amax(traces) - v_min = np.amin(traces) - - fig = plt.figure(figsize=(75, 20)) - fig.suptitle('neuropil subtraction for ROI: {}'.format(roi_ind)) - for chunk_ind, chunk in enumerate(chunks): - curr_ax = fig.add_subplot(len(chunks), 1, chunk_ind + 1) - for trace_ind in range(traces.shape[0]): - curr_ax.plot(traces[trace_ind, chunk[0]: chunk[1]], label=labels[trace_ind]) - - curr_ax.set_xlim([0, chunk_size]) - curr_ax.set_ylim([v_min, v_max * 1.2]) - curr_ax.legend() - - return fig - -def plot_traces_for_multi_process(params): - - curr_traces, plot_chunk_size, roi_ind, figures_folder = params - - print('roi_{:04d}'.format(roi_ind)) - - curr_fig = plot_traces_chunks(traces=curr_traces, - labels=['center', 'surround', 'subtracted'], - chunk_size=plot_chunk_size, - roi_ind=roi_ind) - curr_fig.savefig(os.path.join(figures_folder, 'neuropil_subtraction_ROI_{:04d}.png'.format(roi_ind))) - curr_fig.clear() - plt.close(curr_fig) - -def run(): - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - data_f = h5py.File('rois_and_traces.hdf5') - traces_raw = data_f['traces_center_raw'].value - traces_srround = data_f['traces_surround_raw'].value - - traces_subtracted = np.zeros(traces_raw.shape, np.float32) - ratio = np.zeros(traces_raw.shape[0], np.float32) - err = np.zeros(traces_raw.shape[0], np.float32) - - for i in range(traces_raw.shape[0]): - curr_trace_c = traces_raw[i] - curr_trace_s = traces_srround[i] - curr_r, curr_err, curr_trace_sub = hl.neural_pil_subtraction(curr_trace_c, curr_trace_s, lam=lam) - print "roi_%s \tr = %.4f; error = %.4f." % (ft.int2str(i, 5), curr_r, curr_err) - traces_subtracted[i] = curr_trace_sub - ratio[i] = curr_r - err[i] = curr_err - - print('\nplotting neuropil subtraction results ...') - figures_folder = 'figures/neuropil_subtraction_lam_{}'.format(lam) - if not os.path.isdir(figures_folder): - os.makedirs(figures_folder) - - params = [] - for roi_ind in range(traces_raw.shape[0]): - - curr_traces = np.array([traces_raw[roi_ind], traces_srround[roi_ind], traces_subtracted[roi_ind]]) - - params.append((curr_traces, plot_chunk_size, roi_ind, figures_folder)) - - p = Pool(process_num) - p.map(plot_traces_for_multi_process, params) - - # wait for keyboard abortion - # msg = raw_input('Do you want to save? (y/n)\n') - # while True: - # if msg == 'y': - # break - # elif msg == 'n': - # sys.exit('Stop process without saving.') - # else: - # msg = raw_input('Do you want to save? (y/n)\n') - - data_f['traces_center_subtracted'] = traces_subtracted - data_f['neuropil_r'] = ratio - data_f['neuropil_err'] = err - - data_f.close() - -if __name__ == "__main__": - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/batch_processing_bouton.bat b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/batch_processing_bouton.bat deleted file mode 100644 index 74aa492..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/batch_processing_bouton.bat +++ /dev/null @@ -1,10 +0,0 @@ -call activate bigmess - -set PYTHONPATH=%PYTHONPATH%;E:\data\python_packages\corticalmapping;E:\data\python_packages\allensdk_internal;E:\data\python_packages\ainwb\ainwb - -python 120_get_cells_file_bouton.py -python 130_refine_cells_bouton.py -python 140_get_weighted_rois_and_surrounds.py -python 150_get_raw_center_and_surround_traces.py -python 160_get_neuropil_subtracted_traces.py -python 135_generate_marked_avi.py \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/batch_processing_soma.bat b/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/batch_processing_soma.bat deleted file mode 100644 index 790c20d..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_movie/within_plane_folder/batch_processing_soma.bat +++ /dev/null @@ -1,10 +0,0 @@ -call activate bigmess - -set PYTHONPATH=%PYTHONPATH%;E:\data\python_packages\corticalmapping;E:\data\python_packages\allensdk_internal;E:\data\python_packages\ainwb\ainwb - -python 120_get_cells_file_soma.py -python 130_refine_cells_soma.py -python 140_get_weighted_rois_and_surrounds.py -python 150_get_raw_center_and_surround_traces.py -python 160_get_neuropil_subtracted_traces.py -python 135_generate_marked_avi.py \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/000_reorganize_files.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/000_reorganize_files.py deleted file mode 100644 index 6d101e1..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/000_reorganize_files.py +++ /dev/null @@ -1,54 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\181102-M412052-deepscope\zstack1" -file_identifier = 'zstack1' -ch_ns = ['red'] -frames_per_step = 200 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -file_ns = [f for f in os.listdir(data_folder) if f[-4:] == '.tif' and file_identifier in f] -file_ns.sort() -print('\n'.join(file_ns)) - -save_folders = [] -for ch_n in ch_ns: - curr_save_folder = os.path.join(data_folder, file_identifier, ch_n) - if not os.path.isdir(curr_save_folder): - os.makedirs(curr_save_folder) - save_folders.append(curr_save_folder) - -curr_step = 0 - -for file_n in file_ns: - curr_mov = tf.imread(os.path.join(data_folder, file_n)) - - - curr_frame_num = curr_mov.shape[0] / len(ch_ns) - - if curr_frame_num % frames_per_step != 0: - raise ValueError('{}: total frame number is not divisible by frames per step.'.format(file_n)) - - curr_mov_chs = [] - for ch_i in range(len(ch_ns)): - curr_mov_chs.append(curr_mov[ch_i::len(ch_ns)]) - - steps = curr_frame_num // frames_per_step - for step_ind in range(steps): - - print ('current step: {}'.format(curr_step)) - - for ch_i in range(len(ch_ns)): - curr_step_mov_ch = curr_mov_chs[ch_i][step_ind * frames_per_step:(step_ind + 1) * frames_per_step,:,:] - curr_step_n = 'step_' + ft.int2str(curr_step, 4) - curr_step_folder = os.path.join(save_folders[ch_i], curr_step_n) - os.mkdir(curr_step_folder) - tf.imsave(os.path.join(curr_step_folder, curr_step_n + '.tif'), curr_step_mov_ch) - - curr_step += 1 diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/050_motion_correction_multichannel.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/050_motion_correction_multichannel.py deleted file mode 100644 index afe5f13..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/050_motion_correction_multichannel.py +++ /dev/null @@ -1,98 +0,0 @@ -import os -import numpy as np -import h5py -import tifffile as tf -import stia.motion_correction as mc -from warnings import warn -from multiprocessing import Pool - -def run(): - data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180816-M376019-zstack\zstack_2p_zoom2" - ref_ch_n = 'red' - n_process = 8 - - anchor_frame_ind_chunk = 10 - iteration_chunk = 10 - max_offset_chunk = (50., 50.) - preprocessing_type = 0 - fill_value = 0. - - is_apply = True - avi_downsample_rate = None - is_equalizing_histogram = False - - curr_folder = os.path.dirname(os.path.realpath(__file__)) - os.chdir(curr_folder) - - ref_data_folder = os.path.join(data_folder, ref_ch_n) - - steps = [f for f in os.listdir(ref_data_folder) if os.path.isdir(os.path.join(ref_data_folder, f)) - and f[0:5] == 'step_'] - steps.sort() - print('\n'.join(steps)) - - params = [] - for step in steps: - - folder_ref = os.path.join(data_folder, ref_ch_n, step) - params.append((folder_ref, anchor_frame_ind_chunk, iteration_chunk, max_offset_chunk, preprocessing_type, - fill_value, is_apply, avi_downsample_rate, is_equalizing_histogram)) - - chunk_p = Pool(n_process) - chunk_p.map(correct_single_step, params) - - -def correct_single_step(param): - - folder_ref, anchor_frame_ind_chunk, iteration_chunk, max_offset_chunk, preprocessing_type, fill_value,\ - is_apply, avi_downsample_rate, is_equalizing_histogram= param - - step_n = os.path.split(folder_ref)[1] - print('\nStart correcting step {} ...'.format(step_n)) - - mov_ref_n = [f for f in os.listdir(folder_ref) if f[-4:] == '.tif' and step_n in f] - if len(mov_ref_n) != 1: - warn('step {}: number of green movie does not equal 1.'.format(step_n)) - return - - mov_paths, _ = mc.motion_correction(input_folder=folder_ref, - input_path_identifier='.tif', - process_num=1, - output_folder=folder_ref, - anchor_frame_ind_chunk=anchor_frame_ind_chunk, - anchor_frame_ind_projection=0, - iteration_chunk=iteration_chunk, - iteration_projection=10, - max_offset_chunk=max_offset_chunk, - max_offset_projection=(30., 30.), - align_func=mc.phase_correlation, - preprocessing_type=preprocessing_type, - fill_value=fill_value) - - if is_apply: - - offsets_path = os.path.join(folder_ref, 'correction_offsets.hdf5') - offsets_f = h5py.File(offsets_path) - ref_path = offsets_f['file_0000'].attrs['path'] - offsets_f.close() - - movie_path = mov_paths[0] - - mc.apply_correction_offsets(offsets_path=offsets_path, - path_pairs=[[ref_path, movie_path]], - output_folder=folder_ref, - process_num=1, - fill_value=fill_value, - avi_downsample_rate=avi_downsample_rate, - is_equalizing_histogram=is_equalizing_histogram) - - - -if __name__ == "__main__": - run() - - - - - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/060_apply_offsets_multichannel.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/060_apply_offsets_multichannel.py deleted file mode 100644 index af0e46d..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/060_apply_offsets_multichannel.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import stia.motion_correction as mc -import numpy as np -import h5py -import tifffile as tf -from multiprocessing import Pool - -def run(): - data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180816-M376019-zstack\zstack_2p_zoom2" - ref_ch_n = 'red' - apply_ch_ns = ['red'] - n_process = 8 - - step_ns = [f for f in os.listdir(os.path.join(data_folder, ref_ch_n))] - step_ns = [f for f in step_ns if os.path.isdir(os.path.join(data_folder, ref_ch_n, f))] - step_ns.sort() - - chunk_p = Pool(n_process) - - for ch_n in apply_ch_ns: - mc_params = [] - for step_n in step_ns: - movie_path = os.path.join(data_folder, ch_n, step_n, step_n + '.tif') - offsets_path = os.path.join(data_folder, ref_ch_n, step_n, 'correction_offsets.hdf5') - mc_params.append((movie_path, offsets_path)) - - chunk_p.map(apply_offset_single, mc_params) - - -def apply_offset_single(param): - - movie_path, offsets_path = param - - offsets_f = h5py.File(offsets_path) - ref_path = offsets_f['file_0000'].attrs['path'] - offsets_f.close() - - mc.apply_correction_offsets(offsets_path=offsets_path, - path_pairs=[[ref_path, movie_path]], - output_folder=os.path.split(os.path.realpath(movie_path))[0], - process_num=1, - fill_value=0., - avi_downsample_rate=None, - is_equalizing_histogram=False) - -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/065_get_uncorrected_zstack_from_server.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/065_get_uncorrected_zstack_from_server.py deleted file mode 100644 index d4938f7..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/065_get_uncorrected_zstack_from_server.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -import numpy as np -import tifffile as tf - -data_folder = r"\\sd2\SD2\jun_backup\raw_data_temp\181217-M421211-2p\zstack" - -chns = ['green', 'red'] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -for chn in chns: - print('processing channel: {} ...'.format(chn)) - ch_folder = os.path.join(data_folder, chn) - steps = [f for f in os.listdir(ch_folder) if os.path.isdir(os.path.join(ch_folder, f)) and f[0:5] == 'step_'] - steps.sort() - print('\ntotal number of steps: {}'.format(len(steps))) - - zstack = [] - for step in steps: - print("\t{}".format(step)) - movie = tf.imread(os.path.join(ch_folder, step, step + '.tif')) - zstack.append(np.mean(movie, axis=0)) - - zstack = np.array(zstack, dtype=np.float32) - save_n = os.path.split(data_folder)[1] + '_uncorrected_' + chn + '.tif' - tf.imsave(save_n, zstack) diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/070_get_zstack_from_server.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/070_get_zstack_from_server.py deleted file mode 100644 index 404feb4..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/070_get_zstack_from_server.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -import numpy as np -import tifffile as tf - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180816-M376019-zstack\zstack_2p_zoom2" - -chns = ['green', 'red'] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -for chn in chns: - print('processing channel: {} ...'.format(chn)) - ch_folder = os.path.join(data_folder, chn) - steps = [f for f in os.listdir(ch_folder) if os.path.isdir(os.path.join(ch_folder, f)) and f[0:5] == 'step_'] - steps.sort() - print('\ntotal number of steps: {}'.format(len(steps))) - - zstack = [] - for step in steps: - print("\t{}".format(step)) - zstack.append(tf.imread(os.path.join(ch_folder, step, 'corrected_mean_projection.tif'))) - - zstack = np.array(zstack) - save_n = os.path.split(data_folder)[1] + '_' + chn + '.tif' - tf.imsave(save_n, zstack) diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/080_remove_uncorrected_files.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/080_remove_uncorrected_files.py deleted file mode 100644 index 10cc96e..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/080_remove_uncorrected_files.py +++ /dev/null @@ -1,26 +0,0 @@ -import os - -base_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180619-M386444-2p\zstack_zoom2\zstack_zoom2" - -channels = ['green', 'red'] - -for ch_n in channels: - print('remove uncorrected files for channle: {}'.format(ch_n)) - - step_fns = [f for f in os.listdir(os.path.join(base_folder, ch_n)) if f.split('_')[-2] == 'step'] - step_fns.sort() - print('\n'.join(step_fns)) - - for step_fn in step_fns: - - print('\n' + step_fn) - step_folder = os.path.join(base_folder, ch_n, step_fn) - - fns = os.listdir(step_folder) - - if step_fn + '.tif' in fns: - os.remove(os.path.join(step_folder, step_fn + '.tif')) - else: - print('Cannot find uncorrected file. Skip.') - \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/090_remove_corrected_files.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/090_remove_corrected_files.py deleted file mode 100644 index caa207f..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/090_remove_corrected_files.py +++ /dev/null @@ -1,46 +0,0 @@ -import os - -base_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\181112-M424454-2p\zstack" -channels = ['green', 'red'] -is_remove_img = False - -for ch in channels: - print('processing channel: {} ...'.format(ch)) - ch_folder = os.path.join(base_folder, ch) - - step_fns = [f for f in os.listdir(ch_folder) if f.split('_')[-2] == 'step'] - step_fns.sort() - print('\n'.join(step_fns)) - - for step_fn in step_fns: - - print('\n' + step_fn) - step_folder = os.path.join(ch_folder, step_fn) - fns = os.listdir(step_folder) - - - if is_remove_img: - if 'corrected_max_projection.tif' in fns: - print('removing corrected_max_projection.tif') - os.remove(os.path.join(step_folder, 'corrected_max_projection.tif')) - - if 'corrected_max_projections.tif' in fns: - print('removing corrected_max_projections.tif') - os.remove(os.path.join(step_folder, 'corrected_max_projections.tif')) - - if 'corrected_mean_projection.tif' in fns: - print('removing corrected_mean_projection.tif') - os.remove(os.path.join(step_folder, 'corrected_mean_projection.tif')) - - if 'corrected_mean_projections.tif' in fns: - print('removing corrected_mean_projections.tif') - os.remove(os.path.join(step_folder, 'corrected_mean_projections.tif')) - - if 'correction_offsets.hdf5' in fns: - print('removing correction_offsets.hdf5') - os.remove(os.path.join(step_folder, 'correction_offsets.hdf5')) - - fn_cor = [f for f in fns if f[-14:] == '_corrected.tif'] - if len(fn_cor) == 1: - print('removing ' + fn_cor[0]) - os.remove(os.path.join(step_folder, fn_cor[0])) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/095_split_channel.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/095_split_channel.py deleted file mode 100644 index c240344..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/095_split_channel.py +++ /dev/null @@ -1,18 +0,0 @@ -import os -import tifffile as tf - -data_fn = 'zstack_zoom2_00001_00001.tif' -ch_ns = ['green', 'red'] -save_prefix = 'zstack' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -ch_num = len(ch_ns) - -stack = tf.imread(data_fn) - -for ch_i, ch_n in enumerate(ch_ns): - tf.imsave('{}_{}.tif'.format(save_prefix, ch_n), stack[ch_i::ch_num]) - -print('done.') \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/100_get_fine_zstack.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/100_get_fine_zstack.py deleted file mode 100644 index 68f4747..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/100_get_fine_zstack.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import h5py -import numpy as np -import tifffile as tf -import stia.motion_correction as mc -import stia.utility.image_analysis as ia - -identifier = 'zstack1' -ch_ref = 'red' -ch_app = ['green', 'red'] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -stack_ref = tf.imread('{}_{}.tif'.format(identifier, ch_ref)) - -step_offsets = [[0., 0.]] # offsets between adjacent steps - -print('calculating step offsets ...') -for step_i in range(1, stack_ref.shape[0]): - curr_offset = mc.phase_correlation(stack_ref[step_i], stack_ref[step_i - 1]) - step_offsets.append(curr_offset) -step_offsets = np.array([np.array(so) for so in step_offsets], dtype=np.float32) -print('\nsetp offsets:') -print(step_offsets) - -print('\ncalculating final offsets ...') -final_offsets_y = np.cumsum(step_offsets[:, 0]) -final_offsets_x = np.cumsum(step_offsets[:, 1]) -final_offsets = np.array([final_offsets_x, final_offsets_y], dtype=np.float32).transpose() - -middle_frame_ind = stack_ref.shape[0] // 2 -middle_offsets = final_offsets[middle_frame_ind: middle_frame_ind + 1] -final_offsets = final_offsets - middle_offsets -print('\nfinal offsets:') -print(final_offsets) - -print('applying final offsets ...') - -for ch in ch_app: - - stack_app = tf.imread('{}_{}.tif'.format(identifier, ch)) - stack_aligned = [] - - for step_i in range(stack_app.shape[0]): - curr_offset = final_offsets[step_i] - frame = stack_app[step_i] - frame_aligned = ia.rigid_transform_cv2_2d(frame, offset=curr_offset, fill_value=0).astype(np.float32) - stack_aligned.append(frame_aligned) - - stack_aligned = np.array(stack_aligned, dtype=np.float32) - - tf.imsave('{}_{}_aligned.tif'.format(identifier, ch), stack_aligned) - # tf.imsave('{}_{}_max_projection.tif'.format(identifier, ch), np.max(stack_aligned, axis=0)) - - - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/110_rotate_zstack.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/110_rotate_zstack.py deleted file mode 100644 index e7f14de..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/110_rotate_zstack.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import stia.motion_correction as mc -import stia.utility.image_analysis as ia - -fn = 'stack1_final.tif' -scope = 'deepscope' # 'sutter' or 'deepscope' or 'scientifica' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -stack = tf.imread(fn) - -if scope == 'sutter': - stack_r = stack.transpose((0, 2, 1))[:, ::-1, :] - -elif scope == 'deepscope': - h_new = int(stack.shape[1] * np.sqrt(2)) - w_new = int(stack.shape[2] * np.sqrt(2)) - stack_r = ia.rigid_transform_cv2(stack, rotation=140, output_shape=(h_new, w_new))[:, :, ::-1] - -elif scope == 'scientifica': - h_new = int(stack.shape[1] * np.sqrt(2)) - w_new = int(stack.shape[2] * np.sqrt(2)) - stack_r = ia.rigid_transform_cv2(stack[:,::-1,:], rotation=135, output_shape=(h_new, w_new)) - -else: - raise LookupError("Do not understand scope type. Should be 'sutter' or 'deepscope' or 'scientifica'.") - -tf.imsave(os.path.splitext(fn)[0] + '_rotated.tif', stack_r.astype(stack.dtype)) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/120_get_depth_profile.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/120_get_depth_profile.py deleted file mode 100644 index 2748ae1..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/120_get_depth_profile.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import matplotlib.pyplot as plt -import corticalmapping.core.ImageAnalysis as ia - -data_fn = 'zstack_2p_zoom2_red_aligned.tif' -save_fn = '2018-08-16-M376019-depth-profile-red.png' -start_depth = 50 # micron -step_depth = 2 # micron -pix_size = 0.7 # sutter scope, zoom2, 512 x 512 -resolution = 512 - -curr_folder = os.path.dirname(os.path.abspath(__file__)) -os.chdir(curr_folder) - -data = tf.imread(data_fn) -dp = ia.array_nor(np.mean(data, axis=1)) - -depth_i = np.array(range(0, dp.shape[0], 50)) -depth_l = depth_i * step_depth + start_depth - -f = plt.figure(figsize=(8, 8)) -ax = f.add_subplot(111) -ax.imshow(dp, vmin=0, vmax=1, cmap='magma', aspect=step_depth / pix_size) -ax.set_xticks([0, resolution-1]) -ax.set_xticklabels(['0', '{:7.2f}'.format(resolution*pix_size)]) -ax.set_yticks(depth_i) -ax.set_yticklabels(depth_l) -ax.set_xlabel('horizontal dis (um)') -ax.set_ylabel('depth (um)') - -plt.show() - -f.savefig(save_fn) diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/130_combine_channels.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/130_combine_channels.py deleted file mode 100644 index 704e5d4..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/130_combine_channels.py +++ /dev/null @@ -1,32 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import cv2 - -fn_lst = ['FOV2_projection_site_zstack_green_aligned.tif', - 'FOV2_projection_site_zstack_red_aligned.tif'] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -mov = [] - -for fn in fn_lst: - curr_mov = tf.imread(fn).astype(np.float32) - - curr_mov_adjust = [] - # histogram equalization - for frame in curr_mov: - # display_frame = (frame - np.amin(frame)) / (np.amax(frame) - np.amin(frame)) - # display_frame = (display_frame * 255).astype(np.uint8) - # display_frame = cv2.equalizeHist(display_frame) - - display_frame = frame - np.mean(frame[:]) - curr_mov_adjust.append(display_frame) - - curr_mov_adjust = np.array(curr_mov_adjust) - mov.append(curr_mov_adjust) - -mov = np.concatenate(mov, axis=2) - -tf.imsave('zstack_aligned_combined.tif', mov) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/000_reorganize_files.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/000_reorganize_files.py deleted file mode 100644 index 7ae2a62..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/000_reorganize_files.py +++ /dev/null @@ -1,55 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.FileTools as ft - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180605-M391355-2p\zstack" -file_identifier = 'zstack_zoom2' -ch_ns = ['green', 'red'] -frames_per_step = 300 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -file_ns = [f for f in os.listdir(data_folder) if f[-4:] == '.tif' and file_identifier in f] -file_ns.sort() -print('\n'.join(file_ns)) - -save_folders = [] -for ch_n in ch_ns: - curr_save_folder = os.path.join(data_folder, file_identifier, ch_n) - if not os.path.isdir(curr_save_folder): - os.makedirs(curr_save_folder) - save_folders.append(curr_save_folder) - -curr_step = 0 - -for file_n in file_ns: - curr_mov = tf.imread(os.path.join(data_folder, file_n)) - - # reorient movie - curr_mov = curr_mov.transpose((0, 2, 1))[:, ::-1, :] - - curr_frame_num = curr_mov.shape[0] / len(ch_ns) - - if curr_frame_num % frames_per_step != 0: - raise ValueError('{}: total frame number is not divisible by frames per step.'.format(file_n)) - - curr_mov_chs = [] - for ch_i in range(len(ch_ns)): - curr_mov_chs.append(curr_mov[ch_i::len(ch_ns)]) - - steps = curr_frame_num // frames_per_step - for step_ind in range(steps): - - print ('current step: {}'.format(curr_step)) - - for ch_i in range(len(ch_ns)): - curr_step_mov_ch = curr_mov_chs[ch_i][step_ind * frames_per_step:(step_ind + 1) * frames_per_step,:,:] - curr_step_n = 'step_' + ft.int2str(curr_step, 4) - curr_step_folder = os.path.join(save_folders[ch_i], curr_step_n) - os.mkdir(curr_step_folder) - tf.imsave(os.path.join(curr_step_folder, curr_step_n + '.tif'), curr_step_mov_ch) - - curr_step += 1 diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/010_motion_correction_zstack_caiman_multichannel.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/010_motion_correction_zstack_caiman_multichannel.py deleted file mode 100644 index fae38c7..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/010_motion_correction_zstack_caiman_multichannel.py +++ /dev/null @@ -1,107 +0,0 @@ -import sys -sys.path.extend([r"E:\data\github_packages\CaImAn"]) - -import caiman as cm -import numpy as np -import os -from caiman.motion_correction import MotionCorrect, tile_and_correct, motion_correction_piecewise -import tifffile as tf -import h5py -import warnings -from multiprocessing import Pool - -base_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180605-M391355-2p\zstack\zstack_zoom2" - -reference_chn = 'green' - -n_processes = 5 - -def correct_single_movie(folder_path): - - #=======================================setup parameters============================================== - # number of iterations for rigid motion correction - niter_rig = 5 - - # maximum allowed rigid shift in pixels (view the movie to get a sense of motion) - max_shifts = (30, 30) - - # for parallelization split the movies in num_splits chuncks across time - # if none all the splits are processed and the movie is saved - splits_rig = 56 - - # intervals at which patches are laid out for motion correction - # num_splits_to_process_rig = None - - # create a new patch every x pixels for pw-rigid correction - strides = (48, 48) - - # overlap between pathes (size of patch strides+overlaps) - overlaps = (24, 24) - - # for parallelization split the movies in num_splits chuncks across time - splits_els = 56 - - # num_splits_to_process_els = [28, None] - - # upsample factor to avoid smearing when merging patches - upsample_factor_grid = 4 - - # maximum deviation allowed for patch with respect to rigid shifts - max_deviation_rigid = 3 - - # if True, apply shifts fast way (but smoothing results) by using opencv - shifts_opencv = True - - # if True, make the SAVED movie and template mostly nonnegative by removing min_mov from movie - nonneg_movie = False - # =======================================setup parameters============================================== - - - offset_mov = 0. - - file_path = [f for f in os.listdir(folder_path) if f[-4:] == '.tif'] - if len(file_path) == 0: - raise LookupError('no tif file found in folder: {}'.format(folder_path)) - elif len(file_path) > 1: - raise LookupError('more than one tif files found in folder: {}'.format(folder_path)) - else: - file_path = os.path.join(folder_path, file_path[0]) - - # create a motion correction object# creat - mc = MotionCorrect(file_path, offset_mov, - dview=None, max_shifts=max_shifts, niter_rig=niter_rig, - splits_rig=splits_rig, strides=strides, overlaps=overlaps, - splits_els=splits_els, upsample_factor_grid=upsample_factor_grid, - max_deviation_rigid=max_deviation_rigid, - shifts_opencv=shifts_opencv, nonneg_movie=nonneg_movie) - - mc.motion_correct_rigid(save_movie=True) - # load motion corrected movie - m_rig = cm.load(mc.fname_tot_rig) - m_rig = m_rig.astype(np.int16) - save_name = os.path.splitext(file_path)[0] + '_corrected.tif' - tf.imsave(os.path.join(folder_path, save_name), m_rig) - tf.imsave(os.path.join(folder_path, 'corrected_mean_projection.tif'), - np.mean(m_rig, axis=0).astype(np.float32)) - tf.imsave(os.path.join(folder_path, 'corrected_max_projection.tif'), - np.max(m_rig, axis=0).astype(np.float32)) - - offset_f = h5py.File(os.path.join(folder_path, 'correction_offsets.hdf5')) - offsets = mc.shifts_rig - offsets = np.array([np.array(o) for o in offsets]).astype(np.float32) - offset_dset = offset_f.create_dataset(name='file_0000', data=offsets) - offset_dset.attrs['format'] = 'height, width' - offset_dset.attrs['path'] = file_path - - os.remove(mc.fname_tot_rig[0]) - - -if __name__ == '__main__': - data_folder = os.path.join(base_folder, reference_chn) - chunk_p = Pool(n_processes) - folder_list = [f for f in os.listdir(data_folder) if os.path.isdir(os.path.join(data_folder, f))] - folder_list.sort() - print('\n'.join(folder_list)) - folder_list = [os.path.join(data_folder, f) for f in folder_list] - chunk_p.map(correct_single_movie, folder_list) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/015_apply_offsets_multichannel.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/015_apply_offsets_multichannel.py deleted file mode 100644 index dc91988..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/015_apply_offsets_multichannel.py +++ /dev/null @@ -1,51 +0,0 @@ -import os -import stia.motion_correction as mc -import numpy as np -import h5py -import tifffile as tf -from multiprocessing import Pool - -def run(): - data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180605-M391355-2p\zstack\zstack_zoom2" - ref_ch_n = 'green' - apply_ch_ns = ['green', 'red'] - n_process = 5 - - step_ns = [f for f in os.listdir(os.path.join(data_folder, ref_ch_n))] - step_ns = [f for f in step_ns if os.path.isdir(os.path.join(data_folder, ref_ch_n, f))] - step_ns.sort() - print('\n'.join(step_ns)) - - chunk_p = Pool(n_process) - - for ch_n in apply_ch_ns: - mc_params = [] - for step_n in step_ns: - movie_path = os.path.join(data_folder, ch_n, step_n, step_n + '.tif') - offsets_path = os.path.join(data_folder, ref_ch_n, step_n, 'correction_offsets.hdf5') - mc_params.append((movie_path, offsets_path)) - # print(mc_params) - - chunk_p.map(apply_offset_single, mc_params) - - -def apply_offset_single(params): - - movie_path, offsets_path = params - - offsets_f = h5py.File(offsets_path) - ref_path = offsets_f['file_0000'].attrs['path'] - offsets_f.close() - - mc.apply_correction_offsets(offsets_path=offsets_path, - path_pairs=[[ref_path, movie_path]], - output_folder=os.path.split(os.path.realpath(movie_path))[0], - process_num=1, - fill_value=0., - avi_downsample_rate=None, - is_equalizing_histogram=False) - - -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/020_get_zstack_from_server.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/020_get_zstack_from_server.py deleted file mode 100644 index 8d4d7c5..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/020_get_zstack_from_server.py +++ /dev/null @@ -1,26 +0,0 @@ -import os -import numpy as np -import tifffile as tf - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project" \ - r"\180605-M391355-2p\zstack\zstack_zoom2" - -chns = ['green', 'red'] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -for chn in chns: - print('processing channel: {} ...'.format(chn)) - ch_folder = os.path.join(data_folder, chn) - steps = [f for f in os.listdir(ch_folder) if os.path.isdir(os.path.join(ch_folder, f)) and f[0:5] == 'step_'] - steps.sort() - print('\n'.join(steps)) - - zstack = [] - for step in steps: - zstack.append(tf.imread(os.path.join(ch_folder, step, 'corrected_mean_projection.tif'))) - - zstack = np.array(zstack) - save_n = os.path.split(data_folder)[1] + '_' + chn + '.tif' - tf.imsave(save_n, zstack) diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/030_get_fine_zstack.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/030_get_fine_zstack.py deleted file mode 100644 index 147bb6c..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/030_get_fine_zstack.py +++ /dev/null @@ -1,49 +0,0 @@ -import os -import h5py -import numpy as np -import tifffile as tf -import stia.motion_correction as mc -import stia.utility.image_analysis as ia - -zstack_fn = 'FOV2_projection_site_zstack_red.tif' - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -zstack = tf.imread(zstack_fn) - -step_offsets = [[0., 0.]] # offsets between adjacent steps - -print('calculating step offsets ...') -for step_i in range(1, zstack.shape[0]): - curr_offset = mc.phase_correlation(zstack[step_i], zstack[step_i - 1]) - step_offsets.append(curr_offset) -step_offsets = np.array([np.array(so) for so in step_offsets], dtype=np.float32) -print('\nsetp offsets:') -print(step_offsets) - -print('\ncalculating final offsets ...') -final_offsets_y = np.cumsum(step_offsets[:, 0]) -final_offsets_x = np.cumsum(step_offsets[:, 1]) -final_offsets = np.array([final_offsets_x, final_offsets_y], dtype=np.float32).transpose() -print('\nfinal offsets:') -print(final_offsets) - -print('applying final offsets ...') - -zstack_f = [] # fine zstack - -for step_i in range(zstack.shape[0]): - - curr_offset = final_offsets[step_i] - - frame = zstack[step_i] - frame_f = ia.rigid_transform_cv2_2d(frame, offset=curr_offset, fill_value=0.).astype(np.float32) - zstack_f.append(frame_f) - -zstack_f = np.array(zstack_f, dtype=np.float32) - -tf.imsave(os.path.splitext(zstack_fn)[0] + '_aligned.tif', zstack_f) -tf.imsave(os.path.splitext(zstack_fn)[0] + '_max_projection.tif', np.max(zstack_f, axis=0)) - - diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/040_combine_channels.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/040_combine_channels.py deleted file mode 100644 index 704e5d4..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/040_combine_channels.py +++ /dev/null @@ -1,32 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import cv2 - -fn_lst = ['FOV2_projection_site_zstack_green_aligned.tif', - 'FOV2_projection_site_zstack_red_aligned.tif'] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -mov = [] - -for fn in fn_lst: - curr_mov = tf.imread(fn).astype(np.float32) - - curr_mov_adjust = [] - # histogram equalization - for frame in curr_mov: - # display_frame = (frame - np.amin(frame)) / (np.amax(frame) - np.amin(frame)) - # display_frame = (display_frame * 255).astype(np.uint8) - # display_frame = cv2.equalizeHist(display_frame) - - display_frame = frame - np.mean(frame[:]) - curr_mov_adjust.append(display_frame) - - curr_mov_adjust = np.array(curr_mov_adjust) - mov.append(curr_mov_adjust) - -mov = np.concatenate(mov, axis=2) - -tf.imsave('zstack_aligned_combined.tif', mov) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/050_get_2p_vas_maps.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/050_get_2p_vas_maps.py deleted file mode 100644 index 359d533..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/050_get_2p_vas_maps.py +++ /dev/null @@ -1,37 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import matplotlib.pyplot as plt - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180605-M391355-2p\vasmap_2p" -file_ns = ["vasmap_2p_zoom2_00001_00001.tif"] - -save_name = 'vasmap_2p_zoom2' - -channels = ['green', 'red'] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -vasmaps = {} -for chn in channels: - vasmaps.update({chn: []}) - -for file_n in file_ns: - print(file_n) - - curr_vasmap = tf.imread(os.path.join(data_folder, file_n)) - - for ch_i, ch_n in enumerate(channels): - curr_vasmap_ch = curr_vasmap[ch_i::len(channels)] - vasmaps[ch_n].append(curr_vasmap_ch.transpose((0, 2, 1))[:, ::-1, :]) - # print(curr_vasmap_ch.shape) - -for ch_n, ch_vasmap in vasmaps.items(): - save_vasmap = np.concatenate(ch_vasmap, axis=0) - # print(save_vasmap.shape) - save_vasmap = ia.array_nor(np.mean(save_vasmap, axis=0)) - # print(save_vasmap.shape) - tf.imsave('{}_{}.tif'.format(save_name, ch_n), save_vasmap.astype(np.float32)) diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/060_get_wf_vas_maps.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/060_get_wf_vas_maps.py deleted file mode 100644 index 7a2ab89..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/caiman/060_get_wf_vas_maps.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -import numpy as np -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -import tifffile as tf - - -vas_map_paths= [r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180605-M391355-2p\vasmap_wf\180605JCamF100"] - -saveFolder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(saveFolder) - -vas_maps = [] - -for vas_map_path in vas_map_paths: - - vas_map_focused, _, _ = ft.importRawJCamF(vas_map_path, column=1024, row=1024, headerLength = 116, - tailerLength=452) - vas_map_focused = vas_map_focused[2:] - vas_map_focused = vas_map_focused[:, ::-1, :] - vas_map_focused[vas_map_focused > 50000] = 400 - vas_map_focused = np.mean(vas_map_focused, axis=0) - vas_maps.append(ia.array_nor(vas_map_focused)) - -vas_map = ia.array_nor(np.mean(vas_maps, axis=0)) - -tf.imsave('vas_map_focused_wf_green.tif', vas_map.astype(np.float32)) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/deepscope/000_reorganize_files.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/deepscope/000_reorganize_files.py deleted file mode 100644 index ccd9742..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/deepscope/000_reorganize_files.py +++ /dev/null @@ -1,60 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data\181102-M412052-deepscope\zstack2" -file_identifier = 'zstack2' -ch_ns = ['red'] -frames_per_step = 200 -is_rotate = True - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -file_ns = [f for f in os.listdir(data_folder) if f[-4:] == '.tif' and file_identifier in f] -file_ns.sort() -print('\n'.join(file_ns)) - -save_folders = [] -for ch_n in ch_ns: - curr_save_folder = os.path.join(data_folder, file_identifier, ch_n) - if not os.path.isdir(curr_save_folder): - os.makedirs(curr_save_folder) - save_folders.append(curr_save_folder) - -curr_step = 0 - -for file_n in file_ns: - curr_mov = tf.imread(os.path.join(data_folder, file_n)) - - # reorient movie - if is_rotate: - h_new = int(curr_mov.shape[1] * np.sqrt(2)) - w_new = int(curr_mov.shape[2] * np.sqrt(2)) - curr_mov = ia.rigid_transform_cv2(curr_mov, rotation=140, outputShape=(h_new, w_new))[:, :, ::-1] - - curr_frame_num = curr_mov.shape[0] / len(ch_ns) - - if curr_frame_num % frames_per_step != 0: - raise ValueError('{}: total frame number is not divisible by frames per step.'.format(file_n)) - - curr_mov_chs = [] - for ch_i in range(len(ch_ns)): - curr_mov_chs.append(curr_mov[ch_i::len(ch_ns)]) - - steps = curr_frame_num // frames_per_step - for step_ind in range(steps): - - print ('current step: {}'.format(curr_step)) - - for ch_i in range(len(ch_ns)): - curr_step_mov_ch = curr_mov_chs[ch_i][step_ind * frames_per_step:(step_ind + 1) * frames_per_step,:,:] - curr_step_n = 'step_' + ft.int2str(curr_step, 4) - curr_step_folder = os.path.join(save_folders[ch_i], curr_step_n) - os.mkdir(curr_step_folder) - tf.imsave(os.path.join(curr_step_folder, curr_step_n + '.tif'), curr_step_mov_ch) - - curr_step += 1 diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/scientifica/000_reorganize_files.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/scientifica/000_reorganize_files.py deleted file mode 100644 index 3bb77ea..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/scientifica/000_reorganize_files.py +++ /dev/null @@ -1,56 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180816-M376019-zstack" -file_identifier = 'zstack_2p_zoom2' -ch_ns = ['green', 'red'] -frames_per_step = 300 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -file_ns = [f for f in os.listdir(data_folder) if f[-4:] == '.tif' and file_identifier in f] -file_ns.sort() -print('\n'.join(file_ns)) - -save_folders = [] -for ch_n in ch_ns: - curr_save_folder = os.path.join(data_folder, file_identifier, ch_n) - if not os.path.isdir(curr_save_folder): - os.makedirs(curr_save_folder) - save_folders.append(curr_save_folder) - -curr_step = 0 - -for file_n in file_ns: - curr_mov = tf.imread(os.path.join(data_folder, file_n)) - - # reorient movie - curr_mov = ia.rigid_transform_cv2_2d(curr_mov[:, ::-1, :], rotation=135) - - curr_frame_num = curr_mov.shape[0] / len(ch_ns) - - if curr_frame_num % frames_per_step != 0: - raise ValueError('{}: total frame number is not divisible by frames per step.'.format(file_n)) - - curr_mov_chs = [] - for ch_i in range(len(ch_ns)): - curr_mov_chs.append(curr_mov[ch_i::len(ch_ns)]) - - steps = curr_frame_num // frames_per_step - for step_ind in range(steps): - - print ('current step: {}'.format(curr_step)) - - for ch_i in range(len(ch_ns)): - curr_step_mov_ch = curr_mov_chs[ch_i][step_ind * frames_per_step:(step_ind + 1) * frames_per_step,:,:] - curr_step_n = 'step_' + ft.int2str(curr_step, 4) - curr_step_folder = os.path.join(save_folders[ch_i], curr_step_n) - os.mkdir(curr_step_folder) - tf.imsave(os.path.join(curr_step_folder, curr_step_n + '.tif'), curr_step_mov_ch) - - curr_step += 1 diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/000_reorganize_files.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/000_reorganize_files.py deleted file mode 100644 index 354c8c2..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/000_reorganize_files.py +++ /dev/null @@ -1,55 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.FileTools as ft - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180816-M376019-zstack" -file_identifier = 'zstack_2p_zoom2' -ch_ns = ['green', 'red'] -frames_per_step = 300 - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -file_ns = [f for f in os.listdir(data_folder) if f[-4:] == '.tif' and file_identifier in f] -file_ns.sort() -print('\n'.join(file_ns)) - -save_folders = [] -for ch_n in ch_ns: - curr_save_folder = os.path.join(data_folder, file_identifier, ch_n) - if not os.path.isdir(curr_save_folder): - os.makedirs(curr_save_folder) - save_folders.append(curr_save_folder) - -curr_step = 0 - -for file_n in file_ns: - curr_mov = tf.imread(os.path.join(data_folder, file_n)) - - # reorient movie - curr_mov = curr_mov.transpose((0, 2, 1))[:, ::-1, :] - - curr_frame_num = curr_mov.shape[0] / len(ch_ns) - - if curr_frame_num % frames_per_step != 0: - raise ValueError('{}: total frame number is not divisible by frames per step.'.format(file_n)) - - curr_mov_chs = [] - for ch_i in range(len(ch_ns)): - curr_mov_chs.append(curr_mov[ch_i::len(ch_ns)]) - - steps = curr_frame_num // frames_per_step - for step_ind in range(steps): - - print ('current step: {}'.format(curr_step)) - - for ch_i in range(len(ch_ns)): - curr_step_mov_ch = curr_mov_chs[ch_i][step_ind * frames_per_step:(step_ind + 1) * frames_per_step,:,:] - curr_step_n = 'step_' + ft.int2str(curr_step, 4) - curr_step_folder = os.path.join(save_folders[ch_i], curr_step_n) - os.mkdir(curr_step_folder) - tf.imsave(os.path.join(curr_step_folder, curr_step_n + '.tif'), curr_step_mov_ch) - - curr_step += 1 diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/050_get_2p_vas_maps.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/050_get_2p_vas_maps.py deleted file mode 100644 index 2da226a..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/050_get_2p_vas_maps.py +++ /dev/null @@ -1,39 +0,0 @@ -import os -import numpy as np -import tifffile as tf -import corticalmapping.core.ImageAnalysis as ia -import matplotlib.pyplot as plt - - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180816-M376019-zstack" -file_ns = ["vasmap_2p_zoom1_00001_00001.tif", - "vasmap_2p_zoom1_00002_00001.tif", - "vasmap_2p_zoom1_00003_00001.tif"] - -save_name = 'vasmap_2p_zoom1' - -channels = ['green', 'red'] - -curr_folder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(curr_folder) - -vasmaps = {} -for chn in channels: - vasmaps.update({chn: []}) - -for file_n in file_ns: - print(file_n) - - curr_vasmap = tf.imread(os.path.join(data_folder, file_n)) - - for ch_i, ch_n in enumerate(channels): - curr_vasmap_ch = curr_vasmap[ch_i::len(channels)] - vasmaps[ch_n].append(curr_vasmap_ch.transpose((0, 2, 1))[:, ::-1, :]) - # print(curr_vasmap_ch.shape) - -for ch_n, ch_vasmap in vasmaps.items(): - save_vasmap = np.concatenate(ch_vasmap, axis=0) - # print(save_vasmap.shape) - save_vasmap = ia.array_nor(np.mean(save_vasmap, axis=0)) - # print(save_vasmap.shape) - tf.imsave('{}_{}.tif'.format(save_name, ch_n), save_vasmap.astype(np.float32)) diff --git a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/060_get_wf_vas_maps.py b/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/060_get_wf_vas_maps.py deleted file mode 100644 index 538d884..0000000 --- a/corticalmapping/scripts/post_recording/analysis_pipeline_zstack/old/sutter_regular_2p/060_get_wf_vas_maps.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -import numpy as np -import corticalmapping.core.FileTools as ft -import corticalmapping.core.ImageAnalysis as ia -import tifffile as tf - -data_folder = r"\\allen\programs\braintv\workgroups\nc-ophys\Jun\raw_data_rabies_project\180816-M376019-zstack" - -vas_map_fns= ["180816JCamF100",] - -saveFolder = os.path.dirname(os.path.realpath(__file__)) -os.chdir(saveFolder) - -vas_map_paths = [os.path.join(data_folder, f) for f in vas_map_fns] - -vas_maps = [] - -for vas_map_path in vas_map_paths: - - # if not work, try: tailerLength=218 - vas_map_focused, _, _ = ft.importRawJCamF(vas_map_path, column=1024, row=1024, headerLength = 116, - tailerLength=452) - vas_map_focused = vas_map_focused[2:] - vas_map_focused = vas_map_focused[:, ::-1, :] - vas_map_focused[vas_map_focused > 50000] = 400 - vas_map_focused = np.mean(vas_map_focused, axis=0) - vas_maps.append(ia.array_nor(vas_map_focused)) - -vas_map = ia.array_nor(np.mean(vas_maps, axis=0)) - -tf.imsave('vas_map_focused_wf_green.tif', vas_map.astype(np.float32)) \ No newline at end of file diff --git a/corticalmapping/scripts/post_recording/00_old/batch_analyzeDetrend.py b/corticalmapping/scripts/post_recording/batch_analyzeDetrend.py similarity index 100% rename from corticalmapping/scripts/post_recording/00_old/batch_analyzeDetrend.py rename to corticalmapping/scripts/post_recording/batch_analyzeDetrend.py diff --git a/corticalmapping/scripts/recording/batch_displayKSstimAllDir.py b/corticalmapping/scripts/recording/batch_displayKSstimAllDir.py index 5ffec14..d8e3ed0 100644 --- a/corticalmapping/scripts/recording/batch_displayKSstimAllDir.py +++ b/corticalmapping/scripts/recording/batch_displayKSstimAllDir.py @@ -1,30 +1,37 @@ import matplotlib.pyplot as plt -import corticalmapping.VisualStim as vs +import CorticalMapping.corticalmapping.VisualStim as vs -mouseID = 'TEST' #'147861' #'TEST' -userID = 'Jun' -numOfTrials = 2 # 20 +mouseID = '326981' #'147861' #'TEST' +userID = 'Natalia' +numOfTrials = 40 # 20 logFolder = r'C:\data' - +isTriggered = False +isRemoteSync = False +psychopyMonitor = 'smartTVgamma' #'smartTVgamma' +logFolder = r'C:\data' +backupFolder = r'\\W7DTMJ03jgl2\data' +remoteSyncIP = 'w7dtmj19vtx' +remoteSyncPort = 11001 +syncOutputFolder = None mon=vs.Monitor(resolution=(1080, 1920), - dis=15.3, - monWcm=88.8, - monHcm=50.1, - C2Tcm=31.1, - C2Acm=41.91, - monTilt=26.56, + dis=9.5, + monWcm=52.0, + monHcm=30.25, + C2Tcm=15.125, + C2Acm=25.5, + monTilt=0.0, downSampleRate=5) #mon.plot_map() #plt.show() indicator=vs.Indicator(mon, - width_cm=3., - height_cm=3., + width_cm=6., + height_cm=6., position = 'southeast', isSync=True, freq=1.) @@ -44,14 +51,14 @@ ds = vs.DisplaySequence(logdir=logFolder, backupdir=None, displayIteration=numOfTrials, - psychopyMonitor='testMonitor', + psychopyMonitor=psychopyMonitor, displayOrder=1, mouseid=mouseID, userid=userID, isInterpolate=False, isRemoteSync=False, - remoteSyncIP='localhost', - remoteSyncPort=10003, + remoteSyncIP=remoteSyncIP, + remoteSyncPort=remoteSyncPort, remoteSyncTriggerEvent="positiveEdge", remoteSyncSaveWaitTime=5., isTriggered=False, @@ -63,7 +70,7 @@ syncPulseNIDev='Dev1', syncPulseNIPort=1, syncPulseNILine=1, - displayScreen=0, + displayScreen=1, initialBackgroundColor=0., isVideoRecord=False, videoRecordIP='w7dtmj007lhu', diff --git a/corticalmapping/setup.py b/corticalmapping/setup.py new file mode 100644 index 0000000..1a37804 --- /dev/null +++ b/corticalmapping/setup.py @@ -0,0 +1,74 @@ +__author__ = 'junz' + +from setuptools import setup, find_packages +from setuptools.command.test import test as TestCommand +import io +import os +import sys + +here = os.path.abspath(os.path.dirname(__file__)) + +def read(*filenames, **kwargs): + encoding = kwargs.get('encoding', 'utf-8') + sep = kwargs.get('sep', '\n') + buf = [] + for filename in filenames: + with io.open(filename, encoding=encoding) as f: + buf.append(f.read()) + return sep.join(buf) + +long_description = read('README.md') + +def prepend_find_packages(*roots): + ''' + Recursively traverse nested packages under the root directories + ''' + packages = [] + + for root in roots: + packages += [root] + packages += [root + '.' + s for s in find_packages(root)] + + return packages + +class PyTest(TestCommand): + def finalize_options(self): + TestCommand.finalize_options(self) + self.test_args = ['--junitxml=result.xml'] + self.test_args_cov = self.test_args + ['--cov=corticalmapping', '--cov-report=term', '--cov-report=html'] + self.test_suite = True + + def run_tests(self): + import pytest + + try: + errcode = pytest.main(self.test_args_cov) + except: + errcode = pytest.main(self.test_args) + sys.exit(errcode) + +setup( + name='corticalmapping', + version = '2.0.0', + url='http://stash.corp.alleninstitute.org/users/junz/repos/corticalmapping/', + author='Jun Zhuang', + tests_require=['pytest'], + install_requires=['numpy','scipy','opencv-python','PyDAQmx','scikit-image','tifffile'], + cmdclass={'test': PyTest}, + author_email='junz@alleninstitute.org', + description='cortical mapping tools', + long_description=long_description, + packages=prepend_find_packages('corticalmapping'), + include_package_data=True, + package_data={'':['*.md', '*.txt', '*.cfg', '*.hdf5']}, + platforms='any', + classifiers = [ + 'Programming Language :: Python', + 'Development Status :: 4 - Beta', + 'Natural Language :: English', + 'Operating System :: OS Independent', + ], + extras_require={ + 'testing': ['pytest'], + } +) \ No newline at end of file diff --git a/corticalmapping/test/data/test.hdf5 b/corticalmapping/test/data/test.hdf5 index f02a3960f71de49d4c062a07cecb0a865d13fc75..98c4300c89d47c144ab5b06ec8646c1d2ea4c0ad 100644 GIT binary patch delta 298 zcmX>Q{~&&X2IGT`n>W4g3=~#%+GU`h}4(Ve(b>cpz!Y;SVHRIqn0=RL=SAj0_A6 z%s@6bmmy7Uas$8J zWHp|1P@@XqMr~H$4Pk_eHo!$EFW`GH@c_qU37r)a4;X9`D3O3FnE+R^d4UF8;{v$o uWCg89lbd*opvpHON$~ENtiZ`JSwU|KRPq2^a&v+%+^h?5(a8_=9smHu^H(nb delta 330 zcmaD5e;|H>2IGN^n&GS*0y+#}FquO;V)FymFN}-~ldrPJ14&a3e<0b)aUV#ga?WQ5 zDPsn*xw#ZseuS@#nry)JgNZ?7;zl`64~Q8I4U+@8)tMNyL1GzDu>+GA0>vEkfMT0p zaPu%i%@Kf`vsr;R1SYBg7oEI-@4@63+;+ku4gw4UV6!?{Ax1__+^D}fL7+qeYLo$7 z( (1. / 7. - 1E-7)) - assert (gDSI_raw == 3. / 7.) - assert (gOSI_ele < (1. / 15. + 1E-7)) - assert (gOSI_ele > (1. / 15. - 1E-7)) - assert (gDSI_ele == 3. / 15.) - assert (gOSI_rec == 0.) - assert (gDSI_rec == 2. / 8.) - - assert (peak_dire_raw == int(vs_dire_raw) == int(vs_dire_ele) == int(vs_dire_rec) == 90) - plt.show() if __name__ == '__main__': - test_get_orientation_properties() + test_SpatialTemporalReceptiveField_getAmpLitudeMap() diff --git a/corticalmapping/test/test_TimingAnalysis.py b/corticalmapping/test/test_TimingAnalysis.py index 6158f20..aa272c2 100644 --- a/corticalmapping/test/test_TimingAnalysis.py +++ b/corticalmapping/test/test_TimingAnalysis.py @@ -49,8 +49,8 @@ def test_get_event_with_pre_iei(self): assert (np.array_equal(ts5, np.array([2, 6, 8]))) np.random.shuffle(ts3) ts6 = ta.get_event_with_pre_iei(ts3, iei=0.5) - print ts3 - print ts6 + print(ts3) + print(ts6) assert (np.array_equal(ts6, np.array([2, 3, 4, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]))) ts7 = ta.get_event_with_pre_iei(ts3, iei=1) assert (np.array_equal(ts7, np.array([2, 6, 8]))) @@ -92,20 +92,6 @@ def test_haramp(self): assert (len(har) == 4) assert (round(1000. * har[1] / har[0]) / 1000. == 2.) - def test_threshold_to_intervals(self): - trace = np.array([2.3, 4.5, 6.7, 5.5, 3.3, 9.2, 4.4, 3.2, 1.0, 0.8, 5.5]) - - intvs1 = ta.threshold_to_intervals(trace=trace, thr=5.0, comparison='>=') - # print(intvs1) - for intv in intvs1: - # print(trace[intv[0]: intv[1]]) - assert(np.min(trace[intv[0]: intv[1]]) >= 5.0) - - intvs2 = ta.threshold_to_intervals(trace=trace, thr=3.0, comparison='<') - for intv in intvs2: - # print(trace[intv[0]: intv[1]]) - assert(np.max(trace[intv[0]: intv[1]]) < 3.0) - if __name__ == '__main__': TestTimingAnalysis.test_get_onset_time_stamps()

    LyfLv(PPD?9-bw{asWjwrbt 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%0AN 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%0D9-HLJTioo6mqit%b!5b>FFz$?q-ceefWV~_ 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