ashlar: stitching error on celldive images
Hi Jeremy, thanks for helping me previously on the mcmicro #519 issue. https://github.com/labsyspharm/mcmicro/issues/519
As you suggested, I’ve been working on genrating an OME-XML “companion” file from celldive moves.xml file.
Here’s part of my moves.xml file:
<pixel_moves>
<image_path>E:\Cell_DIVE_ScansE\swu_LN_10\scanplan\scanplan.tif</image_path>
<image_base_mag>10.000000</image_base_mag>
<objective_mag>20.000000</objective_mag>
<canvas_xform exists="1" a="192.307693" b="0.000000" c="0.000000" d="192.307693" du="127.500000" dv="127.500000"/>
<cam img_size_x="2040.000000" img_size_y="2040.000000" pixel_size_x="6.500000" pixel_size_y="6.500000"/>
<roi_count>1</roi_count>
<roi id="0" type="region">
<label>region_001</label>
<overlap x="0.100000" y="0.100000"/>
<roi_rect x="28.415403" y="2.540199" width="16.931200" height="15.100800" cen_x="36.881004" cen_y="10.090599"/>
<moves>
<move id="0" enabled="0" row="0" col="0" xp="5655" yp="679" xs="28.746902" ys="2.871699"/>
<move id="1" enabled="0" row="0" col="1" xp="5770" yp="679" xs="29.343603" ys="2.871699"/>
<move id="2" enabled="0" row="0" col="2" xp="5885" yp="679" xs="29.940304" ys="2.871699"/>
<move id="3" enabled="0" row="0" col="3" xp="6000" yp="679" xs="30.537004" ys="2.871699"/>
<move id="4" enabled="0" row="0" col="4" xp="6114" yp="679" xs="31.133705" ys="2.871699"/>
<move id="5" enabled="0" row="0" col="5" xp="6229" yp="679" xs="31.730406" ys="2.871699"/>
<move id="6" enabled="0" row="0" col="6" xp="6344" yp="679" xs="32.327106" ys="2.871699"/>
<move id="7" enabled="0" row="0" col="7" xp="6459" yp="679" xs="32.923805" ys="2.871699"/>
<move id="8" enabled="0" row="0" col="8" xp="6573" yp="679" xs="33.520508" ys="2.871699"/>
<move id="9" enabled="0" row="0" col="9" xp="6688" yp="679" xs="34.117207" ys="2.871699"/>
<move id="10" enabled="0" row="0" col="10" xp="6803" yp="679" xs="34.713905" ys="2.871699"/>
<move id="11" enabled="1" row="0" col="11" xp="6918" yp="679" xs="35.310604" ys="2.871699"/>
<move id="12" enabled="1" row="0" col="12" xp="7032" yp="679" xs="35.907303" ys="2.871699"/>
<move id="13" enabled="1" row="0" col="13" xp="7147" yp="679" xs="36.504002" ys="2.871699"/>
<move id="14" enabled="1" row="0" col="14" xp="7262" yp="679" xs="37.100700" ys="2.871699"/>
<move id="15" enabled="0" row="0" col="15" xp="7377" yp="679" xs="37.697399" ys="2.871699"/>
<move id="16" enabled="0" row="0" col="16" xp="7491" yp="679" xs="38.294098" ys="2.871699"/>
<move id="17" enabled="0" row="0" col="17" xp="7606" yp="679" xs="38.890797" ys="2.871699"/>
<move id="18" enabled="0" row="0" col="18" xp="7721" yp="679" xs="39.487495" ys="2.871699"/>
<move id="19" enabled="0" row="0" col="19" xp="7835" yp="679" xs="40.084194" ys="2.871699"/>
<move id="20" enabled="0" row="0" col="20" xp="7950" yp="679" xs="40.680893" ys="2.871699"/>
<move id="21" enabled="0" row="0" col="21" xp="8065" yp="679" xs="41.277592" ys="2.871699"/>
<move id="22" enabled="0" row="0" col="22" xp="8180" yp="679" xs="41.874290" ys="2.871699"/>
<move id="23" enabled="0" row="0" col="23" xp="8294" yp="679" xs="42.470989" ys="2.871699"/>
<move id="24" enabled="0" row="0" col="24" xp="8409" yp="679" xs="43.067688" ys="2.871699"/>
<move id="25" enabled="0" row="0" col="25" xp="8524" yp="679" xs="43.664387" ys="2.871699"/>
And I use the scripts listed here to prepend the xp value and yp value to corresponding image filenames:
import os
import re
import xml.etree.ElementTree as ET
# Function to parse XML and extract xp and yp values
def extract_xp_yp_from_xml(xml_file):
tree = ET.parse(xml_file)
root = tree.getroot()
xp_values = []
yp_values = []
enabled_flag = False
for move in root.iter('move'):
enabled = move.get('enabled')
if enabled == '1':
xp = int(move.get('xp'))
yp = int(move.get('yp'))
xp_values.append(xp)
yp_values.append(yp)
return xp_values, yp_values
# Directory path containing image files
image_dir_path = "/Users/shihongwu/S001_bkgnd_dapi_bkgnd_fitc_bkgnd_cy3_bkgnd_cy5"
# List all .tif files in the directory that contain "P" in the filename
image_files = [f for f in os.listdir(image_dir_path) if f.endswith(".tif") and "P" in f]
# Sort the image files by P number
image_files.sort(key=lambda x: int(re.search(r"_P(\d+)_", x).group(1)))
# XML file path containing xp and yp values
xml_file_path = "/Users/shihongwu/moves.xml"
# Extract xp and yp values from the XML
xp_values, yp_values = extract_xp_yp_from_xml(xml_file_path)
# Check if the number of xp and yp values matches the number of image files * 4 (4 channels per position)
if len(xp_values) * 4 != len(image_files):
print("Error: Number of xp/yp values does not match the number of image files.", len(xp_values), len(image_files))
else:
# Rename image files based on xp and yp values
for i, image_file in enumerate(image_files):
position_index = i // 4 # Calculate the position index
xp = xp_values[position_index]
yp = yp_values[position_index]
# Prepend xp and yp at the start of the filename
new_name = f"{xp:04d}_{yp:04d}_{os.path.splitext(image_file)[0]}.tif"
os.rename(os.path.join(image_dir_path, image_file), os.path.join(image_dir_path, new_name))
print(f"Renamed {image_file} to {new_name}")
Then based on the scripts you provided, I created the companion file using the following scripts:
import copy
import pathlib
import uuid
import ome_types
import tifffile
def make_ome_pixel(
img_path, sample_ome, position_x, position_y, pixel_size =1
):
img_path = pathlib.Path(img_path)
sample_ome = copy.deepcopy(sample_ome)
pixel = sample_ome.images[0].pixels
pixel.physical_size_x = pixel_size
pixel.physical_size_y = pixel_size
UUID = ome_types.model.TiffData.UUID(
file_name=str(img_path.name),
value=uuid.uuid4().urn
)
tiff_block = pixel.tiff_data_blocks[0]
num_planes = tiff_block.plane_count
tiff_block.uuid = UUID
for i in range(num_planes):
plane = ome_types.model.Plane(
the_c=i, the_z=0, the_t=0,
position_x=position_x, position_y=position_y
)
pixel.planes.append(plane)
return pixel
# Create a function to extract the sorting key based on a specific part of the name
def custom_sorting_key(file_path):
# Split the file name by '_' and extract the part you want to sort by
parts = file_path.stem.split('_')
# Assuming you want to sort by the second part, convert it to an integer
# You can change the index (e.g., [0] or [2]) based on the part you want to sort by
part_15 = parts[14].lstrip('P')
# Define a mapping for the substrings to prioritize their sorting order
sorting_order = {"dapi": 0, "fitc": 1, "cy3": 2, "cy5": 3}
# Extract the substring (e.g., "dapi") from the filename
substring = parts[-1]
# Combine the sorting keys: "P" number and sorting order of substring
sorting_key = (int(part_15), sorting_order.get(substring, 999))
return sorting_key
# Obtain a list of file paths and sort them using the custom sorting key
img_paths = sorted(pathlib.Path('/Users/shihongwu/S001_bkgnd_dapi_bkgnd_fitc_bkgnd_cy3_bkgnd_cy5').glob('*P*.tif'), key=custom_sorting_key)
# generate a ome-xml template
tifffile.imwrite('sample.ome.tif', tifffile.imread(img_paths[0]))
sample_ome = ome_types.from_tiff('sample.ome.tif')
# parse x-y positions from filename
xy_positions = [p.name.split('_')[:2] for p in img_paths]
pixels = [
make_ome_pixel(path, sample_ome, float(pos_x), float(pos_y), 0.325)
for path, (pos_x, pos_y) in zip(img_paths, xy_positions)
]
omexml = ome_types.model.OME()
omexml.images = [ome_types.model.Image(pixels=p) for p in pixels]
out_path = '/Users/shihongwu/S001_bkgnd_dapi_bkgnd_fitc_bkgnd_cy3_bkgnd_cy5/test.companion.ome'
print(f"Writing to {out_path}\n")
with open(out_path, 'w') as f:
f.write(omexml.to_xml())
Then I ran ashlar with ashlar ashlar /Users/shihongwu/S001_bkgnd_dapi_bkgnd_fitc_bkgnd_cy3_bkgnd_cy5/test.companion.ome I got this warning “WARNING: Stage coordinates’ measurement unit is undefined; assuming μm.” Ashlar finished its job, but the stitching seems weird. the image it generated doesn’t match the real one. I was wondering if it’s because of the stage coordinates which in my case is xp and yp are not in μm.
Some parameters about the image tile: 2040x2040, pixel size: 0.325μm, overlap = 0.1
Hope these are helpful!
Thanks for your time and assistance! Much appreciated.
About this issue
- Original URL
- State: closed
- Created 9 months ago
- Comments: 18
Hi @Yu-AnChen please find all the materials here: https://drive.google.com/drive/folders/1pW7TiutQmxELv902BtUwrO0rKuaCn3rg?usp=share_link
Thanks a lot!
Hi @Yu-AnChen, Thanks for helping me with this issue! I just tried what you suggested! the preview looks very promising. I think it worked! Please see the preview below
Thanks a lot! I’ll run ashlar tonight on my laptop and hopefully get the image tmr morning. Then I’ll update here!