text:
import Metashape
import pathlib, shutil, os, time
from PySide2 import QtGui, QtCore, QtWidgets

# Checking compatibility
compatible_major_version = "1.8"
found_major_version = ".".join(Metashape.app.version.split('.')[:2])
if found_major_version != compatible_major_version:
    raise Exception("Incompatible Metashape version: {} != {}".format(found_major_version, compatible_major_version))

def pandas_append(df, row, ignore_index=False):
    import pandas as pd
    if isinstance(row, pd.DataFrame):
        result = pd.concat([df, row], ignore_index=ignore_index)
    elif isinstance(row, pd.core.series.Series):
        result = pd.concat([df, row.to_frame().T], ignore_index=ignore_index)
    elif isinstance(row, dict):
        result = pd.concat([df, pd.DataFrame(row, index=[0], columns=df.columns)])
    else:
        raise RuntimeError("pandas_append: unsupported row type - {}".format(type(row)))
    return result

class DetectObjectsDlg(QtWidgets.QDialog):

    def __init__(self, parent):

        self.force_small_patch_size = False
        # Set force_small_patch_size to True if you want to train on small zones with a lot of small objects (train zone still should be at least 400*orthomosaic_resolution)
        # For example, with force_small_patch_size=True and orthomosaic resolution=5cm - train zone should be >= 20x20 m. If orthomosaic=2.5cm - train zone should be >= 10x10 m.
        # Note that this can work only if:
        # 1) You have very small objects
        # 2) Train zone contains a lot of objects

        self.augment_colors = True
        # Set augment_colors to False if you have bad results and you want to force neural network to take into account color of objects

        self.expected_layer_name_train_zones = "Train zone"
        self.expected_layer_name_train_data  = "Train data"
        self.layer_name_detection_data       = "Detected data"

        if len(Metashape.app.document.path) > 0:
            self.working_dir = str(pathlib.Path(Metashape.app.document.path).parent / "objects_detection")
        else:
            self.working_dir = ""

        self.save_model_path = ""
        self.load_model_path = self.readModelLoadPathFromSettings()

        self.cleanup_working_dir = False
        self.debug_tiles = False

        self.train_on_user_data_enabled = False

        self.max_epochs = 20  # bigger number of epochs leads to better neural network training (but slower)
        self.data_augmentation_multiplier = 8  # from 1 to 8, bigger multiplier leads to better neural network training (but slower)
        self.preferred_patch_size = 400  # 400 pixels
        self.preferred_resolution = 0.10  # 10 cm/pix

        self.prefer_original_resolution = True
        self.use_neural_network_pretrained_on_birds = False

        self.tiles_without_annotations_supported = False  # See https://github.com/weecology/DeepForest/issues/216

        QtWidgets.QDialog.__init__(self, parent)
        self.setWindowTitle("Objects detection on orthomosaic")

        self.chunk = Metashape.app.document.chunk
        self.create_gui()

        self.exec()

    def stop(self):
        self.stopped = True

    def check_stopped(self):
        if self.stopped:
            raise InterruptedError("Stop was pressed")

    def process(self):
        try:
            self.stopped = False
            self.btnRun.setEnabled(False)
            self.btnStop.setEnabled(True)

            time_start = time.time()

            self.load_params()

            self.prepair()

            print("Script started...")

            self.create_neural_network()

            self.export_orthomosaic()

            if self.chunk.shapes is None:
                self.chunk.shapes = Metashape.Shapes()

            if self.train_on_user_data_enabled:
                self.train_on_user_data()

            if len(self.save_model_path) > 0:
                self.saveToSettingsModelLoadPath(self.save_model_path)
            else:
                self.saveToSettingsModelLoadPath(self.load_model_path)

            self.detect()

            self.results_time_total = time.time() - time_start

            self.show_results_dialog()
        except:
            if self.stopped:
                Metashape.app.messageBox("Processing was stopped.")
            else:
                Metashape.app.messageBox("Something gone wrong.\n"
                                         "Please check the console.")
                raise
        finally:
            if self.cleanup_working_dir:
                shutil.rmtree(self.working_dir, ignore_errors=True)
            self.reject()

        print("Script finished.")
        return True

    def prepair(self):
        import os, sys, multiprocessing
        import random, string

        if self.working_dir == "":
            raise Exception("You should specify working directory (or save .psx project)")

        print("Working dir: {}".format(self.working_dir))
        try:
            os.mkdir(self.working_dir)
        except FileExistsError:
            already_existing_working_dir = self.working_dir
            random_suffix = ''.join(random.choices(string.ascii_uppercase + string.digits, k=8))
            self.working_dir = self.working_dir + "/tmp_" + random_suffix
            print("Working dir: {} already exists, trying instead: {}".format(already_existing_working_dir, self.working_dir))
            try:
                os.mkdir(self.working_dir)
            except FileExistsError:
                raise Exception("Working directory {} already exists! Please specify another working dir.".format(self.working_dir))

        self.cleanup_working_dir = True

        self.dir_tiles = self.working_dir + "/tiles/"

        self.dir_train_data = self.working_dir + "/train/"
        self.dir_train_subtiles = self.dir_train_data + "inner/"
        self.dir_train_subtiles_debug = self.dir_train_subtiles + "debug/"

        self.dir_detection_results = self.working_dir + "/detection/"
        self.dir_subtiles_results = self.dir_detection_results + "inner/"

        for subdir in [self.dir_tiles, self.dir_train_data, self.dir_train_subtiles, self.dir_train_subtiles_debug, self.dir_detection_results, self.dir_subtiles_results]:
            shutil.rmtree(subdir, ignore_errors=True)
            os.mkdir(subdir)

        import torch
        torch_hub_dir = torch.hub.get_dir()

        from deepforest import utilities
        if not hasattr(utilities, '__models_dir_path_already_patched__'):
            original_use_release = utilities.use_release
            original_use_bird_release = utilities.use_bird_release
            # This is a workaround for Windows permission issues (we can't easily download files into .../site-packages/deepforest/...)
            def patched_use_release(**kwargs):
                kwargs["save_dir"] = torch_hub_dir
                return original_use_release(**kwargs)
            def patched_use_bird_release(**kwargs):
                kwargs["save_dir"] = torch_hub_dir
                return original_use_bird_release(**kwargs)
            utilities.use_release = patched_use_release
            utilities.use_bird_release = patched_use_bird_release
            utilities.__models_dir_path_already_patched__ = True

        if os.name == 'nt': # if Windows
            multiprocessing.set_executable(os.path.join(sys.exec_prefix, 'python.exe'))

    def create_neural_network(self):
        print("Neural network loading...")
        import torch
        import deepforest
        import deepforest.main

        self.m = deepforest.main.deepforest()

        if len(self.load_model_path) > 0:
            self.m.use_release()
            print("Using the neural network loaded from '{}'...".format(self.load_model_path))
            self.m.model = torch.load(self.load_model_path)
        else:
            if self.use_neural_network_pretrained_on_birds:
                # use neural network pre-trained on birds
                print("Using the neural network pre-trained on birds...")
                self.m.use_bird_release()
            else:
                # use neural network pre-trained on trees
                print("Using the neural network pre-trained on trees...")
                self.m.use_release()

    def export_orthomosaic(self):
        import numpy as np

        print("Prepairing orthomosaic...")

        kwargs = {}
        if not self.prefer_original_resolution and (self.chunk.orthomosaic.resolution < self.preferred_resolution*0.90):
            kwargs["resolution"] = self.preferred_resolution
        else:
            print("no resolution downscaling required")
        self.chunk.exportRaster(path=self.dir_tiles + "tile.jpg", source_data=Metashape.OrthomosaicData, image_format=Metashape.ImageFormat.ImageFormatJPEG, save_alpha=False, white_background=True,
                                save_world=True,
                                split_in_blocks=True, block_width=self.patch_size, block_height=self.patch_size,
                                **kwargs)

        tiles = os.listdir(self.dir_tiles)
        self.tiles_paths = {}
        self.tiles_to_world = {}
        for tile in sorted(tiles):
            assert tile.startswith("tile-")

            _, tile_x, tile_y = tile.split(".")[0].split("-")
            tile_x, tile_y = map(int, [tile_x, tile_y])
            if tile.endswith(".jgw") or tile.endswith(".pgw"):  # https://en.wikipedia.org/wiki/World_file
                with open(self.dir_tiles + tile, "r") as file:
                    matrix2x3 = list(map(float, file.readlines()))
                matrix2x3 = np.array(matrix2x3).reshape(3, 2).T
                self.tiles_to_world[tile_x, tile_y] = matrix2x3
            else:
                self.tiles_paths[tile_x, tile_y] = self.dir_tiles + tile

        assert(len(self.tiles_paths) == len(self.tiles_to_world))
        assert(self.tiles_paths.keys() == self.tiles_to_world.keys())

        self.tile_min_x = min([key[0] for key in self.tiles_paths.keys()])
        self.tile_max_x = max([key[0] for key in self.tiles_paths.keys()])
        self.tile_min_y = min([key[1] for key in self.tiles_paths.keys()])
        self.tile_max_y = max([key[1] for key in self.tiles_paths.keys()])
        print("{} tiles, tile_x in [{}; {}], tile_y in [{}; {}]".format(len(self.tiles_paths), self.tile_min_x, self.tile_max_x, self.tile_min_y, self.tile_max_y))

    def train_on_user_data(self):
        import sys
        import cv2
        import random
        import numpy as np
        import pandas as pd
        import multiprocessing
        import torch
        from pytorch_lightning import Trainer
        from pytorch_lightning.callbacks import Callback

        random.seed(2391231231324531)

        app = QtWidgets.QApplication.instance()

        training_start = time.time()
        print("Neural network additional training on user data...")

        self.train_zones_on_ortho = []

        n_train_zone_shapes_out_of_orthomosaic = 0
        for zone_i, shape in enumerate(self.train_zones):
            assert(len(shape.geometry.coordinates) == 1)
            shape_vertices = shape.geometry.coordinates[0]
            zone_from_world = None
            zone_from_world_best = None
            for tile_x in range(self.tile_min_x, self.tile_max_x):
                for tile_y in range(self.tile_min_y, self.tile_max_y):
                    if (tile_x, tile_y) not in self.tiles_paths:
                        continue
                    to_world = self.tiles_to_world[tile_x, tile_y]
                    from_world = self.invert_matrix_2x3(to_world)
                    for p in shape_vertices:
                        p = Metashape.CoordinateSystem.transform(p, self.chunk.shapes.crs, self.chunk.orthomosaic.crs)
                        p_in_tile = from_world @ [p.x, p.y, 1]
                        distance2_to_tile_center = np.linalg.norm(p_in_tile - [self.patch_size/2, self.patch_size/2])
                        if zone_from_world_best is None or distance2_to_tile_center < zone_from_world_best:
                            zone_from_world_best = distance2_to_tile_center
                            zone_from_world = self.invert_matrix_2x3(self.add_pixel_shift(to_world, -tile_x * self.patch_size, -tile_y * self.patch_size))
            if zone_from_world_best > 1.1 * (self.patch_size / 2)**2:
                n_train_zone_shapes_out_of_orthomosaic += 1

            zone_from = None
            zone_to = None
            for p in shape_vertices:
                p = Metashape.CoordinateSystem.transform(p, self.chunk.shapes.crs, self.chunk.orthomosaic.crs)
                p_in_ortho = np.int32(np.round(zone_from_world @ [p.x, p.y, 1]))
                if zone_from is None:
                    zone_from = p_in_ortho
                if zone_to is None:
                    zone_to = p_in_ortho
                zone_from = np.minimum(zone_from, p_in_ortho)
                zone_to = np.maximum(zone_to, p_in_ortho)
            train_size = zone_to - zone_from
            train_size_m = np.int32(np.round(train_size * self.orthomosaic_resolution))
            if np.any(train_size < self.patch_size):
                print("Train zone #{} {}x{} pixels ({}x{} meters) is too small - each side should be at least {} meters"
                      .format(zone_i + 1, train_size[0], train_size[1], train_size_m[0], train_size_m[1], self.patch_size * self.orthomosaic_resolution), file=sys.stderr)
                self.train_zones_on_ortho.append(None)
            else:
                print("Train zone #{}: {}x{} orthomosaic pixels, {}x{} meters".format(zone_i + 1, train_size[0], train_size[1], train_size_m[0], train_size_m[1]))
                self.train_zones_on_ortho.append((zone_from, zone_to, zone_from_world))
        assert len(self.train_zones_on_ortho) == len(self.train_zones)

        if n_train_zone_shapes_out_of_orthomosaic > 0:
            print("Warning, {} train zones shapes are out of orthomosaic".format(n_train_zone_shapes_out_of_orthomosaic))

        area_threshold = 0.3

        all_annotations = pd.DataFrame(columns=['image_path', 'xmin', 'ymin', 'xmax', 'ymax', 'label'])
        nannotated_tiles = 0

        if self.tiles_without_annotations_supported:
            empty_tile_name = "empty_tile.jpg"
            empty_tile = self.create_empty_tile()
            cv2.imwrite(self.dir_train_subtiles + empty_tile_name, empty_tile)

            # See https://github.com/weecology/DeepForest/issues/216
            all_annotations = pandas_append(all_annotations, {'image_path': empty_tile_name, 'xmin': '0', 'ymin': '0', 'xmax': '0', 'ymax': '0', 'label': 'Tree'}, ignore_index=True)

        nempty_tiles = 0

        self.train_nannotations_in_zones = 0
        for zone_i, shape in enumerate(self.train_zones):
            if self.train_zones_on_ortho[zone_i] is None:
                continue
            zone_from, zone_to, zone_from_world = self.train_zones_on_ortho[zone_i]
            annotations = []
            for annotation in self.train_data:
                assert(len(annotation.geometry.coordinates) == 1)
                annotation_vertices = annotation.geometry.coordinates[0]
                annotation_from = None
                annotation_to = None
                for p in annotation_vertices:
                    p = Metashape.CoordinateSystem.transform(p, self.chunk.shapes.crs, self.chunk.orthomosaic.crs)
                    p_in_ortho = np.int32(np.round(zone_from_world @ [p.x, p.y, 1]))
                    if annotation_from is None:
                        annotation_from = p_in_ortho
                    if annotation_to is None:
                        annotation_to = p_in_ortho
                    annotation_from = np.minimum(annotation_from, p_in_ortho)
                    annotation_to = np.maximum(annotation_to, p_in_ortho)
                bbox_from, bbox_to = self.intersect(zone_from, zone_to, annotation_from, annotation_to)
                if self.area(bbox_from, bbox_to) > self.area(annotation_from, annotation_to) * area_threshold:
                    annotations.append((annotation_from, annotation_to))
            self.train_nannotations_in_zones += len(annotations)
            print("Train zone #{}: {} annotations inside".format(zone_i + 1, len(annotations)))

            border = self.patch_inner_border
            inner_path_size = self.patch_size - 2 * border

            zone_size = zone_to - zone_from
            assert np.all(zone_size >= self.patch_size)
            nx_tiles, ny_tiles = np.int32((zone_size - 2 * border + inner_path_size - 1) // inner_path_size)
            assert nx_tiles >= 1 and ny_tiles >= 1
            xy_step = np.int32(np.round((zone_size + [nx_tiles, ny_tiles] - 1) // [nx_tiles, ny_tiles]))
            out_of_orthomosaic_train_tile = 0
            for x_tile in range(0, nx_tiles):
                for y_tile in range(0, ny_tiles):
                    tile_to = zone_from + self.patch_size + xy_step * [x_tile, y_tile]
                    if x_tile == nx_tiles - 1 and y_tile == ny_tiles - 1:
                        assert np.all(tile_to >= zone_to)
                    tile_to = np.minimum(tile_to, zone_to)
                    tile_from = tile_to - self.patch_size
                    if x_tile == 0 and y_tile == 0:
                        assert np.all(tile_from == zone_from)
                    assert np.all(tile_from >= zone_from)

                    tile = self.read_part(tile_from, tile_to)
                    assert tile.shape == (self.patch_size, self.patch_size, 3)

                    if np.all(tile == 255):
                        out_of_orthomosaic_train_tile += 1
                        continue

                    tile_annotations = []
                    for annotation_from, annotation_to in annotations:
                        bbox_from, bbox_to = self.intersect(tile_from, tile_to, annotation_from, annotation_to)
                        if self.area(bbox_from, bbox_to) > self.area(annotation_from, annotation_to) * area_threshold:
                            tile_annotations.append((bbox_from - tile_from, bbox_to - tile_from))

                    max_augmented_versions = 8
                    all_augmented_versions = list(range(max_augmented_versions))

                    augmented_versions = []
                    augmented_versions_to_add = max(1, self.data_augmentation_multiplier)
                    while augmented_versions_to_add > 0:
                        if augmented_versions_to_add < max_augmented_versions:
                            shuffled_augmented_versions = all_augmented_versions
                            random.shuffle(shuffled_augmented_versions)
                            augmented_versions.extend(shuffled_augmented_versions[:augmented_versions_to_add])
                            augmented_versions_to_add = 0
                        else:
                            augmented_versions.extend(all_augmented_versions)
                            augmented_versions_to_add -= max_augmented_versions

                    for version_i in augmented_versions:
                        tile_version = tile
                        tile_annotations_version = tile_annotations

                        is_mirrored = ((version_i % 4) == 1)
                        n90rotation = (version_i % 4)
                        if is_mirrored:
                            tile_annotations_version = self.flip_annotations(tile_annotations_version, tile_version)
                            tile_version = cv2.flip(tile_version, 0)
                        for rotation_i in range(n90rotation):
                            tile_annotations_version = self.rotate90clockwise_annotations(tile_annotations_version, tile_version)
                            tile_version = cv2.rotate(tile_version, cv2.ROTATE_90_CLOCKWISE)

                        tile_version = self.random_augmentation(tile_version)

                        tile_name = "{}-{}-{}-{}.jpg".format((zone_i + 1), x_tile, y_tile, version_i)

                        nannotated_tiles += 1
                        for (xmin, ymin), (xmax, ymax) in tile_annotations_version:
                            all_annotations = pandas_append(all_annotations, {'image_path': tile_name, 'xmin': xmin, 'ymin': ymin, 'xmax': xmax, 'ymax': ymax, 'label': 'Tree'}, ignore_index=True)
                        if len(tile_annotations_version) == 0:
                            if self.tiles_without_annotations_supported:
                                all_annotations = pandas_append(all_annotations, {'image_path': tile_name, 'xmin': '0', 'ymin': '0', 'xmax': '0', 'ymax': '0', 'label': 'Tree'}, ignore_index=True)
                            nempty_tiles += 1

                        cv2.imwrite(self.dir_train_subtiles + tile_name, tile_version)
                        if self.debug_tiles:
                            tile_with_trees = self.debug_draw_trees(tile_version, tile_annotations_version)
                            cv2.imwrite(self.dir_train_subtiles_debug + tile_name, tile_with_trees)

            if out_of_orthomosaic_train_tile == nx_tiles * ny_tiles:
                raise RuntimeError("It seems that zone #{} has no orthomosaic data, please check zones, orthomosaic and its Outer Boundary.".format(zone_i + 1))
            else:
                if out_of_orthomosaic_train_tile > 0:
                    print("{}/{} of tiles in zone #{} has no orthomosaic data".format(out_of_orthomosaic_train_tile, nx_tiles * ny_tiles, zone_i + 1))

        print("{} tiles ({} empty{}) for training prepared with {} annotations"
              .format(nannotated_tiles, nempty_tiles, " - they are not supported" if (nempty_tiles > 0 and not self.tiles_without_annotations_supported) else "", len(all_annotations)))
        print("Training with {} epochs and x{} augmentations (augment colors: {})...".format(self.max_epochs, self.data_augmentation_multiplier, self.augment_colors))
        self.freeze_layers()

        annotations_file = self.dir_train_subtiles + "annotations.csv"
        all_annotations.to_csv(annotations_file, header=True, index=False)

        class MyCallback(Callback):
            def __init__(self, thiz_dlg):
                self.nepochs_done = 0
                self.nepochs = thiz_dlg.max_epochs
                self.pbar = thiz_dlg.trainPBar
                self.thiz_dlg = thiz_dlg
            def on_epoch_end(self, trainer, pl_module):
                self.nepochs_done += 1
                self.pbar.setValue(self.nepochs_done * 100 / self.nepochs)
                Metashape.app.update()
                app.processEvents()
                self.thiz_dlg.check_stopped()

        if torch.cuda.device_count() > 0:
            print("Using GPU...")
            trainer_gpus = 1
            trainer_auto_select_gpus = True
        else:
            print("Using CPU (will be very slow)...")
            trainer_gpus = 0
            trainer_auto_select_gpus = False
            torch.set_num_threads(multiprocessing.cpu_count())

        trainer = Trainer(max_epochs=self.max_epochs, gpus=trainer_gpus, auto_select_gpus=trainer_auto_select_gpus, callbacks=[MyCallback(self)], checkpoint_callback=False, logger=False)
        train_ds = self.m.load_dataset(annotations_file, root_dir=os.path.dirname(annotations_file))
        trainer.fit(self.m, train_ds)

        self.results_time_training = time.time() - training_start

        if len(self.save_model_path) > 0:
            torch.save(self.m.model, self.save_model_path)
            print("Model trained on {} annotations with {} m/pix resolution saved to '{}'".format(self.train_nannotations_in_zones, self.orthomosaic_resolution, self.save_model_path))

    def create_empty_tile(self):
        import numpy as np

        empty_tile = np.zeros((self.patch_size, self.patch_size, 3), np.uint8)
        empty_tile[:, :, :] = 255
        return empty_tile

    def freeze_all_layers(self):
        for p in self.m.model.backbone.parameters():
            p.requires_grad = False

    def freeze_low_layers(self, freezeConv1=False, freezeUpToLevel=2):
        body = self.m.model.backbone.body
        layers = [body.layer1, body.layer2, body.layer3, body.layer4]
        assert (0 <= freezeUpToLevel <= len(layers))

        if freezeConv1:
            for p in self.m.model.backbone.body.conv1.parameters():
                p.requires_grad = False

        for layer in layers[:freezeUpToLevel]:
            for p in layer.parameters():
                p.requires_grad = False

    def freeze_layers(self):
        self.freeze_low_layers(freezeConv1=False, freezeUpToLevel=2)

    def flip_annotations(self, trees, img):
        # x, y -> x, h-y
        import numpy as np

        h, w, cn = img.shape
        flipped_trees = []
        for bbox_from, bbox_to in trees:
            assert np.all(bbox_from >= np.int32([0, 0]))
            assert np.all(bbox_to <= np.int32([w, h]))
            (xmin, ymin), (xmax, ymax) = bbox_from, bbox_to
            flipped_trees.append(((xmin, h - ymax), (xmax, h - ymin)))
        return flipped_trees

    def rotate90clockwise_point(self, x, y, w, h):
        return h-y, x

    def rotate90clockwise_annotations(self, trees, img):
        # 0, 0 -> w, 0
        # x, y -> h-y, x
        import numpy as np

        h, w, cn = img.shape
        rotated_trees = []
        for bbox_from, bbox_to in trees:
            assert np.all(bbox_from >= np.int32([0, 0]))
            assert np.all(bbox_to <= np.int32([w, h]))
            (xmin, ymin), (xmax, ymax) = bbox_from, bbox_to
            xmin2, ymin2 = self.rotate90clockwise_point(xmin, ymin, w, h)
            xmax2, ymax2 = self.rotate90clockwise_point(xmax, ymax, w, h)
            xmin2, xmax2 = min(xmin2, xmax2), max(xmin2, xmax2)
            ymin2, ymax2 = min(ymin2, ymax2), max(ymin2, ymax2)
            rotated_trees.append(((xmin2, ymin2), (xmax2, ymax2)))
        return rotated_trees

    def random_augmentation(self, img):
        import cv2
        import albumentations as A

        stages = []
        if self.augment_colors:
            stages.append(A.HueSaturationValue(hue_shift_limit=360, sat_shift_limit=30, val_shift_limit=20, always_apply=True))
        stages.append(A.ISONoise(p=0.5))
        if self.augment_colors:
            stages.append(A.RandomBrightnessContrast(brightness_limit=0.4, contrast_limit=0, p=0.5))

        transform = A.Compose(stages)

        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = transform(image=img)["image"]
        img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
        return img

    def read_part(self, res_from, res_to):
        import cv2
        import numpy as np

        res_size = res_to - res_from
        assert np.all(res_size >= [self.patch_size, self.patch_size])
        res = np.zeros((res_size[1], res_size[0], 3), np.uint8)
        res[:, :, :] = 255

        tile_xy_from = np.int32(res_from // self.patch_size)
        tile_xy_upto = np.int32((res_to - 1) // self.patch_size)
        assert np.all(tile_xy_from <= tile_xy_upto)
        for tile_x in range(tile_xy_from[0], tile_xy_upto[0] + 1):
            for tile_y in range(tile_xy_from[1], tile_xy_upto[1] + 1):
                if (tile_x, tile_y) not in self.tiles_paths:
                    continue
                part = cv2.imread(self.tiles_paths[tile_x, tile_y])
                part = cv2.copyMakeBorder(part, 0, self.patch_size - part.shape[0], 0, self.patch_size - part.shape[1], cv2.BORDER_CONSTANT, value=[255, 255, 255])
                part_from = np.int32([tile_x, tile_y]) * self.patch_size - res_from
                part_to = part_from + self.patch_size

                res_inner_from = np.int32([max(0, part_from[0]), max(0, part_from[1])])
                res_inner_to = np.int32([min(part_to[0], res_size[0]), min(part_to[1], res_size[1])])

                part_inner_from = res_inner_from - part_from
                part_inner_to = part_inner_from + res_inner_to - res_inner_from

                res[res_inner_from[1]:res_inner_to[1], res_inner_from[0]:res_inner_to[0], :] = part[part_inner_from[1]:part_inner_to[1], part_inner_from[0]:part_inner_to[0], :]

        return res

    def intersect(self, a_from, a_to, b_from, b_to):
        import numpy as np
        c_from = np.maximum(a_from, b_from)
        c_to = np.minimum(a_to, b_to)
        if np.any(c_from >= c_to):
            return c_from, c_from
        else:
            return c_from, c_to

    def area(self, a_from, a_to):
        a_size = a_to - a_from
        return a_size[0] * a_size[1]

    def add_pixel_shift(self, to_world, dx, dy):
        to_world = to_world.copy()
        to_world[0, 2] = to_world[0, :] @ [dx, dy, 1]
        to_world[1, 2] = to_world[1, :] @ [dx, dy, 1]
        return to_world

    def invert_matrix_2x3(self, to_world):
        import numpy as np

        to_world33 = np.vstack([to_world, [0, 0, 1]])
        from_world = np.linalg.inv(to_world33)

        assert(from_world[2, 0] == from_world[2, 1] == 0)
        assert(from_world[2, 2] == 1)
        from_world = from_world[:2, :]

        return from_world

    def detect(self):
        import cv2
        import numpy as np
        import pandas as pd

        app = QtWidgets.QApplication.instance()

        print("Detection...")
        time_start = time.time()

        detected_label = self.layer_name_detection_data + " ({:.2f} cm/pix".format(100.0 * self.orthomosaic_resolution)
        if self.use_neural_network_pretrained_on_birds:
            detected_label += ", birds pre-train"
        if self.augment_colors:
            detected_label += ", any color"
        if self.train_on_user_data_enabled:
            detected_label += ", trained on {} shapes in {} zones".format(self.train_nannotations_in_zones, len(self.train_zones))
        detected_label += ")"

        detected_shapes_layer = self.chunk.shapes.addGroup()
        detected_shapes_layer.label = detected_label

        ntrees_detected = 0

        big_tiles_k = 8
        border = self.patch_inner_border
        area_overlap_threshold = 0.60

        big_tiles = set()
        for tile_x in range(self.tile_min_x, self.tile_max_x):
            for tile_y in range(self.tile_min_y, self.tile_max_y):
                if (tile_x, tile_y) not in self.tiles_paths:
                    continue
                big_tile_x, big_tile_y = tile_x // big_tiles_k, tile_y // big_tiles_k
                big_tiles.add((big_tile_x, big_tile_y))

        bigtiles_trees = {}
        bigtiles_to_world = {}
        bigtiles_idx_on_borders = {}

        for big_tile_index, (big_tile_x, big_tile_y) in enumerate(sorted(big_tiles)):
            big_tile = np.zeros((border + big_tiles_k*self.patch_size + border, border + big_tiles_k*self.patch_size + border, 3), np.uint8)
            big_tile[:, :, :] = 255
            big_tile_to_world = None

            for xi in range(-1, big_tiles_k + 1):
                for yi in range(-1, big_tiles_k + 1):
                    tile_x, tile_y = big_tiles_k * big_tile_x + xi, big_tiles_k * big_tile_y + yi
                    if (tile_x, tile_y) not in self.tiles_paths:
                        continue
                    part = cv2.imread(self.tiles_paths[tile_x, tile_y])
                    part = cv2.copyMakeBorder(part, 0, self.patch_size - part.shape[0], 0, self.patch_size - part.shape[1], cv2.BORDER_CONSTANT, value=[255, 255, 255])
                    if xi in [-1, big_tiles_k] or yi in [-1, big_tiles_k]:
                        fromx, fromy = border + xi * self.patch_size, border + yi * self.patch_size
                        tox, toy = fromx + self.patch_size, fromy + self.patch_size
                        if xi == -1:
                            part = part[:, self.patch_size - border:, :]
                            fromx += self.patch_size - border
                        if xi == big_tiles_k:
                            part = part[:, :border, :]
                            tox = fromx + border
                        if yi == -1:
                            part = part[self.patch_size - border:, :, :]
                            fromy += self.patch_size - border
                        if yi == big_tiles_k:
                            part = part[:border, :, :]
                            toy = fromy + border
                        big_tile[fromy:toy, fromx:tox, :] = part
                    else:
                        big_tile[border + yi * self.patch_size:, border + xi * self.patch_size:, :][:self.patch_size, :self.patch_size, :] = part
                        big_tile_to_world = self.add_pixel_shift(self.tiles_to_world[tile_x, tile_y], -(border + xi * self.patch_size), -(border + yi * self.patch_size))

            assert big_tile_to_world is not None

            subtiles_trees = {}
            tile_inner_size = self.patch_size - 2*border
            inner_tiles_nx = (big_tile.shape[1]-2*border+tile_inner_size-1)//tile_inner_size
            inner_tiles_ny = (big_tile.shape[0]-2*border+tile_inner_size-1)//tile_inner_size
            for xi in range(inner_tiles_nx):
                for yi in range(inner_tiles_ny):
                    tox, toy = min(big_tile.shape[1], 2*border+(xi + 1) * tile_inner_size), min(big_tile.shape[0], 2*border+(yi + 1) * tile_inner_size)
                    fromx, fromy = tox - self.patch_size, toy - self.patch_size
                    subtile = big_tile[fromy:toy, fromx:tox, :]

                    white_pixels_fraction = np.sum(np.all(subtile == 255, axis=-1)) / (subtile.shape[0] * subtile.shape[1])

                    assert(subtile.shape == (self.patch_size, self.patch_size, 3))
                    subtile_trees = self.m.predict_image(subtile.astype('float32'))
                    Metashape.app.update()
                    app.processEvents()
                    self.check_stopped()
                    if subtile_trees is not None:
                        subtile_inner_trees = pd.DataFrame(columns=['image_path', 'xmin', 'ymin', 'xmax', 'ymax', 'label'])
                        subtile_inner_trees_debug = pd.DataFrame(columns=['image_path', 'xmin', 'ymin', 'xmax', 'ymax', 'label'])
                        for idx, row in subtile_trees.iterrows():
                            xmin, ymin, xmax, ymax, label, score = int(row.xmin), int(row.ymin), int(row.xmax), int(row.ymax), row.label, row.score
                            assert (label == "Tree")
                            if xmin >= self.patch_size - border or xmax <= border or ymin >= self.patch_size - border or ymax <= border:
                                continue
                            if white_pixels_fraction > 0.10:
                                subtile_bbox = subtile[ymin:ymax, xmin:xmax, :]
                                bbox_white_pixels_fraction = np.sum(np.all(subtile_bbox == 255, axis=-1)) / (subtile_bbox.shape[0] * subtile_bbox.shape[1])
                                if bbox_white_pixels_fraction > 0.70:
                                    continue
                            xmin, xmax = map(lambda x: fromx + x, [xmin, xmax])
                            ymin, ymax = map(lambda y: fromy + y, [ymin, ymax])
                            subtile_inner_trees_debug = pandas_append(subtile_inner_trees_debug, row, ignore_index=True)
                            row.xmin, row.ymin, row.xmax, row.ymax = xmin, ymin, xmax, ymax
                            subtile_inner_trees = pandas_append(subtile_inner_trees, row, ignore_index=True)

                        if self.debug_tiles:
                            img_with_trees = self.debug_draw_trees(subtile, subtile_trees)
                            cv2.imwrite(self.dir_subtiles_results + "{}-{}-{}-{}.jpg".format(big_tile_x, big_tile_y, xi, yi), img_with_trees)
                            img_with_inner_trees = self.debug_draw_trees(subtile, subtile_inner_trees_debug)
                            cv2.imwrite(self.dir_subtiles_results + "{}-{}-{}-{}_inner.jpg".format(big_tile_x, big_tile_y, xi, yi), img_with_inner_trees)
                    else:
                        subtile_inner_trees = pd.DataFrame(columns=['image_path', 'xmin', 'ymin', 'xmax', 'ymax', 'label'])
                        if self.debug_tiles:
                            cv2.imwrite(self.dir_subtiles_results + "{}-{}-{}-{}_empty.jpg".format(big_tile_x, big_tile_y, xi, yi), subtile)

                    subtiles_trees[xi, yi] = subtile_inner_trees

            big_tile_trees = None
            for xi, yi in sorted(subtiles_trees.keys()):
                tox, toy = min(big_tile.shape[1], 2*border+(xi + 1) * tile_inner_size), min(big_tile.shape[0], 2*border+(yi + 1) * tile_inner_size)
                fromx, fromy = tox - self.patch_size, toy - self.patch_size

                a = subtiles_trees[xi, yi]

                a_idx_on_border = []
                for idx, rowA in a.iterrows():
                    axmin, aymin, axmax, aymax, ascore = int(rowA.xmin), int(rowA.ymin), int(rowA.xmax), int(rowA.ymax), rowA.score
                    if axmin > fromx + border and axmax < tox - border and aymin > fromy + border and aymax < toy - border:
                        continue
                    a_idx_on_border.append(idx)

                for dx in [-1, 0, 1]:
                    for dy in [-1, 0, 1]:
                        if dx == 0 and dy == 0:
                            continue
                        nx, ny = xi + dx, yi + dy
                        if (nx, ny) not in subtiles_trees:
                            continue
                        b = subtiles_trees[nx, ny]

                        indices_to_check = a_idx_on_border

                        # because the last two columns/rows have much bigger overlap
                        if    (xi == inner_tiles_nx - 2 and dx == 1) or (xi == inner_tiles_nx - 1 and dx == -1)\
                           or (yi == inner_tiles_ny - 2 and dy == 1) or (yi == inner_tiles_ny - 1 and dy == -1):
                            indices_to_check = a.index

                        for idx in indices_to_check:
                            rowA = a.loc[idx]
                            if rowA.label == "Suppressed":
                                continue
                            axmin, aymin, axmax, aymax, ascore = int(rowA.xmin), int(rowA.ymin), int(rowA.xmax), int(rowA.ymax), rowA.score
                            areaA = (axmax - axmin) * (aymax - aymin)
                            for _, rowB in b.iterrows():
                                bxmin, bymin, bxmax, bymax, bscore = int(rowB.xmin), int(rowB.ymin), int(rowB.xmax), int(rowB.ymax), rowB.score
                                areaB = (bxmax - bxmin) * (bymax - bymin)

                                intersectionx = max(0, min(axmax, bxmax) - max(axmin, bxmin))
                                intersectiony = max(0, min(aymax, bymax) - max(aymin, bymin))
                                intersectionArea = intersectionx * intersectiony
                                if intersectionArea > min(areaA, areaB) * area_overlap_threshold:
                                    if ascore + 0.2 < bscore:
                                        a.loc[idx, 'label'] = "Suppressed"
                                    elif not (bscore + 0.2 < ascore):
                                        if areaA < areaB:
                                            a.loc[idx, 'label'] = "Suppressed"
                                        elif not (areaB < areaA) and (xi, yi) < (nx, ny):
                                            assert not ((nx, ny) < (xi, yi))
                                            a.loc[idx, 'label'] = "Suppressed"

                if big_tile_trees is None:
                    big_tile_trees = pd.DataFrame(columns=a.columns)
                for idx, row in a.iterrows():
                    if row.label == "Suppressed":
                        continue
                    big_tile_trees = pandas_append(big_tile_trees, row, ignore_index=True)

            idx_on_borders = []
            for idx, rowA in big_tile_trees.iterrows():
                axmin, aymin, axmax, aymax, ascore = int(rowA.xmin), int(rowA.ymin), int(rowA.xmax), int(rowA.ymax), rowA.score
                if axmin > 2*border and axmax < big_tiles_k * self.patch_size and aymin > 2*border and aymax < big_tiles_k * self.patch_size:
                    continue
                idx_on_borders.append(idx)

            bigtiles_trees[big_tile_x, big_tile_y] = big_tile_trees
            bigtiles_to_world[big_tile_x, big_tile_y] = big_tile_to_world
            bigtiles_idx_on_borders[big_tile_x, big_tile_y] = idx_on_borders

            if self.debug_tiles:
                cv2.imwrite(self.dir_detection_results + "{}-{}_clean.jpg".format(big_tile_x, big_tile_y), big_tile)
                img_with_trees = self.debug_draw_trees(big_tile, big_tile_trees)
                cv2.imwrite(self.dir_detection_results + "{}-{}_all_trees.jpg".format(big_tile_x, big_tile_y), img_with_trees)
                img_with_border_trees = self.debug_draw_trees(big_tile, big_tile_trees.loc[idx_on_borders])
                cv2.imwrite(self.dir_detection_results + "{}-{}_border_trees.jpg".format(big_tile_x, big_tile_y), img_with_border_trees)

            self.detectionPBar.setValue((big_tile_index + 1) * 100 / len(big_tiles))
            Metashape.app.update()
            app.processEvents()
            self.check_stopped()

        for big_tile_x, big_tile_y in sorted(big_tiles):
            big_tile_trees = bigtiles_trees[big_tile_x, big_tile_y]
            if big_tile_trees is None:
                continue

            big_tile_to_world = bigtiles_to_world[big_tile_x, big_tile_y]

            a_idx_on_borders = bigtiles_idx_on_borders[big_tile_x, big_tile_y]

            for dx in [-1, 0, 1]:
                for dy in [-1, 0, 1]:
                    if dx == 0 and dy == 0:
                        continue
                    nx, ny = big_tile_x + dx, big_tile_y + dy
                    if (nx, ny) not in bigtiles_trees:
                        continue
                    b = bigtiles_trees[nx, ny]
                    if b is None:
                        continue

                    b_idx_on_borders = bigtiles_idx_on_borders[nx, ny]

                    for idxA in a_idx_on_borders:
                        rowA = big_tile_trees.loc[idxA]
                        if rowA.label == "Suppressed":
                            continue
                        axmin, aymin, axmax, aymax, ascore = int(rowA.xmin), int(rowA.ymin), int(rowA.xmax), int(rowA.ymax), rowA.score
                        areaA = (axmax - axmin) * (aymax - aymin)
                        for idxB in b_idx_on_borders:
                            rowB = b.loc[idxB]
                            bxmin, bymin, bxmax, bymax, bscore = int(rowB.xmin), int(rowB.ymin), int(rowB.xmax), int(rowB.ymax), rowB.score
                            bxmin, bxmax = map(lambda x: x + dx * big_tiles_k * self.patch_size, [bxmin, bxmax])
                            bymin, bymax = map(lambda y: y + dy * big_tiles_k * self.patch_size, [bymin, bymax])
                            areaB = (bxmax - bxmin) * (bymax - bymin)

                            intersectionx = max(0, min(axmax, bxmax) - max(axmin, bxmin))
                            intersectiony = max(0, min(aymax, bymax) - max(aymin, bymin))
                            intersectionArea = intersectionx * intersectiony
                            if intersectionArea > min(areaA, areaB) * area_overlap_threshold:
                                if ascore + 0.2 < bscore:
                                    big_tile_trees.loc[idxA, 'label'] = "Suppressed"
                                elif not (bscore + 0.2 < ascore):
                                    if areaA < areaB:
                                        big_tile_trees.loc[idxA, 'label'] = "Suppressed"
                                    elif not (areaB < areaA) and (big_tile_x, big_tile_y) < (nx, ny):
                                        assert not ((nx, ny) < (big_tile_x, big_tile_y))
                                        big_tile_trees.loc[idxA, 'label'] = "Suppressed"

            big_tile_trees = big_tile_trees[big_tile_trees.label != "Suppressed"]

            if self.debug_tiles:
                big_tile = cv2.imread(self.dir_detection_results + "{}-{}_clean.jpg".format(big_tile_x, big_tile_y))
                img_with_trees = self.debug_draw_trees(big_tile, big_tile_trees)
                cv2.imwrite(self.dir_detection_results + "{}-{}_after_suppression.jpg".format(big_tile_x, big_tile_y), img_with_trees)

            ntrees_detected += len(big_tile_trees)
            self.add_trees(big_tile_to_world, big_tile_trees, detected_shapes_layer)

        self.results_ntrees_detected = ntrees_detected
        self.results_time_detection = time.time() - time_start

    def add_trees(self, to_world, tile_trees, shapes_group):
        import numpy as np

        for row in tile_trees.itertuples():
            xmin, ymin, xmax, ymax, label = int(row.xmin), int(row.ymin), int(row.xmax), int(row.ymax), row.label
            assert (label == "Tree")

            corners = []
            for x, y in [(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)]:
                x, y = to_world @ np.array([x+0.5, y+0.5, 1]).reshape(3, 1)
                p = Metashape.Vector([x, y])
                p = Metashape.CoordinateSystem.transform(p, self.chunk.orthomosaic.crs, self.chunk.shapes.crs)
                corners.append([p.x, p.y])

            shape = self.chunk.shapes.addShape()
            shape.group = shapes_group
            shape.geometry = Metashape.Geometry.Polygon(corners)

    def show_results_dialog(self):
        message = "Finished in {:.2f} sec:\n".format(self.results_time_total)\
                   + "{} trees detected.".format(self.results_ntrees_detected)

        print(message)
        Metashape.app.messageBox(message)

    def create_gui(self):
        self.labelTrainZonesLayer = QtWidgets.QLabel("Train zones:")
        self.labelTrainDataLayer = QtWidgets.QLabel("Train data:")

        self.trainZonesLayer = QtWidgets.QComboBox()
        self.trainDataLayer = QtWidgets.QComboBox()
        self.noTrainDataChoice = (None, "No additional training (use pre-trained model as is)", True)

        self.layers = [self.noTrainDataChoice]

        slow_shape_layers_enumerating_but_with_number_of_shapes = False
        if slow_shape_layers_enumerating_but_with_number_of_shapes:
            print("Enumerating all shape layers...")
            shapes_enumerating_start = time.time()
            self.layersDict = {}
            self.layersSize = {}
            shapes = self.chunk.shapes
            for shape in shapes:
                layer = shape.group
                if layer.key not in self.layersDict:
                    self.layersDict[layer.key] = (layer.key, layer.label, layer.enabled)
                    self.layersSize[layer.key] = 1
                else:
                    self.layersSize[layer.key] += 1
            print("Found {} shapes layers in {:.2f} sec:".format(len(self.layersDict), time.time() - shapes_enumerating_start))
            for key in sorted(self.layersDict.keys()):
                key, label, enabled = self.layersDict[key]
                size = self.layersSize[key]
                print("Shape layer: {} shapes, key={}, label={}".format(size, key, label))
                if label == '':
                    label = 'Layer'
                label = label + " ({} shapes)".format(size)
                self.layers.append((key, label, enabled))
            self.layersDict = None
            self.layersSize = None
        else:
            if self.chunk.shapes is None:
                print("No shapes")
            else:
                for layer in self.chunk.shapes.groups:
                    key, label, enabled = layer.key, layer.label, layer.enabled
                    print("Shape layer: key={}, label={}, enabled={}".format(key, label, enabled))
                    if label == '':
                        label = 'Layer'
                    self.layers.append((key, label, layer.enabled))

        for key, label, enabled in self.layers:
            self.trainZonesLayer.addItem(label)
            self.trainDataLayer.addItem(label)
        self.trainZonesLayer.setCurrentIndex(0)
        self.trainDataLayer.setCurrentIndex(0)
        for i, (key, label, enabled) in enumerate(self.layers):
            if not enabled:
                continue
            if label.lower().startswith(self.expected_layer_name_train_zones.lower()):
                self.trainZonesLayer.setCurrentIndex(i)
            if label.lower().startswith(self.expected_layer_name_train_data.lower()):
                self.trainDataLayer.setCurrentIndex(i)

        self.chkUse10cmResolution = QtWidgets.QCheckBox("Process with 10 cm/pix resolution (for speedup)")
        self.chkUse10cmResolution.setToolTip("Process with downsampling to 10 cm/pix instad of original orthomosaic resolution. Leads to faster processing.")
        self.chkUse10cmResolution.setChecked(not self.prefer_original_resolution)

        # self.chkObjectCanBeOfAnyColor = QtWidgets.QCheckBox("An object can be of any color (f.e. in case you want to detect cars)")
        # self.chkObjectCanBeOfAnyColor.setToolTip("If you want to detect cars - tick this checkbox, and even if there are no red cars in train zone,\nneural network will try to detect cars of all colors thanks to this option.")
        # self.chkObjectCanBeOfAnyColor.setChecked(self.augment_colors)

        # self.chkUseBirdsPretrainedModel = QtWidgets.QCheckBox("Use neural network pre-trained on birds")
        # self.chkUseBirdsPretrainedModel.setToolTip("If you want to detect something looking more like a bird (than like a tree) - you can try neural network pre-trained on birds.\n"
        #                                            "You can try both versions to choose the best result, don't forget to annotate small region for addtional training.")

        self.groupBoxGeneral = QtWidgets.QGroupBox("General")
        generalLayout = QtWidgets.QGridLayout()

        self.txtWorkingDir= QtWidgets.QLabel()
        self.txtWorkingDir.setText("Working dir:")
        self.edtWorkingDir= QtWidgets.QLineEdit()
        self.edtWorkingDir.setText(self.working_dir)
        self.edtWorkingDir.setPlaceholderText("Path to dir for intermediate data")
        self.edtWorkingDir.setToolTip("Path to dir for intermediate data")
        self.btnWorkingDir = QtWidgets.QPushButton("...")
        self.btnWorkingDir.setFixedSize(25, 25)
        QtCore.QObject.connect(self.btnWorkingDir, QtCore.SIGNAL("clicked()"), lambda: self.choose_working_dir())
        generalLayout.addWidget(self.txtWorkingDir, 0, 0)
        generalLayout.addWidget(self.edtWorkingDir, 0, 1)
        generalLayout.addWidget(self.btnWorkingDir, 0, 2)

        generalLayout.addWidget(self.chkUse10cmResolution, 1, 1)
        self.groupBoxGeneral.setLayout(generalLayout)

        self.groupBoxModelTraining = QtWidgets.QGroupBox("Additional model training (recommended to train at least on a 50x50m zone)")
        trainingLayout = QtWidgets.QGridLayout()

        trainingLayout.addWidget(self.labelTrainZonesLayer, 0, 0)
        trainingLayout.addWidget(self.trainZonesLayer, 0, 1, 1, 2)

        trainingLayout.addWidget(self.labelTrainDataLayer, 1, 0)
        trainingLayout.addWidget(self.trainDataLayer, 1, 1, 1, 2)

        self.groupBoxModelTraining.setLayout(trainingLayout)

        self.groupBoxModelSaveLoad = QtWidgets.QGroupBox("Save/Load trained model (optional, note that orthomosaic resolution should be the same)")
        saveLoadLayout = QtWidgets.QGridLayout()

        self.txtModelSavePath = QtWidgets.QLabel()
        self.txtModelSavePath.setText("Save model to:")
        self.edtModelSavePath = QtWidgets.QLineEdit()
        self.edtModelSavePath.setText(self.save_model_path)
        self.edtModelSavePath.setPlaceholderText("File for neural network model to be saved after additional training")
        self.edtModelSavePath.setToolTip("File for neural network model to be saved after additional training")
        self.btnModelSavePath = QtWidgets.QPushButton("...")
        self.btnModelSavePath.setFixedSize(25, 25)
        QtCore.QObject.connect(self.btnModelSavePath, QtCore.SIGNAL("clicked()"), lambda: self.choose_model_save_path())
        saveLoadLayout.addWidget(self.txtModelSavePath, 0, 0)
        saveLoadLayout.addWidget(self.edtModelSavePath, 0, 1)
        saveLoadLayout.addWidget(self.btnModelSavePath, 0, 2)
        self.txtModelLoadPath = QtWidgets.QLabel()
        self.txtModelLoadPath.setText("Load model from:")
        self.edtModelLoadPath = QtWidgets.QLineEdit()
        self.edtModelLoadPath.setText(self.load_model_path)
        self.edtModelLoadPath.setPlaceholderText("File with previously saved neural network model (resolution must be the same)")
        self.edtModelLoadPath.setToolTip("File with previously saved neural network model (resolution must be the same)")
        self.btnModelLoadPath = QtWidgets.QPushButton("...")
        self.btnModelLoadPath.setFixedSize(25, 25)
        QtCore.QObject.connect(self.btnModelLoadPath, QtCore.SIGNAL("clicked()"), lambda: self.choose_model_load_path())
        saveLoadLayout.addWidget(self.txtModelLoadPath, 1, 0)
        saveLoadLayout.addWidget(self.edtModelLoadPath, 1, 1)
        saveLoadLayout.addWidget(self.btnModelLoadPath, 1, 2)

        self.groupBoxModelSaveLoad.setLayout(saveLoadLayout)

        self.btnRun = QtWidgets.QPushButton("Run")
        self.btnStop = QtWidgets.QPushButton("Stop")
        self.btnStop.setEnabled(False)

        layout = QtWidgets.QGridLayout()
        row = 0

        layout.addWidget(self.groupBoxGeneral, row, 0, 1, 3)
        row += 1

        layout.addWidget(self.groupBoxModelTraining, row, 0, 1, 3)
        row += 1

        layout.addWidget(self.groupBoxModelSaveLoad, row, 0, 1, 3)
        row += 1

        # layout.addWidget(self.chkObjectCanBeOfAnyColor, row, 1)
        # row += 1

        # layout.addWidget(self.chkUseBirdsPretrainedModel, row, 1)
        # row += 1

        self.txtTrainPBar = QtWidgets.QLabel()
        self.txtTrainPBar.setText("Training progress:")
        self.trainPBar = QtWidgets.QProgressBar()
        self.trainPBar.setTextVisible(True)
        layout.addWidget(self.txtTrainPBar, row, 0)
        layout.addWidget(self.trainPBar, row, 1, 1, 2)
        row += 1

        self.txtDetectionPBar = QtWidgets.QLabel()
        self.txtDetectionPBar.setText("Detection progress:")
        self.detectionPBar = QtWidgets.QProgressBar()
        self.detectionPBar.setTextVisible(True)
        layout.addWidget(self.txtDetectionPBar, row, 0)
        layout.addWidget(self.detectionPBar, row, 1, 1, 2)
        row += 1

        layout.addWidget(self.btnRun, row, 1)
        layout.addWidget(self.btnStop, row, 2)
        row += 1

        self.setLayout(layout)

        QtCore.QObject.connect(self.btnRun, QtCore.SIGNAL("clicked()"), lambda: self.process())
        QtCore.QObject.connect(self.btnStop, QtCore.SIGNAL("clicked()"), lambda: self.stop())

    def choose_working_dir(self):
        working_dir = Metashape.app.getExistingDirectory()
        self.edtWorkingDir.setText(working_dir)

    def choose_model_save_path(self):
        models_dir = ""
        load_path = Metashape.app.settings.value("scripts/detect_objects/model_load_path")
        if load_path is not None:
            models_dir = str(pathlib.Path(load_path).parent)

        save_path = Metashape.app.getSaveFileName("Trained model save path", models_dir, ".model")
        if len(save_path) > 0 and save_path.split(".")[-1] != "model":
            save_path += ".model"

        self.edtModelSavePath.setText(save_path)

    def choose_model_load_path(self):
        load_path = Metashape.app.getOpenFileName("Trained model load path", "", "*.model")
        self.edtModelLoadPath.setText(load_path)

    def readModelLoadPathFromSettings(self):
        load_path = Metashape.app.settings.value("scripts/detect_objects/model_load_path")
        if load_path is None:
            load_path = ""
        return load_path

    def saveToSettingsModelLoadPath(self, load_path):
        Metashape.app.settings.setValue("scripts/detect_objects/model_load_path", load_path)

    def load_params(self):
        import sys

        self.prefer_original_resolution = not self.chkUse10cmResolution.isChecked()

        # self.use_neural_network_pretrained_on_birds = self.chkUseBirdsPretrainedModel.isChecked()

        # self.augment_colors = self.chkObjectCanBeOfAnyColor.isChecked()

        if not self.prefer_original_resolution:
            self.orthomosaic_resolution = self.preferred_resolution
            self.patch_size = self.preferred_patch_size
        else:
            self.orthomosaic_resolution = self.chunk.orthomosaic.resolution
            if self.orthomosaic_resolution > 0.105:
                raise Exception("Orthomosaic should have resolution <= 10 cm/pix.")
            if self.force_small_patch_size:
                patch_size_multiplier = 1
            else:
                patch_size_multiplier = max(1, min(4, self.preferred_resolution / self.orthomosaic_resolution))
            self.patch_size = round(self.preferred_patch_size * patch_size_multiplier)

        self.patch_inner_border = self.patch_size // 8
        print("Using resolution {} m/pix with patch {}x{}".format(self.orthomosaic_resolution, self.patch_size, self.patch_size))
        self.working_dir = self.edtWorkingDir.text()

        self.load_model_path = self.edtModelLoadPath.text()
        self.save_model_path = self.edtModelSavePath.text()

        trainZonesLayer = self.layers[self.trainZonesLayer.currentIndex()]
        trainDataLayer = self.layers[self.trainDataLayer.currentIndex()]
        if trainZonesLayer == self.noTrainDataChoice or trainDataLayer == self.noTrainDataChoice:
            self.train_on_user_data_enabled = False
            print("Additional neural network training disabled")
        else:
            self.train_on_user_data_enabled = True
            print("Additional neural network training expected on key={} layer data w.r.t. key={} layer zones".format(trainDataLayer[0], trainZonesLayer[0]))
            print("Loading train shapes...")
            loading_train_shapes_start = time.time()
            shapes = self.chunk.shapes
            self.train_zones = []
            self.train_data = []
            for shape in shapes:
                layer = shape.group
                if layer.key == trainZonesLayer[0]:
                    self.train_zones.append(shape)
                elif layer.key == trainDataLayer[0]:
                    self.train_data.append(shape)
            print("{} train zones and {} train data loaded in {:.2f} sec".format(len(self.train_zones), len(self.train_data), time.time() - loading_train_shapes_start))

    def debug_draw_trees(self, img, trees):
        import cv2
        import numpy as np
        import pandas as pd

        img = img.copy()

        if isinstance(trees, pd.DataFrame):
            for row in trees.itertuples():
                xmin, ymin, xmax, ymax, label = int(row.xmin), int(row.ymin), int(row.xmax), int(row.ymax), row.label
                assert label == "Tree"
                cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 0, 255), 2)
        else:
            h, w, cn = img.shape
            for bbox_from, bbox_to in trees:
                assert np.all(bbox_from >= np.int32([0, 0]))
                assert np.all(bbox_to <= np.int32([w, h]))
                (xmin, ymin), (xmax, ymax) = bbox_from, bbox_to
                cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 0, 255), 2)

        return img

def detect_objects():
    chunk = Metashape.app.document.chunk

    if chunk is None or chunk.orthomosaic is None:
        raise Exception("No active orthomosaic.")

    if chunk.orthomosaic.resolution > 0.105:
        raise Exception("Orthomosaic should have resolution <= 10 cm/pix.")

    app = QtWidgets.QApplication.instance()
    parent = app.activeWindow()

    dlg = DetectObjectsDlg(parent)

label = "Scripts/Detect objects"
Metashape.app.addMenuItem(label, detect_objects)
print("To execute this script press {}".format(label))


text:
module Codewars.Kata.CommonDenominators where

type Ratio a = (a, a) -- Data.Ratio not suitable for this kata

convertFracs :: Integral a => [Ratio a] -> [Ratio a]
convertFracs xs = map (\(n, d) -> (dd `div` d * n, dd)) xs where
  dd = foldl lcm 1 (map snd xs)

redpajama_set_name: RedPajamaGithub


text:
#ifndef ASYLO_UTIL_POSIX_ERROR_MATCHERS_H_
#define ASYLO_UTIL_POSIX_ERROR_MATCHERS_H_

#include "asylo/test/util/status_matchers.h"

namespace asylo {

/// Matches a `Status`-like object that contains the given POSIX error. Works on
/// any `Status` that can be inspected by `GetErrno()`.
///
/// \param errnum A POSIX error code.
/// \return A GoogleTest matcher for POSIX errors representing `errnum`.
PolymorphicStatusMatcherType PosixErrorIs(int errnum);

}  // namespace asylo

#endif  // ASYLO_UTIL_POSIX_ERROR_MATCHERS_H_


text:
<?php

namespace luya\admin\assets;

/**
 * Main Asset contains all administration area depending files and should be a dependency for all other assets.
 *
 * @author Basil Suter <basil@nadar.io>
 */
class Main extends \luya\web\Asset
{
    /**
     * @var string The path to the folder where the files of this asset are located.
     */
    public $sourcePath = '@admin/resources';

    /**
     * @var array A list of css style documents located in the $sourcePath folder.
     */
    public $css = [
        'dist/css/admin.css'
    ];

    /**
     * @var array A list of javascript files located in the $sourcePath folder.
     */
    public $js = [
        'dist/js/main.min.js',
    ];

    /**
     * @var array A list of asset files on where this asset file depends on, it means the current files will be included after the depending files.
     */
    public $depends = [
        'luya\admin\assets\Jquery',
    ];
}


text:
package org.springframework.cloud.contract.verifier.converter;

import java.io.File;
import java.util.Arrays;
import java.util.List;

import org.springframework.util.StringUtils;

public final class RecursiveFilesConverterApplication {

	private RecursiveFilesConverterApplication() {
	}

	public static void main(String[] args) {
		if (args.length != 5) {
			throw new RuntimeException("Invalid number of arguments");
		}

		File stubsOutputDir = new File(args[0]);
		File contractsDslDir = new File(args[1]);
		List<String> excludedFiles = Arrays.asList(StringUtils.commaDelimitedListToStringArray(args[2]));
		String includedContracts = args[3];
		boolean excludeBuildFolders = Boolean.parseBoolean(args[4]);

		RecursiveFilesConverter converter = new RecursiveFilesConverter(stubsOutputDir, contractsDslDir, excludedFiles,
				includedContracts, excludeBuildFolders);
		converter.processFiles();
	}

}


text:
package org.apache.spark.deploy.yarn

import scala.collection.mutable.ArrayBuffer

import org.apache.spark.internal.Logging

// TODO: Add code and support for ensuring that yarn resource 'tasks' are location aware !
private[spark] class ClientArguments(args: Array[String]) extends Logging {

  var userJar: String = null
  var userClass: String = null
  var primaryPyFile: String = null
  var primaryRFile: String = null
  var userArgs: ArrayBuffer[String] = new ArrayBuffer[String]()
  var verbose: Boolean = false

  parseArgs(args.toList)

  private def parseArgs(inputArgs: List[String]): Unit = {
    var args = inputArgs

    while (!args.isEmpty) {
      args match {
        case ("--jar") :: value :: tail =>
          userJar = value
          args = tail

        case ("--class") :: value :: tail =>
          userClass = value
          args = tail

        case ("--primary-py-file") :: value :: tail =>
          primaryPyFile = value
          args = tail

        case ("--primary-r-file") :: value :: tail =>
          primaryRFile = value
          args = tail

        case ("--arg") :: value :: tail =>
          userArgs += value
          args = tail

        case ("--verbose" | "-v") :: tail =>
          verbose = true
          args = tail

        case Nil =>

        case _ =>
          throw new IllegalArgumentException(getUsageMessage(args))
      }
    }

    if (primaryPyFile != null && primaryRFile != null) {
      throw new IllegalArgumentException("Cannot have primary-py-file and primary-r-file" +
        " at the same time")
    }

    if (verbose) {
      logInfo(s"Parsed user args for YARN application: [${userArgs.mkString(" ")}]")
    }
  }

  private def getUsageMessage(unknownParam: List[String] = null): String = {
    val message = if (unknownParam != null) s"Unknown/unsupported param $unknownParam\n" else ""
    message +
      s"""
      |Usage: org.apache.spark.deploy.yarn.Client [options]
      |Options:
      |  --jar JAR_PATH           Path to your application's JAR file (required in YARN cluster
      |                           mode)
      |  --class CLASS_NAME       Name of your application's main class (required)
      |  --primary-py-file        A main Python file
      |  --primary-r-file         A main R file
      |  --arg ARG                Argument to be passed to your application's main class.
      |                           Multiple invocations are possible, each will be passed in order.
      |  --verbose, -v            Print additional debug output.
      """.stripMargin
  }
}


text:
/*!
 * @addtogroup GameData Projects/CoX/Common/GameData
 * @{
 */

#include "Friend.h"

#include "CharacterHelpers.h"
#include "Components/serialization_common.h"
#include "Components/serialization_types.h"
#include "GameData/playerdata_definitions.h"
#include "GameData/map_definitions.h"
#include "Entity.h"
#include "Components/Logging.h"
#include "Character.h"

/*
 * Friend Methods -- Sidekick system requires teaming.
 */
void toggleFriendList(Entity &src)
{
    GUIWindow *friendlist = &src.m_player->m_gui.m_wnds.at(WindowIDX::wdw_Friends);
    QString msg = "Toggling FriendList visibility.";
    msg += " " + QString::number(friendlist->m_mode);

    // TODO: How to actually change window visibility?
    if(friendlist->m_mode != WindowVisibility::wv_Visible)
        friendlist->setWindowVisibility(WindowVisibility::wv_Visible);
    else
        friendlist->setWindowVisibility(WindowVisibility::wv_DockedOrHidden);

    msg += " to " + QString::number(friendlist->m_mode);
    qCDebug(logFriends).noquote() << msg;
}

void dumpFriends(const Entity &src)
{
    const FriendsList *fl(&src.m_char->m_char_data.m_friendlist);
    QString msg = QString("FriendsList\n  has_friends: %1 \n friends_count: %2 ")
            .arg(fl->m_has_friends)
            .arg(fl->m_friends_count);

    qDebug().noquote() << msg;

    for(const auto &f : fl->m_friends)
        dumpFriendsList(f);
}

void dumpFriendsList(const Friend &f)
{
    qDebug().noquote() << "Friend:" << f.m_name
             << "\n\t" << "online:" << f.m_online_status
             << "\n\t" << "db_id:" << f.m_db_id
             << "\n\t" << "class_id:" << f.m_class_idx
             << "\n\t" << "origin_id:" << f.m_origin_idx
             << "\n\t" << "map_idx:" << f.m_map_idx
             << "\n\t" << "mapname:" << f.m_mapname;
}

FriendListChangeStatus addFriend(Entity &src, const Entity &tgt,const QString &mapname)
{
    QString msg;
    FriendsList &src_data(src.m_char->m_char_data.m_friendlist);

    if(src_data.m_friends_count >= g_max_friends)
    {
        return FriendListChangeStatus::MAX_FRIENDS_REACHED;
    }

    src_data.m_has_friends = true;
    src_data.m_friends_count++;

    Friend f;
    f.m_online_status   = (tgt.m_client != nullptr); // need some other method for this.
    f.m_db_id           = tgt.m_db_id;
    f.m_name            = tgt.name();
    f.m_class_idx       = tgt.m_entity_data.m_class_idx;
    f.m_origin_idx      = tgt.m_entity_data.m_origin_idx;
    f.m_map_idx         = tgt.m_entity_data.m_map_idx;
    f.m_mapname         = mapname;

    // add to friendlist
    src_data.m_friends.emplace_back(f);
    qCDebug(logFriends) << "friendslist size:" << src_data.m_friends_count << src_data.m_friends.size();

    if(logFriends().isDebugEnabled())
        dumpFriends(src);
    return FriendListChangeStatus::FRIEND_ADDED;
}

FriendListChangeStatus removeFriend(Entity &src, const QString& friend_name)
{
    QString msg;
    FriendsList &src_data(src.m_char->m_char_data.m_friendlist);

    qCDebug(logFriends) << "Searching for friend" << friend_name << "to remove them.";

    QString lower_name = friend_name.toLower();
    auto iter = std::find_if( src_data.m_friends.begin(), src_data.m_friends.end(),
                              [lower_name](const Friend& f)->bool {return lower_name==f.m_name.toLower();});

    if(iter==src_data.m_friends.end())
        return FriendListChangeStatus::FRIEND_NOT_FOUND;

    msg = "Removing " + iter->m_name + " from your friends list.";
    iter = src_data.m_friends.erase(iter);

    qCDebug(logFriends) << msg;
    if(logFriends().isDebugEnabled())
        dumpFriends(src);

    if(src_data.m_friends.empty())
        src_data.m_has_friends = false;

    src_data.m_friends_count = src_data.m_friends.size();

    qCDebug(logFriends).noquote() << msg;
    return FriendListChangeStatus::FRIEND_REMOVED;
}

//const QString &getFriendDisplayMapName(const Friend &f)
//{
//    static const QString offline(QStringLiteral("OFFLINE"));
//    if(!f.m_online_status)
//        return offline;
//    return getDisplayMapName(f.m_map_idx);
//}

template<class Archive>
void Friend::serialize(Archive &archive, uint32_t const version)
{
    if(version != Friend::class_version)
    {
        qCritical() << "Failed to serialize Friend, incompatible serialization format version " << version;
        return;
    }

    archive(cereal::make_nvp("FriendOnline", m_online_status));
    archive(cereal::make_nvp("FriendDbId", m_db_id));
    archive(cereal::make_nvp("FriendName", m_name));
    archive(cereal::make_nvp("FriendClass", m_class_idx));
    archive(cereal::make_nvp("FriendOrigin", m_origin_idx));
    archive(cereal::make_nvp("FriendMapIdx", m_map_idx));
    archive(cereal::make_nvp("FriendMapname", m_mapname));
}
CEREAL_CLASS_VERSION(Friend, Friend::class_version)       // register Friend struct version
SPECIALIZE_CLASS_VERSIONED_SERIALIZATIONS(Friend)

template<class Archive>
void serialize(Archive &archive, FriendsList &fl, uint32_t const version)
{
    if(version != FriendsList::class_version)
    {
        qCritical() << "Failed to serialize FriendsList, incompatible serialization format version " << version;
        return;
    }

    archive(cereal::make_nvp("HasFriends",fl.m_has_friends));
    archive(cereal::make_nvp("FriendsCount",fl.m_friends_count));
    archive(cereal::make_nvp("Friends",fl.m_friends));
}

CEREAL_CLASS_VERSION(FriendsList, FriendsList::class_version)       // register FriendList struct version
SPECIALIZE_VERSIONED_SERIALIZATIONS(FriendsList)

//! @}


text:
/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.flowable.cmmn.engine.impl.runtime;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.flowable.cmmn.api.runtime.PlanItemInstance;
import org.flowable.cmmn.api.runtime.PlanItemInstanceState;
import org.flowable.cmmn.engine.impl.persistence.entity.PlanItemInstanceEntity;
import org.flowable.cmmn.model.EventListener;
import org.flowable.cmmn.model.PlanItemDefinition;
import org.flowable.cmmn.model.PlanItemTransition;

/**
 * Whenever a plan item or event listener changes its state as part of a CMMN engine operation, its current state and transition is checked to be valid.
 * This static class supports methods for this check as well as initializes all possible states and their transitions.
 *
 * @author Joram Barrez
 * @author Micha Kiener
 */
public class StateTransition {

    public static Map<String, Set<String>> PLAN_ITEM_TRANSITIONS = new HashMap<>();

    // See 8.4.2 of CMMN 1.1 spec

    static {
        // a newly created plan item instance can either be used for creation (first time) or reactivation (case reactivation)
        addPlanItemTransition(null,
            PlanItemTransition.CREATE,
            PlanItemTransition.REACTIVATE);

        addPlanItemTransition(PlanItemInstanceState.WAITING_FOR_REPETITION,
            PlanItemTransition.CREATE,
            PlanItemTransition.EXIT);

        addPlanItemTransition(PlanItemInstanceState.AVAILABLE,
                PlanItemTransition.START,
                PlanItemTransition.ENABLE,
                PlanItemTransition.PARENT_SUSPEND,
                PlanItemTransition.EXIT);

        addPlanItemTransition(PlanItemInstanceState.ENABLED,
                PlanItemTransition.DISABLE,
                PlanItemTransition.MANUAL_START,
                PlanItemTransition.PARENT_SUSPEND,
                PlanItemTransition.EXIT);

        addPlanItemTransition(PlanItemInstanceState.DISABLED,
                PlanItemTransition.REENABLE,
                PlanItemTransition.PARENT_SUSPEND,
                PlanItemTransition.EXIT);

        addPlanItemTransition(PlanItemInstanceState.ACTIVE,
                PlanItemTransition.FAULT,
                PlanItemTransition.COMPLETE,
                PlanItemTransition.SUSPEND,
                PlanItemTransition.TERMINATE,
                PlanItemTransition.PARENT_SUSPEND,
                PlanItemTransition.EXIT,
                PlanItemTransition.ASYNC_LEAVE_ACTIVE);

        addPlanItemTransition(PlanItemInstanceState.ASYNC_ACTIVE,
                PlanItemTransition.ASYNC_ACTIVATE);

        addPlanItemTransition(PlanItemInstanceState.ASYNC_ACTIVE_LEAVE,
                PlanItemTransition.FAULT,
                PlanItemTransition.COMPLETE,
                PlanItemTransition.TERMINATE,
                PlanItemTransition.EXIT);

        addPlanItemTransition(PlanItemInstanceState.FAILED,
                PlanItemTransition.REACTIVATE,
                PlanItemTransition.EXIT);

        addPlanItemTransition(PlanItemInstanceState.SUSPENDED,
                PlanItemTransition.RESUME,
                PlanItemTransition.PARENT_RESUME,
                PlanItemTransition.EXIT);

        addPlanItemTransition(PlanItemInstanceState.COMPLETED);

        addPlanItemTransition(PlanItemInstanceState.TERMINATED);
    }

    public static Map<String, Set<String>> EVENT_LISTENER_TRANSITIONS = new HashMap<>();

    static {

        // a newly created event listener might be newly created (first time) or reactivated as part of the case reactivation
        addEventListenerTransition(null,
            PlanItemTransition.CREATE,
            PlanItemTransition.REACTIVATE);

        addEventListenerTransition(PlanItemInstanceState.UNAVAILABLE,
            PlanItemTransition.INITIATE,
            PlanItemTransition.TERMINATE,
            PlanItemTransition.EXIT,
            PlanItemTransition.SUSPEND);

        addEventListenerTransition(PlanItemInstanceState.AVAILABLE,
            PlanItemTransition.DISMISS,
            PlanItemTransition.TERMINATE,
            PlanItemTransition.OCCUR,
            PlanItemTransition.EXIT,
            PlanItemTransition.SUSPEND);

        addEventListenerTransition(PlanItemInstanceState.SUSPENDED,
            PlanItemTransition.RESUME,
            PlanItemTransition.EXIT,
            PlanItemTransition.TERMINATE);

        addEventListenerTransition(PlanItemInstanceState.COMPLETED);

        addEventListenerTransition(PlanItemInstanceState.TERMINATED);
    }

    protected static void addPlanItemTransition(String state, String...transitions) {
        PLAN_ITEM_TRANSITIONS.put(state, new HashSet<>(transitions.length));
        for (String transition : transitions) {
            PLAN_ITEM_TRANSITIONS.get(state).add(transition);
        }
    }

    protected static void addEventListenerTransition(String state, String...transitions) {
        EVENT_LISTENER_TRANSITIONS.put(state, new HashSet<>(transitions.length));
        for (String transition : transitions) {
            EVENT_LISTENER_TRANSITIONS.get(state).add(transition);
        }
    }

    public static boolean isPossible(PlanItemInstance planItemInstance, String transition) {
        PlanItemDefinition planItemDefinition = ((PlanItemInstanceEntity) planItemInstance).getPlanItem().getPlanItemDefinition();
        if (planItemDefinition instanceof EventListener) {
            return isEventListenerTransitionPossible(planItemInstance.getState(), transition);
        } else {
            return isPlanItemTransitionPossible(planItemInstance.getState(), transition);
        }
    }

    protected static boolean isPlanItemTransitionPossible(String currentState, String transition) {
        if (PLAN_ITEM_TRANSITIONS.containsKey(currentState)) {
            return PLAN_ITEM_TRANSITIONS.get(currentState).contains(transition);
        }
        return false;
    }

    protected static boolean isEventListenerTransitionPossible(String currentState, String transition) {
        if (EVENT_LISTENER_TRANSITIONS.containsKey(currentState)) {
            return EVENT_LISTENER_TRANSITIONS.get(currentState).contains(transition);
        }
        return false;
    }

}


text:
'use strict'

var feedback = require('../handlers/feedback')

var routes = [
  {
    method: 'GET',
    path: '/feedback',
    config: {
      handler: feedback.showFeedbackPage,
      description: 'Show feedback page'
    }
  },
  {
    method: 'POST',
    path: '/feedback',
    config: {
      handler: feedback.addFeedback,
      description: 'Registers feedback'
    }
  }
]

module.exports = routes


text:
Ext.define('e4e.ui.FrameBuilder$TocModel', {
	extend : 'Ext.data.Model',
	fields : [ {
		name : 'name',
		type : 'string'
	}, {
		name : 'title',
		type : 'string'
	}, {
		name : 'dc',
		type : 'string'
	} ]
});

Ext.define("e4e.ui.FrameBuilder", {

	/**
	 * Frame instance
	 */
	frame : null,

	constructor : function(config) {
		config = config || {};
		Ext.apply(this, config);
		this.callParent(arguments);
	},

	/**
	 * Add a data-control controller
	 */
	addDc : function(name, obj) {
		this.frame._dcs_.add(name, obj);
		obj._instanceKey_ = name;
		this.frame.mon(obj, "message", this.frame.message, this.frame);
		return this;
	},

	/**
	 * Link data-controls with the given rules.
	 */
	linkDc : function(childName, parentName, relation) {
		Ext.applyIf(relation, {
			fetchMode : "manual",
			strict : true
		});
		var c = this.frame._dcs_.get(childName);
		var p = this.frame._dcs_.get(parentName);
		var ctx = new e4e.dc.DcContext({
			childDc : c,
			parentDc : p,
			relation : relation
		});
		p.addChild(c);
		c.setDcContext(ctx);
		return this;
	},

	/**
	 * Add a data-control edit form view.
	 */
	addDcFormView : function(dc, config) {
		this.addDcView(dc, config);
		return this;
	},

	/**
	 * Add a data-control filter form view.
	 */
	addDcFilterFormView : function(dc, config) {
		this.addDcView(dc, config);
		return this;
	},

	/**
	 * Add a data-control grid view.
	 */
	addDcGridView : function(dc, config) {
		config.stateId = this.frame.$className + "-" + config.name;
		if (getApplication().getSession().rememberViewState) {
			config.stateful = true;
		}
		return this.addDcView(dc, config);
	},

	/**
	 * Add a data-control edit grid view.
	 */
	addDcEditGridView : function(dc, config) {
		return this.addDcGridView(dc, config);
	},

	/**
	 * Add a free-hand data-control view.
	 */
	addDcView : function(dc, config) {
		Ext.apply(config, {
			_controller_ : this.frame._dcs_.get(dc)
		});
		if (config._hasTitle_ === true) {
			config.title = this.frame.translate(config.name + "__ttl");
		}
		this.applyViewSharedConfig(config);
		return this;
	},

	/**
	 * Add a presentation panel
	 */
	addPanel : function(config) {
		config.listeners = config.listeners || {};
		if (config.onActivateDoLayoutFor) {
			var onActivateDoLayoutFor = config.onActivateDoLayoutFor;
			delete config.onActivateDoLayoutFor;
			var activate = {
				scope : this.frame,
				fn : function(cmp, opt) {
					for (var i = 0; i < onActivateDoLayoutFor.length; i++) {
						var e = this._getElement_(onActivateDoLayoutFor[i]);
						e.doLayout();
					}
				}
			}
			if (!config.listeners.activate) {
				config.listeners.activate = activate;
			}
		}
		Ext.applyIf(config, {
			id : Ext.id()
		});

		var ttlKey = config.name + "__ttl";
		if (config._hasTitle_ === true) {
			config.title = this.frame.translate(config.name + "__ttl");
		}
		this.applyViewSharedConfig(config);
		return this;
	},

	/**
	 * Add child elements to the given container
	 */
	addChildrenTo : function(c, list, regions) {
		var isWrapped = this.frame._elems_.get(c)._wrapped_;
		var items = ((isWrapped) ? this.frame._elems_.get(c)["items"]["items"]
				: this.frame._elems_.get(c)["items"])
				|| [];
		for (var i = 0, len = list.length; i < len; i++) {
			var cmp = this.frame._elems_.get(list[i]);
			items[items.length] = cmp;
			if (regions) {
				cmp.region = regions[i];
			}
		}
		if (isWrapped) {
			this.frame._elems_.get(c)["items"]["items"] = items;
		} else {
			this.frame._elems_.get(c)["items"] = items;
		}
		return this;
	},

	/**
	 * Link a toolbar to the given component.
	 */
	addToolbarTo : function(c, tlb) {
		this.frame._linkToolbar_(tlb, c);
		return this;
	},

	/**
	 * Start a toolbar creation process. The returned ActionBuilder provides
	 * methods to add elements with a fluid API
	 */
	beginToolbar : function(name, config) {
		return new e4e.ui.ActionBuilder({
			name : name,
			frame : this.frame,
			dc : config.dc
		});
	},

	/**
	 * Add a table-of-contents elements.Used to include several independent root
	 * components , each on its own canvas with a ToC like navigation.
	 */
	addToc : function(canvases) {
		var data = [];
		for (var i = 0; i < canvases.length; i++) {
			var c = canvases[i].split(":");
			data[i] = {
				"name" : c[0],
				"dc" : c[1],
				"title" : this.frame.translate(c[0] + "__ttl")
			};
		}
		var store = Ext.create('Ext.data.Store', {
			model : 'e4e.ui.FrameBuilder$TocModel',
			data : data
		});

		var config = {
			name : "_toc_",
			collapsible : true,
			layout : "fit",
			id : Ext.id(),
			region : "west",
			title : 'Navigation',
			width : 200,
			frame : false,
			items : [ {
				name : "_toc_items_",
				xtype : 'gridpanel',
				id : Ext.id(),
				hideHeaders : true,
				autoScroll : true,
				viewConfig : {
					stripeRows : false
				},
				singleSelect : true,
				forceFit : true,
				store : store,
				columns : [ {
					header : 'title',
					dataIndex : 'title'
				} ],
				selModel : {
					mode : "SINGLE",
					listeners : {
						"selectionchange" : {
							scope : this.frame,
							fn : function(sm, selected, options) {
								if (this._getElement_("main").rendered) {
									var _d = selected[0].data;
									this._showStackedViewElement_("main",
											_d.name);
									this._rootDc_ = _d.dc;
								}
							}
						}
					}
				},
				listeners : {
					scope : this.frame,

					afterrender : function() {
						this._showTocElement_(0);
					}
				}
			} ]

		}
		this.frame._elems_.add(config.name, config);
		return this;
	},

	/**
	 * Create a button
	 *
	 * @param {}
	 *            config
	 * @return {}
	 */
	addButton : function(config) {
		Ext.applyIf(config, {
			id : Ext.id(),
			xtype : "button"
		});

		var lblKey = config.name + "__lbl";
		var tlpKey = config.name + "__tlp";

		Ext.apply(config, {
			text : this.frame.translate(lblKey),
			tooltip : this.frame.translate(tlpKey)
		});

		if (!Main.viewConfig.USE_BUTTON_ICONS) {
			config.iconCls = null;
		}

		this.frame._elems_.add(config.name, config);

		if (config.stateManager) {
			var options = {
				and : config.stateManager.and
			};
			if (options.and) {
				this.frame._buttonStateRules_[config.name] = options.and;
			}
			e4e.ui.FrameButtonStateManager.register(config.name,
					config.stateManager.name, config.stateManager.dc,
					this.frame, options);
		}
		return this;
	},

	/**
	 * Add assignment component.
	 */
	addAsgn : function(config) {
		Ext.applyIf(config, {
			id : Ext.id(),
			objectIdField : "id"
		});
		this.frame._elems_.add(config.name, config);
		return this;
	},

	/**
	 * Add a window to the elements list.
	 */
	addWindow : function(config) {
		Ext.applyIf(config, {
			id : Ext.id(),
			_window_ : true
		});
		if (!config.listeners) {
			config.listeners = {}
		}
		if (!config.listeners.show) {
			config.listeners.show = {
				fn : function() {
					var _f = this.down(' textfield');
					if (_f) {
						_f.focus();
					}
				}
			}
		}
		config.title = this.frame.translate(config.name + "__ttl");
		this.frame._elems_.add(config.name, config);
		return this;
	},

	/**
	 * Add a generic free-hand component.
	 */
	add : function(config) {
		Ext.applyIf(config, {
			id : Ext.id()
		});
		this.frame._elems_.add(config.name, config);
		return this;
	},

	/**
	 * Merge (add missing, leave existing unchanged) existing configuration of
	 * the specified element with the new configuration.
	 *
	 */
	merge : function(name, config) {
		Ext.applyIf(this.frame._elems_.get(name), config);
		return this;
	},

	/**
	 * Change (overwrite) existing configuration of the specified element with
	 * the new configuration.
	 */
	change : function(name, config) {
		Ext.apply(this.frame._elems_.get(name), config);
		return this;
	},

	/**
	 * Remove the specified element from the list.
	 */
	remove : function(name) {
		this.frame._elems_.remove(name);
		return this;
	},

	// private

	applyViewSharedConfig : function(config) {
		Ext.applyIf(config, {
			id : Ext.id(),
			itemId : config.name
		});
		this.frame._elems_.add(config.name, config);
	}
});


text:
<?php

namespace Picqer\Financials\Exact;

/**
 * Class ServiceRequest.
 *
 * @see https://start.exactonline.nl/docs/HlpRestAPIResourcesDetails.aspx?name=ActivitiesServiceRequests
 *
 * @property string $ID The Primary key
 * @property string $Account The account that is related to the service request
 * @property string $AccountName The name of the account
 * @property string $AssignedTo The user that the service request is assigned to
 * @property string $AssignedToFullName The user name
 * @property RequestAttachment[] $Attachments Attachments linked to the service request
 * @property string $Contact The contact person that is related to the service request
 * @property string $ContactFullName The name of the contact person
 * @property string $Created Creation date
 * @property string $Creator User ID of the creator
 * @property string $CreatorFullName Name of the creator
 * @property string $Description The description
 * @property int $Division The division
 * @property string $Document The document that is linked to the service request
 * @property string $DocumentSubject The subject of the document
 * @property int $HID The human readable key
 * @property string $Modified Last modified date
 * @property string $Modifier User ID of the last modifier
 * @property string $ModifierFullName Name of the last modifier
 * @property string $NextAction The date indicating by when the next action has to be taken
 * @property string $Notes The notes of the service request
 * @property string $ReceiptDate The date the service request was received
 * @property int $Status Status: 0 = Void, 5 = Rejected, 10 = Draft, 20 = Open, 30 = Approved, 40 = Realized, 50 = Processed
 * @property string $StatusDescription The description of the status
 */
class ServiceRequest extends Model
{
    use Query\Findable;
    use Persistance\Storable;

    protected $fillable = [
        'ID',
        'Account',
        'AccountName',
        'AssignedTo',
        'AssignedToFullName',
        'Attachments',
        'Contact',
        'ContactFullName',
        'Created',
        'Creator',
        'CreatorFullName',
        'Description',
        'Division',
        'Document',
        'DocumentSubject',
        'HID',
        'Modified',
        'Modifier',
        'ModifierFullName',
        'NextAction',
        'Notes',
        'ReceiptDate',
        'Status',
        'StatusDescription',
    ];

    protected $url = 'activities/ServiceRequests';
}


text:
require_relative './../nodetree'
# JSONToNodetreeConverter : Converts a list of json nodes
# into a list of NodeTree
class JSONToNodetreeConverter
  def to_nodetree(list_json)
    list_json.map { |node_json| to_nodetree_json_node(node_json) }
  end

  private

  def to_nodetree_json_node(node_json, parent = nil)
    name = node_json['name']
    attributes = node_json['attributes']

    node = NodeTree.new(name, attributes)

    children = node_json['children'].map do |child|
      to_nodetree_json_node(child, node)
    end
    node.children = children

    node.parent = parent
    node
  end
end


text:
Headzoo\Bitcoin\Wallet\Api\Exceptions\ForbiddenException
===============

Thrown when the rpc when the api responds with a forbidden status code.

* Class name: ForbiddenException
* Namespace: Headzoo\Bitcoin\Wallet\Api\Exceptions
* Parent class: [Headzoo\Bitcoin\Wallet\Api\Exceptions\RPCException](Headzoo-Bitcoin-Wallet-Api-Exceptions-RPCException.md)


text:
using namespace dariadb;
using namespace dariadb::storage;
using dariadb::utils::inInterval;

inline bool check_index_rec(IndexReccord &it, dariadb::Time from, dariadb::Time to) {
  return inInterval(from, to, it.stat.minTime) || inInterval(from, to, it.stat.maxTime) ||
         inInterval(it.stat.minTime, it.stat.maxTime, from) ||
         inInterval(it.stat.minTime, it.stat.maxTime, to);
}

inline bool check_blooms(const IndexReccord &_index_it, dariadb::Id id,
                         dariadb::Flag flag) {
  auto id_check_result = false;
  id_check_result = _index_it.target_id == id;
  auto flag_bloom_result = false;
  if (flag == dariadb::Flag(0) ||
      dariadb::storage::bloom_check(_index_it.stat.flag_bloom, flag)) {
    flag_bloom_result = true;
  }
  return id_check_result && flag_bloom_result;
}

PageIndex::~PageIndex() {}

PageIndex_ptr PageIndex::open(const std::string &_filename) {
  PageIndex_ptr res = std::make_shared<PageIndex>();
  res->filename = _filename;
  res->iheader = readIndexFooter(_filename);
  return res;
}

ChunkLinkList PageIndex::get_chunks_links(const dariadb::IdArray &ids, dariadb::Time from,
                                          dariadb::Time to, dariadb::Flag flag) {
  ChunkLinkList result;
  IndexReccord *records = new IndexReccord[this->iheader.recs_count];
  auto index_file = std::fopen(filename.c_str(), "rb");
  if (index_file == nullptr) {
    delete[] records;
    THROW_EXCEPTION("can`t open file ", this->filename);
  }

  auto readed = std::fread(records, sizeof(IndexReccord), iheader.recs_count, index_file);
  if (readed < iheader.recs_count) {
    delete[] records;
    THROW_EXCEPTION("engine: index read error - ", this->filename);
  }
  std::fclose(index_file);
  for (uint32_t pos = 0; pos < this->iheader.recs_count; ++pos) {

    auto _index_it = records[pos];
    if (check_index_rec(_index_it, from, to)) {
      bool bloom_result = false;
      if (ids.size() == size_t(0)) {
        bloom_result = true;
      } else {
        for (auto i : ids) {
          bloom_result = check_blooms(_index_it, i, flag);
          if (bloom_result) {
            break;
          }
        }
      }
      if (bloom_result) {
        ChunkLink sub_result;
        sub_result.id = _index_it.chunk_id;
        sub_result.index_rec_number = pos;
        sub_result.minTime = _index_it.stat.minTime;
        sub_result.maxTime = _index_it.stat.maxTime;
        sub_result.meas_id = _index_it.target_id;
        result.push_back(sub_result);
      }
    }
  }
  delete[] records;

  return result;
}

std::vector<IndexReccord> PageIndex::readReccords() {
  std::vector<IndexReccord> records;
  records.resize(iheader.recs_count);

  auto index_file = std::fopen(filename.c_str(), "rb");
  if (index_file == nullptr) {
    THROW_EXCEPTION("can`t open file ", this->filename);
  }
  auto readed =
      std::fread(records.data(), sizeof(IndexReccord), iheader.recs_count, index_file);
  if (readed < iheader.recs_count) {
    THROW_EXCEPTION("engine: index read error - ", this->filename);
  }
  std::fclose(index_file);
  return records;
}

IndexFooter PageIndex::readIndexFooter(std::string ifile) {
  std::ifstream istream;
  istream.open(ifile, std::fstream::in | std::fstream::binary);
  if (!istream.is_open()) {
    THROW_EXCEPTION("can't open file. filename=", ifile);
  }
  istream.seekg(-(int)sizeof(IndexFooter), istream.end);
  IndexFooter result;
  memset(&result, 0, sizeof(IndexFooter));
  istream.read((char *)&result, sizeof(IndexFooter));
  istream.close();
  if (!result.check()) {
    THROW_EXCEPTION("IndexFooter magic number check.");
  }
  return result;
}