由于很多户型图并没有标注各个房间或者走廊的面积,亦或比较模糊,且很多人并不具备迅速口算多个小数相加再做除法的能力,本帖通过程序粗略计算公摊比例。由于非专业人士,公摊面积涉及到很多建筑学的专业公式,因此本帖只能算作图像学的角度上的估算,结果仅供参考~
目录
准备工作
一.图像读取
二.边缘检测
三.梯度操作
四.轮廓检测
五.寻找户型轮廓
六.外接矩形
七.计算公摊
准备工作
本帖全部的操作都在之前的博客中总结,读过则本帖易如反掌:
OpenCV基础——轮廓检测、模板匹配、图像均衡化-CSDN博客文章浏览阅读537次,点赞10次,收藏10次。从原理上来说很简单,就是在原始图里面,从左到右,从上到下依次遍历每个面积和子图大小一样的子元素,分别计算子图与每个子元素的差别程度,然后将这些差别程度一次性返回。原理是,设置一个阈值,如果曲线上离近似直线的距离小于该阈值,则可以直接近似;不难发现上面的直方图整体来看还是比较不均匀的,当我们将直方图处理得更加均衡一些之后,整体的对比度和亮度都会有所提升。现有一个子图,将原始图分为好几个小的部分,需要从这些小部分中找出与子图最相近的部分。轮廓是可以计算面积的,但必须单个计算,也即轮询。https://blog.csdn.net/jsl123x/article/details/146720244?spm=1001.2014.3001.5501
然后是本帖用到的户型图——代码中的House.jpg:
接着是导包和自制的绘图函数:
import cv2
import numpy as np
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
一.图像读取
读取两个同样的上述图片,draw作为一个副本用做后面的轮廓展示。这里统一先读取为彩色图,然后将img转为灰度图以便后续使用:
img=cv2.imread('D:\\House.jpg')
draw=cv2.imread('D:\\House.jpg')
img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
二.边缘检测
如果直接进行轮廓检测,效果不是很好,因为户型图本身线条粗细不一致,这里先行检测出轮廓,提高计算结果的真实性:
sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx=cv2.convertScaleAbs(sobelx)
sobely=cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely=cv2.convertScaleAbs(sobely)
sobel=cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
tog=np.hstack((img,sobel))
cv_show(tog,'tog')
这里使用sobel算子,检测结果如下:
三.梯度操作
如上右图所示,玄关等处的线条过于狭窄,在轮廓检测的时候有概率会忽略,因此我们要进行一个梯度操作——即将边缘进一步加粗。在那之前,先将图片进行阈值操作直接变成黑白图:
ret, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
cv_show(thresh, 'thresh')
kernel = np.ones((5,5),np.uint8)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_GRADIENT, kernel)
cv_show(thresh, 'thresh')
轮廓得以加粗!
四.轮廓检测
经过上面的处理已经可以进行轮廓检测了:
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
其中contours即为所有轮廓的合集~
五.寻找户型轮廓
但显然我们不需要所有的轮廓,而是真正的户型轮廓。因此我们可以将所有轮廓的面积存入一个列表,然后将面积最大的找出来,即为户型的轮廓!当然也可以考虑试试周长?
list=[]
for item in contours:
list.append(cv2.contourArea(item))
max_index = np.argmax(list)
print(max_index)
print(list[max_index])
同时别忘了把下标返回来,用这个下标去contours里面找轮廓:这里是13号轮廓
用蓝色线条将户型轮廓画出!
res=cv2.drawContours(draw,contours,max_index,(255,0,0),2)
cv_show(res,'res')
六.外接矩形
实际上,户型中如果有有飘窗、弧形阳台、斜墙等非矩形结构,面积计算会按国家标准折算,而非简单长×宽,但是这里主要说明思想,就默认为长乘宽:即真实的面积可以近似为外接矩形。这里获取轮廓的上下左右极点坐标,用红线绘制外接矩形:
x,y,w,h=cv2.boundingRect(contours[max_index])
img1=cv2.rectangle(draw,(x,y),(x+w,y+h),(0,0,255),2)
cv_show(img1,'img1')
七.计算公摊
count=list[max_index]/(w*h)
print(f"公摊面积的比例是:{(1-count)*100}"+"%" )
如上即为本案例的全部流程。再次声明这并非正确规范的公摊计算方法,只是一个思想的展示~
此外户型图有多种,有些过于浅显的线条类型可能不适用本程序,各位可以挑战一下自行修改——难点仅检测轮廓户型唯一。本程序的另一个测试图如下,其实这种类型的户型图效果都还不错——当然这个有点过大,为了显示好看建议resize哦~