You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在OpenSeadragon中以最高分辨率裁剪图片?

客户端用OpenSeadragon裁剪高分辨率图像并导出JPG

先解决你遇到的报错问题

你之前调用setCroppingPolygons时传的是rectForCrop,但这个方法要求传入多边形数组,也就是你定义的polygons变量,正确调用应该是:

polygons = [[rectForCrop.getTopLeft(), rectForCrop.getTopRight(), rectForCrop.getBottomRight(), rectForCrop.getBottomLeft()]]
tiledImage.setCroppingPolygons(polygons);

但要注意:这个方法只是用来修改OpenSeadragon viewer里显示的图像裁剪区域,不能直接导出裁剪后的图片,所以你要实现导出需求的话,得用下面的方案。

客户端裁剪导出高分辨率JPG的实现方案

核心思路是:获取裁剪区域的原始图像坐标,加载对应区域的最高分辨率瓦片,拼接绘制到canvas后导出为JPG。

步骤1:转换坐标(如果用的是视口Rect)

如果你的rectForCrop是视口坐标(viewer内的坐标),先转成图像坐标(图像自身的0-1范围坐标):

const viewer = // 你的OpenSeadragon实例
const viewportRect = rectForCrop; // 用户提供的视口Rect
const imageRect = viewer.viewport.viewportToImageRectangle(viewportRect);

步骤2:获取图像基础信息

const tiledImage = viewer.world.getItemAt(0); // 假设只有一张图,多图的话按需获取
const imageWidth = tiledImage.source.dimensions.x; // 图像原始宽度
const imageHeight = tiledImage.source.dimensions.y; // 图像原始高度
const maxLevel = tiledImage.source.levels.length - 1; // 最高分辨率级别
const level = tiledImage.source.levels[maxLevel];
const tileSize = tiledImage.source.tileSize; // 瓦片尺寸

步骤3:计算裁剪区域在最高分辨率图中的范围

// 图像坐标转原始像素坐标
const x = imageRect.x * imageWidth;
const y = imageRect.y * imageHeight;
const width = imageRect.width * imageWidth;
const height = imageRect.height * imageHeight;

// 最高分辨率图的尺寸
const levelWidth = level.width;
const levelHeight = level.height;
const scale = levelWidth / imageWidth; // 分辨率缩放系数

// 裁剪区域在最高分辨率图中的位置和尺寸
const levelX = x * scale;
const levelY = y * scale;
const levelCropWidth = width * scale;
const levelCropHeight = height * scale;

步骤4:拼接瓦片到Canvas并导出

// 创建Canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = Math.round(width);
canvas.height = Math.round(height);

// 计算需要加载的瓦片范围
const startTileX = Math.floor(levelX / tileSize);
const endTileX = Math.floor((levelX + levelCropWidth) / tileSize);
const startTileY = Math.floor(levelY / tileSize);
const endTileY = Math.floor((levelY + levelCropHeight) / tileSize);

const totalTiles = (endTileX - startTileX + 1) * (endTileY - startTileY + 1);
let loadedTiles = 0;

// 遍历加载每个瓦片并绘制
for (let tileX = startTileX; tileX <= endTileX; tileX++) {
  for (let tileY = startTileY; tileY <= endTileY; tileY++) {
    const tileUrl = tiledImage.source.getTileUrl(maxLevel, tileX, tileY);
    const img = new Image();
    img.crossOrigin = 'anonymous'; // 跨域图片需要设置,服务器要允许跨域
    img.onload = () => {
      // 计算瓦片在Canvas中的绘制位置
      const tileCanvasX = (tileX * tileSize - levelX) / scale;
      const tileCanvasY = (tileY * tileSize - levelY) / scale;
      // 计算瓦片需要绘制的局部区域(边缘瓦片可能只取部分)
      const sourceX = Math.max(0, levelX - tileX * tileSize);
      const sourceY = Math.max(0, levelY - tileY * tileSize);
      const sourceW = Math.min(tileSize, levelX + levelCropWidth - tileX * tileSize);
      const sourceH = Math.min(tileSize, levelY + levelCropHeight - tileY * tileSize);
      // 绘制到Canvas
      ctx.drawImage(
        img,
        sourceX, sourceY, sourceW, sourceH,
        tileCanvasX, tileCanvasY,
        sourceW / scale, sourceH / scale
      );

      loadedTiles++;
      if (loadedTiles === totalTiles) {
        // 所有瓦片绘制完成,导出JPG
        const jpgDataUrl = canvas.toDataURL('image/jpeg', 0.9); // 0.9为图片质量,范围0-1
        // 后续操作:显示到页面或上传服务器
        // 示例:显示预览图
        const preview = document.createElement('img');
        preview.src = jpgDataUrl;
        document.body.appendChild(preview);
        // 示例:上传到服务器(需转换为Blob)
        const blob = dataUrlToBlob(jpgDataUrl);
        const formData = new FormData();
        formData.append('croppedImage', blob, 'crop.jpg');
        fetch('/your-upload-endpoint', {
          method: 'POST',
          body: formData
        });
      }
    };
    img.src = tileUrl;
  }
}

// 辅助函数:DataURL转Blob
function dataUrlToBlob(dataUrl) {
  const arr = dataUrl.split(',');
  const mime = arr[0].match(/:(.*?);/)[1];
  const bstr = atob(arr[1]);
  const u8arr = new Uint8Array(bstr.length);
  for (let i = 0; i < bstr.length; i++) {
    u8arr[i] = bstr.charCodeAt(i);
  }
  return new Blob([u8arr], { type: mime });
}

注意事项

  • 跨域图片需要服务器配置CORS,否则无法加载瓦片绘制
  • 如果图像是单张大图(非瓦片),可以直接用new Image()加载原图后裁剪,不需要处理瓦片
  • 导出的JPG质量可以通过toDataURL的第二个参数调整(0-1,1为最高质量)

内容的提问来源于stack exchange,提问作者gregster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 07:35:22