def saveExp(source_dir: str, save_dir: str, exp: Experiment = None):
    # go through and save all images, if an experiment is provided
    if exp:
        for fov in exp.keys():
            for view in exp[fov].image_types:
                img = exp[fov].get_image(view)
                prefix = f"{view}-{fov}"
                saveImg(save_dir, prefix, img)

    # copy the non-tiff files to the new directory
    cp_files = [x for x in os.listdir(source_dir) if x[-5:] != ".tiff" and x[-4:] != ".log"]
    for file in cp_files:
        if "fov" in file:
            # if file contains images, we need to update sha's
            data = json.load(open(str(source_dir) + "/" + file))
            for i in range(len(data["tiles"])):
                abspath = str(save_dir) + "/" + data["tiles"][i]["file"]
                with open(os.fspath(abspath), "rb") as fh:
                    hsh = hashlib.sha256(fh.read()).hexdigest()
                data["tiles"][i]["sha256"] = hsh
                print(f"\tupdated hash for {data['tiles'][i]['file']}")
            with open(str(save_dir) + "/" + file, "w") as f:
                json.dump(data, f)
            print(f"saved {file} with modified hashes")
        else:
            # we can just copy the rest of the files
            shutil.copyfile(f"{source_dir}/{file}", f"{save_dir}/{file}")
            print(f"copied {file}")
def run(
    output_dir: str,
    experiment: Experiment,
    blob_based: bool,
    use_ref: bool,
    blobRunnerKwargs: dict,
    decodeRunnerKwargs: dict,
    pixelRunnerKwargs: dict,
):
    """
    Main method for executing runs.  Sets up directories and calls appropriate driver methods.

    Parameters
    ----------
    output_dir: str
        Location to put all output from this tool.  Dir will be created if not present.
    experiment: Experiment
        Experiment object with corresponding images and codebook.
    blob_based: bool
        If true, use blob-detection and decoding methods. Else, use pixel-based methods.
    use_ref: bool
        If true, a reference image will be used and created by flattening the fov.
    blobRunnerKwargs: dict
        Dictionary with arguments for blob detection. Refer to blobRunner.
    decodeRunnerKwargs: dict
        Dictionary with arguments for spot-based decoding. Refer to decodeRunner.
    pixelRunnerKwargs: dict
        Dictionary with arguments for pixel-based detection and decoding.  Refer to starfish PixelSpotDecoder.

    """
    if not path.isdir(output_dir):
        makedirs(output_dir)

    if not path.isdir(output_dir + "csv/"):
        makedirs(output_dir + "csv")

    if not path.isdir(output_dir + "cdf/"):
        makedirs(output_dir + "cdf")

    if blob_based and not path.isdir(output_dir + "spots/"):
        makedirs(output_dir + "spots")

    reporter = open(
        path.join(output_dir,
                  datetime.now().strftime("%Y%m%d_%H%M_starfish_runner.log")),
        "w")
    sys.stdout = reporter
    sys.stderr = reporter

    print(
        "output_dir: {}\nexp: {}\nblob_based: {}\nuse_ref: {}\nblobrunner: {}\ndecoderunner: {}\npixelrunner: {}\n"
        .format(
            output_dir,
            experiment,
            blob_based,
            use_ref,
            blobRunnerKwargs,
            decodeRunnerKwargs,
            pixelRunnerKwargs,
        ))

    # disabling tdqm for pipeline runs
    tqdm.__init__ = partialmethod(tqdm.__init__, disable=True)

    imgs = {}
    for fov in experiment.keys():
        imgs[fov] = experiment[fov].get_image("primary")

    ref_imgs = None
    if use_ref:
        ref_imgs = {}
        for fov in experiment.keys():
            ref_imgs[fov] = imgs[fov].reduce(
                {Axes.CH, Axes.ROUND, Axes.ZPLANE}, func="max")

    decoded = {}
    if blob_based:
        blobs, decoded = blobDriver(imgs, ref_imgs, experiment.codebook,
                                    blobRunnerKwargs, decodeRunnerKwargs,
                                    output_dir)
    else:
        decoded = pixelDriver(imgs, experiment.codebook, pixelRunnerKwargs)

    # saving
    for fov in decoded.keys():
        saveTable(decoded[fov], output_dir + "csv/" + fov + "_decoded.csv")
        # decoded[fov].to_decoded_dataframe().save_csv(output_dir+fov+"_decoded.csv")
        decoded[fov].to_netcdf(output_dir + "cdf/" + fov + "_decoded.cdf")

    sys.stdout = sys.__stdout__
    return 0