如何用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/20,减少对细线条的破坏;先膨胀后腐蚀的顺序,强化竖线同时去除噪点
- 轮廓筛选:通过高度和宽度的比例限制,只保留接近图像高度的窄轮廓,过滤掉横向线条或噪点
- 坐标后处理:去重排序后,若检测到多余线条,通过间距均值筛选出均匀分布的4条目标竖线,确保分割准确
内容的提问来源于stack exchange,提问作者Kriss J
相关产品推荐
相关产品推荐

