如何从颜色推断形状的状态

2024-04-01

  • 我已经形成了乐高立方体4x4形状,我试图推断图像内区域的状态:

空/满以及颜色是黄色还是蓝色。

  • 为了简化我的工作,我添加了红色标记定义border由于相机有时会晃动,因此形状会受到影响。
  • 这是我试图检测的形状的清晰图像,由手机摄像头拍摄

( 编辑:请注意,该图像不是我的输入图像,它只是用来清楚地演示所需的形状 ).

  • 我应该使用的侧面摄像头的形状如下所示:

(编辑:现在这是我的输入图像)

  • 为了将我的工作集中在工作区域,我创建了一个遮罩:
  • 到目前为止我尝试过的是按颜色定位红色标记(没有 HSV 颜色空间的简单阈值),如下所示:
import numpy as np
import matplotlib.pyplot as plt
import cv2

img = cv2.imread('sample.png')
RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

mask = cv2.imread('mask.png')
masked = np.minimum(RGB, mask)

masked[masked[...,1]>25] = 0
masked[masked[...,2]>25] = 0
masked = masked[..., 0]

masked = cv2.medianBlur(masked,5)

plt.imshow(masked, cmap='gray')
plt.show()

到目前为止我已经发现了标记:

但我还是很困惑:

如何精确检测所需区域的外部边界以及红色标记内的内部边界(每个乐高立方体(黄-蓝-绿)边界)?.

预先感谢您的好意建议。


我使用您的未失真图像测试了这种方法。假设您有经过校正的相机图像,因此您可以通过“鸟瞰”视角看到乐高积木。现在,我们的想法是使用红色标记来估计中心矩形并裁剪图像的该部分。然后,当您知道每块砖的尺寸(并且它们是恒定的)时,您可以追踪grid并提取每个cell网格,你可以计算一些HSV-based蒙版来估计每个网格上的主色,这样您就知道该空间是否被黄色或蓝色砖块占据,或者它是空的。

这些是步骤:

  1. Get an HSV的面具红色标记
  2. 使用每个标记估计中心矩形通过每个标记的坐标
  3. Crop中心矩形
  4. Divide将矩形变为cells- 这是grid
  5. 运行一系列HSV-based对每个单元格进行 make 并计算主色
  6. Label每个单元格都有主色

我们看一下代码:

# Importing cv2 and numpy:
import numpy as np
import cv2

# image path
path = "D://opencvImages//"
fileName = "Bg9iB.jpg"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)
# Store a deep copy for results:
inputCopy = inputImage.copy()

# Convert the image to HSV:
hsvImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2HSV)

# The HSV mask values (Red):
lowerValues = np.array([127, 0, 95])
upperValues = np.array([179, 255, 255])

# Create the HSV mask
mask = cv2.inRange(hsvImage, lowerValues, upperValues)

第一部分非常简单。您设置了HSV范围及用途cv2.inRange获得目标颜色的二值掩模。这是结果:

我们可以使用一些进一步改进二进制掩码morphology。让我们应用一个closing有点大structuring element and 10迭代。我们希望这些标记尽可能清晰地定义:

# Set kernel (structuring element) size:
kernelSize = 5
# Set operation iterations:
opIterations = 10
# Get the structuring element:
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
# Perform closing:
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, maxKernel, None, None, opIterations, cv2.BORDER_REFLECT101)

其产量:

很不错。现在,我们来检测一下contours在这个面具上。我们将每个轮廓近似为bounding box并存储其起点和尺寸。这个想法是,虽然我们会检测每个轮廓,但我们不确定它们的顺序。我们可以sort稍后列出此列表,并从左到右、从上到下获取每个边界框,以更好地估计中心矩形。我们来检测一下contours:

# Create a deep copy, convert it to BGR for results:
maskCopy = mask.copy()
maskCopy = cv2.cvtColor(maskCopy, cv2.COLOR_GRAY2BGR)

# Find the big contours/blobs on the filtered image:
contours, hierarchy = cv2.findContours(mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

# Bounding Rects are stored here:
boundRectsList = []

# Process each contour 1-1:
for i, c in enumerate(contours):

    # Approximate the contour to a polygon:
    contoursPoly = cv2.approxPolyDP(c, 3, True)

    # Convert the polygon to a bounding rectangle:
    boundRect = cv2.boundingRect(contoursPoly)

    # Get the bounding rect's data:
    rectX = boundRect[0]
    rectY = boundRect[1]
    rectWidth = boundRect[2]
    rectHeight = boundRect[3]

    # Estimate the bounding rect area:
    rectArea = rectWidth * rectHeight

    # Set a min area threshold
    minArea = 100

    # Filter blobs by area:
    if rectArea > minArea:
        #Store the rect:
        boundRectsList.append(boundRect)

我还创建了一个deep copy的掩模图像以供进一步使用。主要是创建这个图像,这是轮廓检测和边界框近似的结果:

请注意,我已经包含了最小面积条件。我想忽略低于某个阈值的噪声minArea。好的,现在我们有了边界框boundRectsList多变的。让我们使用以下命令对这些框进行排序Y协调:

# Sort the list based on ascending y values:
boundRectsSorted = sorted(boundRectsList, key=lambda x: x[1])

现在列表已排序,我们可以从左到右、从上到下枚举框。像这样:First "row" -> 0, 1, Second "Row" -> 2, 3。现在,我们可以使用此信息定义大的中心矩形。我称这些为“内在点”。请注意,矩形被定义为所有边界框的函数。例如,它的左上角起点定义为bounding box 0的右下端点(x 和 y)。它的宽度定义为bounding box 1的左下 x 坐标,高度定义为bounding box 2的最右边的 y 坐标。我将循环遍历每个边界框并提取它们的相关尺寸以按以下方式构造中心矩形:(top left x, top left y, width, height)。实现这一目标的方法不止一种。我更喜欢使用dictionary来获取相关数据。让我们来看看:

# Rectangle dictionary:
# Each entry is an index of the currentRect list
# 0 - X, 1 - Y, 2 - Width, 3 - Height
# Additionally: -1 is 0 (no dimension):
pointsDictionary = {0: (2, 3),
                    1: (-1, 3),
                    2: (2, -1),
                    3: (-1, -1)}

# Store center rectangle coordinates here:
centerRectangle = [None]*4

# Process the sorted rects:
rectCounter = 0

for i in range(len(boundRectsSorted)):

    # Get sorted rect:
    currentRect = boundRectsSorted[i]

    # Get the bounding rect's data:
    rectX = currentRect[0]
    rectY = currentRect[1]
    rectWidth = currentRect[2]
    rectHeight = currentRect[3]

    # Draw sorted rect:
    cv2.rectangle(maskCopy, (int(rectX), int(rectY)), (int(rectX + rectWidth),
                             int(rectY + rectHeight)), (0, 255, 0), 5)

    # Get the inner points:
    currentInnerPoint = pointsDictionary[i]
    borderPoint = [None]*2

    # Check coordinates:
    for p in range(2):
        # Check for '0' index:
        idx = currentInnerPoint[p]
        if idx == -1:
            borderPoint[p] = 0
        else:
            borderPoint[p] = currentRect[idx]

    # Draw the border points:
    color = (0, 0, 255)
    thickness = -1
    centerX = rectX + borderPoint[0]
    centerY = rectY + borderPoint[1]
    radius = 50
    cv2.circle(maskCopy, (centerX, centerY), radius, color, thickness)

    # Mark the circle
    org = (centerX - 20, centerY + 20)
    font = cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(maskCopy, str(rectCounter), org, font,
            2, (0, 0, 0), 5, cv2.LINE_8)

    # Show the circle:
    cv2.imshow("Sorted Rects", maskCopy)
    cv2.waitKey(0)

    # Store the coordinates into list
    if rectCounter == 0:
        centerRectangle[0] = centerX
        centerRectangle[1] = centerY
    else:
        if rectCounter == 1:
            centerRectangle[2] = centerX - centerRectangle[0]
        else:
            if rectCounter == 2:
                centerRectangle[3] = centerY - centerRectangle[1]
    # Increase rectCounter:
    rectCounter += 1

该图像用红色圆圈显示每个内部点。每个圆圈都是从左到右、从上到下依次枚举的。内部点存储在centerRectangle list:

如果你连接每个内部点,你就会得到我们一直在寻找的中心矩形:

# Check out the big rectangle at the center:
bigRectX = centerRectangle[0]
bigRectY = centerRectangle[1]
bigRectWidth = centerRectangle[2]
bigRectHeight = centerRectangle[3]
# Draw the big rectangle:
cv2.rectangle(maskCopy, (int(bigRectX), int(bigRectY)), (int(bigRectX + bigRectWidth),
                     int(bigRectY + bigRectHeight)), (0, 0, 255), 5)
cv2.imshow("Big Rectangle", maskCopy)
cv2.waitKey(0)

一探究竟:

现在,只需裁剪原始图像的这一部分:

# Crop the center portion:
centerPortion = inputCopy[bigRectY:bigRectY + bigRectHeight, bigRectX:bigRectX + bigRectWidth]

# Store a deep copy for results:
centerPortionCopy = centerPortion.copy()

这是图像的中心部分:

太酷了,现在让我们创建grid。你知道一定有4每块砖width and 4每块砖height。我们可以使用此信息来划分图像。我将每个子图像或单元格存储在列表中。我还估计每个单元格的中心,以进行额外的处理。这些也存储在列表中。我们来看看流程:

# Dive the image into a grid:
verticalCells = 4
horizontalCells = 4

# Cell dimensions
cellWidth = bigRectWidth / verticalCells
cellHeight = bigRectHeight / horizontalCells

# Store the cells here:
cellList = []

# Store cell centers here:
cellCenters = []

# Loop thru vertical dimension:
for j in range(verticalCells):

    # Cell starting y position:
    yo = j * cellHeight

    # Loop thru horizontal dimension:
    for i in range(horizontalCells):

        # Cell starting x position:
        xo = i * cellWidth

        # Cell Dimensions:
        cX = int(xo)
        cY = int(yo)
        cWidth = int(cellWidth)
        cHeight = int(cellHeight)

        # Crop current cell:
        currentCell = centerPortion[cY:cY + cHeight, cX:cX + cWidth]

        # into the cell list:
        cellList.append(currentCell)

        # Store cell center:
        cellCenters.append((cX + 0.5 * cWidth, cY + 0.5 * cHeight))

        # Draw Cell
        cv2.rectangle(centerPortionCopy, (cX, cY), (cX + cWidth, cY + cHeight), (255, 255, 0), 5)

    cv2.imshow("Grid", centerPortionCopy)
    cv2.waitKey(0)

这是网格:

现在让我们单独处理每个单元格。当然,您可以在最后一个循环中处理每个单元格,但我目前并不寻求优化,清晰度是我的首要任务。我们需要生成一系列HSV具有目标颜色的蒙版:yellow, blue and green(空的)。我更喜欢再次实施dictionary与目标颜色。我将为每种颜色生成一个蒙版,并使用以下方法计算白色像素的数量cv2.countNonZero。我再次设定了最低阈值。这次的10。通过这些信息,我可以确定哪个蒙版生成了最大数量的白色像素,从而为我提供了主色:

# HSV dictionary - color ranges and color name:
colorDictionary = {0: ([93, 64, 21], [121, 255, 255], "blue"),
                   1: ([20, 64, 21], [30, 255, 255], "yellow"),
                   2: ([55, 64, 21], [92, 255, 255], "green")}

# Cell counter:
cellCounter = 0

for c in range(len(cellList)):

    # Get current Cell:
    currentCell = cellList[c]
    # Convert to HSV:
    hsvCell = cv2.cvtColor(currentCell, cv2.COLOR_BGR2HSV)

    # Some additional info:
    (h, w) = currentCell.shape[:2]

    # Process masks:
    maxCount = 10
    cellColor = "None"

    for m in range(len(colorDictionary)):

        # Get current lower and upper range values:
        currentLowRange = np.array(colorDictionary[m][0])
        currentUppRange = np.array(colorDictionary[m][1])

        # Create the HSV mask
        mask = cv2.inRange(hsvCell, currentLowRange, currentUppRange)

        # Get max number of target pixels
        targetPixelCount = cv2.countNonZero(mask)
        if targetPixelCount > maxCount:
            maxCount = targetPixelCount
            # Get color name from dictionary:
            cellColor = colorDictionary[m][2]

    # Get cell center, add an x offset:
    textX = int(cellCenters[cellCounter][0]) - 100
    textY = int(cellCenters[cellCounter][1])

    # Draw text on cell's center:
    font = cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(centerPortion, cellColor, (textX, textY), font,
                    2, (0, 0, 255), 5, cv2.LINE_8)

    # Increase cellCounter:
    cellCounter += 1

    cv2.imshow("centerPortion", centerPortion)
    cv2.waitKey(0)

这是结果:

从这里可以很容易地识别网格上的空白区域。我没有介绍的是扭曲图像的透视校正,但有很多关于如何做到这一点的信息。希望这可以帮助你!

Edit:

如果您想将此方法应用于扭曲的图像,您需要消除鱼眼和透视扭曲。校正后的图像应如下所示:

您可能需要调整一些值,因为即使在校正之后,一些失真仍然存在。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从颜色推断形状的状态 的相关文章

  • 元组有什么用?

    我现在正在学习 Python 课程 我们刚刚介绍了元组作为数据类型之一 我阅读了它的维基百科页面 但是 我无法弄清楚这种数据类型在实践中会有什么用处 我可以提供一些需要一组不可变数字的示例吗 也许是在 Python 中 这与列表有何不同 每
  • Python 中的舍入浮点问题

    我遇到了 np round np around 的问题 它没有正确舍入 我无法包含代码 因为当我手动设置值 而不是使用我的数据 时 返回有效 但这是输出 In 177 a Out 177 0 0099999998 In 178 np rou
  • 处理 Python 行为测试框架中的异常

    我一直在考虑从鼻子转向行为测试 摩卡 柴等已经宠坏了我 到目前为止一切都很好 但除了以下之外 我似乎无法找出任何测试异常的方法 then It throws a KeyError exception def step impl contex
  • Python getstatusoutput 替换不返回完整输出

    我发现了这个很棒的替代品getstatusoutput Python 2 中的函数在 Unix 和 Windows 上同样有效 不过我觉得这个方法有问题output被构建 它只返回输出的最后一行 但我不明白为什么 任何帮助都是极好的 def
  • 跟踪 pypi 依赖项 - 谁在使用我的包

    无论如何 是否可以通过 pip 或 PyPi 来识别哪些项目 在 Pypi 上发布 可能正在使用我的包 也在 PyPi 上发布 我想确定每个包的用户群以及可能尝试积极与他们互动 预先感谢您的任何答案 即使我想做的事情是不可能的 这实际上是不
  • Pandas 日期时间格式

    是否可以用零后缀表示 pd to datetime 似乎零被删除了 print pd to datetime 2000 07 26 14 21 00 00000 format Y m d H M S f 结果是 2000 07 26 14
  • Pandas Merge (pd.merge) 如何设置索引和连接

    我有两个 pandas 数据框 dfLeft 和 dfRight 以日期作为索引 dfLeft cusip factorL date 2012 01 03 XXXX 4 5 2012 01 03 YYYY 6 2 2012 01 04 XX
  • 使用 xlrd 打开 BytesIO (xlsx)

    我正在使用 Django 需要读取上传的 xlsx 文件的工作表和单元格 使用 xlrd 应该可以 但因为文件必须保留在内存中并且可能不会保存到我不知道如何继续的位置 本例中的起点是一个带有上传输入和提交按钮的网页 提交后 文件被捕获req
  • 如何在 Python 中解析和比较 ISO 8601 持续时间? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个 Python v2 库 它允许我解析和比较 ISO 8601 持续时间may处于不同单
  • 为什么 PyYAML 花费这么多时间来解析 YAML 文件?

    我正在解析一个大约 6500 行的 YAML 文件 格式如下 foo1 bar1 blah name john age 123 metadata whatever1 whatever whatever2 whatever stuff thi
  • Python beautifulsoup 仅限 1 级文本

    我看过其他 beautifulsoup 得到相同级别类型的问题 看来我的有点不同 这是网站 我正试图拿到右边那张桌子 请注意表的第一行如何展开为该数据的详细细分 我不想要那个数据 我只想要最顶层的数据 您还可以看到其他行也可以展开 但在本例
  • “隐藏”内置类对象、函数、代码等的名称和性质[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我很好奇模块中存在的类builtins无法直接访问的 例如 type lambda 0 name function of module
  • 如何使用python在一个文件中写入多行

    如果我知道要写多少行 我就知道如何将多行写入一个文件 但是 当我想写多行时 问题就出现了 但是 我不知道它们会是多少 我正在开发一个应用程序 它从网站上抓取并将结果的链接存储在文本文件中 但是 我们不知道它会回复多少行 我的代码现在如下 r
  • pyspark 将 twitter json 流式传输到 DF

    我正在从事集成工作spark streaming with twitter using pythonAPI 我看到的大多数示例或代码片段和博客是他们从Twitter JSON文件进行最终处理 但根据我的用例 我需要所有字段twitter J
  • import matplotlib.pyplot 给出 AttributeError: 'NoneType' 对象没有属性 'is_interactive'

    我尝试在 Pycharm 控制台中导入 matplotlib pyplt import matplotlib pyplot as plt 然后作为回报我得到 Traceback most recent call last File D Pr
  • 如何断言 Unittest 上的可迭代对象不为空?

    向服务提交查询后 我会收到一本字典或一个列表 我想确保它不为空 我使用Python 2 7 我很惊讶没有任何assertEmpty方法为unittest TestCase类实例 现有的替代方案看起来并不正确 self assertTrue
  • Pandas 将多行列数据帧转换为单行多列数据帧

    我的数据框如下 code df Car measurements Before After amb temp 30 268212 26 627491 engine temp 41 812730 39 254255 engine eff 15
  • 如何在 pygtk 中创建新信号

    我创建了一个 python 对象 但我想在它上面发送信号 我让它继承自 gobject GObject 但似乎没有任何方法可以在我的对象上创建新信号 您还可以在类定义中定义信号 class MyGObjectClass gobject GO
  • 如何解决 PDFBox 没有 unicode 映射错误?

    我有一个现有的 PDF 文件 我想使用 python 脚本将其转换为 Excel 文件 目前正在使用PDFBox 但是存在多个类似以下错误 org apache pdfbox pdmodel font PDType0Font toUnico
  • 在 JavaScript 函数的 Django 模板中转义字符串参数

    我有一个 JavaScript 函数 它返回一组对象 return Func id name 例如 我在传递包含引号的字符串时遇到问题 Dr Seuss ABC BOOk 是无效语法 I tried name safe 但无济于事 有什么解

随机推荐