Search code examples
imageimage-processingcanvasopenseadragon

openseadragon get selection dataurl/blob


I retrieve a rect from openSeadragonSelection:

viewer:

this.viewer = OpenSeadragon(this.config);
this.selection = this.viewer.selection({
    showConfirmDenyButtons:  true,
    styleConfirmDenyButtons: true,
    returnPixelCoordinates:  true,
    onSelection:  rect => console.log(rect)
});
this.selection.enable();

rect by onSelection:

t.SelectionRect {x: 3502, y: 2265, width: 1122, height: 887, rotation:0, degrees: 0, …}

I have no idea how to get the canvas by rect from my viewer instance.

this.viewer.open(new OpenSeadragon.ImageTileSource(this.getTile(this.src)));

A self implemented imageViewer returned the canvas of the selected area. So I could get the blob and post it to the server:

    onSave(canvas){
        let source = canvas.toDataURL();
        this.setState({source:source, crop: false, angle: 0});
        save(this.dataURItoBlob(source), source.match(new RegExp("\/(.*);"))1]);
    }

    dataURItoBlob(dataURI) {
    // convert base64/URLEncoded data component to raw binary data held in a string
        var byteString;
        if (dataURI.split(',')[0].indexOf('base64') >= 0)
            byteString = atob(dataURI.split(',')[1]);
        else
            byteString = unescape(dataURI.split(',')[1]);
        // separate out the mime component
        var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
        // write the bytes of the string to a typed array
        var ia = new Uint8Array(byteString.length);
        for (var i = 0; i < byteString.length; i++) {
            ia[i] = byteString.charCodeAt(i);
        }
        return new Blob([ia], {type:mimeString});
    }

How can I get the image of the viewer by rect. Rotation should be considered as well.

@iangilman:

Thank's alot for your advice. I created another canvas which I crop and after that put it back into the viewer. I was not sure if something similar was supported by your library yet:

const viewportRect = self.viewer.viewport.imageToViewportRectangle(rect);
const webRect = self.viewer.viewport.viewportToViewerElementRectangle(viewportRect);
const { x, y, width, height } = webRect || {};
const { canvas } = self.viewer.drawer;
let source = canvas.toDataURL();

const img = new Image();
img.onload = function () {
  let croppedCanvas = document.createElement('canvas');
  let ctx = croppedCanvas.getContext('2d');
  croppedCanvas.width = width;
  croppedCanvas.height = height;
  ctx.drawImage(img, x, y, width, height, 0, 0, width, height);
  let croppedSrc = croppedCanvas.toDataURL();
  //update viewer with cropped image
  self.tile = self.getTile(croppedSrc);
  self.ImageTileSource = new OpenSeadragon.ImageTileSource(self.tile);
  self.viewer.open(self.ImageTileSource);
}
img.src = source;

Rotation hasn't been considered yet.


Solution

  • I imagine you'll need to convert the rectangle into the proper coordinates, then create a second canvas and copy the appropriate bit out of the OSD canvas into the second one.

    Looks like maybe the selection rectangle is in image coordinates? The OSD canvas will be in web coordinates, or maybe double that on an HDPI display. OSD has a number of conversion functions, for instance:

    var viewportRect = viewer.viewport.imageToViewportRectangle(imageRect);
    var webRect = viewer.viewport.viewportToViewerElementRectangle(viewportRect);
    

    You can find out the pixel density via OpenSeadragon.pixelDensityRatio.

    Once you have the appropriate rectangle it should be easy to copy out of the one canvas into another. I'm not sure how you incorporate rotation, but it might be as simple as adding a rotation call to one of the canvas contexts.

    Sorry this is kind of vague, but I hope it helps!