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

如何用OpenCV识别表格图像竖线坐标并分割单元格?

问题描述

我是OpenCV新手,需要识别一张隐去单元格内容的表格行图像中的4条竖线坐标,以此分割出表格单元格。我尝试了如下代码,但仅得到粗黑竖线,无法识别目标竖线,请求解决方法:

file_path = 'unnamed.jpg'


img = Image.open(file_path)
width = img.width
height = img.height

# Get table list
document_img = cv2.imread(file_path)
table_list = [np.array(document_img, copy=True)]

# Identifying the Vertical lines in the image
img = cv2.cvtColor(table_list[0], cv2.COLOR_BGR2GRAY)
print("img \n")
cv2_imshow(img)
img_height, img_width = img.shape
thresh, img_bin = cv2.threshold(img, 180, 255, cv2.THRESH_BINARY)
print("img_bin \n")
cv2_imshow(img_bin)
img_bin_inv = 255 - img_bin
print("img_bin_inv \n")
cv2_imshow(img_bin_inv)
kernel_len_ver = max(10, img_height // 50)

ver_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, kernel_len_ver))  # shape (kernel_len, 1) inverted! xD
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
# Use vertical kernel to detect and save the vertical lines in a jpg
image_1 = cv2.erode(img_bin_inv, ver_kernel, iterations=3)
print("image_1 \n")
cv2_imshow(image_1)
vertical_lines = cv2.dilate(image_1, ver_kernel, iterations=4)
print("vertical_lines \n")
cv2_imshow(vertical_lines)
# print("\n\n\n")
Image.fromarray(vertical_lines, mode='L').save('vertical_lines.jpg')
file_path = 'unnamed.jpg'


img = Image.open(file_path)
width = img.width
height = img.height

# Get table list
document_img = cv2.imread(file_path)
table_list = [np.array(document_img, copy=True)]

# Identifying the Vertical lines in the image
img = cv2.cvtColor(table_list[0], cv2.COLOR_BGR2GRAY)
print("img \n")
cv2_imshow(img)
img_height, img_width = img.shape
thresh, img_bin = cv2.threshold(img, 180, 255, cv2.THRESH_BINARY)
print("img_bin \n")
cv2_imshow(img_bin)
img_bin_inv = 255 - img_bin
print("img_bin_inv \n")
cv2_imshow(img_bin_inv)
kernel_len_ver = max(10, img_height // 50)

ver_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, kernel_len_ver))  # shape (kernel_len, 1) inverted! xD
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
# Use vertical kernel to detect and save the vertical lines in a jpg
image_1 = cv2.erode(img_bin_inv, ver_kernel, iterations=3)
print("image_1 \n")
cv2_imshow(image_1)
vertical_lines = cv2.dilate(image_1, ver_kernel, iterations=4)
print("vertical_lines \n")
cv2_imshow(vertical_lines)
# print("\n\n\n")
Image.fromarray(vertical_lines, mode='L').save('vertical_lines.jpg')

img = cv2.imread('vertical_lines.jpg')

# Convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Apply a threshold to the grayscale image
ret, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)

# Apply a morphological operation to close gaps in the lines
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# Find the contours in the binary image
contours, hierarchy = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Initialize a list to store the coordinates of the vertical lines
vertical_lines = []

# Iterate through each contour and extract the coordinates of the bounding rectangle if it's a vertical line
for contour in contours:
    x,y,w,h = cv2.boundingRect(contour)
    vertical_lines.append((x,y,x+w,y+h))

vertical_lines = sorted(vertical_lines)

img2 = cv2.imread('unnamed.jpg')
count = 0

for i in range(len(vertical_lines)):
  if i < len(vertical_lines) - 1:
    count = count + 1
    roi = img2[0:width, vertical_lines[i][0]:vertical_lines[i+1][0]]
    cv2.imwrite("/content/roi/roi_"+ str(count) +".jpg", roi)
解决思路与优化方案

你的代码存在重复执行竖线检测逻辑、形态学操作参数不合理、固定阈值适配性差等问题,导致无法提取目标细竖线。以下是针对性优化后的实现:

关键调整

  • 移除冗余的重复代码块,减少无效计算
  • 改用自适应阈值替代固定阈值,适配图像亮度变化,更好保留细线条
  • 优化形态学操作的核大小与迭代次数,避免过度侵蚀目标竖线
  • 增加轮廓筛选逻辑,通过宽高比例过滤干扰轮廓,精准提取竖线

优化后代码

import cv2
import numpy as np

# 读取图像
file_path = 'unnamed.jpg'
img = cv2.imread(file_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_height, img_width = gray.shape

# 自适应阈值处理,保留细线条
thresh = cv2.adaptiveThreshold(
    gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv2.THRESH_BINARY_INV, 11, 2
)

# 定义垂直检测核:宽度1,高度设为图像高度的1/20,适配竖线长度
ver_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, img_height // 20))

# 先膨胀强化竖线,再腐蚀去噪,避免细线条被侵蚀
vertical_lines = cv2.dilate(thresh, ver_kernel, iterations=1)
vertical_lines = cv2.erode(vertical_lines, ver_kernel, iterations=1)

# 寻找轮廓并筛选符合条件的竖线
contours, _ = cv2.findContours(vertical_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
vertical_x_coords = []

for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt)
    # 筛选规则:高度接近图像高度(>80%),宽度较窄(<5像素)
    if h > img_height * 0.8 and w < 5:
        vertical_x_coords.append(x)

# 去重、排序,确保坐标有序
vertical_x_coords = sorted(list(set(vertical_x_coords)))

# 处理线条数量过多的情况:通过间距筛选出均匀分布的4条线
if len(vertical_x_coords) > 4:
    gaps = np.diff(vertical_x_coords)
    avg_gap = np.mean(gaps)
    filtered_coords = [vertical_x_coords[0]]
    for x in vertical_x_coords[1:]:
        if x - filtered_coords[-1] > avg_gap * 0.7:
            filtered_coords.append(x)
            if len(filtered_coords) == 4:
                break
    vertical_x_coords = filtered_coords

# 分割单元格:添加图像左右边界,确保完整分割
if len(vertical_x_coords) >= 4:
    # 补充左右边界,保证第一个和最后一个单元格完整
    full_coords = [0] + vertical_x_coords + [img_width]
    for idx in range(len(full_coords)-1):
        x_start = full_coords[idx]
        x_end = full_coords[idx+1]
        roi = img[:, x_start:x_end]
        cv2.imwrite(f"/content/roi/roi_{idx+1}.jpg", roi)
else:
    print("未检测到足够的目标竖线,请调整阈值或核大小参数")

代码说明

  1. 自适应阈值:通过高斯加权计算局部阈值,能更好处理图像中亮度不均的区域,避免细线条因固定阈值被过滤
  2. 形态学操作:调整核大小为图像高度的1/20,减少对细线条的破坏;先膨胀后腐蚀的顺序,强化竖线同时去除噪点
  3. 轮廓筛选:通过高度和宽度的比例限制,只保留接近图像高度的窄轮廓,过滤掉横向线条或噪点
  4. 坐标后处理:去重排序后,若检测到多余线条,通过间距均值筛选出均匀分布的4条目标竖线,确保分割准确

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 06:54:56