网页截图分块算法
业务场景
在我们的具体应用场景中,用户需要通过输入链接生成相应的网站。为了实现这一目标,我们的任务包括对原有网站进行截图和分块,然后将每一块发送给AI进行代码生成。
问题
当前采用的计算机视觉(CV)方法通过像素检测及自适应宽度算法进行分割,然而其效果与人类的实际感知存在显著差异。
为了帮助理解,下面展示了一个简单的图示例:
745b70f0-a578-4e3e-a0e5-4fe7580ed84f_comparison.png
这种算法存在以下几个主要问题:
- 文本内容被不恰当地切割。
- 图像整体被断裂处理。
- 整体内容被不合理地分割成多个部分。
- 分割效果与人类的视觉感知差距大。
算法思考
为了解决上述问题,我们需要探索和提出更优的算法策略。以下是几种可能的解决方案:
Segment AI
使用Segment AI将长图发送进行语义引导分割。这一方法有助于理解图像内容,提高分割的准确性。我们可以考虑使用Meta AI的Segment Anything工具,了解其是否适用于我们的需求。
链接:https://segment-anything.com/
OmniParser

image.png
OmniParser算法主要应用于对GUI元素的标记,虽然其在许多场景中具有高效性,但在我们当前的任务中,采纳的实用性相对较低。
HTML标注结构 + CV分割
这是一个更符合工程化的方法。我们在原有算法中遇到的问题是,网页图像的像素扰动很大,导致单纯的CV算法难以获利。因此,我们可以考虑通过为原始HTML结构的布局进行染色来优化此方法。HTML本质上是一棵从根到叶的DOM树。我们可以沿着树的结构自上而下遍历元素,如果元素大小与视口一致,则对该部分进行染色。接下来应用CV方法进行图像分割。以下是该算法的实现效果,基本上符合我们的需求:
image.png
算法DEMO
import cv2
import numpy as np
from PIL import Image
import pytesseract
# 配置 Tesseract 路径(仅在 Windows 上需要)
# pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
def detect_red_borders(image):
"""
检测图像中的红色虚线边框,返回边框的轮廓。
"""
# 转换为 HSV 颜色空间
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# 调整红色的 HSV 范围
lower_red1 = np.array([0, 100, 100])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([160, 100, 100])
upper_red2 = np.array([180, 255, 255])
# 创建红色掩模
mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
mask = cv2.bitwise_or(mask1, mask2)
# 使用更大的核进行形态学操作,连接虚线形成完整边框
kernel = np.ones((5,5), np.uint8) # 适当调整核大小
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=2) # 调整迭代次数
# 使用 Canny 边缘检测辅助检测虚线
edges = cv2.Canny(mask, 50, 150)
# 查找轮廓
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 过滤和存储边框轮廓
red_borders = []
min_area = 5000 # 调整最小面积阈值,根据实际情况
for cnt in contours:
area = cv2.contourArea(cnt)
if area > min_area:
x, y, w, h = cv2.boundingRect(cnt)
red_borders.append((x, y, w, h))
# 在原图上画出检测到的区域
cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)
# 保存中间结果用于调试
cv2.imwrite('debug_mask.png', mask)
cv2.imwrite('debug_edges.png', edges)
cv2.imwrite('debug_contours.png', image)
return red_borders
def detect_block_labels(image, red_borders):
"""
在红色边框区域内检测"Block"标签的位置。
返回每个边框对应的区域坐标。
"""
# 将图像转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 应用二值化
_, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV)
# OCR 识别
pil_img = Image.fromarray(thresh)
data = pytesseract.image_to_data(pil_img, config='--psm 6', output_type=pytesseract.Output.DICT)
n_boxes = len(data['level'])
block_labels = []
for i in range(n_boxes):
text = data['text'][i].strip()
if 'block' in text.lower():
(x, y, w, h) = (data['left'][i], data['top'][i], data['width'][i], data['height'][i])
block_labels.append((x, y, w, h))
return block_labels
def extract_blocks(image, red_borders):
"""
根据检测到的红色边框,从图像中提取对应的区块。
返回一个包含区块图像的列表。
"""
blocks = []
for idx, (x, y, w, h) in enumerate(red_borders):
# 提取区块区域
block = image[y:y+h, x:x+w]
blocks.append(block)
# 可选:保存每个区块到文件
# cv2.imwrite(f'block_{idx+1}.png', block)
return blocks
def main():
# 加载长截图
image_path = 'img_1.png' # 替换为您的截图路径
image = cv2.imread(image_path)
if image is None:
print(f"无法加载图像: {image_path}")
return
else:
print(f"成功加载图像: {image_path}, 尺寸: {image.shape}")
# 使用修改后的检测函数
red_borders = detect_red_borders(image)
print(f"检测到 {len(red_borders)} 个红色边框。")
if len(red_borders) == 0:
print("未检测到任何红色边框,请检查边框颜色范围和截图是否正确。")
# 继续执行以查看调试图片
# 查看生成的调试图片:
# - debug_mask.png:红色掩模
# - debug_edges.png:Canny 边缘图
# - debug_contours.png:检测到的轮廓标注在原图上
# 可视化检测到的红色边框
cv2.imshow('Detected Red Borders', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 可选:检测"Block"标签
blocks = detect_block_labels(image, red_borders)
print("检测到的标签:", blocks)
# 提取区块
extracted_blocks = extract_blocks(image, red_borders)
print(f"提取了 {len(extracted_blocks)} 个区块。")
# 保存提取的区块
for idx, block in enumerate(extracted_blocks):
block_path = f'block_{idx+1}.png'
cv2.imwrite(block_path, block)
print(f"保存区块到: {block_path}")
if __name__ == "__main__":
main()
反思
在面对复杂的实际业务场景时,如果遇到难以解决的问题,不妨从简单的算法入手,逐步解决每个算法所面临的障碍。这种方法将为我们提供全新的视角,以便找到更有效的解决方案。