Source code for arthropod_describer.plugins.profile_register.general.profile_registering

import csv
import itertools
import shutil
from pathlib import Path
import typing
from typing import List, Optional, Set

import numpy as np
from skimage import io
import openpyxl
# import matplotlib.pyplot as plt

from arthropod_describer.common.common import Info
from arthropod_describer.common.label_image import LabelImg, RegionProperty
from arthropod_describer.common.photo import Photo

from arthropod_describer.common.plugin import GeneralAction
from arthropod_describer.common.state import State
from arthropod_describer.common.storage import Storage
from arthropod_describer.common.user_params import UserParam
from arthropod_describer.plugins.profile_register.general.profiles import get_median_profile, merge_profiles


[docs]class ProfileFusion(GeneralAction): """ NAME: Profile fusion DESCRIPTION: Fuse body profiles based on their tags. A median profile is created for each group of images that matches the given tags. (how to specify and use the tags must be found) USER_PARAMS: PARAM_NAME: Iteration tag PARAM_KEY: iteration_tags PARAM_SOURCE: Storage PARAM_SOURCE_FIELD: tag_prefixes PARAM_VALUE_CARDINALITY: SingleValue PARAM_NAME: Model group tags PARAM_KEY: model_group_tags PARAM_SOURCE: Storage PARAM_SOURCE_FIELD: tag_prefixes PARAM_VALUE_CARDINALITY: MultiValue PARAM_NAME: Mimic group tags PARAM_KEY: mimic_group_tags PARAM_SOURCE: Storage PARAM_SOURCE_FIELD: tag_prefixes PARAM_VALUE_CARDINALITY: MultiValue PARAM_NAME: Delete existing xlsx files PARAM_KEY: delete_existing_xlsx PARAM_TYPE: BOOL DEFAULT_VALUE: FALSE """ def __init__(self, info: Optional[Info] = None): super().__init__(info) def __call__(self, state: State): # state.storage # state.storage.image_names # image = state.storage.get_photo_by_name() # extract model and mimic image names from storage.image_names # get model and mimic images by calling storage.get_photo_by_name(<image_name>) # for each `image` the contour vector is stored in `image['Labels'].region_props[16842752]['contour'].value`, # that is of type `RegionProperty`, and the vector is stored in `RegionProperty.value`, and in the case of # the contour vector, it is a List[float] # print(f'model prefix is {self._user_params["iteration_tags"].value}, mimic prefix is {self._user_params["group_tags"].value}') output_folder = Path(state.storage.location / 'registered_profiles') if not output_folder.exists(): output_folder.mkdir(exist_ok=True) print(f'iteration tags are {self._user_params["iteration_tags"].param_instances[0].value}') print(f'model group tags are: {self._user_params["model_group_tags"].value}') print(f'mimic group tags are: {self._user_params["mimic_group_tags"].value}') # for group_tags in self._user_params['group_tags'].param_instances.values(): # print(group_tags.value) prefix_tags_map: typing.Dict[str, typing.List[str]] = dict() all_storage_tags = state.storage.used_tags for tag_prefix in self._user_params['iteration_tags'].value: for tag in all_storage_tags: if tag.startswith(tag_prefix): prefix_tags_map.setdefault(tag_prefix, list()).append(tag) print(f'prefix_tags_map = {prefix_tags_map}') # print(f'iteration tag sets are {list(itertools.product(*prefix_tags_map.values()))}') iteration_tags = [tag for tag in all_storage_tags if tag.startswith(self._user_params["iteration_tags"].value)] print(f'iteration tags are {iteration_tags}') model_tags = self._user_params["model_group_tags"].value model_column_name = "_".join(sorted(model_tags)) mimic_tags = self._user_params["mimic_group_tags"].value mimic_column_name = "_".join(sorted(mimic_tags)) registered_column_name = model_column_name + ";" + mimic_column_name if self._user_params['delete_existing_xlsx'].value: for iteration_tag in iteration_tags: shutil.rmtree(output_folder / iteration_tag, ignore_errors=True) for iteration_tag in iteration_tags: sample_folder = output_folder / iteration_tag if not sample_folder.exists(): sample_folder.mkdir(exist_ok=True) if (worksheet_path := sample_folder / 'profiles.xlsx').exists(): wb = openpyxl.load_workbook(worksheet_path, read_only=False) else: wb = openpyxl.Workbook() ws_med_prof = wb.active ws_med_prof.title = 'Median profiles' ws_med_prof.append(['ProfileID'] + [f'G_BP_{i}' for i in range(40)]) ws_aligned = wb.create_sheet('Aligned profiles') ws_aligned.append(['ProfileID', 'AlignedTo'] + [f'G_BP_{i}' for i in range(40)]) ws_med_prof = wb['Median profiles'] model_median = self._get_median_profile_for(iteration_tag, model_tags, state) mimic_median = self._get_median_profile_for(iteration_tag, mimic_tags, state) ws_med_prof.append([model_column_name] + [str(float(val)) for val in model_median]) ws_med_prof.append([mimic_column_name] + [str(float(val)) for val in mimic_median]) # if model_median is not None: # np.savetxt(str(sample_folder / f'{model_column_name}.txt'), model_median) # if mimic_median is not None: # np.savetxt(str(sample_folder / f'{mimic_column_name}.txt'), mimic_median) if model_median is None or mimic_median is None: continue # TODO log a message # registered = merge_profiles(model_median, mimic_median, show_matches=True) # np.savetxt(str(sample_folder / f'{registered_column_name}.txt'), registered) # model_mimic_1 = merge_profiles(model_median, mimic_median, x_weight=1.0, y_weight=1.0) model_mimic_05 = merge_profiles(model_median, mimic_median) # model_mimic_0 = merge_profiles(model_median, mimic_median, x_weight=0.0, y_weight=0.0) # mimic_aligned_to_model = merge_profiles(mimic_median, model_mimic_1) # mimic_aligned_to_mimic = merge_profiles(mimic_median, model_mimic_0) mimic_aligned_to_both = merge_profiles(mimic_median, model_mimic_05) # model_aligned_to_model = merge_profiles(model_median, model_mimic_1) # model_aligned_to_mimic = merge_profiles(model_median, model_mimic_0) model_aligned_to_both = merge_profiles(model_median, model_mimic_05) # plt.subplots(nrows=1, ncols=2) # plt.subplot(121) # plt.plot(range(40), model_aligned_to_both) # # plt.subplot(122) # plt.plot(range(40), mimic_aligned_to_both) # # plt.savefig('D:/plot.png') # plt.show() ws_aligned = wb['Aligned profiles'] # ws_aligned.append([mimic_column_name, model_column_name] + [str(float(val)) for val in mimic_aligned_to_model]) # ws_aligned.append([mimic_column_name, mimic_column_name] + [str(float(val)) for val in mimic_aligned_to_mimic]) ws_aligned.append([model_column_name, f'{model_column_name}_{mimic_column_name}_0.5_0.5'] + [str(float(val)) for val in model_aligned_to_both]) ws_aligned.append([mimic_column_name, f'{model_column_name}_{mimic_column_name}_0.5_0.5'] + [str(float(val)) for val in mimic_aligned_to_both]) # ws_aligned.append([model_column_name, model_column_name] + [str(float(val)) for val in model_aligned_to_model]) # ws_aligned.append([model_column_name, mimic_column_name] + [str(float(val)) for val in model_aligned_to_mimic]) wb.save(worksheet_path) wb.close() # with open(sample_folder / f'{registered_column_name};profiles.csv', newline='\n', mode='w') as csvfile: # writer = csv.writer(csvfile, dialect='excel') # writer.writerow([model_column_name, mimic_column_name, registered_column_name]) # # for mod_val, mim_val, reg_val in zip(model_median, mimic_median, registered): # writer.writerow([mod_val, mim_val, reg_val]) def _get_median_profile_for(self, iteration_tag: str, group_tags: typing.Set[str], state: State) -> typing.Optional[np.ndarray]: tags_set = set(group_tags).union({iteration_tag}) photos = state.storage.photos_satisfying_tags(tags_set) if len(photos) == 0: return None profiles: np.ndarray = np.zeros((len(photos), 40), np.float32) print(f'number of photos satisfying the tag set {tags_set} = {len(photos)}') unit = None for i, photo in enumerate(photos): # io.imsave(str(save_dest / photo.image_name), photo.image) profiles[i] = np.array(photo['Labels'].region_props[16842752]["arthropod_describer.plugins.profile_register.properties.contour"].value[0]) # unit = photo['Labels'].region_props[16842752]["arthropod_describer.plugins.profile_register.properties.contour"].value[1] median = get_median_profile(profiles, show_fig=False) # prop = RegionProperty() # prop.label = 16842752 # prop.num_vals = profiles.shape[1] # prop.value = (median, unit) # state.storage.properties[self.info.key]['_'.join(tags_set)] = prop # np.save('D:/profiles_registering_test/median.npy', median) # np.savetxt(f'median{"_".join(tags_set)}.txt', median) return median @property def user_params(self) -> List[UserParam]: return super().user_params