Python新手提问:如何用Python识别魔方上不同颜色块的数量?

下图由黄,红,绿,蓝方块组成,如何获取每种颜色方块的数量? 如果用 python 来实现的话,有第三方库或思路推荐吗,谢谢 图片

我首先想到了 openCV,如果实在没思路的话,就去 GayHub 找个 python-opencv 的库试试看,考虑到可能会绕圈子,所以在这里问问 pythoner 大佬,感想~


Python新手提问:如何用Python识别魔方上不同颜色块的数量?

44 回复

颜色空间直方图了解一下。


import cv2
import numpy as np
from collections import Counter

def detect_cube_colors(image_path):
    """
    识别魔方图像中不同颜色块的数量
    
    参数:
        image_path: 魔方图像文件路径
    
    返回:
        dict: 颜色名称到数量的映射
    """
    # 1. 读取图像
    img = cv2.imread(image_path)
    if img is None:
        raise ValueError("无法读取图像,请检查文件路径")
    
    # 2. 转换为HSV颜色空间(对光照变化更鲁棒)
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    
    # 3. 定义魔方标准颜色范围(HSV格式)
    # 注意:这些阈值可能需要根据实际图像调整
    color_ranges = {
        'red': [(0, 100, 100), (10, 255, 255)],      # 红色范围1
        'red2': [(170, 100, 100), (180, 255, 255)],  # 红色范围2(HSV中红色在0°和180°附近)
        'orange': [(10, 100, 100), (25, 255, 255)],
        'yellow': [(25, 100, 100), (35, 255, 255)],
        'green': [(35, 100, 100), (85, 255, 255)],
        'blue': [(85, 100, 100), (130, 255, 255)],
        'white': [(0, 0, 200), (180, 50, 255)],      # 高亮度,低饱和度
        'black': [(0, 0, 0), (180, 255, 50)]         # 低亮度
    }
    
    # 4. 创建掩码并统计每个颜色的像素数
    color_counts = {}
    height, width = img.shape[:2]
    total_pixels = height * width
    
    for color_name, (lower, upper) in color_ranges.items():
        # 处理红色(两个范围)
        if color_name == 'red':
            mask1 = cv2.inRange(hsv, tuple(lower), tuple(upper))
            mask2 = cv2.inRange(hsv, tuple(color_ranges['red2'][0]), 
                               tuple(color_ranges['red2'][1]))
            mask = cv2.bitwise_or(mask1, mask2)
            color_counts['red'] = np.sum(mask > 0)
        else:
            mask = cv2.inRange(hsv, tuple(lower), tuple(upper))
            color_counts[color_name] = np.sum(mask > 0)
    
    # 5. 过滤噪声(假设每个色块至少占图像的0.5%)
    min_pixels = total_pixels * 0.005
    filtered_counts = {k: v for k, v in color_counts.items() if v > min_pixels}
    
    # 6. 估算块数(假设每个色块大致等大)
    # 找到最大的颜色计数作为参考
    if filtered_counts:
        max_count = max(filtered_counts.values())
        # 估算每个色块的平均像素数(取最大值的1/9,因为一个面有9个块)
        avg_per_piece = max_count / 9
        
        # 计算每个颜色的块数
        piece_counts = {}
        for color, count in filtered_counts.items():
            pieces = round(count / avg_per_piece)
            if pieces > 0:
                piece_counts[color] = pieces
        
        return piece_counts
    
    return {}

# 使用示例
if __name__ == "__main__":
    # 替换为你的图像路径
    result = detect_cube_colors("rubiks_cube.jpg")
    
    print("魔方颜色块统计:")
    print("-" * 30)
    for color, count in result.items():
        print(f"{color}: {count}块")
    print(f"总块数: {sum(result.values())}块")
    
    # 可选:显示处理结果
    img = cv2.imread("rubiks_cube.jpg")
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    
    # 创建组合掩码
    masks = []
    for color_name in result.keys():
        if color_name == 'red':
            mask1 = cv2.inRange(hsv, (0, 100, 100), (10, 255, 255))
            mask2 = cv2.inRange(hsv, (170, 100, 100), (180, 255, 255))
            masks.append(cv2.bitwise_or(mask1, mask2))
        else:
            lower, upper = color_ranges[color_name]
            masks.append(cv2.inRange(hsv, tuple(lower), tuple(upper)))
    
    if masks:
        combined_mask = sum(masks)
        result_img = cv2.bitwise_and(img, img, mask=combined_mask)
        
        cv2.imshow('Original', img)
        cv2.imshow('Detected Colors', result_img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

核心思路:

  1. 用HSV颜色空间替代RGB,对光照更稳定
  2. 定义常见魔方颜色的HSV阈值范围
  3. 通过颜色阈值分割统计各颜色像素
  4. 根据像素比例估算实际色块数量

关键点说明:

  • 红色在HSV中需要两个范围(0-10°和170-180°)
  • 需要根据实际魔方颜色调整阈值
  • 假设每个色块大小相近来估算块数

一句话建议: 实际使用时先用几张图调整好颜色阈值。

谢谢,我查查相关资料

我的思路是,按颜色设置阈值,分割之后计算每种颜色轮廓的面积,和总面积一比就可以了

好像跟直方图原理差不多 hhh 都是计算像素数量

不知道用一个和方块一样大小的 scanner 一路扫过去,结合 PIL 库有没有戏。

如果方块大小是固定和一致的,如果是的话 使用 PIL 库 隔多少像素去一个点,获得 rgb 值即可。

卖垫子的?

1. 需要被统计的块是否可能本身是白色的?
2. 是否保证存在白色缝隙?
3. 是否对精度有绝对要求?

哈哈,卖地胶的,白色的块无需统计,保证存在白色缝隙,对精度有要求。但是图片分辨率都会和上面的第一张图一样清楚。

如果你有思路可以聊下,我可以付一些辛苦费,告诉我思路自己去实现,嘿嘿

有个简单粗暴的方法,直接遍历所有点的 RGB 值,然后拿同颜色的点( RGB 相近)的数量 除以 1 块的像素点数量(宽*高) 就能得到大概值了。
不过半块的之类的会导致少算一些。

这种估计只能用图形学的区域填充算法,把每块的边界算出来,才能保证精度

查了很多资料,GayHub 也翻了半天没找到好的办法,我看最终的办法只能靠肉眼去数了 = =!

这样相对简单,用白色切开后统计每个块的颜色直方图。
精度要求我认为设计成先显示一个带覆盖层的样子然后人工检验一下比较好。
最好能够比较方便地指出错误类型

“与其费力地尝试完全取代人类的工作,不如想想如何让人类的工作变得更加方便”

from skimage.color import rgb2hsv, rgba2rgb
from skimage import data, io, filters
import numpy as np

image = io.imread(‘1.png’)
hsv = rgb2hsv(rgba2rgb(image))

h_hist, h = np.histogram(hsv[…,0].flatten(), bins=30)

1. 把你的图转成 hsv,在 h 空间求 histogram,黑 /白色的 h 值会是 0 所以不用在意。选择合适的 bins,使峰个数和颜色个数相等。忽略小于某值的 h,因为是白色。这里如果你的图里每个颜色都是纯色,bins 原则多大都是准确的。
2. 建议你尽量把图的 dpi 搞成一样的,也就是说每个方块大小相等,图总大小只和方块数有关。这样你只要算出一个方块有多少像素,用上述的 h_hist 值去除就可以了。因为不到一个方块按一个方块计算,所以向上取整 int(h_hist/square_size)+1 即可。

我没怎么折腾过这类问题,你最好看看 rgb2hsv 和 rgba2rgb 之类的函数,了解一下 hsv 模型。理论上说,白色的话 hsv 里 s 是 0, v 是 1,rgb2hsv 里给出的 h 也应该是 0,但近白色 h 不一定是 0,所以纯色最好。你这图不知道为啥好像不完全是纯色,所以筛选起来可能会比较麻烦,求 histogram 之前可以考虑像 hsv = hsv[np.logical_and(hsv[…,1]>0.05, hsv[…,2]<0.95)]这样按照 s, v (白色 s 值比较小接近 0,v 值比较大接近 1 )尽可能地把白色去掉,以增加准确度。

可以看看这里:
http://www.voidcn.com/article/p-dntnbcyb-ro.html
有常见颜色的 HSV 范围参考。

P.S. 地胶市场咋样?这么丑的设计地胶能卖出去么……

如果要大致计算的话,就用直方图。如果要精确计算的话,就划分网格,然后一个个中心取 RGB 值。不规则形状的话,用一个 mask 来遮住空白区域

第一个图横竖遍历,每个方格中心点像素值取出来,最后统计像素值数量就行。

第二个不规则图形,细节还是小方格,我觉得先把不规则图形补成规则的长方形,再横竖遍历,最后求像素个数,

一般图形颜色不会那么整齐,考虑颜色值的一个偏移范围,

腐蚀好像可以消去细线和封闭区块内的点

这个图只是方便客户,计算需要的数量,实际不是这个样子,市场挺好的

参考图片二值化的做法,把颜色进行三值化就可以了。然后就可以直接数块状大小了。

反正你这每一个格子大小应该都是固定的,用 opencv 查询每个格子中间像素的数值就很好统计

每个格子的大小是固定的,图都是美工做的,「用 opencv 查询每个格子中间像素的数值」这个有点难 = =!

简单提供一个思路,不知道符不符合你的要求:
分别针对 4 个颜色,做 cv::threshold、cv::findContours 等操作,得到各个颜色的总面积。后面就可以除以每个色块的面积得到大概的色块数量了

我试一下

直接通过颜色做阈值分割,分别拿到各种颜色的面积,然后除以单个方块的面积就行了

细线一定存在?都存在的话按细线切割开就行了呀

下午摸鱼写了一下,4 个颜色分别 181 731 14 34 个?

红色我肉眼数了下 37 个

颜色识别有点问题,晚上回去研究一下 HSV

刚刚回家改了改各种颜色的判定范围,没问题了,红色 h 值可以在两个区间,emmmmmm
红色:37
蓝色:14
黄色:178
绿色:731
其他?:0

计算一下间隙距离(从左到右遇到白色块的距离)切割图片,遍历所有小图

我和同事也实现了下,有点误差。你的计算还挺准确的,能提供下思路吗?


先用不同的色彩通道做二值化,然后合并(bitwise_and),再简单的处理一下得到网格的 mask,然后用原图减去网格得到很多小方块。再寻找轮廓就能够得到全部小方块的轮廓,获取每个小方块的中心坐标,再拿着全部坐标到原图(转成 HSV)去取值,最后根据 的方法来判断颜色就好了

谢谢你的回复,我去试试。另外一个不情之请,方便的话能不能提供下部分代码,作为感谢,一本 python 相关的书作为感谢~京东上可以自选,报酬很低,见谅~

可以的,留个邮箱吧


[email protected]
谢谢,顺便把书的京东链接、收货地址一起发送吧~

已发,请注意查收,有问题直接回邮件就是

我觉得可以写个拼图的工具



学 cv 时候的作业,先用 canny edge detector,再用 hough line detector,然后筛选一下,就可以把每个角点确定下来,然后在每个矩形中间去颜色就行了。不用颜色直方图的原因是你收拍的照片是有 projective distortion 的。

hsv 了解一下

直接把所有的像素转换成 rgb 编码,然后 计算所有不同 RGB 编码的数量,就是像素量,然后除以每个色块的像素量就是色块数。

条条大路通罗马,等我实现了把代码贴一下

有的方块可能是半块(不满一块按一块算)所以您的方法可能会有误差

回到顶部