通过分析图片和JSON数据，我可以明确说明坐标系的起始位置和具体规则：
![2023年度报告母公司_page_001](image/图片操作说明/2023年度报告母公司_page_001.png)
[2023年度报告母公司_page_001.json](image/图片操作说明/2023年度报告母公司_page_001.json)

```json
{
	"blocks": [
		{
			"block_label": "header",
			"block_content": "广东荣德会计师事务所有限公司",
			"block_bbox": [175, 157, 594, 219]
		},
		{
			"block_label": "doc_title",
			"block_content": "审计报告",
			"block_bbox": [627, 263, 976, 325]
		},
		{
			"block_label": "footer",
			"block_content": "此码用于证明该审计报告是否由具有执业许可的会计师事务所出具...",
			"block_bbox": [527, 2235, 1411, 2297]
		}
	]
}
```

## 坐标系说明

### **坐标原点位置**

坐标系以**图片左上角为原点 (0,0)**：

- **X轴**：从左到右递增
- **Y轴**：从上到下递增

### **具体举例说明**

#### 1. **标题"审计报告"的坐标**

```json
{
    "block_label": "doc_title",
    "block_content": "审计报告",
    "block_bbox": [627, 263, 976, 325]
}
```

- `[627, 263, 976, 325]` 表示：
  - 左上角：(627, 263) - 距离图片左边627像素，距离顶部263像素
  - 右下角：(976, 325) - 距离图片左边976像素，距离顶部325像素

#### 2. **页眉公司名称的坐标**

```json
{
    "block_label": "header",
    "block_content": "广东荣德会计师事务所有限公司",
    "block_bbox": [175, 157, 594, 219]
}
```

- `[175, 157, 594, 219]` 表示：
  - 左上角：(175, 157) - 很靠近图片顶部
  - 右下角：(594, 219)

#### 3. **底部页脚的坐标**

```json
{
    "block_label": "footer",
    "block_content": "此码用于证明该审计报告是否由具有执业许可的会计师事务所出具...",
    "block_bbox": [527, 2235, 1411, 2297]
}
```

- `[527, 2235, 1411, 2297]` 表示：
  - Y坐标2235、2297很大，说明位于图片底部

### **坐标格式规律**

所有的 `bbox` 都采用 **[x1, y1, x2, y2]** 格式：

- `x1, y1`：矩形框的左上角坐标
- `x2, y2`：矩形框的右下角坐标

### **验证方法**

从JSON中可以看出：

- 页眉元素的Y坐标都在150-220范围（顶部）
- 标题的Y坐标在260-330范围（偏上）
- 正文内容Y坐标逐渐增大
- 页脚元素Y坐标在2200+（底部）

这完全符合**左上角为原点，Y轴向下递增**的坐标系统。

# Plotly坐标系

## 坐标系差异

### **1. JSON中的坐标系（图像坐标系）**
- **原点**：左上角 (0,0)
- **Y轴方向**：向下递增
- **示例**：`"block_bbox": [627, 263, 976, 325]`
  - 627, 263 = 左上角坐标
  - 976, 325 = 右下角坐标

### **2. Plotly的坐标系（数学坐标系）**
- **原点**：左下角 (0,0) 
- **Y轴方向**：向上递增
- **这是标准的笛卡尔坐标系**

## 代码中的坐标转换

````python
fig.add_layout_image(
    dict(
        source=image,
        xref="x", yref="y",
        x=0, y=image.height,  # ← 这里是关键！
        sizex=image.width, 
        sizey=image.height,
        sizing="stretch", 
        opacity=1.0, 
        layer="below"
    )
)
````

### **`y=image.height` 的含义**

- **不是**从左上角开始
- 这是**Plotly坐标系**中的位置，表示图像的**左下角**位置
- 因为Plotly原点在左下角，所以：
  - `y=0` 是图像底部
  - `y=image.height` 是图像顶部

## 坐标转换示例

假设图像高度为2000像素：

````python
# JSON坐标（图像坐标系）
json_bbox = [627, 263, 976, 325]  # [x1, y1, x2, y2]

# 转换为Plotly坐标系
def convert_to_plotly_coords(bbox, image_height):
    x1, y1, x2, y2 = bbox
    # X坐标不变
    plot_x1 = x1
    plot_x2 = x2
    # Y坐标需要翻转
    plot_y1 = image_height - y2  # 原来的底部 -> Plotly的底部
    plot_y2 = image_height - y1  # 原来的顶部 -> Plotly的顶部
    return [plot_x1, plot_y1, plot_x2, plot_y2]

# 示例转换
image_height = 2000
plotly_coords = convert_to_plotly_coords([627, 263, 976, 325], image_height)
# 结果：[627, 1675, 976, 1737]
````

## 修复建议

您的代码中有坐标转换不一致的问题：

````python
def create_resized_interactive_plot(self, image: Image.Image, selected_bbox: Optional[List[int]], zoom_level: float, all_boxes: list[tuple]) -> go.Figure:
    """创建可调整大小的交互式图片"""
    fig = go.Figure()
    
    # 添加图片 - Plotly坐标系，原点在左下角
    fig.add_layout_image(
        dict(
            source=image,
            xref="x", yref="y",
            x=0, y=image.height,  # 图片左下角在Plotly坐标系中的位置
            sizex=image.width, 
            sizey=image.height,
            sizing="stretch", 
            opacity=1.0, 
            layer="below"
        )
    )
    
    # 显示所有bbox - 需要坐标转换
    if len(all_boxes) > 0:
        for bbox in all_boxes:
            if len(bbox) >= 4:
                x1, y1, x2, y2 = bbox[:4]
                
                # 转换为Plotly坐标系（翻转Y轴）
                plot_x1 = x1
                plot_x2 = x2
                plot_y1 = image.height - y2  # JSON的y2 -> Plotly的底部
                plot_y2 = image.height - y1  # JSON的y1 -> Plotly的顶部
                
                color = "rgba(0, 100, 200, 0.2)"
                
                fig.add_shape(
                    type="rect",
                    x0=plot_x1, y0=plot_y1,
                    x1=plot_x2, y1=plot_y2,
                    line=dict(color="blue", width=1),
                    fillcolor=color,
                )

    # 高亮显示选中的bbox
    if selected_bbox and len(selected_bbox) >= 4:
        x1, y1, x2, y2 = selected_bbox[:4]
        
        # 转换为Plotly坐标系
        plot_x1 = x1
        plot_x2 = x2
        plot_y1 = image.height - y2  # 翻转Y坐标
        plot_y2 = image.height - y1  # 翻转Y坐标
        
        fig.add_shape(
            type="rect",
            x0=plot_x1, y0=plot_y1,
            x1=plot_x2, y1=plot_y2,
            line=dict(color="red", width=3),
            fillcolor="rgba(255, 0, 0, 0.3)",
        )
    
    # ...existing code...
````

**关键点**：
- `y=image.height` 表示图像在Plotly坐标系中的顶部位置
- 所有bbox坐标都需要进行Y轴翻转：`plotly_y = image_height - json_y`
- 这样才能确保图像和标注框正确对齐

# ocr json结果坐标旋转说明
在坐标系中，原点位于左上角，x轴向右增加，y轴向下增加（类似于屏幕坐标系）。绕原点旋转任意角度θ（θ为用户视角的逆时针角度）的旋转矩阵为：

```math
\begin{bmatrix}
x' \\
y'
\end{bmatrix}
=
\begin{bmatrix}
\cos \theta & \sin \theta \\
-\sin \theta & \cos \theta
\end{bmatrix}
\begin{bmatrix}
x \\
y
\end{bmatrix}
```

其中：
- \((x, y)\) 是旋转前的坐标，
- \((x', y')\) 是旋转后的坐标，
- \(\theta\) 是旋转角度（以弧度表示），用户视角的逆时针方向为正。

### 说明：
- 这个矩阵实现了从用户视角看到的逆时针旋转。例如，当θ = 90°（π/2弧度）时，点(1, 0)会旋转到(0, -1)，即指向屏幕上方（y负方向）。
- 如果使用角度制，确保在计算前将角度转换为弧度（因为三角函数通常使用弧度）。
- 该矩阵适用于任何旋转角度θ。