这两天的上海自贸区临港新片区的发布使临港成为这几天的热点,可以说是让临港由上海死角摇身一变为投资热土,作为一个在临港生活和工作的居民感觉备受鼓舞,有必要写点关于临港的东西,想来想去最后就决定写临港的大地标滴水湖吧。
滴水湖位于临港主城区的核心位置,是一个直径为2.6公里的正圆形湖,而环滴水湖的一/二/三/沪城环路也都为正圆形,有六条大道和一条入海河呈放射状向滴水湖四周发散,路与路之间夹角为标准的45度(作为强迫症患者想不清楚为啥规划图中正北向的浦港大道被弄没了..)。湖边规划非常工整,风景秀丽让人心旷神怡,而我的家就安在滴水湖畔,没事去湖边坐坐也可以度过一段时光,作为一个Runner经常没事绕湖跑个一两圈,对这个湖就分外有感情。
下面说说我今天要做的事情,下图是从地图上截取的滴水湖地图,我们要测算环湖一路内包含的水域面积。
从上图可以看出在滴水湖中央有三个岛,皇冠酒店所在的南岛为不规则形,而娱乐之岛-西岛为一个近似长方形,而荒芜的北岛像是一条尾巴朝向湖中央的丑鱼。
本文中我打算使用最近初学的opencv来测算刨除三个岛以及岸边的不规则绿化带剩下的蓝色水域面积,思路为使用opencv划出湖水的边界,按照颜色做轮廓提取,然后通过像素点比例计算湖水所占面积,解决方案如下图所示。
首先画个圆选定滴水湖我们要检测的部分,这个步骤通过霍夫变换圆检测方法来实现:
import cv2import numpy as np#图片读取img = cv2.imread("dishuilake.png")# 把图片转换为灰度模式gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#霍夫变换圆检测circles= cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 100, param1=150, param2=50, minRadius=5, maxRadius=500)# 输出霍夫变换圆检测的返回值print('霍夫变换圆检测的信息:\n', circles)# 输出检测到圆的个数print('圆的个数为%d个.'%len(circles[0]))x, y, r = 0, 0, 0output = img.copy()#根据检测到圆的信息,画出每一个圆for circle in circles[0]: #坐标行列 x=int(circle[0]) y=int(circle[1]) #半径 r=int(circle[2]) # 圆的基本信息 print('圆的中心为(%s, %s), 半径为%s.' % (x, y, r)) #在原图用指定颜色标记出圆的位置 output = cv2.circle(output, (x,y), r, (0,0,255), 1) breakcircle_pixes = 0# 去除圆以外的部分for a in range(output.shape[0]): for b in range(output.shape[1]): if ((a - x) **2 + (b -y)**2 > r**2): output[a, b] = [255, 255, 255] else: # 统计园内总pix数量 circle_pixes += 1else: print("circle_pixes:%s" % circle_pixes)#显示新图像cv2.imshow("captured", np.hstack([img, output]))# 图像输出cv2.imwrite('captured.png',output)#按任意键退出cv2.waitKey(0)cv2.destroyAllWindows()
我们就得到了这样一张裁剪之后的图,这个图正好是环湖一路以内部分,同时获得圆的中心为(453, 460), 半径为421,像素点个数circle_pixes:556769:
然后取图中湖水的颜色:
import cv2#图片读取,将图像转化为ndarray数组img = cv2.imread("captured.png")print(img.shape)# 输出为(908, 910, 3),目测其中间点位置为湖水,则取(454,455)组的元素print(img[454,455]) # 输出为[254 199 175],其为湖水的颜色
然后按照颜色划出水域部分的轮廓:
import cv2import numpy as np#图片读取img = cv2.imread("captured.png")# 定义颜色边界 (lower[r,g,b], upper[r,g,b])lower, upper = ([254, 199, 175], [254, 199, 175])# 将颜色边界转成numpy数组lower = np.array(lower, dtype='uint8')upper = np.array(upper, dtype='uint8')#在指定边界内查找颜色并应用掩码mask = cv2.inRange(img, lower, upper)# 进行与操作output = cv2.bitwise_and(img, img, mask=mask)# 显示结果cv2.imshow("images", np.hstack([img, output]))cv2.waitKey(0)# 图像输出cv2.imwrite('result.png',output)cv2.destroyAllWindows()
这个变换步骤我们从左图的圆形区域得到了右图的水域部分,水域部分像素点laker_pixes:435600
我们算得了水域部分的像素点数量为 435600个,圆内像素点个数556769个,水域部分占整个圆形内的78.23%,加上我们已得知滴水湖直径为2.6公里,则水域面积应该为3.14*1.3**2*0.7823 = 4.15平方公里,这就是最终我们要的结果。
总结:本文我们通过opencv使用了简单的图形变换的一些功能,最终得到了我们想要的效果,希望滴水湖以后能越来越美,同时欢迎大家多来滴水湖玩。