spatialdata-plot icon indicating copy to clipboard operation
spatialdata-plot copied to clipboard

Pixel shift in `render_images`

Open quentinblampey opened this issue 1 year ago • 2 comments

Hello,

When plotting an image, it seems that there is a 0.5-pixel shift. This is not visible for large images but becomes very visible if we zoom in, and, mostly, it gets amplified with transformations (a Scale(1000, 1000) transformation will create a shift of 500 pixels).

Minimum reproducible example

import numpy as np
import spatialdata_plot
import spatialdata
from spatialdata.models import Image2DModel
from spatial_image import SpatialImage

image = SpatialImage(np.zeros((1, 4, 8)), dims=("c", "y", "x"))
image = Image2DModel.parse(image)

sdata = spatialdata.SpatialData(images={"image": image})

sdata.pl.render_images().pl.show()

It should render this image (we see that the image is not reaching (8, 4), but (7.5, 3.5) instead): image

Maybe I did something wrong with the x-coords or y-coords? Should we update the coords or they should be fine by default?

Sorry for such a minor issue...

quentinblampey avatar Feb 15 '24 09:02 quentinblampey

Thanks for reporting!

Maybe related to this: https://github.com/scverse/spatialdata/issues/165. Also, if fixed before, better to add a test so that we verify we don't reintroduce this after https://github.com/scverse/spatialdata/issues/308 is closed.

LucaMarconato avatar Feb 19 '24 14:02 LucaMarconato

This is also problematic for the datashader backend, because if an image is rendered before, its limits are used to determine the canvas size => we see an offset of the datashader points compared to what matplotlib would give us (and what the axis labels say!)

e.g.

from spatialdata import SpatialData
from spatialdata.models import Image2DModel, PointsModel
from spatialdata.transformations import Identity
import matplotlib.pyplot as plt

sdata = SpatialData(
    images={
        "image1": Image2DModel.parse(
            np.full((10, 10, 3), fill_value=128), dims=("y", "x", "c")
        )
    },
    points={
        "points1": PointsModel.parse(
            pd.DataFrame({"y": [0.1, 0.1, 0.9, 0.9], "x": [0.1, 0.9, 0.9, 0.1]}),
            transformations={"global": Identity()},
        )
    },
)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
sdata.pl.render_images().pl.render_points("points1", method="datashader", size=40, color="red").pl.show(ax=ax1, title="datashader")
sdata.pl.render_images().pl.render_points("points1", method="matplotlib", color="red", size=2).pl.show(ax=ax2, title="matplotlib")

gives me: Image

@timtreis FYI

Sonja-Stockhaus avatar Oct 25 '24 12:10 Sonja-Stockhaus