如何提取图像中文本颜色并隔离对应彩色文本?
提取图像文本颜色并过滤非文本内容的优化方案
你现在遍历所有颜色逐个掩蔽的方法,确实会生成大量无效图像,这里给你几个更高效的思路,直接定位文本对应的颜色:
方法一:先通过OCR定位文本区域,再提取区域内主颜色
既然已经在用pytesseract,可以先让它识别文本的位置,只在这些区域里统计颜色,不用处理整张图的所有颜色,直接锁定文本的颜色范围:
步骤:
- 用pytesseract获取每个文本块的边界框
- 裁剪出所有文本区域,合并成掩码区域
- 在掩码区域内统计颜色频率,取占比最高的颜色作为文本色
- 基于该颜色生成掩膜,保留文本内容
代码示例:
import cv2 import numpy as np from PIL import Image import pytesseract from pytesseract import Output # 读取图像 img = cv2.imread('z.jpg') rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) pil_img = Image.fromarray(rgb_img) # 用pytesseract获取文本区域的边界框 d = pytesseract.image_to_data(pil_img, output_type=Output.DICT) n_boxes = len(d['text']) # 创建文本区域掩码 mask = np.zeros(img.shape[:2], dtype=np.uint8) for i in range(n_boxes): if int(d['conf'][i]) > 60: # 过滤低置信度识别结果 (x, y, w, h) = (d['left'][i], d['top'][i], d['width'][i], d['height'][i]) mask[y:y+h, x:x+w] = 255 # 提取文本区域的像素 text_pixels = rgb_img[mask == 255] if len(text_pixels) == 0: print("未识别到有效文本区域") exit() # 统计文本区域内的颜色频率 from collections import Counter color_counts = Counter([tuple(p) for p in text_pixels]) # 取占比最高的颜色(文本色) dominant_color = color_counts.most_common(1)[0][0] print(f"识别到的文本主颜色:{dominant_color}") # 生成颜色掩膜(考虑颜色容差) tol = 5 lower = np.array([dominant_color[0]-tol, dominant_color[1]-tol, dominant_color[2]-tol], dtype=np.uint8) upper = np.array([dominant_color[0]+tol, dominant_color[1]+tol, dominant_color[2]+tol], dtype=np.uint8) # 转换为OpenCV的BGR格式 lower_bgr = lower[::-1] upper_bgr = upper[::-1] text_mask = cv2.inRange(img, lower_bgr, upper_bgr) # 保留文本内容,其余置为黑色 result = cv2.bitwise_and(img, img, mask=text_mask) cv2.imshow('Text Only', result) cv2.waitKey(0) cv2.destroyAllWindows()
方法二:用颜色聚类区分文本与背景
如果OCR定位不准,还可以用K-means聚类把图像颜色分成两类(文本和背景),再结合形态学特征判断哪一类是文本:
步骤:
- 将图像像素转为二维数组,用K-means聚成2类
- 分别生成两类的掩膜,通过连通域分析判断哪个是文本(文本通常是分散的小连通域,面积占比更小)
- 保留对应类的颜色
代码示例:
import cv2 import numpy as np from sklearn.cluster import KMeans img = cv2.imread('z.jpg') rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转换为二维像素数组 pixels = rgb_img.reshape((-1, 3)) # K-means聚类成2类 kmeans = KMeans(n_clusters=2, random_state=0).fit(pixels) labels = kmeans.labels_.reshape(rgb_img.shape[:2]) # 获取两类的颜色 colors = kmeans.cluster_centers_.astype(np.uint8) # 生成两个掩膜 mask0 = (labels == 0).astype(np.uint8)*255 mask1 = (labels == 1).astype(np.uint8)*255 # 判断哪个掩膜是文本:文本连通域数量通常更多 def is_text_mask(mask): num_labels, _ = cv2.connectedComponents(mask) return num_labels > 10 # 阈值可根据实际情况调整 text_mask = mask1 if is_text_mask(mask1) else mask0 text_color = colors[1] if is_text_mask(mask1) else colors[0] print(f"聚类得到的文本颜色:{text_color}") # 生成最终结果 result = cv2.bitwise_and(img, img, mask=text_mask) cv2.imshow('Text Only', result) cv2.waitKey(0) cv2.destroyAllWindows()
现有代码的问题修正
你原来的代码有两个小问题:
colors = sorted(image.getcolors(...))里的image应该是cimage,因为你用cimage = Image.open(...)初始化的图像对象- PIL的RGB格式和OpenCV的BGR格式不匹配,直接用
cimage给OpenCV处理会导致颜色判断错误,需要先转换格式
内容的提问来源于stack exchange,提问作者kadash12
相关产品推荐
相关产品推荐

