{"version":3,"sources":["../src/layout-detector.ts","../src/utils/canvas-helper.ts","../src/text-recognizer.ts","../src/reading-order.ts","../src/output-generator.ts","../src/ndl-koten-ocr.ts","../src/worker/ndl-koten-ocr-worker.ts","../src/tei-converter.ts","../src/iiif-processor.ts"],"sourcesContent":["/**\n * NDLKotenOCR Web版 - レイアウト認識モジュール\n *\n * このファイルは、RTMDetモデルを使用して画像内のテキスト領域を検出するモジュールです。\n * 元のPythonコードのsrc/rtmdet.pyを参考に実装しています。\n */\n\nimport * as ort from 'onnxruntime-web';\nimport * as yaml from 'js-yaml';\nimport { createCanvas, getContext2D } from './utils/canvas-helper';\n\ninterface Config {\n  inputShape: number[];\n  scoreThreshold: number;\n  nmsThreshold: number;\n  maxDetections: number;\n  [key: string]: any;\n}\n\n// Removed unused Detection interface\n\n/**\n * RTMDet クラス\n * 画像内のテキスト領域を検出するクラス\n */\nexport class RTMDet {\n  private modelPath: string;\n  private configPath: string | null;\n  private config: Config;\n  private session: ort.InferenceSession | null = null;\n  private initialized: boolean = false;\n  private classNames: string[] = ['text'];\n  private progressCallback?: (progress: number, message: string) => void;\n\n  /**\n   * コンストラクタ\n   */\n  constructor(modelPath: string, config: Partial<Config> = {}, configPath: string | null = null) {\n    this.modelPath = modelPath;\n    this.configPath = configPath;\n    this.config = {\n      inputShape: [1, 3, 1280, 1280],\n      scoreThreshold: 0.3,\n      nmsThreshold: 0.5,\n      maxDetections: 100,\n      ...config,\n    };\n  }\n\n  /**\n   * 設定ファイルを読み込む\n   */\n  async loadConfig(configPath: string | null = null): Promise<Config> {\n    const path = configPath || this.configPath;\n    if (!path) {\n        return this.config;\n    }\n\n    try {\n      // 設定ファイルを取得\n      const response = await fetch(path);\n      if (!response.ok) {\n        throw new Error(\n          `設定ファイルの取得に失敗しました: ${response.statusText}`\n        );\n      }\n\n      const yamlText = await response.text();\n      const yamlConfig = yaml.load(yamlText) as any;\n\n      // レイアウト認識設定を取得\n      if (yamlConfig && yamlConfig.layout_detection) {\n        const layoutConfig = yamlConfig.layout_detection;\n\n        // 設定を更新\n        if (layoutConfig.score_threshold !== undefined) {\n          this.config.scoreThreshold =\n            layoutConfig.score_threshold;\n        }\n        if (layoutConfig.nms_threshold !== undefined) {\n          this.config.nmsThreshold =\n            layoutConfig.nms_threshold;\n        }\n        if (layoutConfig.max_detections !== undefined) {\n          this.config.maxDetections =\n            layoutConfig.max_detections;\n        }\n        if (layoutConfig.input_shape !== undefined) {\n          this.config.inputShape = layoutConfig.input_shape;\n        }\n      }\n\n      return this.config;\n    } catch (error) {\n      return this.config;\n    }\n  }\n\n  /**\n   * 初期化処理\n   * 設定を読み込み、ONNXモデルをロードし、推論セッションを作成します\n   *\n   * @param {string} configPath 設定ファイルのパス（オプション）\n   * @param {(progress: number, message: string) => void} progressCallback 進捗コールバック（オプション）\n   * @returns {Promise<void>}\n   */\n  async initialize(configPath = null, progressCallback?: (progress: number, message: string) => void) {\n    this.progressCallback = progressCallback;\n    try {\n      // 設定ファイルを読み込む\n      if (configPath || this.configPath) {\n        await this.loadConfig(configPath);\n      }\n\n      // WebAssembly実行のためのオプション設定\n      const options = {\n        executionProviders: ['wasm'],\n        graphOptimizationLevel: 'all',\n      };\n\n      // モデルのダウンロード進捗を追跡\n      if (this.progressCallback) {\n        this.progressCallback(0, 'レイアウト検出モデルをダウンロード中...');\n      }\n\n      // モデルのロード（ダウンロード進捗の追跡を含む）\n      this.session = await this.createSessionWithProgress(\n        this.modelPath,\n        options as ort.InferenceSession.SessionOptions,\n        'レイアウト検出モデル'\n      );\n\n      // 入力テンソルの形状を取得 - 修正部分\n      // 注意: 入力形状の取得を試みますが、失敗してもデフォルト値を使用して続行します\n      try {\n        if (\n          this.session &&\n          this.session.inputNames &&\n          this.session.inputNames.length > 0\n        ) {\n          // デフォルトの入力形状をそのまま使用\n        }\n      } catch (shapeError) {\n      }\n\n      this.initialized = true;\n    } catch (error) {\n      throw new Error(\n        `RTMDet モデルの初期化に失敗しました: ${(error as Error).message}`\n      );\n    }\n  }\n\n  /**\n   * 進捗追跡付きでセッションを作成\n   */\n  private async createSessionWithProgress(\n    modelPath: string,\n    options: ort.InferenceSession.SessionOptions,\n    modelName: string\n  ): Promise<ort.InferenceSession> {\n    try {\n      // First, try to fetch the model to track download progress\n      const response = await fetch(modelPath);\n      const contentLength = response.headers.get('content-length');\n      \n      if (contentLength && this.progressCallback) {\n        const total = parseInt(contentLength, 10);\n        let loaded = 0;\n        \n        const reader = response.body!.getReader();\n        const chunks: Uint8Array[] = [];\n        \n        while (true) {\n          const { done, value } = await reader.read();\n          if (done) break;\n          \n          chunks.push(value);\n          loaded += value.length;\n          \n          const progress = Math.round((loaded / total) * 100);\n          this.progressCallback(progress, `${modelName}をダウンロード中... ${progress}%`);\n        }\n        \n        // Combine chunks into a single ArrayBuffer\n        const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);\n        const result = new Uint8Array(totalLength);\n        let position = 0;\n        for (const chunk of chunks) {\n          result.set(chunk, position);\n          position += chunk.length;\n        }\n        \n        if (this.progressCallback) {\n          this.progressCallback(100, `${modelName}のロード中...`);\n        }\n        \n        // Create session from ArrayBuffer\n        return await ort.InferenceSession.create(result.buffer, options);\n      } else {\n        // Fallback to direct loading if content-length is not available\n        return await ort.InferenceSession.create(modelPath, options);\n      }\n    } catch (error) {\n      // Fallback to direct loading on error\n      return await ort.InferenceSession.create(modelPath, options);\n    }\n  }\n\n  /**\n   * 画像の前処理\n   *\n   * @param {ImageData|HTMLImageElement|HTMLCanvasElement} imageData 入力画像\n   * @returns {Object} 前処理された画像データとメタデータ\n   * @private\n   */\n  preprocess(imageData: ImageData | HTMLImageElement | HTMLCanvasElement) {\n    // 入力サイズの取得\n    const [batchSize, channels, height, width] =\n      this.config.inputShape;\n\n    // 画像のサイズを取得\n    let imgWidth, imgHeight;\n    if (imageData instanceof ImageData) {\n      imgWidth = imageData.width;\n      imgHeight = imageData.height;\n    } else {\n      imgWidth = (imageData as HTMLImageElement).naturalWidth || (imageData as HTMLCanvasElement).width;\n      imgHeight =\n        (imageData as HTMLImageElement).naturalHeight || (imageData as HTMLCanvasElement).height;\n    }\n\n    // Pythonコードと同様に、正方形のパディング画像を作成\n    const maxWH = Math.max(imgWidth, imgHeight);\n\n    // Canvasを使用して画像をパディング\n    const paddingCanvas = createCanvas(maxWH, maxWH);\n    const paddingCtx = getContext2D(paddingCanvas);\n\n    // 背景を黒で塗りつぶし\n    paddingCtx!.fillStyle = 'rgb(0, 0, 0)';\n    paddingCtx!.fillRect(0, 0, maxWH, maxWH);\n\n    // 元の画像を左上に配置\n    if (imageData instanceof ImageData) {\n      // ImageDataの場合は一度canvasに描画してから処理\n      const tempCanvas = createCanvas(imageData.width, imageData.height);\n      const tempCtx = getContext2D(tempCanvas);\n      tempCtx.putImageData(imageData, 0, 0);\n      paddingCtx!.drawImage(tempCanvas, 0, 0);\n    } else {\n      // HTMLImageElement または HTMLCanvasElement の場合\n      paddingCtx!.drawImage(imageData, 0, 0);\n    }\n\n    // パディングされた画像をモデルの入力サイズにリサイズ\n    const canvas = createCanvas(width, height);\n    const ctx = getContext2D(canvas);\n\n    // パディングされた画像をリサイズ\n    ctx!.drawImage(\n      paddingCanvas,\n      0,\n      0,\n      maxWH,\n      maxWH,\n      0,\n      0,\n      width,\n      height\n    );\n\n    // Canvas から画素データを取得\n    const imageDataResized = ctx!.getImageData(\n      0,\n      0,\n      width,\n      height\n    );\n    const data = imageDataResized.data;\n\n    // Float32Array に変換し、正規化 (0-255 -> 0-1)\n    // NHWC (バッチ, 高さ, 幅, チャンネル) から NCHW (バッチ, チャンネル, 高さ, 幅) に変換\n    const inputTensor = new Float32Array(\n      batchSize * channels * height * width\n    );\n\n    // チャンネルごとの平均と標準偏差（正規化用）\n    const mean = [123.675, 116.28, 103.53]; // RGB\n    const std = [58.395, 57.12, 57.375]; // RGB\n\n    // 画素データの変換と正規化\n    for (let h = 0; h < height; h++) {\n      for (let w = 0; w < width; w++) {\n        const pixelOffset = (h * width + w) * 4; // RGBA\n\n        // RGB値を取得し、正規化\n        for (let c = 0; c < channels; c++) {\n          const value = data[pixelOffset + c];\n          // NCHW形式でのインデックス計算\n          const tensorIdx =\n            c * height * width + h * width + w;\n          // 正規化: (pixel - mean) / std\n          inputTensor[tensorIdx] =\n            (value - mean[c]) / std[c];\n        }\n      }\n    }\n\n    // メタデータを返す（後処理で使用）\n    const metadata = {\n      originalWidth: imgWidth,\n      originalHeight: imgHeight,\n      maxWH: maxWH,\n      inputWidth: width,\n      inputHeight: height,\n    };\n\n    return {\n      tensor: inputTensor,\n      metadata: metadata,\n    };\n  }\n\n  /**\n   * 検出結果の後処理\n   *\n   * @param {Object} outputs モデルの出力結果\n   * @param {Object} metadata 前処理で生成されたメタデータ\n   * @returns {Array} 検出結果の配列\n   * @private\n   */\n  postprocess(outputs: any, metadata: any) {\n    const dets = outputs['dets'].data;\n    const labels = outputs['labels'].data;\n\n    const detections = [];\n\n    const numDetections = dets.length / 5;\n\n    for (let i = 0; i < numDetections; i++) {\n      const x1 = dets[i * 5 + 0];\n      const y1 = dets[i * 5 + 1];\n      const x2 = dets[i * 5 + 2];\n      const y2 = dets[i * 5 + 3];\n      const score = dets[i * 5 + 4];\n      const classId = Number(labels[i]);\n      if (score >= this.config.scoreThreshold) {\n        // ✅ 入力画像のサイズ（model input）→ 元画像サイズへ変換\n        const normX1 = x1 / this.config.inputShape[3]; // width\n        const normY1 = y1 / this.config.inputShape[2]; // height\n        const normX2 = x2 / this.config.inputShape[3];\n        const normY2 = y2 / this.config.inputShape[2];\n\n        const squareSize = metadata.maxWH;\n\n        const origX1 = normX1 * squareSize;\n        const origY1 = normY1 * squareSize;\n        const origX2 = normX2 * squareSize;\n        const origY2 = normY2 * squareSize;\n        const boxHeight = origY2 - origY1;\n        const deltaH = boxHeight * 0.02;\n\n        detections.push({\n          box: [\n            Math.max(0, Math.round(origX1)),\n            Math.max(0, Math.round(origY1 - deltaH)), // ⬅ 上方向に拡張\n            Math.min(\n              metadata.originalWidth,\n              Math.round(origX2)\n            ),\n            Math.min(\n              metadata.originalHeight,\n              Math.round(origY2 + deltaH)\n            ), // ⬅ 下方向に拡張\n          ],\n          score,\n          class: classId,\n          className:\n            this.classNames?.[classId] ??\n            `class_${classId}`,\n        });\n      }\n    }\n\n    const nmsResults = this.applyNMS(\n      detections,\n      this.config.nmsThreshold\n    );\n\n    return nmsResults;\n  }\n\n  /**\n   * Non-Maximum Suppression (NMS) の適用\n   * 重複する検出結果を除去します\n   *\n   * @param {Array} detections 検出結果の配列\n   * @param {number} threshold IoU閾値\n   * @returns {Array} NMS適用後の検出結果\n   * @private\n   */\n  applyNMS(detections: any[], threshold: number) {\n    // スコアでソート（降順）\n    const sortedDetections = [...detections].sort(\n      (a, b) => b.score - a.score\n    );\n    const selected = [];\n\n    while (sortedDetections.length > 0) {\n      // スコアが最大の検出を選択\n      const current = sortedDetections.shift();\n      selected.push(current);\n\n      // 残りの検出と比較\n      for (\n        let i = sortedDetections.length - 1;\n        i >= 0;\n        i--\n      ) {\n        const iou = this.calculateIoU(\n          current.box,\n          sortedDetections[i].box\n        );\n        if (iou >= threshold) {\n          // IoUが閾値以上なら除去\n          sortedDetections.splice(i, 1);\n        }\n      }\n\n      // 最大検出数に達したら終了\n      if (selected.length >= this.config.maxDetections) {\n        break;\n      }\n    }\n\n    return selected;\n  }\n\n  /**\n   * Intersection over Union (IoU) の計算\n   *\n   * @param {Array} boxA 1つ目のバウンディングボックス [x1, y1, x2, y2]\n   * @param {Array} boxB 2つ目のバウンディングボックス [x1, y1, x2, y2]\n   * @returns {number} IoU値 (0-1)\n   * @private\n   */\n  calculateIoU(boxA: number[], boxB: number[]) {\n    // 交差領域の計算\n    const xA = Math.max(boxA[0], boxB[0]);\n    const yA = Math.max(boxA[1], boxB[1]);\n    const xB = Math.min(boxA[2], boxB[2]);\n    const yB = Math.min(boxA[3], boxB[3]);\n\n    // 交差領域の面積\n    const intersectionArea =\n      Math.max(0, xB - xA) * Math.max(0, yB - yA);\n    if (intersectionArea === 0) {\n      return 0;\n    }\n\n    // 各ボックスの面積\n    const boxAArea =\n      (boxA[2] - boxA[0]) * (boxA[3] - boxA[1]);\n    const boxBArea =\n      (boxB[2] - boxB[0]) * (boxB[3] - boxB[1]);\n\n    // IoUの計算\n    return (\n      intersectionArea /\n      (boxAArea + boxBArea - intersectionArea)\n    );\n  }\n\n  /**\n   * 画像内のテキスト領域を検出\n   *\n   * @param {ImageData|HTMLImageElement|HTMLCanvasElement} imageData 入力画像\n   * @returns {Promise<Array>} 検出結果の配列\n   */\n  async detect(imageData: ImageData | HTMLImageElement | HTMLCanvasElement) {\n    if (!this.initialized) {\n      throw new Error(\n        'RTMDet モデルが初期化されていません'\n      );\n    }\n\n    try {\n      const { tensor, metadata } =\n        this.preprocess(imageData);\n\n      const inputTensor = new ort.Tensor(\n        'float32',\n        tensor,\n        this.config.inputShape\n      );\n      const feeds: Record<string, ort.Tensor> = {};\n      feeds[this.session!.inputNames[0]] = inputTensor;\n\n      const outputs = await this.session!.run(feeds);\n\n      const detections = this.postprocess(\n        outputs,\n        metadata\n      );\n\n      return detections;\n    } catch (error) {\n      throw new Error(\n        `検出処理に失敗しました: ${(error as Error).message}`\n      );\n    }\n  }\n}\n","/**\n * Canvas helper for Web Worker compatibility\n */\n\n/**\n * 環境に応じて適切なCanvasを作成\n */\nexport function createCanvas(width: number, height: number): HTMLCanvasElement | OffscreenCanvas {\n  // Web Worker環境の場合\n  if (typeof OffscreenCanvas !== 'undefined' && typeof document === 'undefined') {\n    return new OffscreenCanvas(width, height);\n  }\n\n  // ブラウザのメインスレッドの場合\n  if (typeof document !== 'undefined') {\n    const canvas = document.createElement('canvas');\n    canvas.width = width;\n    canvas.height = height;\n    return canvas;\n  }\n\n  // Node.js環境など（エラー）\n  throw new Error('No canvas implementation available');\n}\n\n/**\n * 2Dコンテキストを取得\n */\nexport function getContext2D(canvas: HTMLCanvasElement | OffscreenCanvas): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D {\n  const ctx = canvas.getContext('2d');\n  if (!ctx) {\n    throw new Error('Failed to get 2D context');\n  }\n  return ctx as any;\n}","/**\n * NDLKotenOCR Web版 - 文字列認識モジュール\n *\n * このファイルは、PARSeqモデルを使用して検出されたテキスト領域内の文字を認識するモジュールです。\n * 元のPythonコードのsrc/parseq.pyを参考に実装しています。\n */\n\nimport * as ort from 'onnxruntime-web';\nimport * as yaml from 'js-yaml';\nimport { createCanvas, getContext2D } from './utils/canvas-helper';\n\n/**\n * PARSEQ クラス\n * 画像内のテキストを認識するクラス\n */\ninterface ParseqConfig {\n  inputShape: number[];\n  charList: string[];\n  maxLength: number;\n  [key: string]: any;\n}\n\nexport class PARSEQ {\n  private modelPath: string;\n  private configPath: string | null;\n  private charListPath: string | null;\n  private config: ParseqConfig;\n  private session: ort.InferenceSession | null = null;\n  private initialized: boolean = false;\n  private progressCallback?: (progress: number, message: string) => void;\n\n  /**\n   * コンストラクタ\n   *\n   * @param {string} modelPath モデルファイルのパス\n   * @param {Object} config 設定オブジェクト\n   * @param {string} configPath 設定ファイルのパス（オプション）\n   * @param {string} charListPath 文字リストファイルのパス（オプション）\n   */\n  constructor(\n    modelPath: string,\n    config: Partial<ParseqConfig> = {},\n    configPath: string | null = null,\n    charListPath: string | null = null\n  ) {\n    this.modelPath = modelPath;\n    this.configPath = configPath;\n    this.charListPath = charListPath;\n    this.config = {\n      inputShape: [1, 3, 32, 384], // デフォルト入力サイズ\n      charList: [], // 文字リスト\n      maxLength: 25, // 最大文字列長\n      ...config,\n    };\n  }\n\n  /**\n   * 設定ファイルを読み込む\n   *\n   * @param {string} configPath 設定ファイルのパス\n   * @returns {Promise<Object>} 読み込まれた設定\n   */\n  async loadConfig(configPath = null) {\n    const path = configPath || this.configPath;\n    if (!path) {\n      return this.config;\n    }\n\n    try {\n      // 設定ファイルを取得\n      const response = await fetch(path);\n      if (!response.ok) {\n        throw new Error(\n          `設定ファイルの取得に失敗しました: ${response.statusText}`\n        );\n      }\n\n      const yamlText = await response.text();\n      const yamlConfig = yaml.load(yamlText) as any;\n\n      // 文字認識設定を取得\n      if (yamlConfig && yamlConfig.text_recognition) {\n        const textConfig = yamlConfig.text_recognition;\n\n        // 設定を更新\n        if (textConfig.input_shape !== undefined) {\n          this.config.inputShape = textConfig.input_shape;\n        }\n        if (textConfig.max_length !== undefined) {\n          this.config.maxLength = textConfig.max_length;\n        }\n      }\n\n      // 文字リストを取得\n      this.config.charList =\n        yamlConfig.model.charset_train.split('');\n\n      return this.config;\n    } catch (error) {\n      return this.config;\n    }\n  }\n\n  /**\n   * 文字リストファイルを読み込む\n   *\n   * @param {string} charListPath 文字リストファイルのパス\n   * @returns {Promise<Array<string>>} 読み込まれた文字リスト\n   */\n  async loadCharList(charListPath = null) {\n    const path = charListPath || this.charListPath;\n    if (!path) {\n      return this.config.charList;\n    }\n\n    try {\n      // 文字リストファイルを取得\n      const response = await fetch(path);\n      if (!response.ok) {\n        throw new Error(\n          `文字リストファイルの取得に失敗しました: ${response.statusText}`\n        );\n      }\n\n      const yamlText = await response.text();\n      const yamlConfig = yaml.load(yamlText) as any;\n\n      // 文字リストを構築\n      if (\n        yamlConfig &&\n        yamlConfig.model &&\n        yamlConfig.model.charset_train\n      ) {\n        const charListStr = yamlConfig.model.charset_train;\n        this.config.charList = charListStr.split('');\n      }\n\n      return this.config.charList;\n    } catch (error) {\n      return this.config.charList;\n    }\n  }\n\n  /**\n   * 初期化処理\n   * 設定を読み込み、ONNXモデルをロードし、推論セッションを作成します\n   *\n   * @param {string} configPath 設定ファイルのパス（オプション）\n   * @param {string} charListPath 文字リストファイルのパス（オプション）\n   * @param {(progress: number, message: string) => void} progressCallback 進捗コールバック（オプション）\n   * @returns {Promise<void>}\n   */\n  async initialize(configPath = null, charListPath = null, progressCallback?: (progress: number, message: string) => void) {\n    this.progressCallback = progressCallback;\n    try {\n      // 設定ファイルを読み込む\n      if (configPath || this.configPath) {\n        await this.loadConfig(configPath);\n      }\n\n      // 文字リストファイルを読み込む\n      if (charListPath || this.charListPath) {\n        await this.loadCharList(charListPath);\n      }\n\n      // WebAssembly実行のためのオプション設定\n      const options = {\n        executionProviders: ['wasm'],\n        graphOptimizationLevel: 'all',\n      };\n\n      // モデルのダウンロード進捗を追跡\n      if (this.progressCallback) {\n        this.progressCallback(0, '文字認識モデルをダウンロード中...');\n      }\n\n      // モデルのロード（ダウンロード進捗の追跡を含む）\n      this.session = await this.createSessionWithProgress(\n        this.modelPath,\n        options as ort.InferenceSession.SessionOptions,\n        '文字認識モデル'\n      );\n\n      // 入力テンソルの形状を取得 - 修正部分\n      // 注意: 入力形状の取得を試みますが、失敗してもデフォルト値を使用して続行します\n      try {\n        if (\n          this.session &&\n          this.session.inputNames &&\n          this.session.inputNames.length > 0\n        ) {\n          // デフォルトの入力形状をそのまま使用\n        }\n      } catch (shapeError) {\n      }\n\n      this.initialized = true;\n    } catch (error) {\n      throw new Error(\n        `PARSEQ モデルの初期化に失敗しました: ${(error as Error).message}`\n      );\n    }\n  }\n\n  /**\n   * 進捗追跡付きでセッションを作成\n   */\n  private async createSessionWithProgress(\n    modelPath: string,\n    options: ort.InferenceSession.SessionOptions,\n    modelName: string\n  ): Promise<ort.InferenceSession> {\n    try {\n      // First, try to fetch the model to track download progress\n      const response = await fetch(modelPath);\n      const contentLength = response.headers.get('content-length');\n      \n      if (contentLength && this.progressCallback) {\n        const total = parseInt(contentLength, 10);\n        let loaded = 0;\n        \n        const reader = response.body!.getReader();\n        const chunks: Uint8Array[] = [];\n        \n        while (true) {\n          const { done, value } = await reader.read();\n          if (done) break;\n          \n          chunks.push(value);\n          loaded += value.length;\n          \n          const progress = Math.round((loaded / total) * 100);\n          this.progressCallback(progress, `${modelName}をダウンロード中... ${progress}%`);\n        }\n        \n        // Combine chunks into a single ArrayBuffer\n        const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);\n        const result = new Uint8Array(totalLength);\n        let position = 0;\n        for (const chunk of chunks) {\n          result.set(chunk, position);\n          position += chunk.length;\n        }\n        \n        if (this.progressCallback) {\n          this.progressCallback(100, `${modelName}のロード中...`);\n        }\n        \n        // Create session from ArrayBuffer\n        return await ort.InferenceSession.create(result.buffer, options);\n      } else {\n        // Fallback to direct loading if content-length is not available\n        return await ort.InferenceSession.create(modelPath, options);\n      }\n    } catch (error) {\n      // Fallback to direct loading on error\n      return await ort.InferenceSession.create(modelPath, options);\n    }\n  }\n\n  /**\n   * 画像の前処理\n   *\n   * @param {ImageData|HTMLImageElement|HTMLCanvasElement} imageData 入力画像\n   * @returns {Float32Array} 前処理された画像データ\n   * @private\n   */\n  preprocess(imageData: ImageData | HTMLImageElement | HTMLCanvasElement) {\n    const [batchSize, channels, height, width] =\n      this.config.inputShape;\n\n    // 画像サイズ取得\n    let imgWidth, imgHeight;\n    if (imageData instanceof ImageData) {\n      imgWidth = imageData.width;\n      imgHeight = imageData.height;\n    } else {\n      imgWidth = (imageData as HTMLImageElement).naturalWidth || (imageData as HTMLCanvasElement).width;\n      imgHeight =\n        (imageData as HTMLImageElement).naturalHeight || (imageData as HTMLCanvasElement).height;\n    }\n\n    // キャンバス準備（回転含むので一旦大きめ）\n    const canvas = createCanvas(0, 0); // サイズは後で設定\n    const ctx = getContext2D(canvas);\n\n    // let rotated = false; // unused variable removed\n    if (imgHeight > imgWidth) {\n      // 縦長画像は90度回転（時計回り）\n      canvas.width = imgHeight;\n      canvas.height = imgWidth;\n      ctx!.translate(canvas.width / 2, canvas.height / 2);\n      ctx!.rotate(-Math.PI / 2);\n      ctx!.translate(-canvas.height / 2, -canvas.width / 2);\n      // rotated = true;\n    } else {\n      canvas.width = imgWidth;\n      canvas.height = imgHeight;\n    }\n\n    // 描画\n    if (imageData instanceof ImageData) {\n      const tempCanvas = createCanvas(imageData.width, imageData.height);\n      const tempCtx = getContext2D(tempCanvas);\n      tempCtx!.putImageData(imageData, 0, 0);\n      ctx!.drawImage(tempCanvas, 0, 0);\n    } else {\n      ctx!.drawImage(imageData, 0, 0);\n    }\n\n    // 固定リサイズ（アスペクト比無視）→ Pythonと一致\n    const resizeCanvas = createCanvas(width, height);\n    const resizeCtx = getContext2D(resizeCanvas);\n    resizeCtx!.drawImage(canvas, 0, 0, width, height);\n\n    const resizedImageData = resizeCtx!.getImageData(\n      0,\n      0,\n      width,\n      height\n    );\n    const data = resizedImageData.data;\n\n    // Float32Arrayに変換（正規化: [-1, 1]）\n    const inputTensor = new Float32Array(\n      batchSize * channels * height * width\n    );\n    for (let h = 0; h < height; h++) {\n      for (let w = 0; w < width; w++) {\n        const pixelOffset = (h * width + w) * 4;\n        for (let c = 0; c < channels; c++) {\n          const value = data[pixelOffset + c] / 255.0;\n          const tensorIdx =\n            c * height * width + h * width + w;\n          inputTensor[tensorIdx] = 2.0 * (value - 0.5);\n        }\n      }\n    }\n\n    return inputTensor;\n  }\n\n  /**\n   * 認識結果の後処理\n   *\n   * @param {Object} outputs モデルの出力結果\n   * @returns {string} 認識されたテキスト\n   * @private\n   */\n  postprocess(outputs: any) {\n    const outputNames = this.session!.outputNames;\n    const rawLogits = outputs[outputNames[0]].data;\n    const logits = Array.from(rawLogits).map((value) =>\n      typeof value === 'bigint' ? Number(value) : value\n    );\n\n    const [_batchSize, seqLength, vocabSize] =\n      outputs[outputNames[0]].dims;\n\n    const resultClassIds = [];\n\n    for (let i = 0; i < seqLength; i++) {\n      const scores = [];\n      for (let j = 0; j < vocabSize; j++) {\n        scores.push(logits[i * vocabSize + j]);\n      }\n\n      // 最大スコアとインデックスを取得\n      const maxScore = Math.max(...scores as number[]);\n      const maxIndex = scores.indexOf(maxScore);\n      // <eos> トークン（ID=0）が出たら終了（Pythonと一致）\n      if (maxIndex === 0) break;\n      // 特殊トークン（<s>, </s>, <pad>, <unk>）は除外\n      if (maxIndex < 4) continue;\n\n      resultClassIds.push(maxIndex - 1); // Pythonと同様に charlist の 0-index に合わせる\n    }\n    // 文字リストから文字を取得\n    const resultText = [];\n    // 連続を除外して文字列を作成\n    let prevClassId = -1;\n    for (const classId of resultClassIds) {\n      if (classId !== prevClassId) {\n        resultText.push(this.config.charList[classId]);\n        prevClassId = classId;\n      }\n    }\n\n    return resultText.join('');\n  }\n\n  /**\n   * 画像内のテキストを認識\n   *\n   * @param {ImageData|HTMLImageElement|HTMLCanvasElement} imageData 入力画像\n   * @returns {Promise<string>} 認識されたテキスト\n   */\n  async read(imageData: ImageData | HTMLImageElement | HTMLCanvasElement) {\n    if (!this.initialized) {\n      throw new Error(\n        'PARSEQ モデルが初期化されていません。initialize() を先に呼び出してください。'\n      );\n    }\n\n    try {\n      // 前処理\n      const tensor = this.preprocess(imageData);\n\n      // 推論用の入力データを作成\n      const inputTensor = new ort.Tensor(\n        'float32',\n        tensor,\n        this.config.inputShape\n      );\n      const feeds: Record<string, ort.Tensor> = {};\n      feeds[this.session!.inputNames[0]] = inputTensor;\n\n      // 推論実行\n      const outputs = await this.session!.run(feeds);\n\n      // 後処理\n      const text = this.postprocess(outputs);\n\n      return text;\n    } catch (error) {\n      throw new Error(\n        `認識処理に失敗しました: ${(error as Error).message}`\n      );\n    }\n  }\n}\n","/**\n * NDLKotenOCR Web版 - 読み順処理モジュール\n *\n * このファイルは、検出されたテキスト領域を適切な読み順に整列するモジュールです。\n * 元のPythonコードのsrc/reading_order/xy_cut/eval.pyを参考に実装しています。\n */\n\nimport * as yaml from 'js-yaml';\n\n/**\n * 設定インターface\n */\ninterface ReadingOrderConfig {\n  verticalMode: boolean;\n}\n\n/**\n * 検出結果のインターface\n */\ninterface Detection {\n  box: [number, number, number, number];\n  confidence?: number;\n  text?: string;\n  id?: number;\n  center?: {\n    x: number;\n    y: number;\n  };\n}\n\n\n/**\n * 読み順処理の設定\n */\nconst defaultConfig: ReadingOrderConfig = {\n  verticalMode: true, // 縦書きモード（true: 縦書き, false: 横書き）\n};\n\n/**\n * 設定ファイルを読み込む\n *\n * @param {string} configPath 設定ファイルのパス\n * @returns {Promise<ReadingOrderConfig>} 読み込まれた設定\n */\nexport async function loadConfig(configPath: string | null): Promise<ReadingOrderConfig> {\n  const config = { ...defaultConfig };\n\n  if (!configPath) {\n    return config;\n  }\n\n  try {\n    // 設定ファイルを取得\n    const response = await fetch(configPath);\n    if (!response.ok) {\n      throw new Error(\n        `設定ファイルの取得に失敗しました: ${response.statusText}`\n      );\n    }\n\n    const yamlText = await response.text();\n    const yamlConfig = yaml.load(yamlText) as any;\n\n    // 読み順処理設定を取得\n    if (yamlConfig && yamlConfig.reading_order) {\n      const readingConfig = yamlConfig.reading_order as { vertical_mode?: boolean };\n\n      // 設定を更新\n      if (readingConfig.vertical_mode !== undefined) {\n        config.verticalMode = readingConfig.vertical_mode;\n      }\n    }\n\n    return config;\n  } catch (error) {\n    return config;\n  }\n}\n\n/**\n * 読み順処理クラス\n * 検出されたテキスト領域を適切な読み順に整列するクラス\n */\nexport class ReadingOrderProcessor {\n  private config: ReadingOrderConfig;\n\n  /**\n   * コンストラクタ\n   *\n   * @param {ReadingOrderConfig} config 設定オブジェクト\n   */\n  constructor(config: ReadingOrderConfig | null = null) {\n    this.config = config || { ...defaultConfig };\n  }\n\n  /**\n   * XY-cut法による読み順の決定\n   *\n   * @param {Detection[]} detections 検出結果の配列\n   * @param {number} imageWidth 画像の幅\n   * @param {number} imageHeight 画像の高さ\n   * @returns {Detection[]} 読み順に整列された検出結果\n   */\n  process(detections: Detection[], imageWidth: number, imageHeight: number): Detection[] {\n    // 検出結果が空の場合は空配列を返す\n    if (!detections || detections.length === 0) {\n      return [];\n    }\n\n\n    // 検出結果をディープコピー\n    const boxes: Detection[] = JSON.parse(JSON.stringify(detections));\n\n    // 各ボックスにIDを付与\n    boxes.forEach((box, index) => {\n      box.id = index;\n    });\n\n    // XY-cut法による読み順の決定\n    const orderedBoxes = this._xycut(\n      boxes,\n      imageWidth,\n      imageHeight\n    );\n\n    // 元の検出結果の順序を読み順に基づいて並べ替え\n    const orderedDetections: Detection[] = [];\n    for (const box of orderedBoxes) {\n      if (box.id !== undefined) {\n        orderedDetections.push(detections[box.id]);\n      }\n    }\n\n    return orderedDetections;\n  }\n\n  /**\n   * XY-cut法の実装\n   *\n   * @param {Detection[]} boxes バウンディングボックスの配列\n   * @param {number} imageWidth 画像の幅\n   * @param {number} imageHeight 画像の高さ\n   * @returns {Detection[]} 読み順に整列されたボックス\n   * @private\n   */\n  private _xycut(boxes: Detection[], imageWidth: number, imageHeight: number): Detection[] {\n    // ボックスが1つ以下の場合はそのまま返す\n    if (boxes.length <= 1) {\n      return boxes;\n    }\n\n    // 各ボックスの中心座標を計算\n    boxes.forEach((box) => {\n      const [x1, y1, x2, y2] = box.box;\n      box.center = {\n        x: (x1 + x2) / 2,\n        y: (y1 + y2) / 2,\n      };\n    });\n\n    // 縦方向の分割を試みる\n    const verticalGroups = this._trySplitVertical(\n      boxes,\n      imageWidth,\n      imageHeight\n    );\n    if (verticalGroups.length > 1) {\n      // 縦方向の分割が成功した場合、各グループに対して再帰的にXY-cutを適用\n      let result: Detection[] = [];\n      for (const group of verticalGroups) {\n        result = result.concat(\n          this._xycut(group, imageWidth, imageHeight)\n        );\n      }\n      return result;\n    }\n\n    // 横方向の分割を試みる\n    const horizontalGroups = this._trySplitHorizontal(\n      boxes,\n      imageWidth,\n      imageHeight\n    );\n    if (horizontalGroups.length > 1) {\n      // 横方向の分割が成功した場合、各グループに対して再帰的にXY-cutを適用\n      let result: Detection[] = [];\n      for (const group of horizontalGroups) {\n        result = result.concat(\n          this._xycut(group, imageWidth, imageHeight)\n        );\n      }\n      return result;\n    }\n\n    // 分割できない場合は、位置でソート\n    return this._sortBoxesByPosition(boxes);\n  }\n\n  /**\n   * 縦方向の分割を試みる\n   *\n   * @param {Detection[]} boxes バウンディングボックスの配列\n   * @param {number} imageWidth 画像の幅\n   * @param {number} imageHeight 画像の高さ\n   * @returns {Detection[][]} 分割されたグループの配列\n   * @private\n   */\n  private _trySplitVertical(boxes: Detection[], _imageWidth: number, _imageHeight: number): Detection[][] {\n    // ボックスが少ない場合は分割しない\n    if (boxes.length <= 2) {\n      return [boxes];\n    }\n\n    // 各ボックスのY座標の範囲を取得\n    const yRanges = boxes.map((box) => {\n      const [_x1, y1, _x2, y2] = box.box;\n      return { min: y1, max: y2 };\n    });\n\n    // Y座標の最小値と最大値を取得\n    const minY = Math.min(\n      ...yRanges.map((range) => range.min)\n    );\n    const maxY = Math.max(\n      ...yRanges.map((range) => range.max)\n    );\n    const height = maxY - minY;\n\n    // 分割候補となるY座標を探索\n    const candidates: number[] = [];\n    const step = height / 20; // 20分割して探索\n\n    for (let y = minY + step; y < maxY - step; y += step) {\n      let hasIntersection = false;\n\n      // このY座標でボックスが交差するかチェック\n      for (const range of yRanges) {\n        if (range.min < y && range.max > y) {\n          hasIntersection = true;\n          break;\n        }\n      }\n\n      // 交差がなければ分割候補に追加\n      if (!hasIntersection) {\n        candidates.push(y);\n      }\n    }\n\n    // 分割候補がなければ分割しない\n    if (candidates.length === 0) {\n      return [boxes];\n    }\n\n    // 最適な分割位置を選択（中央に近いもの）\n    const middleY = minY + height / 2;\n    const splitY = candidates.reduce((prev, curr) =>\n      Math.abs(curr - middleY) < Math.abs(prev - middleY)\n        ? curr\n        : prev\n    );\n\n    // ボックスを分割\n    const upperGroup = boxes.filter(\n      (box) => box.center && box.center.y < splitY\n    );\n    const lowerGroup = boxes.filter(\n      (box) => box.center && box.center.y >= splitY\n    );\n\n    // どちらかのグループが空の場合は分割しない\n    if (\n      upperGroup.length === 0 ||\n      lowerGroup.length === 0\n    ) {\n      return [boxes];\n    }\n\n    return [upperGroup, lowerGroup];\n  }\n\n  /**\n   * 横方向の分割を試みる\n   *\n   * @param {Detection[]} boxes バウンディングボックスの配列\n   * @param {number} imageWidth 画像の幅\n   * @param {number} imageHeight 画像の高さ\n   * @returns {Detection[][]} 分割されたグループの配列\n   * @private\n   */\n  private _trySplitHorizontal(boxes: Detection[], _imageWidth: number, _imageHeight: number): Detection[][] {\n    // ボックスが少ない場合は分割しない\n    if (boxes.length <= 2) {\n      return [boxes];\n    }\n\n    // 各ボックスのX座標の範囲を取得\n    const xRanges = boxes.map((box) => {\n      const [x1, _y1, x2, _y2] = box.box;\n      return { min: x1, max: x2 };\n    });\n\n    // X座標の最小値と最大値を取得\n    const minX = Math.min(\n      ...xRanges.map((range) => range.min)\n    );\n    const maxX = Math.max(\n      ...xRanges.map((range) => range.max)\n    );\n    const width = maxX - minX;\n\n    // 分割候補となるX座標を探索\n    const candidates: number[] = [];\n    const step = width / 20; // 20分割して探索\n\n    for (let x = minX + step; x < maxX - step; x += step) {\n      let hasIntersection = false;\n\n      // このX座標でボックスが交差するかチェック\n      for (const range of xRanges) {\n        if (range.min < x && range.max > x) {\n          hasIntersection = true;\n          break;\n        }\n      }\n\n      // 交差がなければ分割候補に追加\n      if (!hasIntersection) {\n        candidates.push(x);\n      }\n    }\n\n    // 分割候補がなければ分割しない\n    if (candidates.length === 0) {\n      return [boxes];\n    }\n\n    // 最適な分割位置を選択（中央に近いもの）\n    const middleX = minX + width / 2;\n    const splitX = candidates.reduce((prev, curr) =>\n      Math.abs(curr - middleX) < Math.abs(prev - middleX)\n        ? curr\n        : prev\n    );\n\n    // 縦書きモードに応じて左右のグループを決定\n    let leftGroup: Detection[], rightGroup: Detection[];\n    if (this.config.verticalMode) {\n      // 縦書きモード: 右から左への順序\n      rightGroup = boxes.filter(\n        (box) => box.center && box.center.x >= splitX\n      );\n      leftGroup = boxes.filter(\n        (box) => box.center && box.center.x < splitX\n      );\n      // 縦書きモードでは右から左の順序で返す\n      return [rightGroup, leftGroup];\n    } else {\n      // 横書きモード: 左から右への順序\n      leftGroup = boxes.filter(\n        (box) => box.center && box.center.x < splitX\n      );\n      rightGroup = boxes.filter(\n        (box) => box.center && box.center.x >= splitX\n      );\n      // 横書きモードでは左から右の順序で返す\n      return [leftGroup, rightGroup];\n    }\n  }\n\n  /**\n   * ボックスを位置でソート\n   *\n   * @param {Detection[]} boxes バウンディングボックスの配列\n   * @returns {Detection[]} ソートされたボックス\n   * @private\n   */\n  private _sortBoxesByPosition(boxes: Detection[]): Detection[] {\n    if (this.config.verticalMode) {\n      // 縦書きモード: 右から左、上から下\n      return [...boxes].sort((a, b) => {\n        // centerプロパティが存在しない場合のチェック\n        if (!a.center || !b.center) return 0;\n        \n        // X座標の差が大きい場合（異なる列）\n        const xDiff = b.center.x - a.center.x;\n        if (Math.abs(xDiff) > 20) {\n          return xDiff; // 右から左へ\n        }\n\n        // 同じ列の場合はY座標でソート\n        return a.center.y - b.center.y; // 上から下へ\n      });\n    } else {\n      // 横書きモード: 上から下、左から右\n      return [...boxes].sort((a, b) => {\n        // centerプロパティが存在しない場合のチェック\n        if (!a.center || !b.center) return 0;\n        \n        // Y座標の差が大きい場合（異なる行）\n        const yDiff = a.center.y - b.center.y;\n        if (Math.abs(yDiff) > 20) {\n          return yDiff; // 上から下へ\n        }\n\n        // 同じ行の場合はX座標でソート\n        return a.center.x - b.center.x; // 左から右へ\n      });\n    }\n  }\n}\n\n/**\n * 後方互換性のための関数\n *\n * @param {Detection[]} detections 検出結果の配列\n * @param {number} imageWidth 画像の幅\n * @param {number} imageHeight 画像の高さ\n * @returns {Detection[]} 読み順に整列された検出結果\n */\nexport function evalXml(\n  detections: Detection[],\n  imageWidth: number,\n  imageHeight: number\n): Detection[] {\n  const processor = new ReadingOrderProcessor();\n  return processor.process(\n    detections,\n    imageWidth,\n    imageHeight\n  );\n}\n","/**\n * NDLKotenOCR Web版 - 出力生成モジュール\n *\n * このファイルは、OCR結果を様々な形式（XML, JSON, TXT）で出力するモジュールです。\n * 元のPythonコードのsrc/ndl_parser.pyを参考に実装しています。\n */\n\nimport * as yaml from 'js-yaml';\n\n/**\n * 出力生成の設定\n */\nconst defaultConfig = {\n  // XML出力の設定\n  xml: {\n    includeConfidence: true, // 信頼度スコアを含める\n    prettyPrint: true, // 整形出力\n    encoding: 'UTF-8', // 文字エンコーディング\n  },\n  // JSON出力の設定\n  json: {\n    includeConfidence: true, // 信頼度スコアを含める\n    prettyPrint: true, // 整形出力\n    includeMetadata: true, // メタデータを含める\n  },\n  // テキスト出力の設定\n  txt: {\n    separator: '\\n', // 行区切り文字\n    includeBoundingBox: false, // バウンディングボックス情報を含める\n  },\n};\n\n/**\n * 設定ファイルを読み込む\n *\n * @param {string} configPath 設定ファイルのパス\n * @returns {Promise<Object>} 読み込まれた設定\n */\nexport async function loadConfig(configPath: string | null) {\n  const config = { ...defaultConfig };\n\n  if (!configPath) {\n    return config;\n  }\n\n  try {\n    // 設定ファイルを取得\n    const response = await fetch(configPath);\n    if (!response.ok) {\n      throw new Error(\n        `設定ファイルの取得に失敗しました: ${response.statusText}`\n      );\n    }\n\n    const yamlText = await response.text();\n    const yamlConfig = yaml.load(yamlText) as any;\n\n    // 出力生成設定を取得\n    if (yamlConfig && yamlConfig.output_generation) {\n      const outputConfig = yamlConfig.output_generation;\n\n      // XML設定を更新\n      if (outputConfig.xml) {\n        config.xml = { ...config.xml, ...outputConfig.xml };\n      }\n\n      // JSON設定を更新\n      if (outputConfig.json) {\n        config.json = {\n          ...config.json,\n          ...outputConfig.json,\n        };\n      }\n\n      // テキスト設定を更新\n      if (outputConfig.txt) {\n        config.txt = { ...config.txt, ...outputConfig.txt };\n      }\n    }\n\n    return config;\n  } catch (error) {\n    return config;\n  }\n}\n\n/**\n * 出力生成クラス\n * OCR結果を様々な形式で出力するクラス\n */\ninterface TextElement {\n  id: number;\n  x: number;\n  y: number;\n  width: number;\n  height: number;\n  text: string;\n  confidence?: number;\n}\n\ninterface JSONOutput {\n  document: {\n    image: {\n      name: string;\n      width: number;\n      height: number;\n      text: TextElement[];\n    };\n  };\n  metadata?: {\n    timestamp: string;\n    version: string;\n    engine: string;\n    fileCount?: number;\n  };\n}\n\ninterface OutputConfig {\n  xml: {\n    includeConfidence: boolean;\n    prettyPrint: boolean;\n    encoding: string;\n  };\n  json: {\n    includeConfidence: boolean;\n    prettyPrint: boolean;\n    includeMetadata: boolean;\n  };\n  txt: {\n    separator: string;\n    includeBoundingBox: boolean;\n  };\n}\n\nexport class OutputGenerator {\n  private config: OutputConfig;\n\n  /**\n   * コンストラクタ\n   *\n   * @param {Object} config 設定オブジェクト\n   */\n  constructor(config: OutputConfig | null = null) {\n    this.config = config || { ...defaultConfig };\n  }\n\n  /**\n   * XML形式で出力を生成\n   *\n   * @param {Array} detections 検出結果の配列\n   * @param {number} imageWidth 画像の幅\n   * @param {number} imageHeight 画像の高さ\n   * @param {string} imageName 画像名\n   * @returns {string} XML形式の出力\n   */\n  generateXML(\n    detections: any[],\n    _imageWidth: number,\n    _imageHeight: number,\n    _imageName = 'image',\n    imageUrl: string | null = null\n  ) {\n\n    // 検出結果が空の場合は空のXMLを返す\n    if (!detections || detections.length === 0) {\n      return `<?xml version=\"1.0\" encoding=\"${this.config.xml.encoding}\"?>\n<text>\n  <body>\n    <p>\n      ${imageUrl ? `<pb n=\"1\" facs=\"${imageUrl}\"/>` : ''}\n    </p>\n  </body>\n</text>`;\n    }\n\n    // XMLヘッダー\n    let xml = `<?xml version=\"1.0\" encoding=\"${this.config.xml.encoding}\"?>\n<text>\n  <body>\n    <p>\n`;\n\n    // ページブレーク要素を追加\n    if (imageUrl) {\n      xml += `      <pb n=\"1\" facs=\"${imageUrl}\"/>\\n`;\n    }\n\n    // 各検出結果のテキストを追加\n    for (let i = 0; i < detections.length; i++) {\n      const detection = detections[i];\n      const text = this._escapeXml(detection.text || '');\n      \n      if (text) {\n        xml += `      ${text}\\n`;\n      }\n    }\n\n    // XMLフッター\n    xml += `    </p>\n  </body>\n</text>`;\n\n    return xml;\n  }\n\n  /**\n   * JSON形式で出力を生成\n   *\n   * @param {Array} detections 検出結果の配列\n   * @param {number} imageWidth 画像の幅\n   * @param {number} imageHeight 画像の高さ\n   * @param {string} imageName 画像名\n   * @returns {Object} JSON形式の出力\n   */\n  generateJSON(\n    detections: any[],\n    imageWidth: number,\n    imageHeight: number,\n    imageName = 'image'\n  ) {\n\n    // 検出結果が空の場合は空のJSONを返す\n    if (!detections || detections.length === 0) {\n      return {\n        document: {\n          image: {\n            name: imageName,\n            width: imageWidth,\n            height: imageHeight,\n            text: [],\n          },\n        },\n      };\n    }\n\n    // 各検出結果をJSON要素として追加\n    const textElements = [];\n    for (let i = 0; i < detections.length; i++) {\n      const detection = detections[i];\n      const [x1, y1, x2, y2] = detection.box;\n\n      const textElement: TextElement = {\n        id: i + 1,\n        x: Math.round(x1),\n        y: Math.round(y1),\n        width: Math.round(x2 - x1),\n        height: Math.round(y2 - y1),\n        text: detection.text || '',\n      };\n\n      // 信頼度スコアを含める場合\n      if (\n        this.config.json.includeConfidence &&\n        detection.score !== undefined\n      ) {\n        textElement.confidence = parseFloat(\n          detection.score.toFixed(4)\n        );\n      }\n\n      textElements.push(textElement);\n    }\n\n    // JSON構造を作成\n    const jsonOutput: JSONOutput = {\n      document: {\n        image: {\n          name: imageName,\n          width: imageWidth,\n          height: imageHeight,\n          text: textElements,\n        },\n      },\n    };\n\n    // メタデータを含める場合\n    if (this.config.json.includeMetadata) {\n      jsonOutput.metadata = {\n        timestamp: new Date().toISOString(),\n        version: '1.0.0',\n        engine: 'NDLKotenOCR Web',\n      };\n    }\n\n    return jsonOutput;\n  }\n\n  /**\n   * テキスト形式で出力を生成\n   *\n   * @param {Array} detections 検出結果の配列\n   * @returns {string} テキスト形式の出力\n   */\n  generateTXT(detections: any[]) {\n\n    // 検出結果が空の場合は空の文字列を返す\n    if (!detections || detections.length === 0) {\n      return '';\n    }\n\n    // 各検出結果のテキストを結合\n    let text = '';\n    for (let i = 0; i < detections.length; i++) {\n      const detection = detections[i];\n      if (detection.text) {\n        // バウンディングボックス情報を含める場合\n        if (this.config.txt.includeBoundingBox) {\n          const [x1, y1, x2, y2] = detection.box;\n          text += `[${i + 1}] (${Math.round(\n            x1\n          )},${Math.round(y1)},${Math.round(\n            x2\n          )},${Math.round(y2)}): `;\n        }\n\n        text += detection.text;\n        // 実際の改行文字を追加\n        if (i < detections.length - 1) {\n          text += '\\n';\n        }\n      }\n    }\n\n    return text;\n  }\n\n  /**\n   * 全ての検出結果を統合したXML形式で出力を生成\n   *\n   * @param {Array} resultsArray 複数の処理結果の配列\n   * @param {Array} imageNames 画像名の配列\n   * @returns {string} 統合されたXML形式の出力\n   */\n  generateCombinedXML(resultsArray: any[], imageNames: string[] = []): string {\n\n    // 検出結果が空の場合は空のXMLを返す\n    if (!resultsArray || resultsArray.length === 0) {\n      return `<?xml version=\"1.0\" encoding=\"${this.config.xml.encoding}\"?>\n<document>\n</document>`;\n    }\n\n    // XMLヘッダー\n    let xml = `<?xml version=\"1.0\" encoding=\"${this.config.xml.encoding}\"?>\n<document>\n`;\n\n    // 各画像の結果をXML要素として追加\n    for (let i = 0; i < resultsArray.length; i++) {\n      const result = resultsArray[i];\n      const imageName = imageNames[i] || `image_${i + 1}`;\n\n      // 画像情報\n      xml += `  <image name=\"${imageName}\" width=\"${result.json.document.image.width}\" height=\"${result.json.document.image.height}\">\\n`;\n\n      // 各検出結果をXML要素として追加\n      for (let j = 0; j < result.detections.length; j++) {\n        const detection = result.detections[j];\n        const [x1, y1, x2, y2] = detection.box;\n        const text = this._escapeXml(detection.text || '');\n\n        let attributes = `id=\"${j + 1}\" x=\"${Math.round(\n          x1\n        )}\" y=\"${Math.round(y1)}\" width=\"${Math.round(\n          x2 - x1\n        )}\" height=\"${Math.round(y2 - y1)}\"`;\n\n        // 信頼度スコアを含める場合\n        if (\n          this.config.xml.includeConfidence &&\n          detection.score !== undefined\n        ) {\n          attributes += ` confidence=\"${detection.score.toFixed(\n            4\n          )}\"`;\n        }\n\n        xml += `    <text ${attributes}>${text}</text>\\n`;\n      }\n\n      // 画像要素を閉じる\n      xml += `  </image>\\n`;\n    }\n\n    // XMLフッター\n    xml += `</document>`;\n\n    return xml;\n  }\n\n  /**\n   * 全ての検出結果を統合したJSON形式で出力を生成\n   *\n   * @param {Array} resultsArray 複数の処理結果の配列\n   * @param {Array} imageNames 画像名の配列\n   * @returns {Object} 統合されたJSON形式の出力\n   */\n  generateCombinedJSON(resultsArray: any[], imageNames: string[] = []): any {\n\n    // 検出結果が空の場合は空のJSONを返す\n    if (!resultsArray || resultsArray.length === 0) {\n      return {\n        document: {\n          images: [],\n        },\n      };\n    }\n\n    // 各画像の結果をJSON要素として追加\n    const images = [];\n    for (let i = 0; i < resultsArray.length; i++) {\n      const result = resultsArray[i];\n      const imageName = imageNames[i] || `image_${i + 1}`;\n\n      // 画像情報とテキスト要素を取得\n      const imageData = result.json.document.image;\n      imageData.name = imageName; // 画像名を更新\n\n      images.push(imageData);\n    }\n\n    // JSON構造を作成\n    const jsonOutput: any = {\n      document: {\n        images: images,\n      },\n    };\n\n    // メタデータを含める場合\n    if (this.config.json.includeMetadata) {\n      jsonOutput.metadata = {\n        timestamp: new Date().toISOString(),\n        version: '1.0.0',\n        engine: 'NDLKotenOCR Web',\n        fileCount: resultsArray.length,\n      };\n    }\n\n    return jsonOutput;\n  }\n\n  /**\n   * 全ての検出結果を統合したテキスト形式で出力を生成\n   *\n   * @param {Array} resultsArray 複数の処理結果の配列\n   * @param {Array} imageNames 画像名の配列\n   * @returns {string} 統合されたテキスト形式の出力\n   */\n  generateCombinedTXT(resultsArray: any[], imageNames: string[] = []): string {\n\n    // 検出結果が空の場合は空の文字列を返す\n    if (!resultsArray || resultsArray.length === 0) {\n      return '';\n    }\n\n    // 各画像の結果のテキストを結合\n    let combinedText = '';\n    for (let i = 0; i < resultsArray.length; i++) {\n      const result = resultsArray[i];\n      const imageName = imageNames[i] || `image_${i + 1}`;\n\n      // 画像名をヘッダーとして追加\n      combinedText += `===== ${imageName} =====\\n`;\n\n      // テキスト結果を追加\n      combinedText += result.text;\n\n      // 画像間の区切り\n      if (i < resultsArray.length - 1) {\n        combinedText += '\\n\\n';\n      }\n    }\n\n    return combinedText;\n  }\n\n  /**\n   * XMLで使用される特殊文字をエスケープ\n   *\n   * @param {string} str エスケープする文字列\n   * @returns {string} エスケープされた文字列\n   * @private\n   */\n  _escapeXml(str: string): string {\n    return str\n      .replace(/&/g, '&amp;')\n      .replace(/</g, '&lt;')\n      .replace(/>/g, '&gt;')\n      .replace(/\"/g, '&quot;')\n      .replace(/'/g, '&apos;');\n  }\n}\n\n/**\n * 後方互換性のための関数\n */\nexport function generateXML(\n  detections: any[],\n  imageWidth: number,\n  imageHeight: number,\n  imageName: string = 'image'\n): string {\n  const generator = new OutputGenerator();\n  return generator.generateXML(\n    detections,\n    imageWidth,\n    imageHeight,\n    imageName\n  );\n}\n\nexport function generateJSON(\n  detections: any[],\n  imageWidth: number,\n  imageHeight: number,\n  imageName: string = 'image'\n): any {\n  const generator = new OutputGenerator();\n  return generator.generateJSON(\n    detections,\n    imageWidth,\n    imageHeight,\n    imageName\n  );\n}\n\nexport function generateTXT(detections: any[]): string {\n  const generator = new OutputGenerator();\n  return generator.generateTXT(detections);\n}\n","/**\n * NDLKotenOCR Web版 - メインエントリーポイント\n *\n * このファイルは、NDLKotenOCR Web版のメインエントリーポイントです。\n * 各モジュールを統合し、ブラウザ上でのOCR処理を実行します。\n */\n\nimport { RTMDet } from './layout-detector';\nimport { PARSEQ } from './text-recognizer';\nimport {\n  ReadingOrderProcessor,\n  loadConfig as loadReadingOrderConfig,\n} from './reading-order';\nimport {\n  OutputGenerator,\n  loadConfig as loadOutputConfig,\n} from './output-generator';\nimport { createCanvas, getContext2D } from './utils/canvas-helper';\n\n/**\n * NDLKotenOCR クラス\n * 古典籍OCRの全体処理を管理するクラス\n */\nexport interface SimpleInitOptions {\n  modelPath?: string;\n  modelSize?: 'small' | 'large';\n  progressCallback?: (progress: number, message: string) => void;\n}\n\nexport interface ProcessOptions {\n  imageName?: string;\n  onProgress?: (progress: number, message: string) => void;\n}\n\nexport interface ProcessResult {\n  detections: any[];\n  xml: string;\n  json: any;\n  text: string;\n}\n\nexport class NDLKotenOCR {\n  private layoutDetector: RTMDet | null = null;\n  private textRecognizer: PARSEQ | null = null;\n  private readingOrderProcessor: ReadingOrderProcessor | null = null;\n  private outputGenerator: OutputGenerator | null = null;\n  public initialized: boolean = false;\n  // private progress: number = 0; // unused variable removed\n  private progressCallback: ((progress: number, message: string) => void) | null = null;\n  private configPath: string | null = null;\n  \n  // Default model paths for npm package\n  private static readonly DEFAULT_MODEL_BASE = '../models/';\n\n  /**\n   * 簡単な初期化メソッド（推奨）\n   * @param options オプション設定\n   */\n  async init(options?: SimpleInitOptions): Promise<void> {\n    const modelBase = options?.modelPath || NDLKotenOCR.DEFAULT_MODEL_BASE;\n    const modelSize = options?.modelSize || 'small';\n    \n    // モデルファイル名の決定\n    const layoutModel = modelSize === 'small' \n      ? 'rtmdet-s-1280x1280.onnx'\n      : 'rtmdet-l-1920x1920.onnx';\n    const recognizerModel = modelSize === 'small'\n      ? 'parseq-ndl-32x384-tiny-10.onnx'\n      : 'parseq-ndl-48x576-base-20.onnx';\n    \n    // 内部の初期化メソッドを呼び出し\n    return this.initialize(\n      `${modelBase}${layoutModel}`,\n      {},\n      `${modelBase}ndl.yaml`,\n      `${modelBase}${recognizerModel}`,\n      {},\n      `${modelBase}NDLmoji.yaml`,\n      options?.progressCallback || null\n    );\n  }\n\n  /**\n   * 詳細な初期化処理（上級者向け）\n   * モデルのロードと初期設定を行います\n   */\n  async initialize(\n    layoutModelPath: string,\n    layoutConfig: any = {},\n    layoutConfigPath: string | null = null,\n    recognizerModelPath: string,\n    recognizerConfig: any = {},\n    recognizerConfigPath: string | null = null,\n    progressCallback: ((progress: number, message: string) => void) | null = null\n  ): Promise<void> {\n    this.progressCallback = progressCallback;\n    this.updateProgress(\n      0,\n      '初期化中...（初回はモデルのダウンロードに時間がかかります）'\n    );\n    this.configPath =\n      layoutConfigPath || recognizerConfigPath || null;\n\n    try {\n      // レイアウト検出器の初期化\n      this.layoutDetector = new RTMDet(\n        layoutModelPath,\n        layoutConfig,\n        layoutConfigPath\n      );\n      \n      // レイアウトモデルのダウンロード進捗を追跡\n      await this.layoutDetector.initialize(null, (progress, message) => {\n        // レイアウトモデルの進捗を 0-50% にマッピング\n        const overallProgress = Math.round(progress * 0.5);\n        this.updateProgress(overallProgress, message);\n      });\n      \n      this.updateProgress(\n        50,\n        'レイアウト認識モデルをロードしました'\n      );\n\n      // テキスト認識器の初期化\n      // recognizerConfigPath を文字リストパスとして使用\n      this.textRecognizer = new PARSEQ(\n        recognizerModelPath,\n        recognizerConfig,\n        null, // configPathは使用しない（recognizerConfigは直接渡している）\n        recognizerConfigPath // 文字リストファイルパス\n      );\n      \n      // 文字認識モデルのダウンロード進捗を追跡\n      await this.textRecognizer.initialize(null, null, (progress, message) => {\n        // 文字認識モデルの進捗を 50-100% にマッピング\n        const overallProgress = 50 + Math.round(progress * 0.5);\n        this.updateProgress(overallProgress, message);\n      });\n      \n      this.updateProgress(\n        100,\n        '文字認識モデルをロードしました'\n      );\n\n      // 読み順処理の設定を読み込む\n      const readingOrderConfig = this.configPath\n        ? await loadReadingOrderConfig(this.configPath)\n        : null;\n      this.readingOrderProcessor =\n        new ReadingOrderProcessor(readingOrderConfig || { verticalMode: false });\n\n      // 出力生成の設定を読み込む\n      const outputConfig = this.configPath\n        ? await loadOutputConfig(this.configPath)\n        : null;\n      this.outputGenerator = new OutputGenerator(\n        outputConfig\n      );\n\n      this.initialized = true;\n    } catch (error) {\n      throw new Error(\n        `NDLKotenOCR の初期化に失敗しました: ${error instanceof Error ? error.message : String(error)}`\n      );\n    }\n  }\n\n  /**\n   * 進捗状況の更新\n   */\n  private updateProgress(progress: number, message: string, onProgressCallback?: (progress: number, message: string) => void): void {\n    if (this.progressCallback) {\n      this.progressCallback(progress, message);\n    }\n    if (onProgressCallback) {\n      onProgressCallback(progress / 100, message);\n    }\n  }\n\n  /**\n   * 画像処理の実行\n   */\n  async process(imageData: ImageData | HTMLImageElement | HTMLCanvasElement, options: ProcessOptions = {}): Promise<ProcessResult> {\n    if (!this.initialized) {\n      throw new Error(\n        'NDLKotenOCR が初期化されていません。initialize() を先に呼び出してください。'\n      );\n    }\n\n    this.updateProgress(40, '処理を開始します', options.onProgress);\n    // UIの更新を許可するためのマイクロタスク\n    await new Promise((resolve) => setTimeout(resolve, 0));\n\n    try {\n      // 1. レイアウト検出\n      this.updateProgress(45, 'レイアウト検出中...', options.onProgress);\n      await new Promise((resolve) =>\n        setTimeout(resolve, 0)\n      );\n\n      const detections = await this.layoutDetector!.detect(\n        imageData\n      );\n      this.updateProgress(\n        50,\n        `${detections.length}個のテキスト領域を検出しました`,\n        options.onProgress\n      );\n      await new Promise((resolve) =>\n        setTimeout(resolve, 0)\n      );\n\n      // 2. テキスト認識\n      this.updateProgress(50, '文字認識中...', options.onProgress);\n      await new Promise((resolve) =>\n        setTimeout(resolve, 0)\n      );\n\n      const recognizedDetections = [];\n      let count = 0;\n      for (const detection of detections) {\n        // 検出された領域を切り出し\n        const lineImage = this.cropImage(\n          imageData,\n          detection.box\n        );\n        // テキスト認識\n        const text = await this.textRecognizer!.read(\n          lineImage\n        );\n        recognizedDetections.push({\n          ...detection,\n          text,\n        });\n\n        count++;\n        this.updateProgress(\n          50 + Math.floor((count / detections.length) * 30),\n          `文字認識中... (${count}/${detections.length})`,\n          options.onProgress\n        );\n\n        // UIの更新を許可するためのマイクロタスク\n        await new Promise((resolve) =>\n          setTimeout(resolve, 0)\n        );\n      }\n\n      // 3. 読み順処理\n      this.updateProgress(80, '読み順処理中...', options.onProgress);\n      await new Promise((resolve) =>\n        setTimeout(resolve, 0)\n      );\n\n      const orderedDetections =\n        this.readingOrderProcessor!.process(\n          recognizedDetections,\n          'width' in imageData ? imageData.width : (imageData as HTMLImageElement).naturalWidth || (imageData as HTMLCanvasElement).width,\n          'height' in imageData ? imageData.height : (imageData as HTMLImageElement).naturalHeight || (imageData as HTMLCanvasElement).height\n        );\n\n      // 4. 出力生成\n      this.updateProgress(90, '結果生成中...', options.onProgress);\n      await new Promise((resolve) =>\n        setTimeout(resolve, 0)\n      );\n\n      const width = 'width' in imageData ? imageData.width : (imageData as HTMLImageElement).naturalWidth || (imageData as HTMLCanvasElement).width;\n      const height = 'height' in imageData ? imageData.height : (imageData as HTMLImageElement).naturalHeight || (imageData as HTMLCanvasElement).height;\n\n      const results = {\n        detections: orderedDetections,\n        xml: this.outputGenerator!.generateXML(\n          orderedDetections,\n          width,\n          height,\n          options.imageName || 'image'\n        ),\n        json: this.outputGenerator!.generateJSON(\n          orderedDetections,\n          width,\n          height,\n          options.imageName || 'image'\n        ),\n        text: this.outputGenerator!.generateTXT(\n          orderedDetections\n        ),\n      };\n\n      this.updateProgress(100, '処理完了', options.onProgress);\n      await new Promise((resolve) =>\n        setTimeout(resolve, 0)\n      );\n\n      return results;\n    } catch (error) {\n      throw new Error(\n        `画像処理に失敗しました: ${error instanceof Error ? error.message : String(error)}`\n      );\n    }\n  }\n\n  /**\n   * 画像から指定された領域を切り出す\n   */\n  private cropImage(imageData: ImageData | HTMLImageElement | HTMLCanvasElement, box: number[]): ImageData {\n    const [x1, y1, x2, y2] = box;\n    const width = Math.max(1, Math.round(x2 - x1));\n    const height = Math.max(1, Math.round(y2 - y1));\n\n    // 画像のサイズを取得\n    let imgWidth: number, imgHeight: number;\n    if (imageData instanceof ImageData) {\n      imgWidth = imageData.width;\n      imgHeight = imageData.height;\n    } else {\n      imgWidth = (imageData as HTMLImageElement).naturalWidth || (imageData as HTMLCanvasElement).width;\n      imgHeight = (imageData as HTMLImageElement).naturalHeight || (imageData as HTMLCanvasElement).height;\n    }\n\n    // 座標が画像の範囲内に収まるように調整\n    const safeX1 = Math.max(\n      0,\n      Math.min(imgWidth - 1, Math.round(x1))\n    );\n    const safeY1 = Math.max(\n      0,\n      Math.min(imgHeight - 1, Math.round(y1))\n    );\n    const safeWidth = Math.min(width, imgWidth - safeX1);\n    const safeHeight = Math.min(height, imgHeight - safeY1);\n\n    // Canvas要素を作成\n    const canvas = createCanvas(safeWidth, safeHeight);\n    const ctx = getContext2D(canvas);\n\n    // 画像の種類に応じて適切に描画\n    if (imageData instanceof ImageData) {\n      // ImageDataの場合\n      const tempCanvas = createCanvas(imageData.width, imageData.height);\n      const tempCtx = getContext2D(tempCanvas);\n      tempCtx.putImageData(imageData, 0, 0);\n      ctx.drawImage(\n        tempCanvas,\n        safeX1,\n        safeY1,\n        safeWidth,\n        safeHeight,\n        0,\n        0,\n        safeWidth,\n        safeHeight\n      );\n    } else {\n      // HTMLImageElement または HTMLCanvasElement の場合\n      ctx.drawImage(\n        imageData,\n        safeX1,\n        safeY1,\n        safeWidth,\n        safeHeight,\n        0,\n        0,\n        safeWidth,\n        safeHeight\n      );\n    }\n\n    return ctx.getImageData(0, 0, safeWidth, safeHeight);\n  }\n}","import type { ProcessOptions, ProcessResult, SimpleInitOptions } from '../ndl-koten-ocr';\n\n/**\n * Web Worker版のNDL Koten OCR\n * メインスレッドをブロックせずにOCR処理を実行できます\n */\nexport class NDLKotenOCRWorker {\n  private worker: Worker | null = null;\n  private messageId = 0;\n  private pendingMessages = new Map<string, {\n    resolve: (value: any) => void;\n    reject: (error: Error) => void;\n  }>();\n  private progressCallbacks = new Map<string, (progress: number, message: string) => void>();\n\n  /**\n   * Workerを初期化します\n   * @param workerUrl - Worker スクリプトのURL（省略時は自動的に生成）\n   */\n  constructor(workerUrl?: string | URL) {\n    if (typeof Worker === 'undefined') {\n      throw new Error('Web Workers are not supported in this environment');\n    }\n\n    // workerUrlが指定されていない場合は、バンドルされたWorkerを使用\n    if (!workerUrl) {\n      try {\n        // ESM環境でのみ import.meta.url を使用\n        if (typeof import.meta !== 'undefined' && import.meta.url) {\n          // Webpack 5+ / Vite の場合\n          this.worker = new Worker(new URL('./ocr.worker.ts', import.meta.url), {\n            type: 'module'\n          });\n        } else {\n          throw new Error('Worker URL must be provided when not using ESM format');\n        }\n      } catch (e) {\n        // 動的インポートが失敗した場合のエラーメッセージ\n        throw new Error(\n          'Failed to create Web Worker. Please provide a Worker URL explicitly:\\n' +\n          'new NDLKotenOCRWorker(\"/path/to/ocr.worker.js\")'\n        );\n      }\n    } else {\n      this.worker = new Worker(workerUrl);\n    }\n\n    this.setupWorkerListeners();\n  }\n\n  private setupWorkerListeners(): void {\n    if (!this.worker) return;\n\n    this.worker.addEventListener('message', (event) => {\n      const { type, id, data, error } = event.data;\n      const pending = this.pendingMessages.get(id);\n\n      if (type === 'progress') {\n        const callback = this.progressCallbacks.get(id);\n        if (callback && data) {\n          callback(data.progress, data.message);\n        }\n      } else if (pending) {\n        if (type === 'success') {\n          pending.resolve(data);\n          this.pendingMessages.delete(id);\n          this.progressCallbacks.delete(id);\n        } else if (type === 'error') {\n          pending.reject(new Error(error || 'Unknown error'));\n          this.pendingMessages.delete(id);\n          this.progressCallbacks.delete(id);\n        }\n      }\n    });\n\n    this.worker.addEventListener('error', (error) => {\n      console.error('Worker error:', error);\n      // すべての保留中のメッセージをエラーで解決\n      this.pendingMessages.forEach((pending) => {\n        pending.reject(new Error('Worker error occurred'));\n      });\n      this.pendingMessages.clear();\n      this.progressCallbacks.clear();\n    });\n  }\n\n  private postMessage<T = any>(type: string, data?: any): Promise<T> {\n    return new Promise((resolve, reject) => {\n      if (!this.worker) {\n        reject(new Error('Worker not initialized'));\n        return;\n      }\n\n      const id = String(++this.messageId);\n      this.pendingMessages.set(id, { resolve, reject });\n\n      // 関数は送信できないので、データから除外\n      let cleanData = data;\n      if (data && typeof data === 'object') {\n        cleanData = { ...data };\n        // progressCallbackやonProgressなどの関数を除外\n        for (const key in cleanData) {\n          if (typeof cleanData[key] === 'function') {\n            delete cleanData[key];\n          }\n        }\n        // options内の関数も除外\n        if (cleanData.options && typeof cleanData.options === 'object') {\n          cleanData.options = { ...cleanData.options };\n          for (const key in cleanData.options) {\n            if (typeof cleanData.options[key] === 'function') {\n              delete cleanData.options[key];\n            }\n          }\n        }\n      }\n\n      this.worker.postMessage({ type, id, data: cleanData });\n    });\n  }\n\n  /**\n   * OCRエンジンを初期化します\n   * @param options - 初期化オプション\n   */\n  async init(options?: SimpleInitOptions): Promise<void> {\n    // progressCallbackを分離\n    const callback = options?.progressCallback;\n    const cleanOptions = options ? { ...options } : {};\n    if ('progressCallback' in cleanOptions) {\n      delete (cleanOptions as any).progressCallback;\n    }\n\n    // コールバックがある場合は登録\n    if (callback) {\n      const id = String(this.messageId + 1); // 次のIDを予測\n      this.progressCallbacks.set(id, callback);\n    }\n\n    await this.postMessage('init', cleanOptions);\n  }\n\n  /**\n   * 画像からテキストを認識します\n   * @param imageData - 処理対象の画像データ（ImageData, HTMLImageElement, HTMLCanvasElement）\n   * @param options - 処理オプション\n   * @returns OCR処理結果\n   */\n  async process(\n    imageData: ImageData | HTMLImageElement | HTMLCanvasElement,\n    options?: ProcessOptions\n  ): Promise<ProcessResult> {\n    // HTMLImageElement や HTMLCanvasElement を ImageData に変換\n    let processImageData: ImageData;\n\n    if (imageData instanceof ImageData) {\n      processImageData = imageData;\n    } else {\n      const canvas = document.createElement('canvas');\n      const ctx = canvas.getContext('2d');\n      if (!ctx) {\n        throw new Error('Failed to create canvas context');\n      }\n\n      if (imageData instanceof HTMLImageElement) {\n        canvas.width = imageData.naturalWidth || imageData.width;\n        canvas.height = imageData.naturalHeight || imageData.height;\n        ctx.drawImage(imageData, 0, 0);\n      } else {\n        canvas.width = imageData.width;\n        canvas.height = imageData.height;\n        ctx.drawImage(imageData, 0, 0);\n      }\n\n      processImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n    }\n\n    return await this.postMessage<ProcessResult>('process', {\n      imageData: processImageData,\n      options\n    });\n  }\n\n  /**\n   * Workerを終了してリソースを解放します\n   */\n  terminate(): void {\n    if (this.worker) {\n      this.worker.terminate();\n      this.worker = null;\n    }\n    this.pendingMessages.clear();\n    this.progressCallbacks.clear();\n  }\n\n  /**\n   * Workerが初期化されているかチェックします\n   */\n  isInitialized(): boolean {\n    return this.worker !== null;\n  }\n}","/**\n * TEI/XML Converter\n * OCR結果をTEI/XML形式に変換するユーティリティ\n */\n\nexport interface TEIConversionData {\n  title?: string;\n  sourceUrl?: string;\n  results?: any[];\n  xml?: string;\n  json?: any;\n}\n\nexport class TEIConverter {\n  /**\n   * Static method to convert OCR results to TEI format\n   * @deprecated Use instance method convertOCRResults instead\n   */\n  static convert(data: TEIConversionData): string {\n    const converter = new TEIConverter();\n    return converter.convertOCRResults(data);\n  }\n\n  /**\n   * OCR結果の配列を直接TEI/XMLに変換する便利メソッド\n   */\n  static convertFromOCRResults(\n    ocrResults: Array<{\n      text?: string;\n      json?: any;\n      imageUrl?: string;\n      imageInfo?: any;\n    }>,\n    options?: {\n      title?: string;\n      manifestUrl?: string;\n    }\n  ): string {\n    const converter = new TEIConverter();\n\n    // デバッグ: 入力データを確認\n    console.log('convertFromOCRResults input:', {\n      resultsCount: ocrResults.length,\n      firstResult: ocrResults[0] ? {\n        hasText: !!ocrResults[0].text,\n        hasJson: !!ocrResults[0].json,\n        hasRegions: !!ocrResults[0].json?.regions,\n        regionsCount: ocrResults[0].json?.regions?.length || 0\n      } : null\n    });\n\n    // OCR結果をTEI変換用のフォーマットに整形\n    const teiData: TEIConversionData = {\n      title: options?.title || `OCR Document - ${ocrResults.length} pages`,\n      sourceUrl: options?.manifestUrl,\n      results: ocrResults.map((result) => {\n        // 新しい構造（document.image.text）と古い構造（regions）の両方に対応\n        let detections = [];\n\n        // 新しい構造をチェック\n        if (result.json?.document?.image?.text) {\n          detections = result.json.document.image.text.map((textRegion: any) => ({\n            text: textRegion.text || '',\n            box: [textRegion.x, textRegion.y, textRegion.x + textRegion.width, textRegion.y + textRegion.height],\n            bbox: [textRegion.x, textRegion.y, textRegion.x + textRegion.width, textRegion.y + textRegion.height]\n          }));\n        }\n        // 古い構造にフォールバック\n        else if (result.json?.regions) {\n          detections = result.json.regions.map((region: any) => ({\n            text: region.text || '',\n            box: region.bbox || region.box || [0, 0, 100, 100],\n            bbox: region.bbox || region.box || [0, 0, 100, 100]\n          }));\n        }\n\n        console.log('Mapped result:', {\n          hasDetections: detections.length > 0,\n          detectionsCount: detections.length\n        });\n\n        // 画像サイズも新しい構造から取得\n        const imageWidth = result.json?.document?.image?.width || result.imageInfo?.width || 1000;\n        const imageHeight = result.json?.document?.image?.height || result.imageInfo?.height || 1000;\n\n        return {\n          text: result.text || '',\n          imageUrl: result.imageUrl || '',\n          imageWidth: imageWidth,\n          imageHeight: imageHeight,\n          detections: detections\n        };\n      })\n    };\n\n    return converter.convertOCRResults(teiData);\n  }\n\n  /**\n   * Convert OCR results to TEI/XML format\n   */\n  convertOCRResults(data: TEIConversionData): string {\n    // If we have results with detections or text, process them\n    if (data.results && data.results.length > 0) {\n      const hasContent = data.results.some(r =>\n        r.detections?.length > 0 || r.text || r.xml\n      );\n\n      console.log('convertOCRResults check:', {\n        hasResults: true,\n        resultsCount: data.results.length,\n        hasContent: hasContent,\n        firstResult: data.results[0] ? {\n          hasDetections: !!data.results[0].detections,\n          detectionsCount: data.results[0].detections?.length || 0,\n          hasText: !!data.results[0].text\n        } : null\n      });\n\n      if (hasContent) {\n        return this.combineTEIResults(data);\n      }\n    }\n\n    // Fallback to default template if no content\n    return this.generateXML(data);\n  }\n\n  private combineTEIResults(data: TEIConversionData): string {\n    const paragraphElements: string[] = [];\n    const surfaceElements: string[] = [];\n\n    data.results?.forEach((result, index) => {\n      const pageNumber = index + 1;\n      const imageUrl = result.imageUrl || '';\n      const imageWidth = result.imageWidth || 1000;\n      const imageHeight = result.imageHeight || 1000;\n\n      console.log(`Processing result ${pageNumber}:`, {\n        hasDetections: !!result.detections,\n        detectionsLength: result.detections?.length || 0,\n        hasText: !!result.text,\n        imageUrl: imageUrl\n      });\n\n      // Add page break element\n      let pageContent = `        <pb n=\"${pageNumber}\" facs=\"${imageUrl}\"/>\n`;\n\n      // Process detections to add line breaks with zone references\n      if (result.detections && result.detections.length > 0) {\n        console.log(`Creating surface for page ${pageNumber} with ${result.detections.length} detections`);\n        result.detections.forEach((detection: any, detIndex: number) => {\n          const lineNumber = detIndex + 1;\n          const zoneId = `zone-${pageNumber}-${lineNumber}`;\n          const text = detection.text || '';\n\n          if (text) {\n            pageContent += `        <lb n=\"${pageNumber}.${lineNumber}\" type=\"line\" corresp=\"#${zoneId}\"/>\n`;\n            pageContent += `        ${this._escapeXml(text)}\n`;\n          }\n        });\n\n        // Create surface element with zones\n        let surfaceXml = `    <surface sameAs=\"${imageUrl}\" ulx=\"0\" uly=\"0\" lrx=\"${imageWidth}\" lry=\"${imageHeight}\">\n`;\n        surfaceXml += `      <graphic url=\"${imageUrl}\" width=\"${imageWidth}px\" height=\"${imageHeight}px\"/>\n`;\n\n        result.detections.forEach((detection: any, detIndex: number) => {\n          const lineNumber = detIndex + 1;\n          const zoneId = `zone-${pageNumber}-${lineNumber}`;\n          // detection.box is [x1, y1, x2, y2] format\n          const box = detection.box || detection.bbox || [0, 0, 100, 100];\n\n          // Ensure we have valid coordinates\n          const ulx = Math.round(Math.min(box[0], box[2]));\n          const uly = Math.round(Math.min(box[1], box[3]));\n          const lrx = Math.round(Math.max(box[0], box[2]));\n          const lry = Math.round(Math.max(box[1], box[3]));\n\n          surfaceXml += `      <zone xml:id=\"${zoneId}\" ulx=\"${ulx}\" uly=\"${uly}\" lrx=\"${lrx}\" lry=\"${lry}\"/>\n`;\n        });\n\n        surfaceXml += `    </surface>`;\n        surfaceElements.push(surfaceXml);\n      } else if (result.text) {\n        // Fallback: if no detections but has text, add lines without zones\n        const lines = result.text.split('\\n');\n        lines.forEach((line: string, lineIndex: number) => {\n          if (line.trim()) {\n            const lineNumber = lineIndex + 1;\n            pageContent += `        <lb n=\"${pageNumber}.${lineNumber}\" type=\"line\"/>\n`;\n            pageContent += `        ${this._escapeXml(line.trim())}\n`;\n          }\n        });\n      }\n\n      paragraphElements.push(pageContent);\n    });\n\n    // Build combined TEI document\n    let tei = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?xml-model href=\"http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng\" type=\"application/xml\" schematypens=\"http://relaxng.org/ns/structure/1.0\"?>\n<?xml-model href=\"http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng\" type=\"application/xml\" schematypens=\"http://purl.oclc.org/dsdl/schematron\"?>\n<TEI xmlns=\"http://www.tei-c.org/ns/1.0\">\n  <teiHeader>\n    <fileDesc>\n      <titleStmt>\n        <title>${this._escapeXml(data.title || 'OCR処理結果')}</title>\n        <respStmt>\n          <resp>Automated Transcription</resp>\n          <name ref=\"https://github.com/ndl-lab/ndlkotenocr-lite\">NDL古典籍OCR-Liteアプリケーション</name>\n        </respStmt>\n      </titleStmt>\n      <publicationStmt>\n        <p>${data.sourceUrl?.startsWith('http') ? 'Converted from IIIF Manifest' : 'Converted from uploaded images'}</p>\n      </publicationStmt>\n      <sourceDesc>\n        <p>${this._escapeXml(data.sourceUrl || 'User uploaded images')}</p>\n      </sourceDesc>\n    </fileDesc>\n  </teiHeader>\n  <text>\n    <body>\n      <p>\n${paragraphElements.join('')}      </p>\n    </body>\n  </text>`;\n\n    // Add facsimile section if we have surface elements\n    console.log('Surface elements check:', {\n      count: surfaceElements.length,\n      hasSurfaces: surfaceElements.length > 0\n    });\n\n    if (surfaceElements.length > 0) {\n      const facsimileAttr = data.sourceUrl?.startsWith('http') ? ` sameAs=\"${this._escapeXml(data.sourceUrl)}\"` : '';\n      tei += `\n  <facsimile${facsimileAttr}>\n${surfaceElements.join('\\n')}\n  </facsimile>`;\n    }\n\n    tei += '\\n</TEI>';\n\n    return tei;\n  }\n\n  private _escapeXml(text: string): string {\n    if (!text) return '';\n    return text\n      .replace(/&/g, '&amp;')\n      .replace(/</g, '&lt;')\n      .replace(/>/g, '&gt;')\n      .replace(/\"/g, '&quot;')\n      .replace(/'/g, '&apos;');\n  }\n\n  private generateXML(data: TEIConversionData): string {\n    return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?xml-model href=\"http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng\" type=\"application/xml\" schematypens=\"http://relaxng.org/ns/structure/1.0\"?>\n<?xml-model href=\"http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng\" type=\"application/xml\" schematypens=\"http://purl.oclc.org/dsdl/schematron\"?>\n<TEI xmlns=\"http://www.tei-c.org/ns/1.0\">\n  <teiHeader>\n    <fileDesc>\n      <titleStmt>\n        <title>${this._escapeXml(data.title || 'OCR Results')}</title>\n        <respStmt>\n          <resp>Automated Transcription</resp>\n          <name ref=\"https://github.com/ndl-lab/ndlkotenocr-lite\">NDL古典籍OCR-Liteアプリケーション</name>\n        </respStmt>\n      </titleStmt>\n      <publicationStmt>\n        <p>${data.sourceUrl?.startsWith('http') ? 'Converted from IIIF Manifest' : 'No source URL provided'}</p>\n      </publicationStmt>\n      <sourceDesc>\n        <p>${this._escapeXml(data.sourceUrl || 'No source information')}</p>\n      </sourceDesc>\n    </fileDesc>\n  </teiHeader>\n  <text>\n    <body>\n      <p>\n        <!-- No OCR results available -->\n      </p>\n    </body>\n  </text>\n</TEI>`;\n  }\n}","/**\n * IIIF Manifest Processor\n * IIIFマニフェストからの画像取得と処理\n */\n\nimport { NDLKotenOCR, ProcessOptions, ProcessResult } from './ndl-koten-ocr';\nimport { TEIConverter, TEIConversionData } from './tei-converter';\n\nexport interface IIIFImageInfo {\n  canvasId: string;\n  imageUrl: string;\n  thumbnailUrl: string;\n  label: string;\n  index: number;\n}\n\nexport interface IIIFProcessOptions extends ProcessOptions {\n  maxImages?: number;\n  downloadImages?: boolean;\n  onImageProgress?: (imageIndex: number, progress: number, message: string) => void;\n}\n\nexport class IIIFProcessor {\n  private ocrEngine: NDLKotenOCR;\n  private teiConverter: TEIConverter;\n\n  constructor(ocrEngine?: NDLKotenOCR) {\n    this.ocrEngine = ocrEngine || new NDLKotenOCR();\n    this.teiConverter = new TEIConverter();\n  }\n\n  /**\n   * IIIFマニフェストから画像リストを取得する静的メソッド\n   */\n  static async getImagesFromManifest(manifestUrl: string): Promise<IIIFImageInfo[]> {\n    const processor = new IIIFProcessor();\n    const manifest = await processor.fetchManifest(manifestUrl);\n    return processor.extractImages(manifest);\n  }\n\n  /**\n   * IIIFマニフェストURLから画像を取得してOCR処理を実行\n   */\n  async processManifestUrl(\n    manifestUrl: string,\n    options: IIIFProcessOptions = {}\n  ): Promise<{\n    results: ProcessResult[];\n    teiXml: string;\n    manifest: any;\n  }> {\n    // マニフェストを取得\n    const manifest = await this.fetchManifest(manifestUrl);\n\n    // 画像情報を抽出\n    const imageInfos = this.extractImages(manifest);\n\n    // 処理する画像数を制限\n    const imagesToProcess = options.maxImages\n      ? imageInfos.slice(0, options.maxImages)\n      : imageInfos;\n\n    // 各画像を処理\n    const results: ProcessResult[] = [];\n\n    for (let i = 0; i < imagesToProcess.length; i++) {\n      const imageInfo = imagesToProcess[i];\n\n      if (options.onImageProgress) {\n        options.onImageProgress(i, 0, `画像 ${i + 1}/${imagesToProcess.length} を処理中...`);\n      }\n\n      // 画像を取得\n      const imageData = await this.fetchImage(imageInfo.imageUrl);\n\n      // OCR処理\n      const result = await this.ocrEngine.process(imageData, {\n        imageName: imageInfo.label,\n        onProgress: (progress, message) => {\n          if (options.onImageProgress) {\n            options.onImageProgress(i, progress, message);\n          }\n        }\n      });\n\n      // 結果に画像情報を追加\n      const enhancedResult = {\n        ...result,\n        imageUrl: imageInfo.imageUrl,\n        imageName: imageInfo.label,\n        imageWidth: imageData.width,\n        imageHeight: imageData.height\n      };\n\n      results.push(enhancedResult);\n\n      if (options.onImageProgress) {\n        options.onImageProgress(i, 100, `画像 ${i + 1}/${imagesToProcess.length} 完了`);\n      }\n    }\n\n    // TEI/XMLに変換\n    const teiData: TEIConversionData = {\n      title: this.getManifestTitle(manifest),\n      sourceUrl: manifestUrl,\n      results: results\n    };\n\n    const teiXml = this.teiConverter.convertOCRResults(teiData);\n\n    return {\n      results,\n      teiXml,\n      manifest\n    };\n  }\n\n  /**\n   * マニフェストを取得\n   */\n  public async fetchManifest(url: string): Promise<any> {\n    const response = await fetch(url);\n\n    if (!response.ok) {\n      throw new Error(`Failed to fetch manifest: HTTP ${response.status}`);\n    }\n\n    const manifest = await response.json();\n\n    // Presentation API v2の場合はv3形式に変換が必要\n    // ここでは簡易的な変換を実装\n    if (manifest['@context']?.includes('presentation/2')) {\n      return this.convertV2ToV3(manifest);\n    }\n\n    return manifest;\n  }\n\n  /**\n   * マニフェストから画像情報を抽出\n   */\n  public extractImages(manifest: any): IIIFImageInfo[] {\n    const imageInfos: IIIFImageInfo[] = [];\n\n    if (!manifest.items) {\n      return imageInfos;\n    }\n\n    manifest.items.forEach((canvas: any, canvasIndex: number) => {\n      // サムネイルURLの取得\n      let thumbnailUrl: string | undefined;\n      if (canvas.thumbnail) {\n        if (Array.isArray(canvas.thumbnail) && canvas.thumbnail[0]) {\n          thumbnailUrl = canvas.thumbnail[0].id || canvas.thumbnail[0];\n        } else if (typeof canvas.thumbnail === 'string') {\n          thumbnailUrl = canvas.thumbnail;\n        } else if (canvas.thumbnail.id) {\n          thumbnailUrl = canvas.thumbnail.id;\n        }\n      }\n\n      if (canvas.items?.[0]?.items) {\n        canvas.items[0].items.forEach((annotation: any) => {\n          if (annotation.body?.id) {\n            const imageUrl = annotation.body.id;\n            const label = this.getCanvasLabel(canvas) || `Page ${canvasIndex + 1}`;\n\n            // サムネイルが無い場合は、画像URLを小さいサイズで要求\n            if (!thumbnailUrl && imageUrl.includes('/full/full/')) {\n              thumbnailUrl = imageUrl.replace('/full/full/', '/full/200,/');\n            } else if (!thumbnailUrl) {\n              thumbnailUrl = imageUrl;\n            }\n\n            imageInfos.push({\n              canvasId: canvas.id,\n              imageUrl: imageUrl,\n              thumbnailUrl: thumbnailUrl || imageUrl,\n              label: label,\n              index: canvasIndex\n            });\n          }\n        });\n      }\n    });\n\n    return imageInfos;\n  }\n\n  /**\n   * 画像を取得してImageDataに変換\n   */\n  private async fetchImage(imageUrl: string): Promise<ImageData> {\n    // ブラウザ環境の場合\n    if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n      const img = new Image();\n      img.crossOrigin = 'anonymous';\n\n      await new Promise((resolve, reject) => {\n        img.onload = resolve;\n        img.onerror = reject;\n        img.src = imageUrl;\n      });\n\n      const canvas = document.createElement('canvas');\n      canvas.width = img.naturalWidth;\n      canvas.height = img.naturalHeight;\n\n      const ctx = canvas.getContext('2d');\n      if (!ctx) {\n        throw new Error('Failed to create canvas context');\n      }\n\n      ctx.drawImage(img, 0, 0);\n      return ctx.getImageData(0, 0, canvas.width, canvas.height);\n    }\n\n    // Node.js環境の場合は別の実装が必要\n    throw new Error('Image fetching in Node.js environment is not yet implemented');\n  }\n\n  /**\n   * Presentation API v2からv3への簡易変換\n   */\n  private convertV2ToV3(v2Manifest: any): any {\n    // 基本的な構造の変換\n    const v3Manifest: any = {\n      '@context': 'http://iiif.io/api/presentation/3/context.json',\n      id: v2Manifest['@id'],\n      type: 'Manifest',\n      label: this.convertLabel(v2Manifest.label),\n      items: []\n    };\n\n    // sequences[0].canvasesをitemsに変換\n    if (v2Manifest.sequences?.[0]?.canvases) {\n      v3Manifest.items = v2Manifest.sequences[0].canvases.map((canvas: any) => ({\n        id: canvas['@id'],\n        type: 'Canvas',\n        label: this.convertLabel(canvas.label),\n        width: canvas.width,\n        height: canvas.height,\n        items: [{\n          id: `${canvas['@id']}/page`,\n          type: 'AnnotationPage',\n          items: canvas.images?.map((image: any) => ({\n            id: `${canvas['@id']}/annotation`,\n            type: 'Annotation',\n            motivation: 'painting',\n            body: {\n              id: image.resource?.['@id'] || image.resource?.id,\n              type: 'Image',\n              format: image.resource?.format || 'image/jpeg',\n              width: image.resource?.width,\n              height: image.resource?.height\n            },\n            target: canvas['@id']\n          })) || []\n        }],\n        thumbnail: canvas.thumbnail ? [{\n          id: canvas.thumbnail['@id'] || canvas.thumbnail,\n          type: 'Image'\n        }] : undefined\n      }));\n    }\n\n    return v3Manifest;\n  }\n\n  /**\n   * ラベルの変換（文字列または多言語オブジェクト）\n   */\n  private convertLabel(label: any): any {\n    if (typeof label === 'string') {\n      return { none: [label] };\n    }\n    if (Array.isArray(label)) {\n      return { none: label };\n    }\n    return label;\n  }\n\n  /**\n   * マニフェストタイトルの取得\n   */\n  private getManifestTitle(manifest: any): string {\n    const label = manifest.label;\n    if (typeof label === 'string') {\n      return label;\n    }\n    if (label?.ja?.[0]) {\n      return label.ja[0];\n    }\n    if (label?.en?.[0]) {\n      return label.en[0];\n    }\n    if (label?.none?.[0]) {\n      return label.none[0];\n    }\n    return 'IIIF Manifest';\n  }\n\n  /**\n   * キャンバスラベルの取得\n   */\n  private getCanvasLabel(canvas: any): string {\n    const label = canvas.label;\n    if (typeof label === 'string') {\n      return label;\n    }\n    if (label?.ja?.[0]) {\n      return label.ja[0];\n    }\n    if (label?.en?.[0]) {\n      return label.en[0];\n    }\n    if (label?.none?.[0]) {\n      return label.none[0];\n    }\n    return '';\n  }\n}"],"mappings":"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","names":["ort","yaml","createCanvas","width","height","canvas","getContext2D","ctx","RTMDet","modelPath","config","configPath","path","response","yamlText","yamlConfig","layoutConfig","progressCallback","options","error","modelName","contentLength","total","loaded","reader","chunks","done","value","progress","totalLength","acc","chunk","result","position","imageData","batchSize","channels","height","width","imgWidth","imgHeight","maxWH","paddingCanvas","createCanvas","paddingCtx","getContext2D","tempCanvas","canvas","ctx","data","inputTensor","mean","std","h","w","pixelOffset","c","tensorIdx","outputs","metadata","dets","labels","detections","numDetections","i","x1","y1","x2","y2","score","classId","normX1","normY1","normX2","normY2","squareSize","origX1","origY1","origX2","origY2","deltaH","threshold","sortedDetections","a","b","selected","current","boxA","boxB","xA","yA","xB","yB","intersectionArea","boxAArea","boxBArea","tensor","feeds","ort","yaml","PARSEQ","modelPath","config","configPath","charListPath","path","response","yamlText","yamlConfig","textConfig","charListStr","progressCallback","options","error","modelName","contentLength","total","loaded","reader","chunks","done","value","progress","totalLength","acc","chunk","result","position","imageData","batchSize","channels","height","width","imgWidth","imgHeight","canvas","createCanvas","ctx","getContext2D","tempCanvas","resizeCanvas","resizeCtx","data","inputTensor","h","w","pixelOffset","c","tensorIdx","outputs","outputNames","rawLogits","logits","_batchSize","seqLength","vocabSize","resultClassIds","i","scores","j","maxScore","maxIndex","resultText","prevClassId","classId","tensor","feeds","yaml","defaultConfig","loadConfig","configPath","config","response","yamlText","yamlConfig","readingConfig","ReadingOrderProcessor","detections","imageWidth","imageHeight","boxes","box","index","orderedBoxes","orderedDetections","x1","y1","x2","y2","verticalGroups","result","group","horizontalGroups","_imageWidth","_imageHeight","yRanges","_x1","_x2","minY","range","maxY","height","candidates","step","y","hasIntersection","middleY","splitY","prev","curr","upperGroup","lowerGroup","xRanges","_y1","_y2","minX","maxX","width","x","middleX","splitX","leftGroup","rightGroup","a","b","xDiff","yDiff","yaml","defaultConfig","loadConfig","configPath","config","response","yamlText","yamlConfig","outputConfig","OutputGenerator","detections","_imageWidth","_imageHeight","_imageName","imageUrl","xml","i","detection","text","imageWidth","imageHeight","imageName","textElements","x1","y1","x2","y2","textElement","jsonOutput","resultsArray","imageNames","result","j","attributes","images","imageData","combinedText","str","_NDLKotenOCR","options","modelBase","modelSize","layoutModel","recognizerModel","layoutModelPath","layoutConfig","layoutConfigPath","recognizerModelPath","recognizerConfig","recognizerConfigPath","progressCallback","RTMDet","progress","message","overallProgress","PARSEQ","readingOrderConfig","loadConfig","ReadingOrderProcessor","outputConfig","OutputGenerator","error","onProgressCallback","imageData","resolve","detections","recognizedDetections","count","detection","lineImage","text","orderedDetections","width","height","results","box","x1","y1","x2","y2","imgWidth","imgHeight","safeX1","safeY1","safeWidth","safeHeight","canvas","createCanvas","ctx","getContext2D","tempCanvas","NDLKotenOCR","NDLKotenOCRWorker","workerUrl","event","type","id","data","error","pending","callback","resolve","reject","cleanData","key","options","cleanOptions","imageData","processImageData","canvas","ctx","TEIConverter","_TEIConverter","data","ocrResults","options","converter","teiData","result","detections","textRegion","region","imageWidth","imageHeight","hasContent","r","paragraphElements","surfaceElements","index","pageNumber","imageUrl","pageContent","detection","detIndex","lineNumber","zoneId","text","surfaceXml","box","ulx","uly","lrx","lry","line","lineIndex","tei","facsimileAttr","IIIFProcessor","_IIIFProcessor","ocrEngine","NDLKotenOCR","TEIConverter","manifestUrl","processor","manifest","options","imageInfos","imagesToProcess","results","i","imageInfo","imageData","enhancedResult","progress","message","teiData","teiXml","url","response","canvas","canvasIndex","thumbnailUrl","annotation","imageUrl","label","img","resolve","reject","ctx","v2Manifest","v3Manifest","image"]}