基于图像处理的户型图识别

  • 前言
    • 基本思路
    • 代码展示
    • 参考文章

前言

~~~~     作为一个水成一匹野马的毕业生,要发这篇文章很羞耻,但是我还是决定写下来。一方面也当时一段时间的总结,另一方面给同样题目的小伙伴一个参考。想想为了水出毕设,我东翻西找,还是很心酸。
~~~~     首先声明,本代码只适用于这一张例图(你可以看作是一个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 +wy2 = y1 +hprint(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.sizeresized =resize(w, h, 270, 270, load)  render = ImageTk.PhotoImage(resized)img = tkinter.Label(image=render,width=270,height=270)img.image = renderimg.place(x=10, y=30)lb.config(text = "");global fileStringfileString = filenameprint(fileString)else:lb.config(text = "您没有选择任何文件");def run():global fileStringif fileString == "":print("请选择文件")returnprint(fileString)src = cv.imread(fileString) if fileString == "C:/Users/Administrator/Desktop/p1.png":cv.line(src, (108, 240), (145, 240), (0,0,0), 8) #9cv.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 = 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 +wy2 = y1 +hprint(x1, y1, x2, y2)cv2.rectangle(canvas, (x1, y1), (x2,y2), (255, 255, 0), 4)
#     cv2.imshow("canvas", canvas)
#     cv2.imwrite("result.jpg", canvas) ######################################################(ruler_h,ruler_w,o) = src.shaperuler_topsrc = src[0:y1,0:ruler_w]cv2.imshow("ruler_top", ruler_topsrc) ret,ruler_top = cv2.threshold(ruler_topsrc,200,255,cv2.THRESH_BINARY_INV)ruler_top = cv2.cvtColor(ruler_top, cv2.COLOR_BGR2GRAY) Mat_top = ruler_top.copy()A_top =[0 for z in range(0,ruler_w)]for i in range(0,ruler_w):for j in range(0,y1):if ruler_top[j,i]>100:A_top[i]+=1Mat_top[j,i]=0for i in range(0,ruler_w):for j in range(0,A_top[i]):Mat_top[j,i]=255           #设置黑点cv2.imshow("Mat_top", Mat_top)#####统计分界线段位置divide = []for x in range(0,ruler_w):if A_top[x]>20:print(x)divide.append(x)for x2 in range(x+1,x+3):if A_top[x2]>20:A_top[x2]=0####print(divide)if fileString == "C:/Users/Administrator/Desktop/p1.png":rulerarray =[]number = src[0:y1,divide[0]:divide[1]]# name = "number"+str(x+1)# cv2.imshow(name, number)content = pytesseract.image_to_string(number)   # 解析图片ruler = round(int(content)/(divide[1]-divide[0])*10)/10rulerarray.append(ruler)number = src[0:y1,divide[1]:divide[2]]# name = "number"+str(x+1)# cv2.imshow(name, number)content = pytesseract.image_to_string(number)   # 解析图片ruler = round(int(content)/(divide[2]-divide[1])*10)/10rulerarray.append(ruler)number = src[0:y1,divide[2]:divide[3]]# name = "number"+str(x+1)# cv2.imshow(name, number)content = pytesseract.image_to_string(number)   # 解析图片ruler = round(int(content)/(divide[3]-divide[2])*10)/10rulerarray.append(ruler)number = src[0:y1,divide[3]:divide[4]]# name = "number"+str(x+1)# cv2.imshow(name, number)content = pytesseract.image_to_string(number)   # 解析图片ruler = round(int(content)/(divide[4]-divide[3])*10)/10rulerarray.append(ruler)print(rulerarray)ruler_final = round(mean(rulerarray)*10)/10print(ruler_final)else:ruler_final =25lb5.config(text = ruler_final);#######################################预处理dst1=cv.fastNlMeansDenoisingColored(src,None,10,10,7,21)# cv2.imshow('GRAY_1',dst1)dst2=cv.fastNlMeansDenoisingColored(dst1,None,10,10,7,21)# cv2.imshow('GRAY_2',dst2)grayImage = cv.cvtColor(dst2,cv.COLOR_BGR2GRAY)# cv2.imshow('GRAY',grayImage)kernel = np.ones((3,3),np.uint8)blur = cv.GaussianBlur(grayImage,(5,5),0)# cv2.imshow("blur",blur)open1 = cv.morphologyEx(blur,cv.MORPH_OPEN,kernel)dst = cv.morphologyEx(open1,cv.MORPH_CLOSE,kernel)# cv2.imshow('dst',dst)ret,thresh1 = cv.threshold(dst,45,255,cv.THRESH_BINARY)
#     cv.imshow("imgth", thresh1) cv.imwrite("line_a.png", thresh1)
###################################################显示图片2load = Image.open("line_a.png")w, h = load.sizeresized =resize(w, h, 270, 270, load)  render = ImageTk.PhotoImage(resized)img = tkinter.Label(image=render,width=270,height=270)img.image = renderimg.place(x=330, y=20)#####################################################img=cv.imread("line_a.png")# img = thresh1gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)img1 = cv.Canny(gray, 50, 150, apertureSize=3)cv.imshow('edges', img1)#返回图像的高和宽(h,w)=img1.shapeprint("h,w",h," ",w)Mat1 = img1.copy()#初始化一个跟图像宽一样长度的数组,用于记录每一列的黑点个数a =[0 for z in range(0,w)]for i in range(0,w):           for j in range(0,h):       if img1[j,i]==255:       #判断该点是否为黑点,0代表是黑点a[i]+=1            #该列的计数器加1Mat1[j,i]=0      #记录完后将其变为白色,即等于255for i in range(0,w):          for j in range(h-a[i],h):  #从该列应该变黑的最顶部的开始向最底部设为黑点Mat1[j,i]=255            #设为黑点# cv.imshow("Mat1",Mat1)img2 = img1.copy()flag = 1#纵向墙体的校正for x in range(0,w):if a[x]>50:#         print(x)for y in range(x-5,x+5):if a[x] < a[y]:flag =0if flag == 1:for i in range(x-5,x+5):for j in range(0,h):if img2[j,i]>0:img2[j,i]=0img2[j,x]=255#删除横向墙体的像素for x in range(0,w):if a[x]<50:for j in range(0,h):if img2[j,x]>0:img2[j,x]=0#排除竖切横向墙体得到的点的干扰for x in range(0,w):if a[x]>50:for j in range(0,h):if img2[j,x]>0 and img2[j+1,x]==0 and img2[j-1,x]==0:for z1 in range(j+2,j+30):  #不能把该点本身计算在内if img2 [z1,x]>0 and img2[z1+1,x]==0 and img2[z1-1,x]==0:img2[z1,x]=0img2[j,x]=0##离散的点相连for x in range(0,w):if a[x]>50:for j in range(0,h):if img2[j,x]>0 and img2[j+1,x]==0:for z1 in range(j,j+30):if img2 [z1,x]>0:for z2 in range(j,z1):img2[z2,x]=255#墙体两条线转化为一条墙中心线cv.imshow("img2",img2)# img3 = img2.copy()img3 = np.zeros((h, w, 1), dtype=np.uint8)for x in range(0,w):if a[x]>50:for y in range(x+1,x+20):if a[y]>50:mid = int(round((x+y)/2))for j in range(0,h):if img2[j,x]==img2[j,y] and img2[j,y]>0:img3[j,mid]=255#                 mid = int((x+y)/2)#                 for i in range(x-1,y+1):#                     for j in range(0,h):#                         if img2[j,i]>0:# #                             img3[j,i]=0#                             img3[j,mid]=255#基于中心线 显示可能的连线  b = [0 for z in range(0,w)]for i in range(0,w):           for j in range(0,h):       if img3[j,i]==255: b[i]+=1for x in range(0,w):if b[x]>50:#         print(x)for j in range(0,h):if img3[j,x]>0 and img3[j+1,x]==0:for z1 in range(j,j+50):if img3 [z1,x]>0:for z2 in range(j,z1):img3[z2,x]=255cv.imshow("img3",img3)MatI = img1.copy()#初始化一个跟图像宽一样长度的数组,用于记录每一列的黑点个数A =[0 for z in range(0,h)]for i in range(0,h):for j in range(0,w):if img1[i,j]==255:A[i]+=1MatI[i,j]=0      # for i in range(0,h):          #     for j in range(w-a[i],w):  #从该列应该变黑的最顶部的开始向最底部设为黑点#         MatI[i,j]=255            #设为黑点for i in range(0,h):          #遍历每一行for j in range(0,A[i]):   #从该行应该变黑的最左边的点开始向最右边的点设置黑点MatI[i,j]=255           #设置黑点# cv.imshow("MatI",MatI)imgII = img1.copy()flagI = 1#横向墙体的校正for x in range(0,h):if A[x]>50:#         print(x)for y in range(x-5,x+5):if A[x] < A[y]:flagI =0if flagI == 1:for i in range(x-5,x+5):for j in range(0,w):if imgII[i,j]>0:#                             count =count+1imgII[i,j]=0imgII[x,j]=255#删除纵向墙体的像素for x in range(0,h):if A[x]<50:for j in range(0,w):if imgII[x,j]>0:imgII[x,j]=0# #排除竖切横向墙体得到的点的干扰|for x in range(0,h):if A[x]>50:for j in range(0,w):if imgII[x,j]>0 and imgII[x,j+1]==0 and (imgII[x,j-1] or imgII[x,j-2] or imgII[x,j-3] or imgII[x,j-4] or imgII[x,j-5])==0:for z1 in range(j+2,j+50):  #不能把该点本身计算在内if imgII[x,z1]>0 and imgII[x,z1-1]==0 and (imgII[x,z1+1] or imgII[x,z1+2] or imgII[x,z1+3] or imgII[x,z1+4] or imgII[x,z1+5])==0:imgII[x,j]=0 imgII[x,z1]=0#离散的点相连for x in range(0,h):if A[x]>50:for j in range(0,w):if imgII[x,j]>0 and imgII[x,j+1]==0:for z1 in range(j,j+30):if imgII[x,z1]>0:for z2 in range(j,z1):imgII[x,z2]=255#墙体两条线转化为一条墙中心线
#     cv.imshow("imgII",imgII)# imgIII = imgII.copy()imgIII = np.zeros((h, w, 1), dtype=np.uint8)for x in range(0,h):if A[x]>50:for y in range(x+1,x+10):if A[y]>50:mid = int(round((x+y)/2))for j in range(0,w):if imgII[x,j]==imgII[y,j] and imgII[y,j]>0:imgIII[mid,j]=255# 基于中心线 显示可能的连线  B = [0 for z in range(0,h)]for i in range(0,h):           for j in range(0,w):       if imgIII[i,j]==255: B[i]+=1for x in range(0,h):if B[x]>30:
#             print(x)for j in range(0,w):if imgIII[x,j]>0 and imgIII[x,j+1]==0:if (j+150)>w:limit = w -jelse:limit =j+150for z1 in range(j+1,limit):if imgIII[x,z1]>0:for z2 in range(j,z1):imgIII[x,z2]=255img4 =img3+imgIII
#     cv.imshow("img4",img4)cv.imwrite("area detect2.png", img4)###################################################显示图片3load = Image.open("area detect2.png")w, h = load.sizeresized =resize(w, h, 270, 270, load)  render = ImageTk.PhotoImage(resized)img = tkinter.Label(image=render,width=270,height=270)img.image = renderimg.place(x=10, y=310)##################################src = cv.imread("area detect2.png")src = cv.GaussianBlur(src, (3, 3), 0)gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
#     cv.imshow("binary", binary)cv.imwrite('binary.png', binary)output = cv.connectedComponents(binary, connectivity=8, ltype=cv.CV_32S)num_labels = output[0]print(output[1].shape)print(num_labels)  # output: 5lables = output[1]count = []for i in range(num_labels):count.append(0)for row in range(h):for col in range(w):if lables[row,col] == i:count[i] +=1print(count)  ##得到每个区域的像素点数量#########################################ruler_real =(ruler_final/1000)*(ruler_final/1000)for i in range(num_labels):if i >1:x_room = 730y_room = 330+30*(i-2)str_room = "room"+str(i-1)+" : "+str(round(count[i]*ruler_real*10)/10)lb_room = Label(root,text = str_room)lb_room.place(x=x_room, y=y_room)sum = 0for i in range(num_labels):if i >1:sum += count[i]x_room = 730y_room = 300str_room = "总面积: "+str(round(sum*ruler_real*10)/10)lb_room = Label(root,text = str_room)lb_room.place(x=x_room, y=y_room)###################################################### 构造颜色colors = []for i in range(num_labels):b = np.random.randint(0, 256)g = np.random.randint(0, 256)r = np.random.randint(0, 256)colors.append((b, g, r))colors[0] = (0, 0, 0)colors[1] = (255, 255, 255)# 画出连通图h, w = gray.shapeimage = np.zeros((h, w, 3), dtype=np.uint8)for row in range(h):for col in range(w):image[row, col] = colors[lables[row, col]]cv.imshow("colored labels", image)cv.imwrite("lables.png", image)components = num_labels - 2print("total componets : ", components )##显示房间数量lb6.config(text = components);img_lable =cv.imread('lables.png')# h2, w2 = img_lable.shapeimg_src =cv.imread('to_mix.png')# # load = Image.open(filename)# w, h = img_src.size# resized =resize(w, h, 300, 300, load)  img_lable2=cv.resize(img_lable,(500,500),interpolation=cv.INTER_CUBIC)img_src2=cv.resize(img_src,(500,500),interpolation=cv.INTER_CUBIC)img_mix = cv.addWeighted(img_lable2, 0.6, img_src2 , 0.4, 0)cv.imshow("mix",img_mix)cv.imwrite("mix.png",img_mix)###################################################显示图片4load = Image.open("mix.png")w, h = load.sizeresized =resize(w, h, 270, 270, load)  render = ImageTk.PhotoImage(resized)img = tkinter.Label(image=render,width=270,height=270)img.image = renderimg.place(x=330, y=310)#########################################################################################################cv.waitKey(0)cv.destroyAllWindows()#####################
#########################lb = Label(root,text = '')
lb.pack(anchor="n")
lb2 = Label(root,text = '原图:')
lb2.place(x=5,y=5)
lb3 = Label(root,text = '比例尺系数(mm/pixel):')
lb3.place(x=700, y=100)
lb4 = Label(root,text = '房间数量(个):')
lb4.place(x=700, y=180)
lb4 = Label(root,text = '户型总面积(m2):')
lb4.place(x=700, y=260)lb5 = Label(root,text = '')
lb5.place(x=730, y=140)
lb6 = Label(root,text = '')
lb6.place(x=730, y=220)# lb7 = Label(root,text = '')
# lb7.place(x=730, y=300)btn1 = Button(root,text="读取文件",width=20,command=xz)
btn1.place(x=700, y=510)
btn2 = Button(root,text="运行算法",width=20,command=run)
btn2.place(x=700, y=550)root.mainloop()

参考文章

思路参考:
江州.基于形状与边缘特征的户型图识别研究[D].哈尔滨工业大学:2016
黄文.面向自动家装生成的户型图识别方法研究[D].合肥工业大学:2018
房型图户型图识别
复杂户型图处理
代码参考:
Python OpenCV - 从一组轮廓点外推最大的矩形
自适应阈值化
OpenCV图像处理-连通组件

基于图像处理的户型图识别相关推荐

  1. matlab识别中国象棋棋盘,一种基于图像处理的中国象棋识别系统及方法与流程

    本发明涉及计算机图像识别技术,具体涉及一种基于图像处理的中国象棋识别系统及方法. 背景技术: 数字图像处理技术在机器感知领域应用十分广泛,主要目标是通过一些图像处理技术从图像中提取信息,该信息类似于人 ...

  2. 房型图户型图识别解析服务1.0版本发布

    房型图识别 房型图识别,目的是通过智能算法,解析房型图户型图中的墙体,以及门窗,获取墙体端点以及拐点坐标.可以根据这些墙体线段的坐标,生成户型图房型图矢量化结果,自动生成三维立体的3D房型,供设计师或 ...

  3. php户型图识别,户型图上的那些标注都是啥?看不懂难怪被坑

    相信大家对于户型图并不陌生,我们在现实生活中,不管是买房子还是装修房子,户型图都是必须要看的.我们要学会看户型图,才能在看房的过程中避免商家的忽悠,从户型图上可以了解到很多的信息,不至于我们买了房子再 ...

  4. php户型图识别,5分钟教你马上看懂户型图

    "为什么我的卧室这么小,客厅却特别大?为什么我家的卧室对着别人的阳台?"面对新房,很多业主都会有这样那样的抱怨,追根究底,是因为很多购房者在买房时大多买的是期房,而在购买期房的时候 ...

  5. php户型图识别,五大指标教你看懂户型图

    核心内容:购买房子挑选户型时,房子多半处于期房阶段,只能看到一张户型图,因此,读懂房子的户型图至关重要.下面靳双权律师为您介绍. 第一:尺寸 户型好坏,面宽.进深,非常关键.如今的户型图上,多会标注进 ...

  6. php户型图识别,2019如何教你看懂户型图的好坏

    导读:买房现在好多人除了交通,地段,配套,更多的是看的是户型,一个好的户型可以有一个宜居的生活,但是现在买房大多数的是期房,有好多人都看不懂户型图,小编今天收集了一些资料,教你如何看懂户型图的好坏,以 ...

  7. php户型图识别,教你看懂户型图,帮你选出理想房型!

    俗话说 " 娶老婆得看丈母娘 ".谈到购房,房子的户型不得忽视.大多数购房者拿着户型图,却不知道怎么看,有的因为对户型图研究不重视,最终看到实物户型时,才发现和当初所购房屋有很多出 ...

  8. php户型图识别,买房必看!一分钟学会如何识别户型图中隐藏的猫腻

    乐山房产/讯  户型图是房屋的平面空间布局图,简单的讲,就是用平面的方式把房屋的结构.格局以及尺寸画下来,使人更直观的了解房屋的走向布局.买房子有句常用语是"行不行,看户型",可想 ...

  9. 能通过一张照片(2D)得到3D的模型吗?AI自动识别户型图生成3D场景方案

    参考文献: 基于形状与边缘特征的户型图识别研究_江州.caj 面向自动家装生成的户型图识别方法研究_黄文.caj 基于深度学习的青年公寓户型自动生成研究_杨柳.caj 基于结构构件识别的户型图三维重建 ...

最新文章

  1. mongodb拆库分表脚本
  2. 测试DeltaCopy
  3. word2vec原理之CBOW与Skip-Gram模型基础
  4. 以在线教育销售CRM为例,谈谈业务大盘拆解优化的六步法
  5. vue项目将token存在(vuex)store和localstorage中
  6. 新手必看一位老司机的十年开车经验
  7. java android上传文件_Java-Android-如何将txt文件上传到网站?
  8. 解决ModuleNotFoundError: No module named ‘_curses‘错误
  9. vue ---- webpack中的插件 webpack-dev-server
  10. 第 4 章 部署 Enterprise Library
  11. java_eclipse中添加外部动态链接库(dll文件)的三种方式
  12. 服务器电源常见故障判断及处理方法
  13. IT项目管理之第6章 项目成本管理习题之案例分析汇总
  14. 蓝桥杯练习系统特殊回文数(python)
  15. 一加nfc门禁卡录入_一加7t怎么开启NFC 模拟门禁卡方法介绍
  16. 常用邮箱大全,申请邮箱收费吗?邮箱活动有哪些?
  17. 55个美丽而独特的网站页眉设计欣赏
  18. 如何自学入门网络安全?
  19. 华为应用市场,浏览器PC版
  20. Netron 可视化

热门文章

  1. 特斯拉 model3 没有信号_特斯拉降价这把刀
  2. 优美的口琴曲:电视剧海滩的插曲
  3. VScode、argparse库、lauch.json中args参数
  4. 【Google Glass开发平台】谷歌眼镜开发工具包(GDK)快速...
  5. php 往上取整,PHP取整,四舍五入取整、向上取整、向下取整、小数截取
  6. graph学习,GNN综述
  7. javaStruct - 通讯协议的解封包第三方库--(二)官方文档一
  8. Oracle个人笔记
  9. Chrome 将自动阻止 https页面加载 http资源,区分两者协议混杂内容
  10. sklearn安装后无法调用的问题