前言
~~~~ 作为一个水成一匹野马的毕业生,要发这篇文章很羞耻,但是我还是决定写下来。一方面也当时一段时间的总结,另一方面给同样题目的小伙伴一个参考。想想为了水出毕设,我东翻西找,还是很心酸。
~~~~ 首先声明,本代码只适用于这一张例图(你可以看作是一个PPT程序),如果想做出商业级应用的同学,可以参考专业论文,我只是给一个保底方案,互励共勉。大部分参考都列在文章末尾,侵删致歉。
基本思路
~~~~ 首先是比例尺识别,本文使用findcontours获得图像轮廓集合,以轮廓的尺寸大小为筛选条件,遍历墙体集合,得到户型图中墙体最大外轮廓。
contours, hierarchy = cv2.findContours(img_th, cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
max_brightness = 0
canvas = src.copy()
for cnt in contours:
rect = cv2.boundingRect(cnt)
x, y, w, h = rect
if w*h > 40000 and x>0:
mask = np.zeros(src.shape, np.uint8)
mask[y:y+h, x:x+w] = src[y:y+h, x:x+w]
brightness = np.sum(mask)
if brightness > max_brightness:
brightest_rectangle = rect
max_brightness = brightness
cv2.imshow("mask", mask)
x1, y1, w, h = brightest_rectangle
x2 = x1 +w
y2 = y1 +h
print(x1, y1, x2, y2)
cv2.rectangle(canvas, (x1, y1), (x2,y2), (255, 255, 0), 4) `
~~~~ 其次,通过轮廓信息的得到比例尺位置,做图像的直方图投影,以像素点数量为筛选条件得到间隔数量。通过Tesseract开源识别库识别简单的数字,并将数字(单位mm)除以像素点的间隔,得到户型图的比例系数。
~~~~ 然后识别墙体,先通过简单的自适应阈值化操作,得到二值化图像。通过直方图投影的归一化操作,分别得到y轴和x轴方向的边缘线段。再对图像进行提取中心线操作,最后将同一坐标轴的断开线段相连,形成闭合空间。使用封闭空间检测算法,找到封闭空间,得到各个房间的面积大小。
代码展示
开发环境:Anaconda+jupyter
from tkinter import *
import tkinter.filedialog
from PIL import Image, ImageTk
import tkinter
import numpy as np
import cv2 as cv
import cv2
import pytesseract
from numpy import *
def resize(w, h, w_box, h_box, pil_image):
f1 = 1.0*w_box/w # 1.0 forces float division in Python2
f2 = 1.0*h_box/h
factor = min([f1, f2])
width = int(w*factor)
height = int(h*factor)
return pil_image.resize((width, height), Image.ANTIALIAS)
def clahe(img, clip_limit=2.0, grid_size=(8,8)):
clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=grid_size)
return clahe.apply(img)
root = Tk()
root.geometry('900x600')
root.title("户型识别")
fileString = ""
def xz():
filename = tkinter.filedialog.askopenfilename()
if filename != '':
load = Image.open(filename)
w, h = load.size
resized =resize(w, h, 270, 270, load)
render = ImageTk.PhotoImage(resized)
img = tkinter.Label(image=render,width=270,height=270)
img.image = render
img.place(x=10, y=30)
lb.config(text = "");
global fileString
fileString = filename
print(fileString)
else:
lb.config(text = "您没有选择任何文件");
def run():
global fileString
if fileString == "":
print("请选择文件")
return
print(fileString)
src = cv.imread(fileString)
if fileString == "C:/Users/Administrator/Desktop/p1.png":
cv.line(src, (108, 240), (145, 240), (0,0,0), 8) #9
cv.imwrite("to_mix.png",src.copy())
# cv.imshow("src",src)
###################比例尺
# src_2 = src.copy()
# HSV thresholding to get rid of as much background as possible
hsv = cv2.cvtColor(src.copy(), cv2.COLOR_BGR2HSV)
lower_blue = np.array([0, 0, 120])
upper_blue = np.array([180, 38, 255])
mask = cv2.inRange(hsv, lower_blue, upper_blue)
result = cv2.bitwise_and(src, src, mask=mask)
b, g, r = cv2.split(result)
g = clahe(g, 5, (3, 3))
# Adaptive Thresholding to isolate the bed
img_blur = cv2.blur(g, (9, 9))
img_th = cv2.adaptiveThreshold(img_blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 51, 2)
contours, hierarchy = cv2.findContours(img_th, cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
# Filter the rectangle by choosing only the big ones
# and choose the brightest rectangle as the bed
max_brightness = 0
canvas = src.copy()
for cnt in contours:
rect = cv2.boundingRect(cnt)
x, y, w, h = rect
if w*h > 40000 and x>0:
mask = np.zeros(src.shape, np.uint8)
mask[y:y+h, x:x+w] = src[y:y+h, x:x+w]
brightness