python - 使用 OpenCV 在平面图中分隔房间

标签 python opencv image-processing

输入平面图图像

上图是我输入的平面图,我需要分别识别每个房间,然后裁剪这些房间。之后,我可以将这些图像用于后续步骤。到目前为止,我能够使用 cv2.connectedComponentsWithStats 从输入平面图中删除小项目。所以我认为这将有助于轻松识别墙壁。之后我的输入图像看起来像这样。

去除小物体后的输出图像

然后我做了 MorphologicalTransform 以从图像中删除文本和其他符号,只留下墙壁。之后我的输入图像看起来像这样。

形态变换之后

所以我能够识别墙壁。然后我如何使用这些墙从原始输入平面图裁剪房间。有人能帮我吗?您可以在此链接中找到我的 python 代码。 Download My Code

#Import packages
import os
import cv2
import numpy as np
import tensorflow as tf
import sys

# This is needed since the notebook is stored in the object_detection folder.
sys.path.append("..")

# Import utilites
from utils import label_map_util
from utils import visualization_utils as vis_util

# Name of the directory containing the object detection module we're using
MODEL_NAME = 'inference_graph'
IMAGE_NAME = 'floorplan2.jpg'
#Remove Small Items
im_gray = cv2.imread(IMAGE_NAME, cv2.IMREAD_GRAYSCALE)
(thresh, im_bw) = cv2.threshold(im_gray, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
thresh = 127
im_bw = cv2.threshold(im_gray, thresh, 255, cv2.THRESH_BINARY)[1]
#find all your connected components 
nb_components, output, stats, centroids = cv2.connectedComponentsWithStats(im_bw, connectivity=8)
#connectedComponentswithStats yields every seperated component with information on each of them, such as size
#the following part is just taking out the background which is also considered a component, but most of the time we don't want that.
sizes = stats[1:, -1]; nb_components = nb_components - 1

# minimum size of particles we want to keep (number of pixels)
#here, it's a fixed value, but you can set it as you want, eg the mean of the sizes or whatever
min_size = 150  

#your answer image
img2 = np.zeros((output.shape))
#for every component in the image, you keep it only if it's above min_size
for i in range(0, nb_components):
    if sizes[i] >= min_size:
        img2[output == i + 1] = 255

cv2.imshow('room detector', img2)
#MorphologicalTransform
kernel = np.ones((5, 5), np.uint8)
dilation = cv2.dilate(img2, kernel)
erosion = cv2.erode(img2, kernel, iterations=6)

#cv2.imshow("img2", img2)
cv2.imshow("Dilation", dilation)
cv2.imwrite("Dilation.jpg", dilation)
#cv2.imshow("Erosion", erosion)


# Press any key to close the image
cv2.waitKey(0)

# Clean up
cv2.destroyAllWindows()

最佳答案

这是我想出的东西。它并不完美(我做了一些评论你可能想尝试),如果你提高输入图像质量会更好。

import cv2
import numpy as np




def find_rooms(img, noise_removal_threshold=25, corners_threshold=0.1,
               room_closing_max_length=100, gap_in_wall_threshold=500):
    """

    :param img: grey scale image of rooms, already eroded and doors removed etc.
    :param noise_removal_threshold: Minimal area of blobs to be kept.
    :param corners_threshold: Threshold to allow corners. Higher removes more of the house.
    :param room_closing_max_length: Maximum line length to add to close off open doors.
    :param gap_in_wall_threshold: Minimum number of pixels to identify component as room instead of hole in the wall.
    :return: rooms: list of numpy arrays containing boolean masks for each detected room
             colored_house: A colored version of the input image, where each room has a random color.
    """
    assert 0 <= corners_threshold <= 1
    # Remove noise left from door removal

    img[img < 128] = 0
    img[img > 128] = 255
    _, contours, _ = cv2.findContours(~img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    mask = np.zeros_like(img)
    for contour in contours:
        area = cv2.contourArea(contour)
        if area > noise_removal_threshold:
            cv2.fillPoly(mask, [contour], 255)

    img = ~mask

    # Detect corners (you can play with the parameters here)
    dst = cv2.cornerHarris(img ,2,3,0.04)
    dst = cv2.dilate(dst,None)
    corners = dst > corners_threshold * dst.max()

    # Draw lines to close the rooms off by adding a line between corners on the same x or y coordinate
    # This gets some false positives.
    # You could try to disallow drawing through other existing lines for example.
    for y,row in enumerate(corners):
        x_same_y = np.argwhere(row)
        for x1, x2 in zip(x_same_y[:-1], x_same_y[1:]):

            if x2[0] - x1[0] < room_closing_max_length:
                color = 0
                cv2.line(img, (x1, y), (x2, y), color, 1)

    for x,col in enumerate(corners.T):
        y_same_x = np.argwhere(col)
        for y1, y2 in zip(y_same_x[:-1], y_same_x[1:]):
            if y2[0] - y1[0] < room_closing_max_length:
                color = 0
                cv2.line(img, (x, y1), (x, y2), color, 1)


    # Mark the outside of the house as black
    _, contours, _ = cv2.findContours(~img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contour_sizes = [(cv2.contourArea(contour), contour) for contour in contours]
    biggest_contour = max(contour_sizes, key=lambda x: x[0])[1]
    mask = np.zeros_like(mask)
    cv2.fillPoly(mask, [biggest_contour], 255)
    img[mask == 0] = 0

    # Find the connected components in the house
    ret, labels = cv2.connectedComponents(img)
    img = cv2.cvtColor(img,cv2.COLOR_GRAY2RGB)
    unique = np.unique(labels)
    rooms = []
    for label in unique:
        component = labels == label
        if img[component].sum() == 0 or np.count_nonzero(component) < gap_in_wall_threshold:
            color = 0
        else:
            rooms.append(component)
            color = np.random.randint(0, 255, size=3)
        img[component] = color

    return rooms, img



#Read gray image
img = cv2.imread("/home/veith/Pictures/room.png", 0)
rooms, colored_house = find_rooms(img.copy())
cv2.imshow('result', colored_house)
cv2.waitKey()
cv2.destroyAllWindows()

这将显示这样的图像,其中每个房间都有随机颜色: 你可以看到它有时会找到一个没有房间的房间,但我认为这对你来说是一个不错的起点。

Found rooms

为此,我在您的问题中使用了图片的屏幕截图。 您可以使用返回的每个房间的蒙版来索引原始图像并裁剪它。 要裁剪,只需使用类似的东西(未经测试,但在大多数情况下应该有效):

for room in rooms:
    crop = np.zeros_like(room).astype(np.uint8)
    crop[room] = original_img[room]  # Get the original image from somewhere
    # if you need to crop the image into smaller parts as big as each room
    r, c = np.nonzero(room)
    min_r, max_r = r.argmin(), r.argmax()
    min_c, max_c = c.argmin(), c.argmax()
    crop = crop[min_r:max_r, min_c:max_c]
    cv2.imshow("cropped room", crop)
    cv2.waitKey()
    cv2.destroyAllWindows()

关于python - 使用 OpenCV 在平面图中分隔房间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54274610/

相关文章:

python - 如何使用 Python delta-rs 从 Azure Blob 存储中读取数据

c++ - 调用静态内联函数

image-processing - 跟踪与人脸检测相关的人体 Blob

python - 如何在数据集的所有行上应用正则表达式?

python - 为什么 datetime 函数会更改我字典中的值

python - pip安装后如何启用mod_wsgi

c++ - Findcontours() 运行时错误

python - 视频捕获窗口未关闭 - OpenCV

iphone - 在 iPhoneSDK 中检测文档的边缘

opencv - 是否可以在OpenCV的特定图像上存储数据?