Markup script misplacing control points

My mark up script is placing mark up control points inaccurately when loaded at a later point: I am creating a mark up script by placing markup control points manually, then saving the mark up script as a json file. Later, when I drag and drop the mark up json file back into Slicer, the control point RAS values have changed positive/negative values, resulting in mark up fiducial points which are no longer co-registered to the original MRI.

Here is an example of the correctly positioned mark up script which I save as a JSON file:

{“@schema”: “”,
“markups”: [{“type”: “Fiducial”, “coordinateSystem”: “LPS”, “controlPoints”: [
{ “label”: “A”, “position”: [ -13.225200 , 328.873000 , 589.979000 ]},
{ “label”: “A1”, “position”: [ -5.954760 , 166.782000 , 581.928000 ]}

However the markup file when later drag and dropped into Slicer inverts the +/- values, giving positions as following. The position values are the same, but the +/- have been inverted:

{ “label”: “A”, “position”: [ 13.225200 , -328.873000 , 589.979000 ]},
{ “label”: “A1”, “position”: [ 5.954760 , -166.782000 , 581.928000 ]}

Can anyone please let me know what I am doing wrong?

Thanks in advance.

Markup coordinates are displayed as RAS, but written in LPS. So, the sign flip in the first two coordinates are normal. For correctly written and interpreted json/fcsv files, this would not affect the position of the landmarks, it is simply different ways of book keeping.

But are you saying that when you load your JSON file, the coordinates are not retrieved correctly?

I suspect your JSON is ill-formed. It should look something like this:.

    "@schema": "",
    "markups": [
            "type": "Fiducial",
            "coordinateSystem": "LPS",
            "coordinateUnits": "mm",
            "locked": false,
            "fixedNumberOfControlPoints": false,
            "labelFormat": "%N-%d",
            "lastUsedControlPointNumber": 2,
            "controlPoints": [
                    "id": "1",
                    "label": "A",
                    "description": "",
                    "associatedNodeID": "",
                    "position": [-13.225, 328.873, 589.979],
                    "orientation": [-1.0, -0.0, -0.0, -0.0, -1.0, -0.0, 0.0, 0.0, 1.0],
                    "selected": true,
                    "locked": false,
                    "visibility": true,
                    "positionStatus": "defined"
                    "id": "2",
                    "label": "A1",
                    "description": "",
                    "associatedNodeID": "",
                    "position": [-5.955, 166.782, 581.928],
                    "orientation": [-1.0, -0.0, -0.0, -0.0, -1.0, -0.0, 0.0, 0.0, 1.0],
                    "selected": true,
                    "locked": false,
                    "visibility": true,
                    "positionStatus": "defined"
            "measurements": [],
            "display": {
                "visibility": true,
                "opacity": 1.0,
                "color": [0.4, 1.0, 0.0],
                "selectedColor": [0.5529411764705883, 0.4745098039215686, 1.0],
                "activeColor": [1.0, 0.8823529411764706, 0.7372549019607844],
                "propertiesLabelVisibility": false,
                "pointLabelsVisibility": true,
                "textScale": 2.9000000000000005,
                "glyphType": "StarBurst2D",
                "glyphScale": 1.7000000000000002,
                "glyphSize": 5.0,
                "useGlyphScale": true,
                "sliceProjection": false,
                "sliceProjectionUseFiducialColor": true,
                "sliceProjectionOutlinedBehindSlicePlane": false,
                "sliceProjectionColor": [1.0, 1.0, 1.0],
                "sliceProjectionOpacity": 0.6,
                "lineThickness": 0.2,
                "lineColorFadingStart": 1.0,
                "lineColorFadingEnd": 10.0,
                "lineColorFadingSaturation": 1.0,
                "lineColorFadingHueOffset": 0.0,
                "handlesInteractive": false,
                "translationHandleVisibility": true,
                "rotationHandleVisibility": true,
                "scaleHandleVisibility": true,
                "interactionHandleScale": 1.9000000000000002,
                "snapMode": "toVisibleSurface"

Thanks for this. Yes, when I reload the json file, the markups are no-longer co-registered.
However I have just realized that when I change coordinateSystem to ‘RAS’ and add “coordinateUnits”: “um”, it will load and co-register correctly. So I guess that’s a solution for now. Unless there is something about that that is bad practice.
Thanks again for your response and script.

It might give you some additional problems down the line in terms of interoperability, as most other imaging software expects coordinates in LPS. That way Slicer writes them in LPS as opposed to RAS.

So perhaps you can do the reverse. Keep your JSON header LPS, but multiply the RAS value by -1, -1, before writing it. That should read back correctly as you expect.