Export
PciSeq
For probabilistic cell typing with pciSeq, a DAPI background image and gene spot positions must be exported.
DAPI image export
Filtered DAPI
To export the anchor's filtered DAPI stitched image
from coppafisher import Notebook
from coppafisher.results import export_pciseq_dapi_image
nb = Notebook("/path/to/notebook")
export_pciseq_dapi_image(nb)
The DAPI image can then be loaded into memory by
import numpy as np
dapi_image = np.load("/path/to/dapi_image.npz")["arr_0"]
Unfiltered DAPI
To export the anchor's unfiltered DAPI stitched image
from coppafisher import Notebook
from coppafisher.results import export_pciseq_unfiltered_dapi_image
nb = Notebook("/path/to/notebook")
config_path = "/path/to/config.ini"
export_pciseq_unfiltered_dapi_image(nb, config_path, radius_norm_file=None)
You can set radius_norm_file="default" to use the default dapi radius normalisation when the dapi channel is 0.
The DAPI image can then be loaded into memory by
import numpy as np
dapi_image = np.load("/path/to/dapi_image.npz")["arr_0"]
Gene spots export
Export gene spots into a compatible csv file by
from coppafisher import Notebook
from coppafisher.results import export_to_pciseq
nb = Notebook("/path/to/notebook")
export_to_pciseq(nb, method)
where method can be "omp", "prob", or "anchor" for each gene calling method. To set a score and/or intensity
minimum threshold:
from coppafisher import Notebook
from coppafisher.results import export_to_pciseq
nb = Notebook("/path/to/notebook")
export_to_pciseq(nb, method, score_thresh, intensity_thresh)
score_thresh and intensity_thresh must be numbers. Use the Viewer to help decide on thresholds.
intensity_thresh is set to 0.15 for OMP in the Viewer by default.
Merge cell mask chunks
Multiple cell masks for separate tiles can be merged together that are produced by cellpose. The cell masks must be
uint16 3d images all of the same shape (im_z, im_y, im_x). They must be saved as .npy files.
from coppafisher.results import merge_cell_masks
merged_cell_mask = merge_cell_masks(
cell_mask_file_paths=["/path/to/cell_mask_0.npy", "/path/to/cell_mask_1.npy"],
cell_mask_origin_yxzs=[[0.0, 0.0, 0.0], [1100, 1.0, 0.0]],
expected_tile_overlap=0.15,
merge_cells_method="",
)
where cell_mask_origin_yxzs is the bottom-leftmost position for each cell mask in a global coordinate frame. For more
details, see the merge_cell_masks docstring at coppafisher/results/cell_mask.py.
If you have a coppafisher notebook, you can base the merging off the tile stitch results:
from coppafisher import Notebook
from coppafisher.results import merge_cell_masks
nb = Notebook("/path/to/notebook")
merged_cell_mask = merge_cell_masks(
cell_mask_file_paths=["/path/to/cell_mask_0.npy", "/path/to/cell_mask_1.npy"],
cell_mask_origin_yxzs=nb.stitch.tile_origin,
expected_tile_overlap=nb.stitch.associated_config["stitch"]["expected_overlap"],
merge_cells_method="",
)
Merging methods
There are two strategies for merging cell masks together:
1) merge_cells_method="" does no cell merging and the tile that has the closest tile centre to the overlapping
pixel data is used. This can leave erroneous split cells at the midpoint between the tile overlap.
2) merge_cells_method="merge 0.5" merges cells together in the overlapping region when the cells have at
least 50% overlap. You can adjust the number 0.5 for anything between 0 and 1 to best suit your data. For more
details on the merging method:
from coppafisher.results import merge_cell_masks
print(merge_cell_masks.__doc__)
Saving the merged cell mask
You can save the resulting merged cell mask as a .npy file
import numpy as np
np.save("/path/to/merged_cell_mask.npy", merged_cell_mask)
Or a compressed .npz file
import numpy as np
np.savez_compressed("/path/to/merged_cell_mask.npy", merged_cell_mask)
Custom Images
Additional custom images can be aligned with coppafisher images and gene spots provided that you have a dapi channel (or something similar to align with the anchor-DAPI image).
Extract the additional image(s)
The additional images must be extracted from ND2 files. You will likely require the DAPI channel for best results. They are saved as tiff files. If you do not have ND2 input files, you need to first manually convert them to tiff files.
from coppafisher.custom_alignment import extract_raw
from coppafisher import Notebook
config_file = "/path/to/used/config.ini"
custom_nd2 = "/path/to/input/file.nd2"
output_dir = "/path/to/extract/directory/"
nb = Notebook("/path/to/notebook")
extract_raw(
nb,
config_file,
save_dir=output_dir,
read_dir=custom_nd2,
use_tiles=nb.basic_info.use_tiles,
use_channels=[nb.basic_info.dapi_channel, 9, 23],
reverse_custom_z=False,
radius_norm_file=None,
radius_norm_channels=None,
dapi_radius_norm_file=None,
)
use_channels can be any valid channel(s) inside the custom image .nd2 file. This will also extract the anchor round in
the DAPI channel. You can reverse the z planes in the custom image by setting reverse_custom_z to True.
Set radius_norm_file="default_nine" to use the default nine channel tile radius/channel normalisation file
at
coppafisher/setup/nine_channel_normalisations.npz.
Set radius_norm_file="default_seven" to use the default seven channel tile radius/channel normalisation file
at
coppafisher/setup/seven_channel_normalisations.npz.
Set dapi_radius_norm_file="default" to use the default dapi channel tile radius/channel normalisation file at
coppafisher/setup/dapi_channel_normalisations.npz.
Config File
The config file must be a valid configuration, like the one used during the experiment. input_dir must be a real
input directory.
Stitch
The extracted raw anchor-DAPI images are stitched using coppafisher's stitch method. The custom image is stitched by the same method separately. This then needs to be registered with the anchor-DAPI in the next step. Do this for each custom image channel separately. I suggest starting with the dapi channels first
from coppafisher.custom_alignment import fuse_custom_and_dapi
fused_custom_dapi_image, fused_anchor_dapi_image = fuse_custom_and_dapi(nb, output_dir, channel=nb.basic_info.dapi_channel)
Dapi Register
Alignment is done using the package LineStuffUp maintained by Max Shinn
(m.shinn@ucl.ac.uk). This allows control over the type of transformation to apply based on their custom images. Install
via pip
python -m pip install LineStuffUp
Then start the interactive alignment process
import linestuffup.gui
from linestuffup.base import TranslateRotate
round_transform = linestuffup.gui.alignment_gui(
fused_custom_dapi_image, fused_anchor_dapi_image, transform_type=TranslateRotate
)
Press Add new point and click twice to place two corresponding points. Do this a few times, preferably in various z
planes too. Press Perform transform to see the resulting transform. Once you are happy with the result, close the
napari window.
Type of Transform
You can change the type of transform you wish to find, please see the transfrom readme for details.
For example, you could use the more robust transform type of TranslateRotateRescale. It requires four
points in every corner of the image on both edges of the z stack for best results.
Save and Load Transforms
Every transform can be saved and reloaded at a later point. You just need to save the text representation of the transform which can be found by
str(round_transform)
You can save it using Python
with open("/path/to/saved/transform.txt", "w") as file:
file.write(str(round_transform))
It can be reloaded by
from linestuffup.base import *
with open("/path/to/saved/transform.txt", "r") as file:
exec("round_transform = " + "\n".join(file.readlines()))
You can rename the variable from round_transform to anything.
Do not run exec on stranger's code (it could be malicious)!
You can now apply the resulting transform to the custom dapi image and save the result as a .tif file
import numpy as np
import tifffile
fused_custom_dapi_image_transformed = round_transform.transform_image(
fused_custom_dapi_image, output_size=fused_anchor_dapi_image.shape, force_size=True, labels=True
)
tifffile.imwrite("/path/to/saved/custom_dapi_image_transformed.tif", fused_custom_dapi_image_transformed)
del fused_custom_dapi_image_transformed
del fused_anchor_dapi_image
Non-Dapi Register
For a non-dapi custom image channel c, it is recommended to find a specific transform to move to the dapi channel for
best registration. To do this, first find a transform to move into the dapi custom image's frame
from coppafisher.custom_alignment import fuse_custom_and_dapi
import linestuffup.gui
from linestuffup.base import TranslateRotate
fused_custom_channel_image, _ = fuse_custom_and_dapi(nb, output_dir, channel=c)
channel_transform = linestuffup.gui.alignment_gui(
fused_custom_channel_image, fused_custom_dapi_image, transform_type=TranslateRotate
)
Now save the fully registered channel image
import tifffile
fused_custom_channel_image_transformed = (channel_transform + round_transform).transform_image(
fused_custom_channel_image, output_size=fused_custom_channel_image.shape, force_size=True, labels=True
)
tifffile.imwrite(f"/path/to/saved/custom_channel_{c}_image_transformed.tif", fused_custom_channel_image_transformed)
del fused_custom_channel_image_transformed
del fused_custom_channel_image