"""
词典词条分割程序 - 最终优化版（带底部空白裁剪）

优化参数设置：
1. 二值化：块大小=19, C=20
2. 空隙高度阈值：30像素
3. 投影阈值系数：0.05
4. 修复左栏顶部词条丢失问题

安装依赖（使用清华源）:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python numpy

使用方法:
1. 将词典页面保存为dictionary_page.jpg
2. 运行程序: python dictionary_splitter.py
3. 分割后的词条图像保存在output目录
"""

import cv2
import numpy as np
import os

def process_dictionary_page(image_path, output_dir="output"):
    """
    处理词典页面图片，分割为单个词条图像
    :param image_path: 词典页面图片路径
    :param output_dir: 输出目录
    :return: 分割后的词条图像列表
    """
    # 创建输出目录
    os.makedirs(output_dir, exist_ok=True)
    
    # 1. 图像加载与预处理
    #print(f"加载图像: {image_path}")
    image = cv2.imread(image_path)
    if image is None:
        raise ValueError(f"无法加载图像: {image_path}")
    
    # 转换为灰度图
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # 二值化处理 - 使用优化参数 (块大小=19, C=20)
    #print("图像二值化处理 (块大小=19, C=20)...")
    binary = cv2.adaptiveThreshold(
        gray, 255,
        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY_INV, 19, 20  # 优化参数
    )
    
    # 降噪处理
    #print("图像降噪处理...")
    kernel = np.ones((3, 3), np.uint8)
    processed = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
    processed = cv2.morphologyEx(processed, cv2.MORPH_CLOSE, kernel)
    
    # 2. 版面分析 - 检测双栏布局
    #print("检测双栏布局...")
    # 垂直投影计算
    vertical_projection = np.sum(processed, axis=0)
    
    # 寻找中间空白区域（分栏空隙）
    
    height, width = processed.shape
    center = width // 2
    search_width = width // 8  # 在中间区域搜索
    
    # 找到投影最小的区域（最可能是分栏空隙）
    min_val = np.inf
    gap_center = center
    
    # 确定分栏位置
    for i in range(center - search_width, center + search_width):
        window = vertical_projection[i-5:i+5]
        window_sum = np.sum(window)
        if window_sum < min_val:
            min_val = window_sum
            gap_center = i
    
    # 优化边界检测：寻找左栏的实际右边界
    #print("优化左栏边界检测...")
    
    # 方法1：在分栏中心左侧区域寻找文字结束位置
    left_region = processed[:, max(0, gap_center-100):gap_center]
    left_region_proj = np.sum(left_region, axis=0)
    
    # 从右侧向左扫描，找到第一个有文字的位置
    text_end = gap_center
    for i in range(len(left_region_proj)-1, 0, -1):
        if left_region_proj[i] > 0:
            text_end = gap_center - 100 + i
            break
    
    # 方法2：计算文字密度变化
    window_size = 10
    density = []
    for i in range(gap_center-50, gap_center+50):
        col_density = np.sum(processed[:, i-window_size//2:i+window_size//2]) / (height * window_size)
        density.append(col_density)
    
    # 找到密度开始下降的点作为左栏边界
    min_density_idx = np.argmin(density)
    density_boundary = gap_center - 50 + min_density_idx
    
    # 结合两种方法的结果，选择更靠右的边界
    left_end = max(text_end, density_boundary)
    
    # 添加安全边距
    safety_margin = 15  # 增加15像素的安全边距
    left_end += safety_margin
    
    # 确保边界在合理范围内
    left_end = min(left_end, width - 50)  # 不要超过图像宽度
    
    # 右栏开始位置
    right_start = gap_center + 10
    
    # 3. 词条分割
    gap_threshold = 5  # 优化参数：空隙高度阈值30像素
    #print(f"分割词条 (空隙阈值: {gap_threshold}像素)...")
    
    # 分割左右两栏
    left_column = processed[:, :left_end]
    left_column_img = image[:, :left_end]
    right_column = processed[:, right_start:]
    right_column_img = image[:, right_start:]
    
    # 处理左侧栏
    #print("处理左侧栏...")
    left_entries = process_column(left_column, left_column_img, "L", output_dir, 
                                 gap_threshold=gap_threshold,height=height)
    
    # 处理右侧栏
    #print("处理右侧栏...")
    right_entries = process_column(right_column, right_column_img, "R", output_dir, 
                                  gap_threshold=gap_threshold,height=height)
    
    return left_entries + right_entries

def process_column(column_bin, column_img, side, output_dir, gap_threshold, height):
    """
    处理单栏图像，分割词条
    :param column_bin: 二值化的栏图像
    :param column_img: 原始栏图像
    :param side: 左栏或右栏标识
    :param output_dir: 输出目录
    :param gap_threshold: 行间空隙高度阈值（像素）
    :return: 分割后的词条图像列表
    """
    #col_height, col_width = column_img.shape
    # 水平投影计算
    horizontal_projection = np.sum(column_bin, axis=1)
    
    # 寻找词条之间的空隙（水平投影值接近0的区域）
    threshold_coef = 0.02  # 优化参数：投影阈值系数0.05
    threshold = 600 * threshold_coef
    gaps = [0]
    in_gap = False
    gap_start = 0
    
    # 检测空隙区域 - 使用参数化的空隙高度阈值
    for i, val in enumerate(horizontal_projection):
        if val < threshold and not in_gap:
            in_gap = True
            gap_start = i
        elif val >= threshold and in_gap:
            in_gap = False
            # 使用参数化的空隙高度阈值
            if i - gap_start > gap_threshold:  # 空隙高度大于阈值
                gaps.append((gap_start+i)//2)
    #print(f"gaps={gaps}")
    # 提取词条区域边界
    entry_boundaries = []
    prev_end = 0
    
    # 收集所有词条边界

    
    for n in range(len(gaps)-2):
        entry_boundaries.append((gaps[n],gaps[n+1]))
    entry_boundaries.append((gaps[len(gaps)-1],height))
    #print(f"entry_boundaries={entry_boundaries}")
#####    ########   

    # 假设这是您的参数
    if side == "L":
        BLANK_THRESHOLD = 50  # 空白区域长度阈值
    else:
        BLANK_THRESHOLD = 60
        
    # 修改后的代码
    new_merged_boundaries = []  # 用于存储合并后的新边界
    prev_y_start, prev_y_end = None, None  # 用于跟踪上一个有效条目的边界

    split_point = []
    for idx, (y_start, y_end) in enumerate(entry_boundaries):
       # 提取词条图像
        entry_img1 = column_img[y_start+10:y_start+20, :]
        inverted_img = 255 - entry_img1
        height_sel = y_end - y_start
        #gray_img = entry_img[:, 0, 0]
            
        # 计算垂直投影（列方向求和）
        vertical_proj = np.sum(inverted_img, axis=0)
        vertical_proj = vertical_proj[:, 0]
        #print(vertical_proj)
        
       # 检测左侧空白区域
        left_blank_length1 = 0
        for col_sum in vertical_proj:
            #if isinstance(col_sum, np.ndarray):
             #   col_sum = col_sum.item()  # 转换为Python标量
            if col_sum // 10 > 0.2:  # 遇到非空白列，停止计数
                break
            left_blank_length1 += 1
        left_blank_length2 = 0
        if height_sel < 75:
            left_blank_length2 = left_blank_length1 
        else:
            entry_img2 = column_img[y_end-30:y_end-20, :]    
            inverted_img = 255 - entry_img2  
            vertical_proj = np.sum(inverted_img, axis=0)
            vertical_proj = vertical_proj[:, 0]
            for col_sum in vertical_proj:
            #if isinstance(col_sum, np.ndarray):
             #   col_sum = col_sum.item()  # 转换为Python标量
                if col_sum // 10 > 0.2:  # 遇到非空白列，停止计数
                    break
                left_blank_length2 += 1
        left_blank_length = max(left_blank_length1, left_blank_length2)    
            
            
        #print(f"left_blank_length={left_blank_length}")
            
        
        # 判断分割点
        if left_blank_length > BLANK_THRESHOLD:
            #print(f"Appending y_start: {y_start}")
            split_point.append(y_start)
    #print(f"split_point={split_point}")
    
    # 用新的边界列表替换原始列表
    for n in range(len(split_point)-1):
        new_merged_boundaries.append((split_point[n],split_point[n+1]))
    new_merged_boundaries.append((split_point[len(split_point)-1],height))
        
    merged_boundaries = new_merged_boundaries
    #print(f"merged_boundaries={merged_boundaries}")
    
    #########
    entries = []
    entry_count = 0
    #skipped_page_number = False
    
    
    
    for idx, (y_start, y_end) in enumerate(merged_boundaries):
        # 提取词条图像
        entry_img = column_img[y_start:y_end, :]
        height_lemma = y_end - y_start
        
        # 跳过页码（通常高度较小且位于顶部）
        if height_lemma < 90 :
            #print(f"跳过{side}栏的页码词条 (高度: {height}像素)")
            #skipped_page_number = True
            continue
            
        # 保存词条图像为JPG格式
        entry_count += 1
        output_path = os.path.join(output_dir, f"{image_num}{side}{entry_count:03d}.jpg")
        
        # 使用高质量JPEG保存
        cv2.imwrite(output_path, entry_img, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
        
        entries.append(entry_img)
    
    print(f"{side}栏分割完成: 共 {len(merged_boundaries)} 个词条, 保存 {entry_count} 个有效词条")
    return entries

if __name__ == "__main__":
    # 循环处理
    for num in range(101385,101386):  # 确保包含
        image_num = num
        input_image = f"{image_num}.jpg"
        
        if not os.path.exists(input_image):
            print(f"警告: 图像 {input_image} 不存在，跳过处理")
            continue  # 跳过不存在的文件
        
        print(f"\n处理图像: {input_image}")
        entries = process_dictionary_page(input_image)
    
    #print(f"\n处理完成! 成功分割出 {len(entries)} 个有效词条图像")
    #print(f"分割结果保存在 output 目录中 (JPG格式)")