Example #1
0
class FigureOptions:
    def __init__(self, should_save: bool, dpi: int, size_w: float, size_h: float) -> None:
        self.bn_should_save = BoxBindable(should_save)
        self.bn_dpi = BoxBindable(dpi)
        self.bn_size_w = BoxBindable(size_w)
        self.bn_size_h = BoxBindable(size_h)

        # Validation

        self.dpi_err = validate(
            value=self.bn_dpi,
            checks=(check_is_not_empty,
                    check_is_positive),
            enabled=self.bn_should_save)
        self.size_w_err = validate(
            value=self.bn_size_w,
            checks=(check_is_not_empty,
                    check_is_positive),
            enabled=self.bn_should_save)
        self.size_h_err = validate(
            value=self.bn_size_h,
            checks=(check_is_not_empty,
                    check_is_positive),
            enabled=self.bn_should_save)

        self.errors = bn_apply(lambda *args: any(args), self.dpi_err, self.size_w_err, self.size_h_err)

    @property
    def size(self) -> Tuple[float, float]:
        return self.bn_size_w.get(), self.bn_size_h.get()
Example #2
0
class CameraAcquirer(ImageAcquirer):
    def __init__(self) -> None:
        self._loop = asyncio.get_event_loop()

        self.bn_camera = BoxBindable(None)  # type: Bindable[Optional[Camera]]

        self.bn_num_frames = BoxBindable(1)
        self.bn_frame_interval = BoxBindable(
            None)  # type: Bindable[Optional[float]]

    def acquire_images(self) -> Sequence[InputImage]:
        camera = self.bn_camera.get()

        if camera is None:
            raise ValueError("'camera' can't be None")

        num_frames = self.bn_num_frames.get()

        if num_frames is None or num_frames <= 0:
            raise ValueError(
                "'num_frames' must be > 0 and not None, currently: '{}'".
                format(num_frames))

        frame_interval = self.bn_frame_interval.get()

        if frame_interval is None or frame_interval <= 0:
            if num_frames == 1:
                frame_interval = 0
            else:
                raise ValueError(
                    "'frame_interval' must be > 0 and not None, currently: '{}'"
                    .format(frame_interval))

        input_images = []

        for i in range(num_frames):
            capture_delay = i * frame_interval

            input_image = _BaseCameraInputImage(
                camera=camera,
                delay=capture_delay,
                first_image=input_images[0] if input_images else None,
                loop=self._loop,
            )

            input_images.append(input_image)

        return input_images

    def get_image_size_hint(self) -> Optional[Tuple[int, int]]:
        camera = self.bn_camera.get()
        if camera is None:
            return

        return camera.get_image_size_hint()
Example #3
0
class ConanAnalysisSaverOptions:
    def __init__(self) -> None:
        self.bn_save_dir_parent = BoxBindable(None)  # type: Bindable[Optional[Path]]
        self.bn_save_dir_name = BoxBindable('')

        self.angle_figure_opts = FigureOptions(
            should_save=True,
            dpi=300,
            size_w=15,
            size_h=9
        )

    @property
    def save_root_dir(self) -> Path:
        return self.bn_save_dir_parent.get() / self.bn_save_dir_name.get()
Example #4
0
class WizardController:
    def __init__(self, pages: Iterable) -> None:
        self._pages = tuple(pages)

        self.bn_current_page = BoxBindable(self._pages[0])

    def next_page(self) -> None:
        current_page = self.bn_current_page.get()
        next_page_idx = self._pages.index(current_page) + 1
        next_page = self._pages[next_page_idx]

        self.bn_current_page.set(next_page)

    def prev_page(self) -> None:
        current_page = self.bn_current_page.get()
        prev_page_idx = self._pages.index(current_page) - 1
        prev_page = self._pages[prev_page_idx]

        self.bn_current_page.set(prev_page)
Example #5
0
class ImageSequenceAcquirer(ImageAcquirer):
    IS_REPLICATED = False

    def __init__(self) -> None:
        self.bn_images = BoxBindable(
            tuple())  # type: Bindable[Sequence[np.ndarray]]

        self.bn_frame_interval = BoxBindable(
            None)  # type: Bindable[Optional[int]]

    def acquire_images(self) -> Sequence[InputImage]:
        images = self.bn_images.get()
        if len(images) == 0:
            raise ValueError("'_images' can't be empty")

        frame_interval = self.bn_frame_interval.get()
        if frame_interval is None or frame_interval <= 0:
            if len(images) == 1:
                # Since only one image, we don't care about the frame_interval.
                frame_interval = 0
            else:
                raise ValueError(
                    "'frame_interval' must be > 0 and not None, currently: '{}'"
                    .format(frame_interval))

        input_images = []

        for i, img in enumerate(images):
            input_image = _BaseImageSequenceInputImage(image=img,
                                                       timestamp=i *
                                                       frame_interval)
            input_image.is_replicated = self.IS_REPLICATED
            input_images.append(input_image)

        return input_images

    def get_image_size_hint(self) -> Optional[Tuple[int, int]]:
        images = self.bn_images.get()
        if images is None or len(images) == 0:
            return None

        first_image = images[0]
        return first_image.shape[1::-1]
Example #6
0
class WizardModel:
    def __init__(self, pages: Iterable[Any]) -> None:
        self._pages = tuple(pages)

        self._interpage_actions = {}

        self.bn_current_page = BoxBindable(self._pages[0])

    def next_page(self) -> None:
        current_page = self.bn_current_page.get()
        next_page_idx = self._pages.index(current_page) + 1
        next_page = self._pages[next_page_idx]

        self.perform_interpage_action(current_page, next_page)

        self.bn_current_page.set(next_page)

    def prev_page(self) -> None:
        current_page = self.bn_current_page.get()
        prev_page_idx = self._pages.index(current_page) - 1
        prev_page = self._pages[prev_page_idx]

        self.perform_interpage_action(current_page, prev_page)

        self.bn_current_page.set(prev_page)

    def perform_interpage_action(self, start_page: Any, end_page: Any) -> None:
        if (start_page, end_page) not in self._interpage_actions:
            return

        callback = self._interpage_actions[(start_page, end_page)]

        callback()

    def register_interpage_action(self, start_page: Any, end_page: Any,
                                  callback: Callable[[], Any]) -> None:
        self._interpage_actions[(start_page, end_page)] = callback
Example #7
0
class IFTAnalysisSaverOptions:
    def __init__(self) -> None:
        self.bn_save_dir_parent = BoxBindable(None)  # type: Bindable[Optional[Path]]
        self.bn_save_dir_name = BoxBindable('')

        self.drop_residuals_figure_opts = FigureOptions(
            should_save=True,
            dpi=300,
            size_w=10,
            size_h=10
        )

        self.ift_figure_opts = FigureOptions(
            should_save=True,
            dpi=300,
            size_w=15,
            size_h=9
        )

        self.volume_figure_opts = FigureOptions(
            should_save=True,
            dpi=300,
            size_w=15,
            size_h=9
        )

        self.surface_area_figure_opts = FigureOptions(
            should_save=True,
            dpi=300,
            size_w=15,
            size_h=9
        )

    @property
    def save_root_dir(self) -> Path:
        return self.bn_save_dir_parent.get() / self.bn_save_dir_name.get()
Example #8
0
class ConanSession:
    def __init__(self, do_exit: Callable[[], Any], *,
                 loop: asyncio.AbstractEventLoop) -> None:
        self._loop = loop

        self._do_exit = do_exit

        self._feature_extractor_params = FeatureExtractorParams()
        self._conancalc_params = ContactAngleCalculatorParams()

        self._bn_analyses = BoxBindable(
            tuple())  # type: Bindable[Sequence[ConanAnalysis]]
        self._analyses_saved = False

        self.image_acquisition = ImageAcquisitionModel()
        self.image_acquisition.use_acquirer_type(AcquirerType.LOCAL_STORAGE)

        self.image_processing = ConanImageProcessingModel(
            image_acquisition=self.image_acquisition,
            feature_extractor_params=self._feature_extractor_params,
            conancalc_params=self._conancalc_params,
            do_extract_features=self.extract_features,
        )

        self.results = ConanResultsModel(
            in_analyses=self._bn_analyses,
            do_cancel_analyses=self.cancel_analyses,
            do_save_analyses=self.save_analyses,
            create_save_options=self._create_save_options,
            check_if_safe_to_discard=self.check_if_safe_to_discard_analyses,
        )

    def start_analyses(self) -> None:
        assert len(self._bn_analyses.get()) == 0

        new_analyses = []

        input_images = self.image_acquisition.acquire_images()
        for input_image in input_images:
            new_analysis = ConanAnalysis(
                input_image=input_image,
                do_extract_features=self.extract_features,
                do_calculate_conan=self.calculate_contact_angle,
            )

            new_analyses.append(new_analysis)

        self._bn_analyses.set(new_analyses)
        self._analyses_saved = False

    def cancel_analyses(self) -> None:
        analyses = self._bn_analyses.get()

        for analysis in analyses:
            analysis.cancel()

    def clear_analyses(self) -> None:
        self.cancel_analyses()
        self._bn_analyses.set(tuple())

    def save_analyses(self, options: ConanAnalysisSaverOptions) -> None:
        analyses = self._bn_analyses.get()
        if len(analyses) == 0:
            return

        save_drops(analyses, options)
        self._analyses_saved = True

    def _create_save_options(self) -> ConanAnalysisSaverOptions:
        return ConanAnalysisSaverOptions()

    def check_if_safe_to_discard_analyses(self) -> bool:
        if self._analyses_saved:
            return True
        else:
            analyses = self._bn_analyses.get()
            if len(analyses) == 0:
                return True

            all_images_replicated = all(analysis.is_image_replicated
                                        for analysis in analyses)

            if not all_images_replicated:
                return False

            return True

    def extract_features(self,
                         image: Bindable[np.ndarray]) -> FeatureExtractor:
        return FeatureExtractor(
            image=image,
            params=self._feature_extractor_params,
            loop=self._loop,
        )

    def calculate_contact_angle(
            self,
            extracted_features: FeatureExtractor) -> ContactAngleCalculator:
        return ContactAngleCalculator(
            features=extracted_features,
            params=self._conancalc_params,
        )

    def exit(self) -> None:
        self.clear_analyses()
        self.image_acquisition.destroy()
        self._do_exit()
Example #9
0
class IFTSession:
    def __init__(self, do_exit: Callable[[], Any], *,
                 loop: asyncio.AbstractEventLoop) -> None:
        self._loop = loop

        self._do_exit = do_exit

        self._feature_extractor_params = FeatureExtractorParams()
        self._physprops_calculator_params = PhysicalPropertiesCalculatorParams(
        )

        self._bn_analyses = BoxBindable(
            tuple())  # type: Bindable[Sequence[IFTDropAnalysis]]
        self._analyses_saved = False

        self.image_acquisition = ImageAcquisitionModel()
        self.image_acquisition.use_acquirer_type(AcquirerType.LOCAL_STORAGE)

        self.physical_parameters = PhysicalParametersModel(
            physprops_calculator_params=self._physprops_calculator_params, )

        self.image_processing = IFTImageProcessingModel(
            image_acquisition=self.image_acquisition,
            feature_extractor_params=self._feature_extractor_params,
            do_extract_features=self.extract_features,
        )

        self.results = IFTResultsModel(
            in_analyses=self._bn_analyses,
            do_cancel_analyses=self.cancel_analyses,
            do_save_analyses=self.save_analyses,
            create_save_options=self._create_save_options,
            check_if_safe_to_discard=self.check_if_safe_to_discard_analyses,
        )

    def start_analyses(self) -> None:
        assert len(self._bn_analyses.get()) == 0

        new_analyses = []

        input_images = self.image_acquisition.acquire_images()
        for input_image in input_images:
            new_analysis = IFTDropAnalysis(
                input_image=input_image,
                do_extract_features=self.extract_features,
                do_young_laplace_fit=self.young_laplace_fit,
                do_calculate_physprops=self.calculate_physprops)

            new_analyses.append(new_analysis)

        self._bn_analyses.set(new_analyses)
        self._analyses_saved = False

    def cancel_analyses(self) -> None:
        analyses = self._bn_analyses.get()

        for analysis in analyses:
            analysis.cancel()

    def clear_analyses(self) -> None:
        self.cancel_analyses()
        self._bn_analyses.set(tuple())

    def save_analyses(self, options: IFTAnalysisSaverOptions) -> None:
        analyses = self._bn_analyses.get()
        if len(analyses) == 0:
            return

        save_drops(analyses, options)
        self._analyses_saved = True

    def _create_save_options(self) -> IFTAnalysisSaverOptions:
        return IFTAnalysisSaverOptions()

    def check_if_safe_to_discard_analyses(self) -> bool:
        if self._analyses_saved:
            return True
        else:
            analyses = self._bn_analyses.get()
            if len(analyses) == 0:
                return True

            all_images_replicated = all(analysis.is_image_replicated
                                        for analysis in analyses)

            if not all_images_replicated:
                return False

            return True

    def extract_features(self,
                         image: Bindable[np.ndarray]) -> FeatureExtractor:
        return FeatureExtractor(
            image=image,
            params=self._feature_extractor_params,
            loop=self._loop,
        )

    def young_laplace_fit(
            self, extracted_features: FeatureExtractor) -> YoungLaplaceFitter:
        return YoungLaplaceFitter(features=extracted_features, loop=self._loop)

    def calculate_physprops(
            self, extracted_features: FeatureExtractor,
            young_laplace_fit: YoungLaplaceFitter
    ) -> PhysicalPropertiesCalculator:
        return PhysicalPropertiesCalculator(
            features=extracted_features,
            young_laplace_fit=young_laplace_fit,
            params=self._physprops_calculator_params,
        )

    def exit(self) -> None:
        self.clear_analyses()
        self.image_acquisition.destroy()
        self._do_exit()