使用OpenCV布置平面图中的各个房间

时间:2019-01-20 08:05:08

标签: python opencv image-processing

输入平面图

以上图像是我输入的平面图,我需要分别标识每个房间,然后裁剪这些房间。之后,我可以将这些图像用于后续步骤。到目前为止,我可以使用cv2.connectedComponentsWithStats从输入的楼层平面图中删除小物品。因此,我认为这将有助于轻松识别墙壁。之后,我的输入图像如下所示。

移除小物体后输出图像

然后,我进行了MorphologicTransform来从图像中删除文本和其他符号,只留下墙壁。之后,我的输入图像如下所示。

在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()

2 个答案:

答案 0 :(得分:0)

这是我想到的。它不是完美的(我对您可能想尝试的内容发表了一些评论),并且如果您改善输入图像的质量会更好。

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()

答案 1 :(得分:0)

我使用了三个for循环来裁剪每个房间。

height, width = img.shape[:2]
rooms, colored_house = find_rooms(img.copy())
roomId = 0
images = []
for room in rooms:
    x = 0
    image = np.zeros ((height, width, 3), np.uint8)
    image[np.where ((image == [0, 0, 0]).all (axis=2))] = [0, 33, 166]
    roomId = roomId + 1
    for raw in room:
        y = 0
        for value in raw:
            if value == True:
                image[x,y] = img[x,y]

            y = y +1
            #print (value)
            #print (img[x,y])
        x = x + 1
    cv2.imwrite ('result' + str(roomId)+ '.jpg', image)