from __future__ import print_function
import open3d as o3d # open3d needs to be imported before other packages!
import argparse
import os
import random
import numpy as np
import torch
import torch.nn.parallel
import torch.utils.data

from model.VAE import VAE
from dataset.dataset import RIODatasetSceneGraph, collate_fn_vaegan, collate_fn_vaegan_points
from helpers.util import bool_flag, batch_torch_denormalize_box_params
from helpers.metrics import validate_constrains, validate_constrains_changes, estimate_angular_std
from helpers.visualize_graph import run as vis_graph
from helpers.visualize_scene import render
import helpers.retrieval as retrieval
from model.atlasnet import AE_AtlasNet

import extension.dist_chamfer as ext
chamfer = ext.chamferDist()
import json

parser = argparse.ArgumentParser()
parser.add_argument('--num_points', type=int, default=1024, help='number of points in the shape')

parser.add_argument('--dataset', required=False, type=str, default="./GT", help="dataset path")
parser.add_argument('--dataset_3RScan', type=str, default='', help="dataset path of 3RScan")
parser.add_argument('--label_file', required=False, type=str, default='labels.instances.align.annotated.ply', help="label file name")

parser.add_argument('--with_points', type=bool_flag, default=False, help="if false, only predicts layout")
parser.add_argument('--with_feats', type=bool_flag, default=True, help="Load Feats directly instead of points.")

parser.add_argument('--manipulate', default=True, type=bool_flag)
parser.add_argument('--path2atlas', default="./experiments/model_36.pth", type=str)
parser.add_argument('--exp', default='./experiments/layout_test', help='experiment name')
parser.add_argument('--epoch', type=str, default='100', help='saved epoch')
parser.add_argument('--recompute_stats', type=bool_flag, default=False, help='Recomputes statistics of evaluated networks')
parser.add_argument('--evaluate_diversity', type=bool_flag, default=False, help='Computes diversity based on multiple predictions')
parser.add_argument('--visualize', default=False, type=bool_flag)
parser.add_argument('--export_3d', default=False, type=bool_flag, help='Export the generated shapes and boxes in json files for future use')
args = parser.parse_args()

def evaluate():
    print(torch.__version__)

    random.seed(48)
    torch.manual_seed(48)

    argsJson = os.path.join(args.exp, 'args.json')
    assert os.path.exists(argsJson), 'Could not find args.json for experiment {}'.format(args.exp)
    with open(argsJson) as j:
        modelArgs = json.load(j)

    saved_model = torch.load(args.path2atlas)
    point_ae = AE_AtlasNet(num_points=1024, bottleneck_size=128, nb_primitives=25)
    point_ae.load_state_dict(saved_model, strict=True)
    if torch.cuda.is_available():
        point_ae = point_ae.cuda()

    test_dataset_rels_changes = RIODatasetSceneGraph(
        root=args.dataset,
        atlas=point_ae,
        path2atlas=args.path2atlas,
        root_3rscan=args.dataset_3RScan,
        label_file=args.label_file,
        split='val_scans',
        npoints=args.num_points,
        data_augmentation=False,
        use_points=args.with_points,
        use_scene_rels=modelArgs['use_scene_rels'],
        vae_baseline=modelArgs['network_type']=='sln',
        with_changes=True,
        eval=True,
        eval_type='relationship',
        with_feats=args.with_feats,
        recompute_feats=False,
        use_rio27=modelArgs['rio27'],
        use_canonical=modelArgs['use_canonical'],
        large=modelArgs['large'],
        use_splits=modelArgs['use_splits'],
        crop_floor=modelArgs['crop_floor'] if 'crop_floor' in modelArgs.keys() else False,
        center_scene_to_floor=modelArgs['crop_floor'] if 'crop_floor' in modelArgs.keys() else False)

    test_dataset_addition_changes = RIODatasetSceneGraph(
        root=args.dataset,
        atlas=point_ae,
        path2atlas=args.path2atlas,
        root_3rscan=args.dataset_3RScan,
        label_file=args.label_file,
        split='val_scans',
        npoints=args.num_points,
        data_augmentation=False,
        use_points=args.with_points,
        use_scene_rels=modelArgs['use_scene_rels'],
        vae_baseline=modelArgs['network_type']=='sln',
        with_changes=True,
        eval=True,
        eval_type='addition',
        with_feats=args.with_feats,
        use_rio27=modelArgs['rio27'],
        use_canonical=modelArgs['use_canonical'],
        large=modelArgs['large'],
        use_splits=modelArgs['use_splits'],
        crop_floor=modelArgs['crop_floor'] if 'crop_floor' in modelArgs.keys() else False,
        center_scene_to_floor=modelArgs['crop_floor'] if 'crop_floor' in modelArgs.keys() else False)

    # used to collect train statistics
    stats_dataset = RIODatasetSceneGraph(
        root=args.dataset,
        atlas=point_ae,
        path2atlas=args.path2atlas,
        root_3rscan=args.dataset_3RScan,
        label_file=args.label_file,
        npoints=args.num_points,
        split='train_scans',
        use_points=args.with_points,
        use_scene_rels=modelArgs['use_scene_rels'],
        with_changes=False,
        vae_baseline=modelArgs['network_type']=='sln',
        eval=False,
        with_feats=args.with_feats,
        use_rio27=modelArgs['rio27'],
        use_canonical=modelArgs['use_canonical'],
        large=modelArgs['large'],
        use_splits=modelArgs['use_splits'],
        crop_floor=modelArgs['crop_floor'] if 'crop_floor' in modelArgs.keys() else False,
        center_scene_to_floor=modelArgs['crop_floor'] if 'crop_floor' in modelArgs.keys() else False)

    test_dataset_no_changes = RIODatasetSceneGraph(
        root=args.dataset,
        atlas=point_ae,
        path2atlas=args.path2atlas,
        root_3rscan=args.dataset_3RScan,
        label_file=args.label_file,
        split='val_scans',
        npoints=args.num_points,
        data_augmentation=False,
        use_points=args.with_points,
        use_scene_rels=modelArgs['use_scene_rels'],
        vae_baseline=modelArgs['network_type']=='sln',
        with_changes=False,
        eval=True,
        with_feats=args.with_feats,
        use_rio27=modelArgs['rio27'],
        use_canonical=modelArgs['use_canonical'],
        large=modelArgs['large'],
        use_splits=modelArgs['use_splits'],
        crop_floor=modelArgs['crop_floor'] if 'crop_floor' in modelArgs.keys() else False,
        center_scene_to_floor=modelArgs['crop_floor'] if 'crop_floor' in modelArgs.keys() else False)

    if args.with_points:
        collate_fn = collate_fn_vaegan_points
    else:
        collate_fn = collate_fn_vaegan

    test_dataloader_rels_changes = torch.utils.data.DataLoader(
        test_dataset_rels_changes,
        batch_size=1,
        collate_fn=collate_fn,
        shuffle=False,
        num_workers=0)

    test_dataloader_add_changes = torch.utils.data.DataLoader(
        test_dataset_addition_changes,
        batch_size=1,
        collate_fn=collate_fn,
        shuffle=False,
        num_workers=0)

    # dataloader to collect train data statistics
    stats_dataloader = torch.utils.data.DataLoader(
        stats_dataset,
        batch_size=1,
        collate_fn=collate_fn,
        shuffle=False,
        num_workers=0)

    test_dataloader_no_changes = torch.utils.data.DataLoader(
        test_dataset_no_changes,
        batch_size=1,
        collate_fn=collate_fn,
        shuffle=False,
        num_workers=0)

    modeltype_ = modelArgs['network_type']
    replacelatent_ = modelArgs['replace_latent'] if 'replace_latent' in modelArgs else None
    with_changes_ = modelArgs['with_changes'] if 'with_changes' in modelArgs else None

    model = VAE(type=modeltype_, vocab=test_dataset_no_changes.vocab, replace_latent=replacelatent_,
                with_changes=with_changes_, residual=modelArgs['residual'], gconv_pooling=modelArgs['pooling'],
                with_angles=modelArgs['with_angles'])
    model.load_networks(exp=args.exp, epoch=args.epoch)
    if torch.cuda.is_available():
        model = model.cuda()

    model = model.eval()
    point_ae = point_ae.eval()

    model.compute_statistics(exp=args.exp, epoch=args.epoch, stats_dataloader=stats_dataloader, force=args.recompute_stats)

    cat2objs = None
    if model.type_ == 'sln':
        # prepare data for retrieval for the 3d-sln baseline
        rel_json_file = stats_dataset.root + '/relationships_merged_train_clean.json'
        cat2objs = retrieval.read_box_json(rel_json_file, stats_dataset.box_json_file)

    def reseed():
        np.random.seed(47)
        torch.manual_seed(47)
        random.seed(47)

    print('\nEditing Mode - Additions')
    reseed()
    validate_constrains_loop_w_changes(test_dataloader_add_changes, model, with_diversity=args.evaluate_diversity,
                                       atlas=point_ae, with_angles=modelArgs['with_angles'], cat2objs=cat2objs)
    reseed()
    print('\nEditing Mode - Relationship changes')
    validate_constrains_loop_w_changes(test_dataloader_rels_changes, model, with_diversity=args.evaluate_diversity,
                                       atlas=point_ae, with_angles=modelArgs['with_angles'], cat2objs=cat2objs)

    reseed()
    print('\nGeneration Mode')
    validate_constrains_loop(test_dataloader_no_changes, model, with_diversity=args.evaluate_diversity,
                             with_angles=modelArgs['with_angles'], vocab=test_dataset_no_changes.vocab,
                             point_classes_idx=test_dataset_no_changes.point_classes_idx, point_ae=point_ae,
                             export_3d=args.export_3d, cat2objs=cat2objs, datasize='large' if modelArgs['large'] else 'small')

def validate_constrains_loop_w_changes(testdataloader, model, with_diversity=True, atlas=None, with_angles=False, num_samples=10, cat2objs=None):
    if with_diversity and num_samples < 2:
        raise ValueError('Diversity requires at least two runs (i.e. num_samples > 1).')

    accuracy = {}
    accuracy_unchanged = {}
    accuracy_in_orig_graph = {}

    for k in ['left', 'right', 'front', 'behind', 'smaller', 'bigger', 'lower', 'higher', 'same', 'total']:
        accuracy_in_orig_graph[k] = []
        accuracy_unchanged[k] = []
        accuracy[k] = []

    all_diversity_boxes = []
    all_diversity_angles = []
    all_diversity_chamfer = []

    for i, data in enumerate(testdataloader, 0):
        try:
            enc_objs, enc_triples, enc_tight_boxes, enc_objs_to_scene, enc_triples_to_scene = data['encoder']['objs'], \
                                                                                data['encoder']['tripltes'], \
                                                                                data['encoder']['boxes'], \
                                                                                data['encoder']['obj_to_scene'], \
                                                                                data['encoder'][ 'tiple_to_scene']
            if 'feats' in data['encoder']:
                encoded_enc_points = data['encoder']['feats']
                encoded_enc_points = encoded_enc_points.float().cuda()
            if 'points' in data['encoder']:
                enc_points = data['encoder']['points']
                enc_points = enc_points.cuda()
                with torch.no_grad():
                    encoded_enc_points = atlas.encoder(enc_points.transpose(2,1).contiguous())

            dec_objs, dec_triples, dec_tight_boxes, dec_objs_to_scene, dec_triples_to_scene = data['decoder']['objs'], \
                                                                                data['decoder']['tripltes'], \
                                                                                data['decoder']['boxes'], \
                                                                                data['decoder']['obj_to_scene'], \
                                                                                data['decoder']['tiple_to_scene']

            missing_nodes = data['missing_nodes']
            manipulated_nodes = data['manipulated_nodes']

        except Exception as e:
            # skipping scene
            continue

        enc_objs, enc_triples, enc_tight_boxes = enc_objs.cuda(), enc_triples.cuda(), enc_tight_boxes.cuda()
        dec_objs, dec_triples, dec_tight_boxes = dec_objs.cuda(), dec_triples.cuda(), dec_tight_boxes.cuda()

        model = model.eval()

        all_pred_boxes = []

        enc_boxes = enc_tight_boxes[:, :6]
        enc_angles = enc_tight_boxes[:, 6].long() - 1
        enc_angles = torch.where(enc_angles > 0, enc_angles, torch.zeros_like(enc_angles))
        enc_angles = torch.where(enc_angles < 24, enc_angles, torch.zeros_like(enc_angles))

        attributes = None

        with torch.no_grad():
            (z_box, _), (z_shape, _) = model.encode_box_and_shape(enc_objs, enc_triples, encoded_enc_points, enc_boxes,
                                                                  enc_angles, attributes)

            if args.manipulate:
                boxes_pred, points_pred, keep = model.decoder_with_changes_boxes_and_shape(z_box, z_shape, dec_objs,
                                                                                dec_triples, attributes, missing_nodes, manipulated_nodes, atlas)
                if with_angles:
                    boxes_pred, angles_pred = boxes_pred
            else:
                boxes_pred, angles_pred, points_pred, keep = model.decoder_with_additions_boxes_and_shape(z_box, z_shape,
                                                                                dec_objs, dec_triples, attributes, missing_nodes,  manipulated_nodes, atlas)
                if with_angles and angles_pred is None:
                    boxes_pred, angles_pred = boxes_pred

            if with_diversity:
                # Run multiple times to obtain diversity
                # Only when a node was added or manipulated we run the diversity computation
                if len(missing_nodes) > 0 or len(manipulated_nodes) > 0:
                    # Diversity results for this dataset sample
                    boxes_diversity_sample, shapes_sample, angle_diversity_sample, diversity_retrieval_ids_sample = [], [], [], []

                    for sample in range(num_samples):
                        # Generated changes
                        diversity_angles = None
                        if args.manipulate:
                            diversity_boxes, diversity_points, diversity_keep = model.decoder_with_changes_boxes_and_shape(
                                z_box, z_shape, dec_objs, dec_triples, attributes, missing_nodes, manipulated_nodes,
                                atlas)
                        else:
                            diversity_boxes, diversity_angles, diversity_points, diversity_keep = model.decoder_with_additions_boxes_and_shape(
                                z_box, z_shape, dec_objs, dec_triples, attributes, missing_nodes, manipulated_nodes,
                                atlas)

                        if with_angles and diversity_angles is None:
                            diversity_boxes, diversity_angles = diversity_boxes

                        if model.type_ == 'sln':
                            dec_objs_filtered = dec_objs[diversity_keep[:,0] == 0]
                            diversity_boxes_filtered = diversity_boxes[diversity_keep[:,0] == 0]
                            dec_objs_filtered = dec_objs_filtered.reshape((-1, 1))
                            diversity_boxes = diversity_boxes.reshape((-1, 6))
                            diversity_points_retrieved, diversity_retrieval_ids_ivd = retrieval.rio_retrieve(
                                dec_objs_filtered, diversity_boxes_filtered, testdataloader.dataset.vocab, cat2objs,
                                testdataloader.dataset.root_3rscan, skip_scene_node=False, return_retrieval_id=True)
                            diversity_points = torch.zeros((len(dec_objs), 1024, 3))
                            diversity_points[diversity_keep[:,0] == 0] = diversity_points_retrieved
                            diversity_retrieval_ids = [''] * len(dec_objs)
                            diversity_retrieval_ids_ivd = diversity_retrieval_ids_ivd.tolist()
                            for i in range(len(dec_objs)):
                                if diversity_keep[i, 0].cpu().numpy() == 0:
                                    diversity_retrieval_ids[i] = diversity_retrieval_ids_ivd.pop(0)
                            diversity_retrieval_ids = np.asarray(diversity_retrieval_ids, dtype=np.str_)

                        # Computing shape diversity on canonical and normalized shapes
                        normalized_points = []
                        filtered_diversity_retrieval_ids = []
                        for ins_id, obj_id in enumerate(dec_objs):
                            if obj_id != 0 and obj_id in testdataloader.dataset.point_classes_idx:
                                # We only care for manipulated nodes
                                if diversity_keep[ins_id, 0] == 1:
                                    continue
                                points = diversity_points[ins_id]
                                if type(points) is torch.Tensor:
                                    points = points.cpu().numpy()
                                if points is None:
                                    continue
                                # Normalizing shapes
                                points = torch.from_numpy(normalize(points))
                                if torch.cuda.is_available():
                                    points = points.cuda()
                                normalized_points.append(points)
                                if model.type_ == 'sln':
                                    filtered_diversity_retrieval_ids.append(diversity_retrieval_ids[ins_id])

                        # We use keep to filter changed nodes
                        boxes_diversity_sample.append(diversity_boxes[diversity_keep[:, 0] == 0])

                        if with_angles:
                            # We use keep to filter changed nodes
                            angle_diversity_sample.append(np.expand_dims(np.argmax(diversity_angles[diversity_keep[:, 0] == 0].cpu().numpy(), 1), 1) / 24. * 360.)

                        if len(normalized_points) > 0:
                            shapes_sample.append(torch.stack(normalized_points)) # keep has already been applied for points
                            if model.type_ == 'sln':
                                diversity_retrieval_ids_sample.append(np.stack(filtered_diversity_retrieval_ids)) # keep has already been applied for points
                    # Compute standard deviation for box for this sample
                    if len(boxes_diversity_sample) > 0:
                        boxes_diversity_sample = torch.stack(boxes_diversity_sample, 1)
                        bs = boxes_diversity_sample.shape[0]
                        if model.type_ != 'sln':
                            boxes_diversity_sample = batch_torch_denormalize_box_params(boxes_diversity_sample.reshape([-1, 6])).reshape([bs, -1, 6])
                        all_diversity_boxes += torch.std(boxes_diversity_sample, dim=1).cpu().numpy().tolist()

                    # Compute standard deviation for angle for this sample
                    if len(angle_diversity_sample) > 0:
                        angle_diversity_sample = np.stack(angle_diversity_sample, 1)
                        all_diversity_angles += [estimate_angular_std(d[:,0]) for d in angle_diversity_sample]

                    # Compute chamfer distances for shapes for this sample
                    if len(shapes_sample) > 0:
                        if len(diversity_retrieval_ids_sample) > 0:
                            diversity_retrieval_ids_sample = np.stack(diversity_retrieval_ids_sample, 1)

                        shapes_sample = torch.stack(shapes_sample, 1)
                        for shapes_id in range(len(shapes_sample)):
                            # Taking a single predicted shape
                            shapes = shapes_sample[shapes_id]
                            if len(diversity_retrieval_ids_sample) > 0:
                                # To avoid that retrieval the object ids like 0,1,0,1,0 gives high error
                                # We sort them to measure how often different objects are retrieved 0,0,0,1,1
                                diversity_retrieval_ids = diversity_retrieval_ids_sample[shapes_id]
                                sorted_idx = diversity_retrieval_ids.argsort()
                                shapes = shapes[sorted_idx]
                            sequence_diversity = []
                            # Iterating through its multiple runs
                            for shape_sequence_id in range(len(shapes) - 1):
                                # Compute chamfer with the next shape in its sequences
                                dist1, dist2 = chamfer(shapes[shape_sequence_id:shape_sequence_id + 1].float(),
                                                       shapes[shape_sequence_id + 1:shape_sequence_id + 2].float())
                                chamfer_dist = torch.mean(dist1) + torch.mean(dist2)
                                # Save the distance
                                sequence_diversity += [chamfer_dist.cpu().numpy().tolist()]
                            all_diversity_chamfer.append(np.mean(sequence_diversity))
        bp = []
        for i in range(len(keep)):
            if keep[i] == 0:
                bp.append(boxes_pred[i].cpu().detach())
            else:
                bp.append(dec_tight_boxes[i,:6].cpu().detach())

        all_pred_boxes.append(boxes_pred.cpu().detach())

        # compute relationship constraints accuracy through simple geometric rules
        accuracy = validate_constrains_changes(dec_triples, boxes_pred, dec_tight_boxes, keep, model.vocab, accuracy,
                                               with_norm=model.type_ != 'sln')
        accuracy_in_orig_graph = validate_constrains_changes(dec_triples, torch.stack(bp, 0), dec_tight_boxes, keep,
                                                             model.vocab, accuracy_in_orig_graph, with_norm=model.type_ != 'sln')
        accuracy_unchanged = validate_constrains(dec_triples, boxes_pred, dec_tight_boxes, keep, model.vocab,
                                                 accuracy_unchanged, with_norm=model.type_ != 'sln')

    if with_diversity:
        print("DIVERSITY:")
        print("\tShape (Avg. Chamfer Distance) = %f" % (np.mean(all_diversity_chamfer)))
        print("\tBox (Std. metric size and location) = %f, %f" % (
            np.mean(np.mean(all_diversity_boxes, axis=0)[:3]),
            np.mean(np.mean(all_diversity_boxes, axis=0)[3:])))
        print("\tAngle (Std.) %s = %f" % (k, np.mean(all_diversity_angles)))

    keys = list(accuracy.keys())
    for dic, typ in [(accuracy, "changed nodes"), (accuracy_unchanged, 'unchanged nodes'),
                     (accuracy_in_orig_graph, 'changed nodes placed in original graph')]:
        # NOTE 'changed nodes placed in original graph' are the results reported in the paper!
        # The unchanged nodes are kept from the original scene, and the accuracy in the new nodes is computed with
        # respect to these original nodes
        print('{} & {:.2f} & {:.2f} & {:.2f} & {:.2f} &{:.2f} & {:.2f}'.format(typ, np.mean([np.mean(dic[keys[0]]), np.mean(dic[keys[1]])]),
                                                                               np.mean([np.mean(dic[keys[2]]), np.mean(dic[keys[3]])]), np.mean([np.mean(dic[keys[4]]), np.mean(dic[keys[5]])]),
                                                                               np.mean([np.mean(dic[keys[6]]), np.mean(dic[keys[7]])]), np.mean(dic[keys[8]]), np.mean(dic[keys[9]])))
        print('means of mean: {:.2f}'.format(np.mean([np.mean([np.mean(dic[keys[0]]), np.mean(dic[keys[1]])]),
                                                      np.mean([np.mean(dic[keys[2]]), np.mean(dic[keys[3]])]), np.mean([np.mean(dic[keys[4]]), np.mean(dic[keys[5]])]),
                                                      np.mean([np.mean(dic[keys[6]]), np.mean(dic[keys[7]])]), np.mean(dic[keys[8]])])))

def validate_constrains_loop(testdataloader, model, with_diversity=True, with_angles=False, vocab=None,
                             point_classes_idx=None, point_ae=None, export_3d=False, cat2objs=None, datasize='large',
                             num_samples=10):

    if with_diversity and num_samples < 2:
        raise ValueError('Diversity requires at least two runs (i.e. num_samples > 1).')

    accuracy = {}
    for k in ['left', 'right', 'front', 'behind', 'smaller', 'bigger', 'lower', 'higher', 'same', 'total']:
        # compute validation for these relation categories
        accuracy[k] = []

    all_diversity_boxes = []
    all_diversity_angles = []
    all_diversity_chamfer = []

    all_pred_shapes_exp = {} # for export
    all_pred_boxes_exp = {}

    for i, data in enumerate(testdataloader, 0):
        try:
            dec_objs, dec_triples = data['decoder']['objs'], data['decoder']['tripltes']
            instances = data['instance_id'][0]
            scan = data['scan_id'][0]
            split = data['split_id'][0]

        except Exception as e:
            continue

        dec_objs, dec_triples = dec_objs.cuda(), dec_triples.cuda()

        all_pred_boxes = []

        with torch.no_grad():
            boxes_pred, shapes_pred = model.sample_box_and_shape(point_classes_idx, point_ae, dec_objs, dec_triples, attributes=None)
            if with_angles:
                boxes_pred, angles_pred = boxes_pred
                angles_pred = torch.argmax(angles_pred, dim=1, keepdim=True) * 15.0
            else:
                angles_pred = None

            if model.type_ != 'sln':
                shapes_pred, shape_enc_pred = shapes_pred

        if model.type_ != 'sln':
            boxes_pred_den = batch_torch_denormalize_box_params(boxes_pred)
        else:
            boxes_pred_den = boxes_pred

        if export_3d:
            if with_angles:
                boxes_pred_exp = torch.cat([boxes_pred_den.float(),
                                            angles_pred.view(-1,1).float()], 1).detach().cpu().numpy().tolist()
            else:
                boxes_pred_exp = boxes_pred_den.detach().cpu().numpy().tolist()
            if model.type_ != 'sln':
                # save point encodings
                shapes_pred_exp = shape_enc_pred.detach().cpu().numpy().tolist()
            else:
                # 3d-sln baseline does not generate shapes
                # save object labels to use for retrieval instead
                shapes_pred_exp = dec_objs.view(-1,1).detach().cpu().numpy().tolist()
            for i in range(len(shapes_pred_exp)):
                if dec_objs[i] not in testdataloader.dataset.point_classes_idx:
                    shapes_pred_exp[i] = []
            shapes_pred_exp = list(shapes_pred_exp)

            if scan not in all_pred_shapes_exp:
                all_pred_boxes_exp[scan] = {}
                all_pred_shapes_exp[scan] = {}
            if split not in all_pred_shapes_exp[scan]:
                all_pred_boxes_exp[scan][split] = {}
                all_pred_shapes_exp[scan][split] = {}

            all_pred_boxes_exp[scan][split]['objs'] = list(instances)
            all_pred_shapes_exp[scan][split]['objs'] = list(instances)
            for i in range(len(dec_objs) - 1):
                all_pred_boxes_exp[scan][split][instances[i]] = list(boxes_pred_exp[i])
                all_pred_shapes_exp[scan][split][instances[i]] = list(shapes_pred_exp[i])

        if args.visualize:
            # scene graph visualization. saves a picture of each graph to the outfolder
            colormap = vis_graph(use_sampled_graphs=False, scan_id=scan, split=str(split), data_path=args.dataset,
                                 outfolder=args.exp + "/vis_graphs/")
            colors = []
            # convert colors to expected format
            def hex_to_rgb(hex):
                hex = hex.lstrip('#')
                hlen = len(hex)
                return tuple(int(hex[i:i+hlen//3], 16) for i in range(0, hlen, hlen//3))
            for i in instances:
                h = colormap[str(i)]
                rgb = hex_to_rgb(h)
                colors.append(rgb)
            colors = np.asarray(colors) / 255.

            # layout and shape visualization through open3d
            render(boxes_pred_den, angles_pred, classes=vocab['object_idx_to_name'], render_type='points', classed_idx=dec_objs,
                   shapes_pred=shapes_pred.cpu().detach(), colors=colors, render_boxes=True)

        all_pred_boxes.append(boxes_pred_den.cpu().detach())
        if with_diversity:

            # Run multiple times to obtain diversities
            # Diversity results for this dataset sample
            boxes_diversity_sample, shapes_sample, angle_diversity_sample, diversity_retrieval_ids_sample = [], [], [], []
            for sample in range(num_samples):
                diversity_boxes, diversity_points = model.sample_box_and_shape(point_classes_idx, point_ae, dec_objs, dec_triples,
                                                                               attributes=None)
                if with_angles:
                    diversity_boxes, diversity_angles = diversity_boxes
                if model.type_ == 'sln':
                    diversity_points, diversity_retrieval_ids = retrieval.rio_retrieve(
                        dec_objs, diversity_boxes, vocab, cat2objs, testdataloader.dataset.root_3rscan,
                        return_retrieval_id=True)
                else:
                    diversity_points = diversity_points[0]

                # Computing shape diversity on canonical and normalized shapes
                normalized_points = []
                filtered_diversity_retrieval_ids = []
                for ins_id, obj_id in enumerate(dec_objs):
                    if obj_id != 0 and obj_id in testdataloader.dataset.point_classes_idx:
                        points = diversity_points[ins_id]
                        if type(points) is torch.Tensor:
                            points = points.cpu().numpy()
                        if points is None:
                            continue
                        # Normalizing shapes
                        points = torch.from_numpy(normalize(points))
                        if torch.cuda.is_available():
                            points = points.cuda()
                        normalized_points.append(points)
                        if model.type_ == 'sln':
                            filtered_diversity_retrieval_ids.append(diversity_retrieval_ids[ins_id])

                # We use keep to filter changed nodes
                boxes_diversity_sample.append(diversity_boxes)

                if with_angles:
                    # We use keep to filter changed nodes
                    angle_diversity_sample.append(np.expand_dims(np.argmax(diversity_angles.cpu().numpy(), 1), 1) / 24. * 360.)

                if len(normalized_points) > 0:
                    shapes_sample.append(torch.stack(normalized_points)) # keep has already been aplied for points
                    if model.type_ == 'sln':
                        diversity_retrieval_ids_sample.append(np.stack(filtered_diversity_retrieval_ids))

            # Compute standard deviation for box for this sample
            if len(boxes_diversity_sample) > 0:
                boxes_diversity_sample = torch.stack(boxes_diversity_sample, 1)
                bs = boxes_diversity_sample.shape[0]
                if model.type_ != 'sln':
                    boxes_diversity_sample = batch_torch_denormalize_box_params(boxes_diversity_sample.reshape([-1, 6])).reshape([bs, -1, 6])
                all_diversity_boxes += torch.std(boxes_diversity_sample, dim=1).cpu().numpy().tolist()

                # Compute standard deviation for angle for this sample
            if len(angle_diversity_sample) > 0:
                angle_diversity_sample = np.stack(angle_diversity_sample, 1)
                all_diversity_angles += [estimate_angular_std(d[:,0]) for d in angle_diversity_sample]

                # Compute chamfer distances for shapes for this sample
            if len(shapes_sample) > 0:
                shapes_sample = torch.stack(shapes_sample, 1)
                for shapes_id in range(len(shapes_sample)):
                    # Taking a single predicted shape
                    shapes = shapes_sample[shapes_id]
                    if len(diversity_retrieval_ids_sample) > 0:
                        # To avoid that retrieval the object ids like 0,1,0,1,0 gives high error
                        # We sort them to measure how often different objects are retrieved 0,0,0,1,1
                        diversity_retrieval_ids = diversity_retrieval_ids_sample[shapes_id]
                        sorted_idx = diversity_retrieval_ids.argsort()
                        shapes = shapes[sorted_idx]
                    sequence_diversity = []
                    # Iterating through its multiple runs
                    for shape_sequence_id in range(len(shapes) - 1):
                        # Compute chamfer with the next shape in its sequences
                        dist1, dist2 = chamfer(shapes[shape_sequence_id:shape_sequence_id + 1].float(),
                                               shapes[shape_sequence_id + 1:shape_sequence_id + 2].float())
                        chamfer_dist = torch.mean(dist1) + torch.mean(dist2)
                        # Save the distance
                        sequence_diversity += [chamfer_dist.cpu().numpy().tolist()]

                    if len(sequence_diversity) > 0:  # check if sequence has shapes
                        all_diversity_chamfer.append(np.mean(sequence_diversity))

        # compute constraints accuracy through simple geometric rules
        accuracy = validate_constrains(dec_triples, boxes_pred, None, None, model.vocab, accuracy, with_norm=model.type_ != 'sln')

    if export_3d:
        # export box and shape predictions for future evaluation
        result_path = os.path.join(args.exp, 'results')
        if not os.path.exists(result_path):
            # Create a new directory for results
            os.makedirs(result_path)
        shape_filename = os.path.join(result_path, 'shapes_' + ('large' if datasize else 'small') + '.json')
        box_filename = os.path.join(result_path, 'boxes_' + ('large' if datasize else 'small') + '.json')
        json.dump(all_pred_boxes_exp, open(box_filename, 'w')) # 'dis_nomani_boxes_large.json'
        json.dump(all_pred_shapes_exp, open(shape_filename, 'w'))

    if with_diversity:
        print("DIVERSITY:")
        print("\tShape (Avg. Chamfer Distance) = %f" % (np.mean(all_diversity_chamfer)))
        print("\tBox (Std. metric size and location) = %f, %f" % (
            np.mean(np.mean(all_diversity_boxes, axis=0)[:3]),
            np.mean(np.mean(all_diversity_boxes, axis=0)[3:])))
        print("\tAngle (Std.) %s = %f" % (k, np.mean(all_diversity_angles)))

    keys = list(accuracy.keys())
    for dic, typ in [(accuracy, "acc")]:

        print('{} & {:.2f} & {:.2f} & {:.2f} & {:.2f} &{:.2f} & {:.2f}'.format(typ, np.mean([np.mean(dic[keys[0]]), np.mean(dic[keys[1]])]),
                                                                               np.mean([np.mean(dic[keys[2]]), np.mean(dic[keys[3]])]), np.mean([np.mean(dic[keys[4]]), np.mean(dic[keys[5]])]),
                                                                               np.mean([np.mean(dic[keys[6]]), np.mean(dic[keys[7]])]), np.mean(dic[keys[8]]), np.mean(dic[keys[9]])))
        print('means of mean: {:.2f}'.format(np.mean([np.mean([np.mean(dic[keys[0]]), np.mean(dic[keys[1]])]),
                                                      np.mean([np.mean(dic[keys[2]]), np.mean(dic[keys[3]])]), np.mean([np.mean(dic[keys[4]]), np.mean(dic[keys[5]])]),
                                                      np.mean([np.mean(dic[keys[6]]), np.mean(dic[keys[7]])]), np.mean(dic[keys[8]])])))

def normalize(vertices, scale=1):
    xmin, xmax = np.amin(vertices[:, 0]), np.amax(vertices[:, 0])
    ymin, ymax = np.amin(vertices[:, 1]), np.amax(vertices[:, 1])
    zmin, zmax = np.amin(vertices[:, 2]), np.amax(vertices[:, 2])

    vertices[:, 0] += -xmin - (xmax - xmin) * 0.5
    vertices[:, 1] += -ymin - (ymax - ymin) * 0.5
    vertices[:, 2] += -zmin - (zmax - zmin) * 0.5

    scalars = np.max(vertices, axis=0)
    scale = scale

    vertices = vertices / scalars * scale
    return vertices

if __name__ == "__main__": evaluate()


#!/usr/bin/env python

import os
import sys
import numpy as np
import random
from scipy.stats import binom
from scipy import spatial
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import connected_components

try:
    from scripts.bedpe import *
except ImportError:
    from bedpe import *

try:
    from scripts.my_utils import *
except ImportError:
    from my_utils import *

tab  = '\t'
endl = '\n'

class AlignedRead:

    def __init__(self, sam_line):

        line = sam_line.strip().split(tab)
        self.readname = line[0]
        self.flag = int(line[1])
        self.chrm = line[2]
        self.left_pos = int(line[3])
        self.right_pos = None
        self.mapq = int(line[4])
        self.cigar_string = line[5]
        self.right_pos = None
        self.cigar_operation_list = None
        self.cigar_operation_length_list = None
        self.n_cigar = None
        self.endR_pos = None
        self.cigar_analysis()
        self.get_right_ref_pos()
        self.get_endR_pos()

        self.bcd = ''
        self.hp = 0
        for i in range(11, len(line)):
            a = line[i].split(':')
            if a[0] == 'BX': self.bcd = a[2]
            if a[0] == 'HP': self.hp = int(a[2])

    def output_core(self):
        outstring = '%s\t%d\t%s\t%d\t%s\t%d' % (self.readname, self.flag, self.chrm, self.left_pos, self.cigar_string, self.n_cigar)
        return outstring

    def map_orientation(self):

        if self.flag & 0x10:
            return -1
        else:
            return 1

    def cigar_analysis(self):
        p1 = p2 = 0
        number_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
        n_cigar = 0
        self.cigar_operation_list = list()
        self.cigar_operation_length_list = list()
        for i in range(0, len(self.cigar_string)):
            c = self.cigar_string[i]
            if c in number_list: continue
            p2 = i
            cigar_operation = self.cigar_string[p2]
            self.cigar_operation_list.append(cigar_operation)
            cigar_operation_length = int(self.cigar_string[p1:p2])
            self.cigar_operation_length_list.append(cigar_operation_length)
            p1 = p2 + 1
            p2 = p2 + 1

        self.n_cigar = len(self.cigar_operation_list)

    def get_right_ref_pos(self):
        if self.cigar_operation_list == None:
            self.cigar_analysis()

        ref_align_length = 0
        affect_ref_align_cigar = ['M', 'D', 'N', '=', 'X']
        for i in range(0, self.n_cigar):
            if self.cigar_operation_list[i] in affect_ref_align_cigar:
                ref_align_length += self.cigar_operation_length_list[i]

        self.right_pos = self.left_pos + ref_align_length - 1

    def has_left_clip(self):

        if self.n_cigar == 0 or self.n_cigar == None:
            return False

        if self.cigar_operation_list[0] == 'S' or self.cigar_operation_list[0] == 'H':
            return True
        else:
            return False

    def has_right_clip(self):
        if self.n_cigar == 0 or self.n_cigar == None:
            return False

        if self.cigar_operation_list[-1] == 'S' or self.cigar_operation_list[-1] == 'H':
            return True
        else:
            return False

    def has_clip(self):
        return (self.has_left_clip() or self.has_right_clip())

    def get_endR_pos(self):
        if self.flag & 0x10:
            self.endR_pos = self.left_pos
        else:
            self.endR_pos = self.right_pos

    def major_clip_side(self):

        if self.has_left_clip() == True and self.has_right_clip() == False:
            return 'left'
        elif self.has_left_clip() == False and self.has_right_clip() == True:
            return 'right'
        else:
            left_clip_length = self.cigar_operation_length_list[0]
            right_clip_length = self.cigar_operation_length_list[-1]
            if left_clip_length > right_clip_length:
                return 'left'
            elif left_clip_length < right_clip_length:
                return 'right'
            else:
                randnumber = random.randint(1,2)
                if randnumber == 1:
                    return 'left'
                else:
                    return 'right'

class PairedEndSupport:

    def __init__(self, aligned_read1 = None, aligned_read2 = None):

        self.aligned_read1 = None
        self.aligned_read2 = None
        self.endtype1 = None
        self.endtype2 = None

        if aligned_read1 != None:
            self.aligned_read1 = aligned_read1
            self.aligned_read2 = aligned_read2

            if self.aligned_read1.map_orientation() == 1:
                self.endtype1 = 'R_end'
            else:
                self.endtype1 = 'L_end'

            if self.aligned_read2.map_orientation() == 1:
                self.endtype2 = 'R_end'
            else:
                self.endtype2 = 'L_end'

class SplitReadSupport:

    def __init__(self, aligned_read1 = None, aligned_read2 = None):

        self.aligned_read1 = None
        self.aligned_read2 = None
        self.endtype1 = None
        self.endtype2 = None
        self.major_clip_pos1 = None
        self.major_clip_pos2 = None

        if aligned_read1 != None:
            self.aligned_read1 = aligned_read1
            self.aligned_read2 = aligned_read2

            if self.aligned_read1.major_clip_side() == 'left':
                self.endtype1 = 'L_end'
            else:
                self.endtype1 = 'R_end'

            if self.aligned_read2.major_clip_side() == 'left':
                self.endtype2 = 'L_end'
            else:
                self.endtype2 = 'R_end'

            if self.aligned_read1.major_clip_side() == 'left':
                self.major_clip_pos1 = self.aligned_read1.left_pos
            else:
                self.major_clip_pos1 = self.aligned_read1.right_pos

            if self.aligned_read2.major_clip_side() == 'left':
                self.major_clip_pos2 = self.aligned_read2.left_pos
            else:
                self.major_clip_pos2 = self.aligned_read2.right_pos

class PESupportGroup:

    def __init__(self, pe_support_list = None, endtype1 = None, endtype2 = None, median_readpair_gap_distance = None):
        self.pe_support_list = list()
        self.n_pe_support    = 0
        self.endtype1 = None
        self.endtype2 = None
        self.bk1_pos = -1
        self.bk2_pos = -1
        self.median_endR_pos1 = -1
        self.median_endR_pos2 = -1
        self.map_orientation1  = 0
        self.map_orientation2  = 0

        if pe_support_list != None and median_readpair_gap_distance != None:
            self.pe_support_list = pe_support_list # list of objects of class PairedEndSupport
            self.n_pe_support = len(self.pe_support_list)
            self.endtype1 = endtype1
            self.endtype2 = endtype1
            self.map_orientation1 = self.pe_support_list[0].aligned_read1.map_orientation()
            self.map_orientation2 = self.pe_support_list[0].aligned_read2.map_orientation()
            self.predict_bk_pos(median_readpair_gap_distance)

    def predict_bk_pos(self, median_readpair_gap_distance):
        if len(self.pe_support_list) < 1: return
        endR_pos_list1 = list()
        endR_pos_list2 = list()
        for pe_support in self.pe_support_list:
            endR_pos_list1.append(pe_support.aligned_read1.endR_pos)
            endR_pos_list2.append(pe_support.aligned_read2.endR_pos)

        self.median_endR_pos1 = np.median(endR_pos_list1)
        self.median_endR_pos2 = np.median(endR_pos_list2)

        self.bk1_pos = self.median_endR_pos1 + self.map_orientation1 * median_readpair_gap_distance / 2
        self.bk2_pos = self.median_endR_pos2 + self.map_orientation2 * median_readpair_gap_distance / 2
        return

class SRSupportGroup:

    def __init__(self, sr_support_list = None, endtype1 = None, endtype2 = None):

        self.sr_support_list = list()
        self.n_sr_support = 0
        self.endtype1 = None
        self.endtype2 = None
        self.bk1_pos = -1
        self.bk2_pos = -1

        if sr_support_list != None:
            self.sr_support_list = sr_support_list
            self.n_sr_support = len(self.sr_support_list)
            self.endtype1 = endtype1
            self.endtype2 = endtype2
            self.bk1_pos = None
            self.bk2_pos = None
            self.predict_bk_pos()

    def predict_bk_pos(self):

        major_clip_pos_list1 = list()
        major_clip_pos_list2 = list()
        for sr_support in self.sr_support_list:
            major_clip_pos_list1.append(sr_support.major_clip_pos1)
            major_clip_pos_list2.append(sr_support.major_clip_pos2)

        self.bk1_pos = np.median(major_clip_pos_list1)
        self.bk2_pos = np.median(major_clip_pos_list2)

def main():

    args, dbo_args, endpoint_args = parse_user_arguments()

    import global_distribution
    myprint('estimation global parameters')
    global_distribution.estimate_global_distribution(args, dbo_args, endpoint_args)

    refine_breakpoints(args, dbo_args, endpoint_args)

    return

def get_isize_distribution(args):

    out_dir = os.path.join(args.out_dir, 'refinebreakpoints')

    os.system('mkdir -p %s' % out_dir)
    tmp_file = os.path.join(out_dir, 'isize.txt')
    tmp_sh_file = os.path.join(out_dir, 'tmp.sh')
    tmp_sh_fp = open(tmp_sh_file, 'w')
    tmp_sh_fp.write('#!/bin/bash\n')
    cmd = '%s view %s | cut -f 9 | head -1000000 > %s\n' % (args.samtools, args.bam, tmp_file)
    tmp_sh_fp.write(cmd)
    tmp_sh_fp.close()

    os.system('sh %s ' % tmp_sh_file)
    tmp_fp = open(tmp_file, 'r')
    isize_list = list()
    while 1:
        line = tmp_fp.readline()
        if not line: break
        isize = int(line.strip())
        if isize > 0: isize_list.append(isize)
    tmp_fp.close()

    percentiles = [0, 25, 50, 75, 100]
    q = np.percentile(isize_list, percentiles)
    args.median_isize = int(q[2])
    args.max_isize_cutoff = int(q[3] + 1.5 * (q[3] - q[1]))
    myprint('median insert size is: %d, isize cut-off is: %d' % (args.median_isize, args.max_isize_cutoff))
    os.system('rm -rf %s' % tmp_file)

    return

def get_read_length(args):

    out_dir = os.path.join(args.out_dir, 'refinebreakpoints')

    tmp_sh_file = os.path.join(out_dir, 'tmp.sh')
    tmp_sh_fp = open(tmp_sh_file, 'w')
    tmp_sh_fp.write('#!/bin/bash\n')
    tmp_file = os.path.join(out_dir, 'tmp.sam')
    cmd = '%s view %s | head -10000 > %s\n' % (args.samtools, args.bam, tmp_file)
    tmp_sh_fp.write(cmd)
    tmp_sh_fp.close()
    os.system('sh %s ' % tmp_sh_file)

    tmp_fp = open(tmp_file, 'r')

    rd_length_list1 = list()
    rd_length_list2 = list()

    while 1:
        line = tmp_fp.readline()
        if not line: break
        line = line.strip().split(tab)
        flag = int(line[1])
        read_length = len(line[9])
        if flag & 0x40: rd_length_list1.append(read_length)
        if flag & 0x80: rd_length_list2.append(read_length)
    tmp_fp.close()

    args.rd1_length = int(np.median(rd_length_list1))
    args.rd2_length = int(np.median(rd_length_list2))

    myprint('read1 length is %d, read2 length is %d' % (args.rd1_length, args.rd2_length))
    os.system('rm -rf %s' % tmp_file)

    return

def refine_breakpoints(args, dbo_args, endpoint_args):

    myprint('refine breakpoints')
    in_bam = args.bam
    in_bam_prefix = os.path.splitext(in_bam)[0]

    bai_file1 = in_bam_prefix + '.bai'
    bai_file2 = in_bam_prefix + '.bam.bai'

    if os.path.isfile(bai_file1) == False and os.path.isfile(bai_file2) == False:
        myprint('bam index file not found, indexing using samtools')
        cmd = args.samtools + ' index ' + in_bam
        os.system(cmd)

    get_isize_distribution(args)
    get_read_length(args)

    svcall_bedpe_list = read_object_file(args.quantified_bk_pair_file, QuantifiedBKCand)

    myprint ('number of SV calls: %d' % len(svcall_bedpe_list))

    refined_svcall_list = list()
    out_file = args.refinedbedpe_file
    out_fp = open(out_file, 'w')
    for svcall_bedpe in svcall_bedpe_list:
        if svcall_bedpe.score > 0 and svcall_bedpe.logp_nosv_two_mol > svcall_bedpe.logp_nosv_one_mol:
            refined_svcall = refine1sv(args, dbo_args, endpoint_args, svcall_bedpe)
            refined_svcall_list.append(refined_svcall)
            out_fp.write(refined_svcall.output() + endl)

    out_fp.close()

    return

def get_refined_sv_call(args, svcall_bedpe, pe_support_group, sr_support_group):

    n_pe_support = pe_support_group.n_pe_support
    n_sr_support = sr_support_group.n_sr_support
    n_support_reads = n_pe_support + n_sr_support
    n_support_fragment = svcall_bedpe.num_fragment_support

    refined_bk1 = -1
    refined_bk2 = -1

    if sr_support_group.bk1_pos != None and sr_support_group.bk2_pos != None:
        refined_bk1 = sr_support_group.bk1_pos
        refined_bk2 = sr_support_group.bk2_pos
    elif pe_support_group.bk1_pos != None and pe_support_group.bk2_pos != None:
        refined_bk1 = pe_support_group.bk1_pos
        refined_bk2 = pe_support_group.bk2_pos

    median_isize = args.median_isize
    median_inter_readpair_gap_distance = args.gap_distance750
    p0 = float(median_isize/median_inter_readpair_gap_distance)

    if n_support_reads > n_support_fragment:
        logp = 0
    else:
        logp = binom.logcdf(n_support_reads, n_support_fragment, p0) / np.log(10)

    refine_score = logp

    attr_list = svcall_bedpe.attr_list() + [refine_score, n_pe_support, n_sr_support, n_support_reads, refined_bk1, refined_bk2]

    refinded_sv_call = RefinedQuantBKCand(attr_list)

    return refinded_sv_call

def refine1sv(args, dbo_args, endpoint_args, svcall_bedpe):

    max_split_pos_variation = 50
    in_bam = args.bam
    search_range = args.gap_distance_cutoff

    endtype1 = svcall_bedpe.endtype1
    endtype2 = svcall_bedpe.endtype2
    support_barcode_set = set(svcall_bedpe.support_barcodes.strip(',').split(','))

    out_dir = os.path.join(args.out_dir, 'refinebreakpoints')

    os.system('mkdir -p %s' % out_dir)

    myprint('current sv call is: %s' % (svcall_bedpe.output_svcall()))
    search_start1 = svcall_bedpe.start1 - search_range
    search_end1   = svcall_bedpe.end1 + search_range

    search_start2 = svcall_bedpe.start2 - search_range
    search_end2   = svcall_bedpe.end2 + search_range

    bam_name  = os.path.split(in_bam)[1]

    region1_sam_file = os.path.join(out_dir, bam_name + '.%s_%d_%d.sam' % (svcall_bedpe.chrm1, search_start1, search_end1))
    cmd1 = '%s view %s %s:%d-%d > %s' % (args.samtools, in_bam, svcall_bedpe.chrm1, search_start1, search_end1, region1_sam_file)
    os.system(cmd1)

    region2_sam_file = os.path.join(out_dir, bam_name + '.%s_%d_%d.sam' % (svcall_bedpe.chrm2, search_start2, search_end2))
    cmd2 = '%s view %s %s:%d-%d > %s' % (args.samtools, in_bam, svcall_bedpe.chrm2, search_start2, search_end2, region2_sam_file)
    os.system(cmd2)

    region1_read_list = list()
    region1_sam_fp = open(region1_sam_file, 'r')
    while 1:
        line = region1_sam_fp.readline()
        if not line: break
        aligned_read = AlignedRead(line)
        if aligned_read.bcd not in support_barcode_set: continue
        region1_read_list.append(aligned_read)

    region1_sam_fp.close()

    region2_read_list = list()
    region2_sam_fp = open(region2_sam_file, 'r')
    while 1:
        line = region2_sam_fp.readline()
        if not line: break
        aligned_read = AlignedRead(line)
        if aligned_read.bcd not in support_barcode_set: continue
        region2_read_list.append(aligned_read)

    region2_sam_fp.close()

    myprint ('number of reads in region1 and region2: %d, %d' % (len(region1_read_list), len(region2_read_list)))

    pe_support_list = list()
    sr_support_list = list()

    for aligned_read1 in region1_read_list:
        for aligned_read2 in region2_read_list:
            if aligned_read1.readname != aligned_read2.readname: continue
            if aligned_read1.flag & 0x40 != aligned_read2.flag & 0x40:
                pe_support = PairedEndSupport(aligned_read1, aligned_read2)
                if pe_support.endtype1 == endtype1 and pe_support.endtype2 == endtype2: pe_support_list.append(pe_support)
            else:
                if not aligned_read1.has_clip() or not aligned_read2.has_clip(): continue
                sr_support = SplitReadSupport(aligned_read1, aligned_read2)
                if sr_support.endtype1 == endtype1 and sr_support.endtype2 == endtype2: sr_support_list.append(sr_support)

    pe_support_group = analyze_pe_support_list(args, pe_support_list, svcall_bedpe)
    sr_support_group = analyze_sr_support_list(sr_support_list, svcall_bedpe, max_split_pos_variation)

    refined_sv_call = get_refined_sv_call(args, svcall_bedpe, pe_support_group, sr_support_group)

    os.system('rm ' + region1_sam_file)
    os.system('rm ' + region2_sam_file)

    return refined_sv_call

def analyze_sr_support_list(sr_support_list, svcall_bedpe, max_split_pos_variation):

    if len(sr_support_list) == 0: return SRSupportGroup()

    myprint ('analysis of split-read support' )
    edge_list  = list()
    row = list()
    col = list()
    data = list()

    for i in range(0, len(sr_support_list)):
        for j in range(i+1, len(sr_support_list)):
            sr_support1 = sr_support_list[i]
            sr_support2 = sr_support_list[j]
            dist1 = abs(sr_support1.major_clip_pos1 - sr_support2.major_clip_pos1)
            dist2 = abs(sr_support1.major_clip_pos2 - sr_support2.major_clip_pos2)
            if dist1 < max_split_pos_variation and dist2 < max_split_pos_variation:
                graph_weight = dist1 + dist2
                edge = (i, j, graph_weight)
                edge_list.append(edge)

    for edge in edge_list:
        row.append (edge[0])
        col.append (edge[1])
        data.append (edge[2])

    n_node = len(sr_support_list)
    sr_support_csr_matrix = csr_matrix((data, (row, col)), shape=[n_node, n_node])
    n_components, label_list = connected_components(sr_support_csr_matrix, directed = False)
    component_element_db = [0] * n_components
    for i in range(0, len(component_element_db)):
        component_element_db[i] = list()
    # component_element_db[component_id] = index of pe_support
    for i in range(0, len(label_list)):
        component_element_db[label_list[i]].append(i)

    sr_support_group_list = list()
    for component_id in range(0, len(component_element_db)):
        sr_support_sublist = list()
        for index in component_element_db[component_id]:
            sr_support = sr_support_list[index]
            sr_support_sublist.append(sr_support)

        sr_support_group = SRSupportGroup(sr_support_sublist, svcall_bedpe.endtype1, svcall_bedpe.endtype2)
        sr_support_group_list.append(sr_support_group)

    if len(sr_support_group_list) == 0: return SRSupportGroup()

    max_group_id = 0
    for i in range(1, len(sr_support_group_list)):
        if sr_support_group_list[i].n_sr_support > sr_support_group_list[max_group_id]: max_group_id = i

    return sr_support_group_list[max_group_id]

def analyze_pe_support_list (args, pe_support_list, svcall_bedpe):

    if len(pe_support_list) == 0: return PESupportGroup()

    myprint ('analysis of PE support')
    max_isize_cutoff = args.max_isize_cutoff
    median_readpair_gap_distance = args.median_isize - (args.rd1_length + args.rd2_length)

    if median_readpair_gap_distance < 0: median_readpair_gap_distance = 0

    endtype1 = svcall_bedpe.endtype1
    endtype2 = svcall_bedpe.endtype2
    edge_list  = list()
    row = list()
    col = list()
    data = list()

    for i in range(0, len(pe_support_list)):
        for j in range(i+1, len(pe_support_list)):
            pe_support1 = pe_support_list[i]
            pe_support2 = pe_support_list[j]
            dist1 = abs(pe_support1.aligned_read1.endR_pos - pe_support2.aligned_read1.endR_pos)
            dist2 = abs(pe_support1.aligned_read2.endR_pos - pe_support2.aligned_read2.endR_pos)
            map_ori_match = False
            if pe_support1.aligned_read1.map_orientation() == pe_support2.aligned_read1.map_orientation() and pe_support1.aligned_read2.map_orientation() == pe_support2.aligned_read2.map_orientation(): map_ori_match = True
            if dist1 < max_isize_cutoff and dist2 < max_isize_cutoff and map_ori_match == True:
                graph_weight = dist1 + dist2
                edge = (i, j, graph_weight)
                edge_list.append(edge)

    for edge in edge_list:
        row.append (edge[0])
        col.append (edge[1])
        data.append (edge[2])

    n_node = len(pe_support_list)
    pe_support_csr_matrix = csr_matrix((data, (row, col)), shape=[n_node, n_node])
    n_components, label_list = connected_components(pe_support_csr_matrix, directed=False)
    component_element_db = [0] * n_components
    for i in range(0, len(component_element_db)):
        component_element_db[i] = list()
    # component_element_db[component_id] = index of pe_support
    for i in range(0, len(label_list)):
        component_element_db[label_list[i]].append(i)

    pe_support_group_list = list()
    for component_id in range(0, len(component_element_db)):
        pe_support_sublist = list()
        for index in component_element_db[component_id]:
            pe_support = pe_support_list[index]
            pe_support_sublist.append(pe_support)

        pe_support_group = PESupportGroup(pe_support_sublist, endtype1, endtype2, median_readpair_gap_distance)
        pe_support_group_list.append(pe_support_group)

    if len(pe_support_group_list) == 0: return PESupportGroup()

    max_group_id = 0
    for i in range(1, len(pe_support_group_list)):
        if pe_support_group_list[i].n_pe_support > pe_support_group_list[max_group_id]: max_group_id = i

    return pe_support_group_list[max_group_id]

if __name__ == '__main__':
    main()


# Copyright (c) 2018 NVIDIA Corporation
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals

import math
import os

import h5py
import numpy as np
import resampy as rs
import scipy.io.wavfile as wave
BACKENDS = []
try:
  import python_speech_features as psf
  BACKENDS.append('psf')
except ImportError:
  pass
try:
  import librosa
  BACKENDS.append('librosa')
except ImportError:
  pass

WINDOWS_FNS = {"hanning": np.hanning, "hamming": np.hamming, "none": None}

class PreprocessOnTheFlyException(Exception):
  """ Exception that is thrown to not load preprocessed features from disk;
  recompute on-the-fly.
  This saves disk space (if you're experimenting with data input
  formats/preprocessing) but can be slower.
  The slowdown is especially apparent for small, fast NNs."""
  pass

class RegenerateCacheException(Exception):
  """ Exception that is thrown to force recomputation of (preprocessed) features
  """
  pass

def load_features(path, data_format):
  """ Function to load (preprocessed) features from disk

  Args:
      :param path:    the path where the features are stored
      :param data_format:  the format in which the features are stored
      :return:        tuple of (features, duration)
      """
  if data_format == 'hdf5':
    with h5py.File(path + '.hdf5', "r") as hf5_file:
      features = hf5_file["features"][:]
      duration = hf5_file["features"].attrs["duration"]
  elif data_format == 'npy':
    features, duration = np.load(path + '.npy')
  elif data_format == 'npz':
    data = np.load(path + '.npz')
    features = data['features']
    duration = data['duration']
  else:
    raise ValueError("Invalid data format for caching: ", data_format, "!\n",
                     "options: hdf5, npy, npz")
  return features, duration

def save_features(features, duration, path, data_format, verbose=False):
  """ Function to save (preprocessed) features to disk

  Args:
      :param features:            features
      :param duration:            metadata: duration in seconds of audio file
      :param path:                path to store the data
      :param data_format:              format to store the data in ('npy',
      'npz',
      'hdf5')
  """
  if verbose: print("Saving to: ", path)

  if data_format == 'hdf5':
    with h5py.File(path + '.hdf5', "w") as hf5_file:
      dset = hf5_file.create_dataset("features", data=features)
      dset.attrs["duration"] = duration
  elif data_format == 'npy':
    np.save(path + '.npy', [features, duration])
  elif data_format == 'npz':
    np.savez(path + '.npz', features=features, duration=duration)
  else:
    raise ValueError("Invalid data format for caching: ", data_format, "!\n",
                     "options: hdf5, npy, npz")

def get_preprocessed_data_path(filename, params):
  """ Function to convert the audio path into the path to the preprocessed
  version of this audio
  Args:
      :param filename:    WAVE filename
      :param params:      dictionary containing preprocessing parameters
      :return:            path to new file (without extension). The path is
      generated from the relevant preprocessing parameters.
  """
  if isinstance(filename, bytes):  # convert binary string to normal string
    filename = filename.decode('ascii')

  filename = os.path.realpath(filename)  # decode symbolic links

  ## filter relevant parameters # TODO is there a cleaner way of doing this?
  # print(list(params.keys()))
  ignored_params = ["cache_features", "cache_format", "cache_regenerate",
                    "vocab_file", "dataset_files", "shuffle", "batch_size",
                    "max_duration",
                    "mode", "interactive", "autoregressive", "char2idx",
                    "tgt_vocab_size", "idx2char", "dtype"]

  def fix_kv(text):
    """ Helper function to shorten length of filenames to get around
    filesystem path length limitations"""
    text = str(text)
    text = text.replace("speed_perturbation_ratio", "sp") \
      .replace("noise_level_min", "nlmin", ) \
      .replace("noise_level_max", "nlmax") \
      .replace("add_derivatives", "d") \
      .replace("add_second_derivatives", "dd")
    return text

  # generate the identifier by simply concatenating preprocessing key-value
  # pairs as strings.
  preprocess_id = "-".join(
      [fix_kv(k) + "_" + fix_kv(v) for k, v in params.items() if
       k not in ignored_params])

  preprocessed_dir = os.path.dirname(filename).replace("wav",
                                                       "preprocessed-" +
                                                       preprocess_id)
  preprocessed_path = os.path.join(preprocessed_dir,
                                   os.path.basename(filename).replace(".wav",
                                                                      ""))

  # create dir if it doesn't exist yet
  if not os.path.exists(preprocessed_dir):
    os.makedirs(preprocessed_dir)

  return preprocessed_path

def get_speech_features_from_file(filename, params):
  """Function to get a numpy array of features, from an audio file.
      if params['cache_features']==True, try load preprocessed data from
      disk, or store after preprocesseng.
      else, perform preprocessing on-the-fly.

  Args:
    filename (string): WAVE filename.
    params (dict): the following parameters
      num_features (int): number of speech features in frequency domain.
      features_type (string): 'mfcc' or 'spectrogram'.
      window_size (float): size of analysis window in milli-seconds.
      window_stride (float): stride of analysis window in milli-seconds.
      augmentation (dict, optional): dictionary of augmentation parameters. See
        :func:`augment_audio_signal` for specification and example.
      window (str): window function to apply
      dither (float): weight of Gaussian noise to apply to input signal for
          dithering/preventing quantization noise
      num_fft (int): size of fft window to use if features require fft,
          defaults to smallest power of 2 larger than window size
      norm_per_feature (bool): if True, the output features will be normalized
          (whitened) individually. if False, a global mean/std over all features
          will be used for normalization
  Returns:
    np.array: np.array of audio features with shape=[num_time_steps,
    num_features].
  """
  cache_features = params.get('cache_features', False)
  cache_format = params.get('cache_format', 'hdf5')
  cache_regenerate = params.get('cache_regenerate', False)
  try:
    if not cache_features:
      raise PreprocessOnTheFlyException(
          "on-the-fly preprocessing enforced with 'cache_features'==True")

    if cache_regenerate:
      raise RegenerateCacheException("regenerating cache...")

    preprocessed_data_path = get_preprocessed_data_path(filename, params)
    features, duration = load_features(preprocessed_data_path,
                                       data_format=cache_format)

  except PreprocessOnTheFlyException:
    sample_freq, signal = wave.read(filename)
    features, duration = get_speech_features(signal, sample_freq, params)

  except (OSError, FileNotFoundError, RegenerateCacheException):
    sample_freq, signal = wave.read(filename)
    features, duration = get_speech_features(signal, sample_freq, params)

    preprocessed_data_path = get_preprocessed_data_path(filename, params)
    save_features(features, duration, preprocessed_data_path,
                  data_format=cache_format)

  return features, duration

def normalize_signal(signal):
  """
  Normalize float32 signal to [-1, 1] range
  """
  return signal / (np.max(np.abs(signal)) + 1e-5)

def augment_audio_signal(signal, sample_freq, augmentation):
  """Function that performs audio signal augmentation.

  Args:
    signal (np.array): np.array containing raw audio signal.
    sample_freq (float): frames per second.
    augmentation (dict, optional): None or dictionary of augmentation parameters.
        If not None, has to have 'speed_perturbation_ratio',
        'noise_level_min', or 'noise_level_max' fields, e.g.::
          augmentation={
            'speed_perturbation_ratio': 0.2,
            'noise_level_min': -90,
            'noise_level_max': -46,
          }
        'speed_perturbation_ratio' can either be a list of possible speed
        perturbation factors or a float. If float, a random value from
        U[1-speed_perturbation_ratio, 1+speed_perturbation_ratio].
  Returns:
    np.array: np.array with augmented audio signal.
  """
  signal_float = normalize_signal(signal.astype(np.float32))

  if 'speed_perturbation_ratio' in augmentation:
    stretch_amount = -1
    if isinstance(augmentation['speed_perturbation_ratio'], list):
      stretch_amount = np.random.choice(augmentation['speed_perturbation_ratio'])
    elif augmentation['speed_perturbation_ratio'] > 0:
      # time stretch (might be slow)
      stretch_amount = 1.0 + (2.0 * np.random.rand() - 1.0) * \
                       augmentation['speed_perturbation_ratio']
    if stretch_amount > 0:
      signal_float = rs.resample(
          signal_float,
          sample_freq,
          int(sample_freq * stretch_amount),
          filter='kaiser_best',
      )

  # noise
  if 'noise_level_min' in augmentation and 'noise_level_max' in augmentation:
    noise_level_db = np.random.randint(low=augmentation['noise_level_min'],
                                       high=augmentation['noise_level_max'])
    signal_float += np.random.randn(signal_float.shape[0]) * \
                    10.0 ** (noise_level_db / 20.0)

  return normalize_signal(signal_float)

def preemphasis(signal, coeff=0.97):
  return np.append(signal[0], signal[1:] - coeff * signal[:-1])

def get_speech_features(signal, sample_freq, params):
  """
  Get speech features using either librosa (recommended) or
  python_speech_features
  Args:
    signal (np.array): np.array containing raw audio signal
    sample_freq (float): sample rate of the signal
    params (dict): parameters of pre-processing
  Returns:
    np.array: np.array of audio features with shape=[num_time_steps,
    num_features].
    audio_duration (float): duration of the signal in seconds
  """

  backend = params.get('backend', 'psf')

  features_type = params.get('input_type', 'spectrogram')
  num_features = params['num_audio_features']
  window_size = params.get('window_size', 20e-3)
  window_stride = params.get('window_stride', 10e-3)
  augmentation = params.get('augmentation', None)

  if backend == 'librosa':
    window_fn = WINDOWS_FNS[params.get('window', "hanning")]
    dither = params.get('dither', 0.0)
    num_fft = params.get('num_fft', None)
    norm_per_feature = params.get('norm_per_feature', False)
    mel_basis = params.get('mel_basis', None)
    if mel_basis is not None and sample_freq != params["sample_freq"]:
      raise ValueError(
          ("The sampling frequency set in params {} does not match the "
           "frequency {} read from file {}").format(params["sample_freq"],
                                                    sample_freq, filename)
      )
    features, duration = get_speech_features_librosa(
        signal, sample_freq, num_features, features_type,
        window_size, window_stride, augmentation, window_fn=window_fn,
        dither=dither, norm_per_feature=norm_per_feature, num_fft=num_fft,
        mel_basis=mel_basis
    )
  else:
    pad_to = params.get('pad_to', 8)
    features, duration = get_speech_features_psf(
        signal, sample_freq, num_features, pad_to, features_type,
        window_size, window_stride, augmentation
    )

  return features, duration

def get_speech_features_librosa(signal, sample_freq, num_features,
                                features_type='spectrogram',
                                window_size=20e-3,
                                window_stride=10e-3,
                                augmentation=None,
                                window_fn=np.hanning,
                                num_fft=None,
                                dither=0.0,
                                norm_per_feature=False,
                                mel_basis=None):
  """Function to convert raw audio signal to numpy array of features.
  Backend: librosa
  Args:
    signal (np.array): np.array containing raw audio signal.
    sample_freq (float): frames per second.
    num_features (int): number of speech features in frequency domain.
    pad_to (int): if specified, the length will be padded to become divisible
        by ``pad_to`` parameter.
    features_type (string): 'mfcc' or 'spectrogram'.
    window_size (float): size of analysis window in milli-seconds.
    window_stride (float): stride of analysis window in milli-seconds.
    augmentation (dict, optional): dictionary of augmentation parameters. See
        :func:`augment_audio_signal` for specification and example.

  Returns:
    np.array: np.array of audio features with shape=[num_time_steps,
    num_features].
    audio_duration (float): duration of the signal in seconds
  """
  if augmentation:
    signal = augment_audio_signal(signal.astype(np.float32), sample_freq, augmentation)
  else:
    signal = normalize_signal(signal.astype(np.float32))

  audio_duration = len(signal) * 1.0 / sample_freq

  n_window_size = int(sample_freq * window_size)
  n_window_stride = int(sample_freq * window_stride)
  num_fft = num_fft or 2**math.ceil(math.log2(window_size*sample_freq))

  if dither > 0:
    signal += dither*np.random.randn(*signal.shape)

  if features_type == 'spectrogram':
    # ignore 1/n_fft multiplier, since there is a post-normalization
    powspec = np.square(np.abs(librosa.core.stft(
        signal, n_fft=n_window_size,
        hop_length=n_window_stride, win_length=n_window_size, center=True,
        window=window_fn)))
    # remove small bins
    powspec[powspec <= 1e-30] = 1e-30
    features = 10 * np.log10(powspec.T)

    assert num_features <= n_window_size // 2 + 1, \
      "num_features for spectrogram should be <= (sample_freq * window_size // 2 + 1)"

    # cut high frequency part
    features = features[:, :num_features]

  elif features_type == 'mfcc':
    signal = preemphasis(signal, coeff=0.97)
    S = np.square(
            np.abs(
                librosa.core.stft(signal, n_fft=num_fft,
                                  hop_length=int(window_stride * sample_freq),
                                  win_length=int(window_size * sample_freq),
                                  center=True, window=window_fn
                )
            )
        )
    features = librosa.feature.mfcc(sr=sample_freq, S=S,
        n_mfcc=num_features, n_mels=2*num_features).T
  elif features_type == 'logfbank':
    signal = preemphasis(signal,coeff=0.97)
    S = np.abs(librosa.core.stft(signal, n_fft=num_fft,
                                 hop_length=int(window_stride * sample_freq),
                                 win_length=int(window_size * sample_freq),
                                 center=True, window=window_fn))**2.0
    if mel_basis is None:
      # Build a Mel filter
      mel_basis = librosa.filters.mel(sample_freq, num_fft, n_mels=num_features,
                                      fmin=0, fmax=int(sample_freq/2))
    features = np.log(np.dot(mel_basis, S) + 1e-20).T
  else:
    raise ValueError('Unknown features type: {}'.format(features_type))

  norm_axis = 0 if norm_per_feature else None
  mean = np.mean(features, axis=norm_axis)
  std_dev = np.std(features, axis=norm_axis)
  features = (features - mean) / std_dev

  # now it is safe to pad
  # if pad_to > 0:
  #   if features.shape[0] % pad_to != 0:
  #     pad_size = pad_to - features.shape[0] % pad_to
  #     if pad_size != 0:
  #         features = np.pad(features, ((0,pad_size), (0,0)), mode='constant')
  return features, audio_duration

def get_speech_features_psf(signal, sample_freq, num_features,
                            pad_to=8,
                            features_type='spectrogram',
                            window_size=20e-3,
                            window_stride=10e-3,
                            augmentation=None):
  """Function to convert raw audio signal to numpy array of features.
  Backend: python_speech_features
  Args:
    signal (np.array): np.array containing raw audio signal.
    sample_freq (float): frames per second.
    num_features (int): number of speech features in frequency domain.
    pad_to (int): if specified, the length will be padded to become divisible
        by ``pad_to`` parameter.
    features_type (string): 'mfcc' or 'spectrogram'.
    window_size (float): size of analysis window in milli-seconds.
    window_stride (float): stride of analysis window in milli-seconds.
    augmentation (dict, optional): dictionary of augmentation parameters. See
        :func:`augment_audio_signal` for specification and example.
    apply_window (bool): whether to apply Hann window for mfcc and logfbank.
        python_speech_features version should accept winfunc if it is True.
  Returns:
    np.array: np.array of audio features with shape=[num_time_steps,
    num_features].
    audio_duration (float): duration of the signal in seconds
  """
  if augmentation is not None:
    signal = augment_audio_signal(signal, sample_freq, augmentation)
  else:
    signal = (normalize_signal(signal.astype(np.float32)) * 32767.0).astype(
        np.int16)

  audio_duration = len(signal) * 1.0 / sample_freq

  n_window_size = int(sample_freq * window_size)
  n_window_stride = int(sample_freq * window_stride)

  # making sure length of the audio is divisible by 8 (fp16 optimization)
  length = 1 + int(math.ceil(
      (1.0 * signal.shape[0] - n_window_size) / n_window_stride
  ))
  if pad_to > 0:
    if length % pad_to != 0:
      pad_size = (pad_to - length % pad_to) * n_window_stride
      signal = np.pad(signal, (0, pad_size), mode='constant')

  if features_type == 'spectrogram':
    frames = psf.sigproc.framesig(sig=signal,
                                  frame_len=n_window_size,
                                  frame_step=n_window_stride,
                                  winfunc=np.hanning)

    # features = np.log1p(psf.sigproc.powspec(frames, NFFT=N_window_size))
    features = psf.sigproc.logpowspec(frames, NFFT=n_window_size)
    assert num_features <= n_window_size // 2 + 1, \
      "num_features for spectrogram should be <= (sample_freq * window_size // 2 + 1)"

    # cut high frequency part
    features = features[:, :num_features]

  elif features_type == 'mfcc':
    features = psf.mfcc(signal=signal,
                        samplerate=sample_freq,
                        winlen=window_size,
                        winstep=window_stride,
                        numcep=num_features,
                        nfilt=2 * num_features,
                        nfft=512,
                        lowfreq=0, highfreq=None,
                        preemph=0.97,
                        ceplifter=2 * num_features,
                        appendEnergy=False)

  elif features_type == 'logfbank':
    features = psf.logfbank(signal=signal,
                            samplerate=sample_freq,
                            winlen=window_size,
                            winstep=window_stride,
                            nfilt=num_features,
                            nfft=512,
                            lowfreq=0, highfreq=sample_freq / 2,
                            preemph=0.97)
  else:
    raise ValueError('Unknown features type: {}'.format(features_type))

  if pad_to > 0:
    assert features.shape[0] % pad_to == 0
  mean = np.mean(features)
  std_dev = np.std(features)
  features = (features - mean) / std_dev

  return features, audio_duration


# This is a demo of running face recognition on a Raspberry Pi.
# This program will print out the names of anyone it recognizes to the console.

# To run this, you need a Raspberry Pi 2 (or greater) with face_recognition and
# the picamera[array] module installed.
# You can follow this installation instructions to get your RPi set up:
# https://gist.github.com/ageitgey/1ac8dbe8572f3f533df6269dab35df65

import cv2
import face_recognition
import picamera
import numpy as np
import json
import time
import datetime
import glob
import os
import dropboximage
import RPi.GPIO as GPIO2
break_script = False

def facemain():
	pin_num = 22
	#setup gpio
	GPIO2.setmode(GPIO2.BCM)
	# GPIO 23 & 17 set up as inputs, pulled up to avoid false detection.
	# Both ports are wired to connect to GND on button press.
	# So we'll be setting up falling edge detection for both
	GPIO2.setup(pin_num, GPIO2.IN, pull_up_down=GPIO2.PUD_UP)

	# Get a reference to the Raspberry Pi camera.
	# If this fails, make sure you have a camera connected to the RPi and that you
	# enabled your camera in raspi-config and rebooted first.
	camera = picamera.PiCamera()
	camera.resolution = (320, 240)
	output = np.empty((240, 320, 3), dtype=np.uint8)

	# Load a sample picture and learn how to recognize it.
	print("Loading known face image(s)")
	encodings = []
	os.chdir("images")
	for file in glob.glob("*.jpg"):
		image = face_recognition.load_image_file(file)
		encodings.append(face_recognition.face_encodings(image)[0])
	os.chdir("..")
	# Initialize some variables
	face_locations = []
	face_encodings = []

	while True:
		print("Getting image.")
		# Grab a single frame of video from the RPi camera as a numpy array
		camera.capture(output, format="rgb")

		# Find all the faces and face encodings in the current frame of video
		face_locations = face_recognition.face_locations(output)
		print("Found {} faces in image.".format(len(face_locations)))
		face_encodings = face_recognition.face_encodings(output, face_locations)

		intruder = False
		# Loop over each face found in the frame to see if it's someone we know.
		for face_encoding in face_encodings:
			# See if the face is a match for the known face(s)
			match = face_recognition.compare_faces(encodings, face_encoding)

			if match[0] == False:
				intruder = True

			if intruder:
				print("I see an Intruder!")
				camera.close()
				time.sleep(.5)
				#run the send to dropbox script
				dropboximage.dbxmain()
				camera = picamera.PiCamera()
				camera.resolution = (320, 240)

			else:
				print("Everyone is friendly!")
		if GPIO2.input(pin_num) == False:
			print("ending script")
			break
	GPIO2.cleanup()
	camera.close()
	print("camera closed")

if __name__ == '__main__':
	facemain()


import numpy as np
import sys
PAGE_CATEGORIES_PRE = [
  '04f2', '00a0', '0370', '05a2', '0690', '0110', '04b0', '02b0', '05a0', '0165', '0081', '018f', '02a0', '0220', '0080', '01f1', '0120', '0153', '0260', '0545', '04f1', '0043', '059b', '0587', '05e4', '05f0', '04b1', '0329', '0382', '043f', '0510', '02c0', '051a', '0050', '00a1', '0018', '0034', '0044', '0440', '0042', '04f0', '0517', '0164', '0350', '0316'
]

num = np.load("numEn.npy")
id = np.load("idEn.npy")
c = len(num)
d = len(id)
count = 0
# sdrs = np.zeros([c*d,4096], dtype='uint8')
sdrs = {}
for i1 in range(45):
    for i2 in range(3):
        np.random.shuffle(num[i2])
        sdr = np.zeros(1024, dtype='uint8')
        for i3 in range(1024):
            if i3%4:
                sdr[i3] = id[i1][i3/2]
            else:
                sdr[i3] = num[i2][i3/2]
        sdrs[PAGE_CATEGORIES_PRE[i1]+'_'+str(i2)] = sdr
        count += 1
        sys.stdout.write("\rLearned {} Sessions".format(count + 1))
        sys.stdout.flush()
np.save("En.npy", sdrs)

p = np.load("En.npy").item()
pass


r"""
:mod:`mirgecom.transport` provides methods/utils for transport properties.

Transport Models
^^^^^^^^^^^^^^^^
This module is designed provide Transport Model objects used to compute and
manage the transport properties in viscous flows.  The transport properties
currently implemented are the dynamic viscosity ($\mu$), the bulk viscosity
($\mu_{B}$), the thermal conductivity ($\kappa$), and the species diffusivities
($d_{\alpha}$).

.. autoclass:: TransportDependentVars
.. autoclass:: TransportModel
.. autoclass:: SimpleTransport
.. autoclass:: PowerLawTransport
"""

__copyright__ = """
Copyright (C) 2021 University of Illinois Board of Trustees
"""

__license__ = """
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""

from dataclasses import dataclass
import numpy as np
from meshmode.mesh import BTAG_ALL, BTAG_NONE  # noqa
from mirgecom.fluid import ConservedVars
from mirgecom.eos import GasEOS

@dataclass
class TransportDependentVars:
    """State-dependent quantities for :class:`TransportModel`.

    Prefer individual methods for model use, use this
    structure for visualization or probing.

    .. attribute:: bulk_viscosity
    .. attribute:: viscosity
    .. attribute:: thermal_conductivity
    .. attribute:: species_diffusivity
    """

    bulk_viscosity: np.ndarray
    viscosity: np.ndarray
    thermal_conductivity: np.ndarray
    species_diffusivity: np.ndarray

class TransportModel:
    r"""Abstract interface to thermo-diffusive transport model class.

    Transport model classes are responsible for
    computing relations between fluid or gas state variables and
    thermo-diffusive transport properties for those fluids.

    .. automethod:: bulk_viscosity
    .. automethod:: viscosity
    .. automethod:: thermal_conductivity
    .. automethod:: species_diffusivity
    .. automethod:: volume_viscosity
    """

    def bulk_viscosity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the bulk viscosity for the gas (${\mu}_{B}$)."""
        raise NotImplementedError()

    def viscosity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the gas dynamic viscosity, $\mu$."""
        raise NotImplementedError()

    def volume_viscosity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the 2nd coefficent of viscosity, $\lambda$."""
        raise NotImplementedError()

    def thermal_conductivity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the gas thermal_conductivity, $\kappa$."""
        raise NotImplementedError()

    def species_diffusivity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the vector of species diffusivities, ${d}_{\alpha}$."""
        raise NotImplementedError()

class SimpleTransport(TransportModel):
    r"""Transport model with uniform, constant properties.

    Inherits from (and implements) :class:`TransportModel`.

    .. automethod:: __init__
    .. automethod:: bulk_viscosity
    .. automethod:: viscosity
    .. automethod:: volume_viscosity
    .. automethod:: species_diffusivity
    .. automethod:: thermal_conductivity
    """

    def __init__(self, bulk_viscosity=0, viscosity=0,
                 thermal_conductivity=0,
                 species_diffusivity=None):
        """Initialize uniform, constant transport properties."""
        if species_diffusivity is None:
            species_diffusivity = np.empty((0,), dtype=object)
        self._mu_bulk = bulk_viscosity
        self._mu = viscosity
        self._kappa = thermal_conductivity
        self._d_alpha = species_diffusivity

    def bulk_viscosity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the bulk viscosity for the gas, $\mu_{B}$."""
        return self._mu_bulk

    def viscosity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the gas dynamic viscosity, $\mu$."""
        return self._mu

    def volume_viscosity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the 2nd viscosity coefficent, $\lambda$.

        In this transport model, the second coefficient of viscosity is defined as:

        $\lambda = \left(\mu_{B} - \frac{2\mu}{3}\right)$
        """
        return self._mu_bulk - 2 * self._mu / 3

    def thermal_conductivity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the gas thermal_conductivity, $\kappa$."""
        return self._kappa

    def species_diffusivity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the vector of species diffusivities, ${d}_{\alpha}$."""
        return self._d_alpha

class PowerLawTransport(TransportModel):
    r"""Transport model with simple power law properties.

    Inherits from (and implements) :class:`TransportModel` based on a
    temperature-dependent power law.

    .. automethod:: __init__
    .. automethod:: bulk_viscosity
    .. automethod:: viscosity
    .. automethod:: volume_viscosity
    .. automethod:: species_diffusivity
    .. automethod:: thermal_conductivity
    """

    # air-like defaults here
    def __init__(self, alpha=0.6, beta=4.093e-7, sigma=2.5, n=.666,
                 species_diffusivity=None):
        """Initialize power law coefficients and parameters."""
        if species_diffusivity is None:
            species_diffusivity = np.empty((0,), dtype=object)
        self._alpha = alpha
        self._beta = beta
        self._sigma = sigma
        self._n = n
        self._d_alpha = species_diffusivity

    def bulk_viscosity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the bulk viscosity for the gas, $\mu_{B}$.

        $\mu_{B} = \alpha\mu$
        """
        return self._alpha * self.viscosity(eos, cv)

    # TODO: Should this be memoized? Avoid multiple calls?
    def viscosity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the gas dynamic viscosity, $\mu$.

        $\mu = \beta{T}^n$
        """
        return self._beta * eos.temperature(cv)**self._n

    def volume_viscosity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the 2nd viscosity coefficent, $\lambda$.

        In this transport model, the second coefficient of viscosity is defined as:

        $\lambda = \left(\alpha - \frac{2}{3}\right)\mu$
        """
        return (self._alpha - 2.0/3.0)*self.viscosity(eos, cv)

    def thermal_conductivity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the gas thermal_conductivity, $\kappa$.

        $\kappa = \sigma\mu{C}_{v}$
        """
        return self._sigma * self.viscosity(eos, cv) * eos.heat_capacity_cv(cv)

    def species_diffusivity(self, eos: GasEOS, cv: ConservedVars):
        r"""Get the vector of species diffusivities, ${d}_{\alpha}$."""
        return self._d_alpha


import json
import tarfile
import typing
from io import BufferedIOBase, BytesIO, IOBase, RawIOBase, TextIOBase
from pathlib import Path

import numpy as np

from PartSegCore.analysis.io_utils import ProjectTuple
from PartSegCore.io_utils import LoadBase
from PartSegCore.roi_info import ROIInfo
from PartSegCore.segmentation.noise_filtering import DimensionType
from PartSegCore.universal_const import UNIT_SCALE, Units
from PartSegImage import Image

class LoadPartSegOld(LoadBase):
    @classmethod
    def get_name(cls):
        return "Project old (*.tgz *.tbz2 *.gz *.bz2)"

    @classmethod
    def get_short_name(cls):
        return "project_old"

    # noinspection DuplicatedCode
    @classmethod
    def load(
        cls,
        load_locations: typing.List[typing.Union[str, BytesIO, Path]],
        range_changed: typing.Callable[[int, int], typing.Any] = None,
        step_changed: typing.Callable[[int], typing.Any] = None,
        metadata: typing.Optional[dict] = None,
    ):
        """Load project from archive old format"""
        file_ob: typing.Union[str, tarfile.TarFile, TextIOBase, BufferedIOBase, RawIOBase, IOBase] = load_locations[0]
        if isinstance(file_ob, tarfile.TarFile):
            tar_file = file_ob
            file_path = ""
        elif isinstance(file_ob, str):
            tar_file = tarfile.open(file_ob)
            file_path = file_ob
        elif isinstance(file_ob, (TextIOBase, BufferedIOBase, RawIOBase, IOBase)):
            tar_file = tarfile.open(fileobj=file_ob)
            file_path = ""
        else:
            raise ValueError(f"wrong type of file_ argument: {type(file_ob)}")
        image_buffer = BytesIO()
        image_tar = tar_file.extractfile(tar_file.getmember("image.npy"))
        image_buffer.write(image_tar.read())
        image_buffer.seek(0)
        image_arr = np.load(image_buffer)
        try:
            res_buffer = BytesIO()
            res_tar = tar_file.extractfile(tar_file.getmember("res_mask.npy"))
            res_buffer.write(res_tar.read())
            res_buffer.seek(0)
            seg_array = np.load(res_buffer)
        except KeyError:
            seg_array = None
        algorithm_str = tar_file.extractfile("data.json").read()
        algorithm_dict = json.loads(algorithm_str)
        spacing = np.array(algorithm_dict["spacing"][::-1]) / UNIT_SCALE[Units.nm.value]
        image = Image(image_arr.reshape((1,) + image_arr.shape + (1,)), spacing, file_path)
        values = {
            "channel": 0,
            "minimum_size": algorithm_dict["minimum_size"],
            "threshold": {"name": "Manual", "values": {"threshold": algorithm_dict["threshold"]}},
            "noise_removal": {"name": "Gauss", "values": {"gauss_type": DimensionType.Layer, "radius": 1.0}}
            if algorithm_dict["use_gauss"]
            else {"name": "None", "values": {}},
            "side_connection": True,
        }

        algorithm_parameters = {
            "name": "Upper threshold" if algorithm_dict["threshold_type"] == "Upper" else "Lower threshold",
            "values": values,
            "threshold_list": algorithm_dict["threshold_list"],
        }

        return ProjectTuple(file_path, image, ROIInfo(seg_array), algorithm_parameters=algorithm_parameters)