如何使用OpenCV检测无贴纸魔方(Stickerless Rubik's Cube)的面颜色?
如何用OpenCV识别无贴纸魔方的面颜色
我需要实现用OpenCV识别无贴纸魔方的面颜色,但找不到最优方案。网上多数方案采用Canny边缘检测,这种方法对带黑底贴纸的魔方效果很好:
但对无贴纸魔方效果极差,例如:
我的问题是:如何用OpenCV检测这类无贴纸魔方的面颜色?
我目前尝试的代码如下:
import cv2 as cv import numpy as np from google.colab.patches import cv2_imshow image = cv.imread('cube.png') grey_frame = cv.cvtColor(image, cv.COLOR_BGR2GRAY) noiseless_frame = cv.fastNlMeansDenoising(grey_frame, None, 20, 7, 7) blurred_frame = cv.blur(noiseless_frame, (3, 3)) canny_frame = cv.Canny(blurred_frame, 30, 60, 3) dilated_frame = cv.dilate(canny_frame, cv.getStructuringElement(cv.MORPH_RECT, (9, 9))) contours, _ = cv.findContours(dilated_frame, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) square_contours = [] for contour in contours: approx = cv.approxPolyDP(contour, 0.1*cv.arcLength(contour, True), True) if len(approx) == 4 or True: x, y, w, h = cv.boundingRect(approx) ratio = float(w) / h area = cv.contourArea(approx) if ratio >= 0.8 and ratio <= 1.2 and w >= 30 and w <= 80 and area >= 900: square_contours.append({"x": x, "y": y, "w": w, "h": h}) new_img = image.copy() for contour in square_contours: x, y, w, h = contour["x"], contour["y"], contour["w"], contour["h"] cv.rectangle(new_img, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.imshow(new_img)
但这段代码大多时候无法检测到无贴纸魔方的色块,且当相邻色块颜色相近、边界不清晰时,会将它们识别为同一轮廓。附一张无贴纸魔方图片:
针对无贴纸魔方的改进方案
无贴纸魔方的色块没有黑边,灰度差异极小,Canny边缘检测自然失效,得换思路从颜色或局部灰度特征入手:
1. 基于HSV颜色空间分割(优先推荐)
无贴纸魔方的核心特征是色块颜色差异,直接在HSV空间做分割更稳定,HSV对光照变化的鲁棒性远优于BGR:
- 先将图像转换为HSV格式
- 针对魔方的6种颜色(白、黄、红、橙、绿、蓝),用取色工具获取对应HSV阈值范围(需根据实际光照微调)
- 用
cv.inRange()生成单颜色掩码,再通过形态学闭操作(膨胀+腐蚀)去除噪声 - 对每个掩码提取轮廓,筛选出接近正方形的区域(比例0.9~1.1,面积符合实际大小)
示例代码片段:
import cv2 as cv import numpy as np image = cv.imread('cube.png') hsv_img = cv.cvtColor(image, cv.COLOR_BGR2HSV) # 示例:白色的HSV阈值(需根据实际场景调整) lower_white = np.array([0, 0, 200]) upper_white = np.array([180, 25, 255]) white_mask = cv.inRange(hsv_img, lower_white, upper_white) # 形态学操作去除噪声 kernel = np.ones((5, 5), np.uint8) clean_mask = cv.morphologyEx(white_mask, cv.MORPH_CLOSE, kernel) # 提取外部轮廓并筛选正方形 contours, _ = cv.findContours(clean_mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) for cnt in contours: approx = cv.approxPolyDP(cnt, 0.02 * cv.arcLength(cnt, True), True) if len(approx) == 4: x, y, w, h = cv.boundingRect(approx) aspect_ratio = w / h if 0.9 <= aspect_ratio <= 1.1 and cv.contourArea(cnt) > 500: cv.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2) cv.imshow('Detected Cubes', image) cv.waitKey(0) cv.destroyAllWindows()
2. 自适应阈值边缘检测
如果颜色分割受光照影响大,可以试试自适应阈值,它能根据局部区域灰度调整阈值,适合边界灰度差异小的场景:
grey_img = cv.cvtColor(image, cv.COLOR_BGR2GRAY) # 高斯自适应阈值二值化 adaptive_thresh = cv.adaptiveThreshold( grey_img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY_INV, 11, 2 ) # 提取轮廓并筛选正方形 contours, _ = cv.findContours(adaptive_thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) # 后续筛选逻辑同颜色分割方案
3. 关键优化点
- 颜色阈值需根据拍摄环境微调,可借助OpenCV的取色工具实时获取目标色块的HSV值
- 轮廓提取优先用
RETR_EXTERNAL模式,避免嵌套轮廓干扰 - 筛选轮廓时,结合宽高比、面积、凸性三个特征,减少误检
- 若拍摄角度倾斜,可先做透视变换校正魔方平面,再进行色块检测
内容的提问来源于stack exchange,提问作者Tripaloski
相关产品推荐
相关产品推荐

