前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于街景图像的武汉城市绿化空间分析

基于街景图像的武汉城市绿化空间分析

作者头像
陈南GISer
发布2024-05-20 14:08:12
1200
发布2024-05-20 14:08:12
举报
文章被收录于专栏:点点GIS点点GIS

作者:郭子豪 中国地质大学(武汉)研究生 HPSCIL Urban Comp 城市之光团队成员

前言

1.1 项目背景

近年来,随着数字技术的飞速发展和城市化进程的加速,街景图像在捕捉城市空间信息方面发挥着越来越重要的作用。大规模的城市更新和建设项目导致城市景观和建筑风貌的日益复杂化,这对于决策者和城市规划者提出了新的挑战。如何准确、高效地捕捉和分析街景图像中的城市地理信息,为城市规划、交通管理和环境监测提供有力的支持,成为了当下的迫切问题。

计算机视觉技术作为一种强大的图像处理工具,已经逐渐成为解决这些问题的关键。通过对街景图像进行深度学习和分析,计算机视觉能够自动识别和标注图像中的各种物体、建筑和地理特征,提供更为精细和全面的城市空间描述。这些信息可以帮助我们更好地理解城市的空间结构、交通流量和环境变化,为城市规划、交通优化和环境保护提供科学的依据。

随着互联网和大数据技术的不断发展,越来越多的街景图像和相关数据被集中在数字平台上。这些数据不仅丰富了我们对城市空间的理解,还为计算机视觉算法的训练和优化提供了宝贵的资源。例如,通过分析街景图像中的交通流量、人流分布和建筑类型,我们可以更准确地预测城市的发展趋势,制定更为合理和可持续的规划策略。

绿视率是衡量城市绿色覆盖面积与总体城市面积之比,是评估城市绿化程度和环境质量的重要指标。已有研究表明,街景图像在计算城市绿视率上具有重要的应用价值。通过分析街景图像中的各像素信息,计算机视觉可以准确计算出绿视率,为城市绿化规划和生态保护提供科学依据。

1.2 任务简介

本期,我们将基于和鲸 ModelWhale 平台,手把手教大家动手学习如何利用接近图像进行城市绿化率分析,在这里我们将向大家演示街景数据的爬取、读取、处理、以及可视化分析等一套完整的基本流程。我们将学习内容分为了三个 notebook:

  1. 基于百度地图 API 接口,爬取百度地图武汉市街景图像数据。
  2. 基于 Python 对爬取得到的街景图像进行语义分割。
  3. 根据街景图像的经纬度信息生成 POI 点,并在武汉市的矢量图上进行可视化。

核心挑战包括:

  1. 如何通过百度 API 从网站上爬取街景图像?
  2. 如何读取、处理街景图像?
  3. 如何对处理得到的数据,在武汉市矢量图上进行可视化?

街景爬虫和实现

本节使用镜像为 Python 3.7 ,使用的计算资源是 2 核 8G CPU 资源,Kernel 类型为 Python3。使用的镜像很基础,爬取街景数据不涉及 GPU 的使用,只使用 CPU 资源就可以了。

街景图像是通过特定的图像采集设备(如摄像头)捕捉的城市街道、建筑和周围环境的全景图像。这些图像提供了对城市空间的直观和全面的视角,捕捉到的是日常生活中的实际场景和人们的活动。街景图像为城市规划师提供了宝贵的信息,帮助他们了解现有的城市结构和环境条件。通过分析图像,可以更好地评估现有的交通流量、建筑布局和绿化情况,从而制定更为合理和可持续的城市规划策略。

传统方式获取街景图像通常需要复杂的设备和大量的人力资源,这包括专业的摄像车辆、高精度的摄像头以及多次实地采集。这种方法不仅耗时耗力,还可能受到天气、交通和其他不可控因素的影响,限制了数据的获取和更新速度。

相比之下,使用网络爬虫获取街景图像提供了一种更为简单和便捷的解决方案。网络爬虫可以自动化地浏览和下载在线地图服务(如 Google 地图、百度地图等)上的街景图像,无需人工干预和实地采集。这种方法不仅能够大大提高数据获取的效率,还可以在短时间内获取大量的图像数据,满足各种分析和应用的需求。

本节利用百度地图 API ,实现批量抓取武汉市街景数据。那如何简单地爬取街景数据呢?

2.1 任务准备

个人 AK 码:可以理解为秘钥,需要进行百度地图开放平台开发者认证即可获得。

进入 平台官网,注册登录百度账号后完成开发者认证,创建应用---->填写应用名称---->应用类型选择浏览器端---->refer 白名单输入“*”---->创建应用成功---->得到密匙(AK)

代码语言:javascript
复制
【平台官网】https://lbs.baidu.com/

在应用类型处选择浏览器端,在白名单处填写“*”,表示允许所有网站调用。

创建完成后,点击提交,获取得到自己的 ak。

2.2 街景爬取

2.2.1 确定爬取 url

在进行爬取之前,我们需要做好准备工作,先导入需要的库,其中,最重要的库就是 requests 库。我们提供的数据为高德坐标系,需要转换成wgs84坐标系,这里并不需要掌握坐标转换代码。

requests 是一个 Python 第三方库,专为简化 HTTP 请求而设计。该库提供了一套直观的 API,使得与 HTTP 服务进行交互变得高效而简便。其设计遵循了 HTTP 协议的标准,支持多种常见 HTTP 方法,包括 GET、POST、PUT 和 DELETE 等。

代码语言:javascript
复制
import re, os
import json
import requests
import time, glob
import csv
import traceback
import math

# 定义常量
x_pi = math.pi * 3000.0 / 180.0
pi = math.pi
a = 6378245.0  # 长半轴
ee = 0.00669342162296594323  # 扁率

# 定义将GCJ02坐标转换为WGS84坐标的函数
def gcj02_to_wgs84(lng, lat):
    # 计算偏移量
    dlat = _transformlat(lng - 105.0, lat - 35.0)
    dlng = _transformlng(lng - 105.0, lat - 35.0)
    # 将偏移量转换为经纬度的增量
    radlat = lat / 180.0 * math.pi
    magic = math.sin(radlat)
    magic = 1 - ee * magic * magic
    sqrtmagic = math.sqrt(magic)
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * math.pi)
    dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * math.pi)
    # 计算WGS84坐标
    mglat = lat + dlat
    mglng = lng + dlng
    return [lng * 2 - mglng, lat * 2 - mglat]

# 定义经纬度转换函数中的子函数
def _transformlat(lng, lat):
    ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + \
        0.1 * lng * lat + 0.2 * math.sqrt(math.fabs(lng))
    ret += (20.0 * math.sin(6.0 * lng * math.pi) + 20.0 * math.sin(2.0 * lng * math.pi)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lat * math.pi) + 40.0 * math.sin(lat / 3.0 * math.pi)) * 2.0 / 3.0
    ret += (160.0 * math.sin(lat / 12.0 * math.pi) + 320 * math.sin(lat * math.pi / 30.0)) * 2.0 / 3.0
    return ret

# 定义经纬度转换函数中的子函数
def _transformlng(lng, lat):
    ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + \
        0.1 * lng * lat + 0.1 * math.sqrt(math.fabs(lng))
    ret += (20.0 * math.sin(6.0 * lng * math.pi) + 20.0 * math.sin(2.0 * lng * math.pi)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lng * math.pi) + 40.0 * math.sin(lng / 3.0 * math.pi)) * 2.0 / 3.0
    ret += (150.0 * math.sin(lng / 12.0 * math.pi) + 300.0 * math.sin(lng / 30.0 * math.pi)) * 2.0 / 3.0
    return ret

# 定义函数,将指定CSV文件中的GCJ02坐标转换为WGS84坐标,并保存到另一个CSV文件中
def convert_coord(input_file, output_file):
    # 打开输入CSV文件
    with open(input_file, 'r', newline='') as csvfile:
        reader = csv.reader(csvfile)
        data = list(reader)  # 读取行数据
        data = data[1:]  # 跳过标题行

        # 替换原有列的经纬度数据
        for row in data:
            if len(row) < 2:
                continue
            lng, lat = float(row[0]), float(row[1])  # 提取经纬度数据
            result = gcj02_to_wgs84(lng, lat)  # 将GCJ02坐标转换为WGS84坐标
            row[0], row[1] = result  # 替换原有的经纬度数据列

    # 检查并创建输出目录
    output_dir = os.path.dirname(output_file)
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # 打开输出CSV文件
    with open(output_file, 'w', newline='') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(["x", "y"])  # 写入标题行
        writer.writerows(data)  # 写入转换后的数据

# 调用函数进行坐标转换
input_file = r'/home/mw/input/wuhan_point5106/wuhan_point_sample2_gcj02.csv'  # 输入CSV文件名
output_file = r'/home/mw/project/output_folder/wuhan_point_wgs_84.csv'  # 输出CSV文件名
convert_coord(input_file, output_file)
print(f"转换完成,结果已保存到 {output_file}")  # 打印转换完成的消息
2.2.2 反爬虫机制破解

在进行爬虫教学之前,我们强烈倡导遵循爬虫的专业道德准则和相关法律法规。虽然爬虫技术为我们提供了便捷地获取互联网信息的手段,但违反网站协议进行爬取可能会触犯法律,侵犯他人的合法权益,甚至面临法律追责。

本教学仅供学术和研究目的使用,我们坚决反对将所学知识用于任何商业行为或违法活动。我们鼓励学习者在使用爬虫技术时始终遵循法律规定和专业道德准则,确保行为的合法性和正当性。

网站反爬机制是为了防止自动化程序如爬虫恶意访问和抓取网站数据而采取的措施。常见的反爬手段包括 User-Agent 检测、请求频率限制等。

而 grab_img_baidu 函数与 openUrl 函数通过设置特定的 User-Agent、请求头信息以及检查响应类型来模拟浏览器行为,以规避网站的反爬机制。使我们的请求看起来更像是由真实用户发出的,从而减少被识别和阻止的风险,从而成功获取目标图片数据。

代码语言:javascript
复制
def grab_img_baidu(_url, _headers=None):
    """
    从百度地图获取图片数据的函数。

    参数:
    _url (str): 要获取的图片的 URL。
    _headers (dict, 可选): HTTP 请求的自定义头。默认为 None。

    返回:
    bytes: 如果成功,返回图片内容;否则返回 None。
    """

    # 检查是否提供了自定义头,如果没有,则使用默认头
    if _headers == None:
        # 设置默认请求头
        headers = {
            "sec-ch-ua": '" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"',  # 用户代理
            "Referer": "https://map.baidu.com/",  # 请求来源页
            "sec-ch-ua-mobile": "?0",  # 移动设备的用户代理信息
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"  # 用户代理
        }
    else:
        headers = _headers

    # 使用指定的头发送 GET 请求
    response = requests.get(_url, headers=headers)

    # 检查响应是否成功,并且内容类型是否为 'image/jpeg'
    if response.status_code == 200 and response.headers.get('Content-Type') == 'image/jpeg':
        return response.content
    else:
        return None


def openUrl(_url):
    """
    发送 HTTP GET 请求并返回响应内容。

    参数:
    _url (str): 要请求的 URL。

    返回:
    bytes: 如果成功,返回响应内容;否则返回 None。
    """
    # 设置默认请求头
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"  # 用户代理
    }

    # 发送 GET 请求
    response = requests.get(_url, headers=headers)

    # 如果状态码为 200,服务器已成功处理了请求,则继续处理数据
    if response.status_code == 200:
        return response
        .content
    else:
        return None

2.2.3 确定 url 解析地址

点击百度地图官网,切换为全景模式,并随机获取某位置街景图。点击 f12 打开开发者模式,在清空所有响应后,点击向前,可以看到一次完整的图片请求 url。

点击负载,查看加载字符串的具体含义。

分析该 URL 请求,并结合 API 服务网站 ,可以总结出如下初步结论

代码语言:javascript
复制
https://lbsyun.baidu.com/faq/api?title=viewstatic-base

请求影像切片所需的几个关键参数分别为: "panoid"是用于标识全景图像的标识符,"headings" 代表的是图像的方向或角度,范围从 0 到 360 度。 所以我们的街景爬取 url 设置如下,只需要得到 panoid 即可,headings 则设置为 ['0', '90', '180', '270'],可获得街景四个角度的图像。

代码语言:javascript
复制
url = 'https://mapsv0.bdimg.com/?qt=pr3d&fovy=90&quality=100&panoid=%7B%7D&heading=%7B%7D&pitch=0&width=480&height=320%27.format(panoid, headings[h])

再返回图片请求处,我们通过观察,可以发现,sid 与 panoid 相同。

而通过搜索各请求的响应,我们可以找到 sid 的请求 url。

通过多次实验,我们可以发现,该请求是通过经纬度坐标定位得到 sid 的。

至此,我们得到获取街景 panoid 的 url 以及获取街景图像的 url。

代码语言:javascript
复制
def getPanoId(_lng, _lat):
    """
    获取百度街景中的 panovid。

    参数:
    _lng (str): 街景点的经度。
    _lat (str): 街景点的纬度。

    返回:
    str: 成功返回 panovid,失败返回 None。
    """
    # 构造请求 URL,包含经度、纬度、层级和其他查询参数
    url = "https://mapsv0.bdimg.com/udt=20200825?&qt=qsdata&x=%s&y=%s&l=14&action=0&mode=day" % (
        str(_lng), str(_lat))

    # 发起 HTTP 请求并获取响应内容,使用 UTF-8 解码
    response = openUrl(url).decode("utf8")

    # 检查响应是否为空,如果为空则返回 None
    if (response == None):
        return None

    # 定义正则表达式,用于从响应中提取 panovid
    reg = r'"id":"(.+?)",'
    pat = re.compile(reg)

    try:
        # 使用正则表达式从响应中提取 panovid
        svid = re.findall(pat, response)[0]
        # 返回提取到的 panovid
        return svid
    except:
        # 如果提取失败,返回 None
        return None

2.2.4 坐标转换 API 调用

尽管我们已经解析获取了 url 地址,但百度街景获取时采用的是经过二次加密的百度墨卡托投影 bd09mc 坐标系,我们下载得到的路网数据一般为 wgs 坐标系。这时,就需要进行坐标转换,将我们的 wgs 坐标系转换为 bd09mc 坐标系,从而获取 panoid 地址来获取街景图像。

此处的坐标系转换,需要用到申请得到的 ak 码,通过调用百度 api 接口。

此处的接口百度开发文档中有详细的介绍

代码语言:javascript
复制
def wgs2bd09mc(wgs_x, wgs_y):
    """
    将 WGS84 坐标转换为百度墨卡托坐标。

    参数:
    wgs_x (str): WGS84 坐标的经度。
    wgs_y (str): WGS84 坐标的纬度。

    返回:
    tuple: 包含转换后的百度墨卡托坐标的元组 (bd09mc_x, bd09mc_y)。
    """
    # 构造请求 URL
    url = 'http://api.map.baidu.com/geoconv/v1/?coords={}+&from=1&to=6&output=json&ak={}'.format(
        wgs_x + ',' + wgs_y,  # 组合经纬度
        'AxBljsdLeG0IrFxKkyD2H58gNgjuMHEm'  # 在此处放置你的 AK 码
    )

    try:
        res = openUrl(url).decode()  # 发起请求并解码响应
    except Exception as e:
        print(f"Error fetching data from API: {e}")  # 捕获异常并打印错误信息
        return 0, 0

    try:
        temp = json.loads(res)  # 尝试解析 JSON 格式的响应内容
    except json.JSONDecodeError as e:
        print(f"Error decoding JSON: {e}")  # 捕获异常并打印错误信息
        return 0, 0

    bd09mc_x = 0  # 初始化百度墨卡托坐标 x
    bd09mc_y = 0  # 初始化百度墨卡托坐标 y

    # 检查 JSON 数据中是否包含所需的字段,并且状态码是否为 0
    if 'status' in temp and temp['status'] == 0 and 'result' in temp and len(temp['result']) > 0:
        bd09mc_x = temp['result'][0]['x']  # 获取百度墨卡托坐标 x
        bd09mc_y = temp['result'][0]['y']  # 获取百度墨卡托坐标 y
    else:
        print(f"API returned unexpected data: {temp}")  # 打印错误信息
        return 0, 0

    # 返回百度墨卡托坐标
    return bd09mc_x, bd09mc_y

2.2.5 爬取街景图像

目前为止,我们已经做好了所有的准备工作,可以开始进行街景图像爬取啦。

这段代码从 CSV 文件中读取经纬度坐标,这里的 CSV 文件我们会提供,其是通过在 osm 路网数据采样点获取得到的。

通过百度 API 获取对应的街景图像,并将这些图像保存到指定目录。如果在下载过程中出现错误,它会记录错误信息并将这些信息保存到一个新的 CSV 文件中,方便下次收集,无svid代表该点无对应的街景图像。

代码语言:javascript
复制
代码语言:javascript
复制
if __name__ == "__main__":
    # 设置文件和目录路径
    root = r'/home/mw/project/output_folder/'  # 根目录路径
    read_fn = r'wuhan_point_wgs_84.csv'  # 输入的 CSV 文件名
    error_fn = r'wuhan_error.csv'  # 错误记录的 CSV 文件名
    dir = r'/home/mw/project/wuhan'  # 输出图片保存目录

    # 检查并创建输出目录
    if not os.path.exists(dir):
        os.makedirs(dir)

    # 获取已存在的图片文件名列表
    filenames_exist = glob.glob1(dir, "*.png")  # 获取已存在的图片文件名列表

    # 读取 CSV 文件中的数据
    with open(os.path.join(root, read_fn), 'r', newline='', encoding='utf-8') as csvfile:
        reader = csv.reader(csvfile)
        data = list(reader)

    # 存储 CSV 文件的完整路径
    a = os.path.join(root, read_fn)
    # 记录 CSV 文件的 header
    header = data[0]
    # 去掉 header,只保留数据
    data = data[1:]

    # 初始化错误图片列表
    error_img = []

    # 定义方向列表和 pitch
    headings = ['0', '90', '180', '270']  # 定义方向列表
    pitchs = '0'  # 初始 pitch 为 0,pitch为视角俯仰度

    # 只爬取前 10 个数据
    for i in range(min(len(data), 10)):
        print('当前处理到了第{}个点'.format(i + 1))  # 打印当前处理到的点的序号

        # 从数据中获取经纬度
        wgs_x, wgs_y = data[i][0], data[i][1]  # 获取经纬度数据

        try:
            # 将 WGS-84 坐标转换为百度墨卡托坐标
            bd09mc_x, bd09mc_y = wgs2bd09mc(wgs_x, wgs_y)  # 调用函数进行坐标转换
            print(f"Converted WGS to BD09MC: {bd09mc_x}, {bd09mc_y}")  # 打印转换后的坐标
        except Exception as e:
            print("解析错误:" + str(e))  # 打印异常信息并继续下一次循环
            error_img.append(data[i] + ["解析错误"])
            continue

        # 检查当前坐标下的四个方向的图片是否已经存在
        flag = True  # 初始化标志位
        for k in range(len(headings)):
            # 检查是否存在对应方向的图片文件
            flag = flag and "%s_%s_%s_%s.png" % (wgs_x, wgs_y, headings[k], pitchs) in filenames_exist

        # 如果所有方向的图片都存在,则跳过当前坐标
        if flag:
            continue

        # 获取当前坐标的 panoid
        panoid = getPanoId(bd09mc_x, bd09mc_y)  # 获取当前坐标的 panoid
        print("Panoid:")
        print(panoid)  # 打印 panoid
        if panoid is None:
            error_img.append(data[i] + ["无svid"])
            continue

        # 遍历每个方向,下载街景图片
        for h in range(len(headings)):
            save_fn = os.path.join(dir, '%s_%s_%s_%s.png' % (wgs_x, wgs_y, headings[h], pitchs))  # 设置保存图片的路径
            url = 'https://mapsv0.bdimg.com/?qt=pr3d&fovy=90&quality=100&panoid={}&heading={}&pitch=0&width=480&height=320'.format(panoid, headings[h])  # 设置请求图片的 URL
            img = grab_img_baidu(url)  # 请求图片数据

            # 如果图片下载成功,保存图片
            if img is not None:
                with open(save_fn, "wb") as f:
                    f.write(img)
                print(f"储存成功: {save_fn}")  # 输出储存成功消息
            else:
                error_img.append(data[i] + [headings[h]])
                print(f"储存失败: {save_fn}")  # 输出储存失败消息

    # 保存错误信息
    if len(error_img) > 0:
        with open(os.path.join(root, error_fn), 'w', newline='', encoding='utf-8') as f:
            writer = csv.writer(f)
            writer.writerow(header + ['error_info'])  # 写入 header
            writer.writerows(error_img)  # 写入错误数据
            print("输出错误文件")


# 爬取所有数据的代码如下:将下方代码取消注释,上方代码“ # 只爬取前 10 个数据”下的代码注释即可运行
#     while True:
#         try:

#             for i in range(start_index,len(data)):
#                 print('当前处理到了第{}个点'.format(i + 1))
#                 # 从数据中获取经纬度
#                 wgs_x, wgs_y = data[i][0], data[i][1]

#                 try:
#                     # 将WGS-84坐标转换为百度墨卡托坐标
#                     bd09mc_x, bd09mc_y = wgs2bd09mc(wgs_x, wgs_y)
#                     print(f"Converted WGS to BD09MC: {bd09mc_x}, {bd09mc_y}")
#                 except Exception as e:
#                     print("解析错误:"+str(e))  # 打印异常信息并继续下一次循环

#                 # 检查当前坐标下的四个方向的图片是否已经存在
#                 flag = True
#                 for k in range(len(headings)):
#                     flag = flag and "%s_%s_%s_%s.png" % (wgs_x, wgs_y, headings[k], pitchs) in filenames_exist

#                 # 如果所有方向的图片都存在,则跳过当前坐标
#                 if flag:
#                     continue

#                 # 获取当前坐标的panoid
#                 panoid = getPanoId(bd09mc_x, bd09mc_y)
#                 print(panoid)
#                 if panoid is None:
#                     error_img.append(data[i] + ["无svid"])
#                     continue

#                 # 遍历每个方向,下载街景图片
#                 for h in range(len(headings)):
#                     save_fn = os.path.join(root, dir, '%s_%s_%s_%s.png' % (wgs_x, wgs_y, headings[h], pitchs))
#                     url = 'https://mapsv0.bdimg.com/?qt=pr3d&fovy=90&quality=100&panoid={}&heading={}&pitch=0&width=480&height=320'.format(panoid, headings[h])
#                     img = grab_img_baidu(url)

#                     # 如果图片下载成功,保存图片
#                     if img is not None:
#                         with open(save_fn, "wb") as f:
#                             f.write(img)
#                     #如果图片下载失败,记录错误信息
#                     if img is None:
#                         data[i].append(headings[h])
#                         error_img.append(data[i])    

#                 # 更新起始索引
#                 start_index = i + 1

#         # 捕获并处理任何异常
#         except Exception as e:
#             print("爬取错误:"+str(e))  # 打印异常信息
#             time.sleep(300)  # 休眠60秒
#         # 保存失败的图片
#     if len(error_img) > 0:
#         with open(os.path.join(root, error_fn), 'w', newline='', encoding='utf-8') as f:
#             writer = csv.writer(f)
#             writer.writerow(header + ['error_info'])  # 写入 header
#             writer.writerows(error_img)  # 写入错误数据
#             print("输出错误文件")
代码语言:javascript
复制

2.2.6 街景图像查看

目前我们已经爬取了街景图像,那么如何检验目前街景图象是否有效,而不是乱码或者残缺数据呢。 首先在左侧列表中的数据查看,查看变量“dir”设置的路径,本代码中“dir”为 dir = r'/home/mw/project/wuhan' # 输出图片保存目录。 首先可以查看到列表中出现数据信息,如下图。

然后根据以下代码,可以对列表中的一个图像进行检查,查看图片信息并可视化。

代码语言:javascript
复制
代码语言:javascript
复制
def check_image_info(image_path):
    """
    检查图片信息并进行可视化。

    参数:
    image_path (str): 图片文件路径。
    """
    # 检查文件是否存在
    if not os.path.exists(image_path):
        print("图片文件不存在")
        return

    # 打开图片文件
    img = Image.open(image_path)

    # 显示图片信息
    print("图片信息:")
    print(f"格式: {img.format}")
    print(f"模式: {img.mode}")
    print(f"尺寸: {img.size}")

    # 显示图片
    img.show()

# 指定图片文件路径
image_path = "/home/mw/project/wuhan/114.0446183_30.6338842_0_0.png"  # 更改为你的图片文件路径,即上文的dir路径+图片名

# 检查图片信息并进行可视化
check_image_info(image_path)
代码语言:javascript
复制

武汉市街景图像绿视率计算

本节使用镜像为 Python 3.7 ,使用的计算资源是 2 核 8G CPU 资源,Kernel 类型为 Python3。使用的镜像很基础,爬取 poi 数据不涉及 GPU 的使用,只使用 CPU 资源就可以了。

城市绿视率是基于街景图像技术的城市规划和设计指标,用于评估城市的绿化水平。绿视率是通过对街景图像进行分析,提取出绿地、植被覆盖等绿化要素,并计算它们在整体城市面积中所占的比例得到。这个指标可以帮助识别出绿地不足或分布不均匀的区域,为城市规划和设计提供科学依据。提高城市绿化水平有助于改善城市环境质量,提升居民生活质量,促进城市的可持续发展。

首先我们需要导入所需库,以下是各个库的介绍。

  • "os"库提供了 Python 与操作系统之间的桥梁,让我们能够执行文件和目录操作,如创建、删除、重命名等。在本代码中,它用于列出目标文件夹中特定扩展名的所有图像文件。
  • "PILLOW"库是 Python 中广泛使用的图像处理库,它支持多种图像格式的打开、编辑和保存。在这段代码中,Pillow 用于打开图像文件,进行基本的图像处理操作,如提取绿色像素,以及可视化处理结果。
  • "pandas"库是一个强大的数据分析库,它提供了丰富的数据结构和数据操作工具,特别适用于处理结构化的数据。在这里,pandas 被用于存储和组织计算出的绿视率结果,并将结果保存到 CSV 文件中。
  • "numpy"库是 Python 中的科学计算库,它提供了大量的数学函数和数据操作工具,特别是在处理多维数组和矩阵方面非常有用。在本代码中,numpy 用于进行图像数据的数组化操作,使得图像处理更为高效。
  • "matplotlib"库是 Python 中的绘图库,它提供了一系列用于创建各种类型图形的函数,如折线图、柱状图、散点图等。这里,matplotlib 用于可视化图像处理的结果,展示原始图像与提取的绿色像素图像。
代码语言:javascript
复制
# 导入所需库
import os
from PIL import Image
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

3.1 武汉市绿视率计算

绿视率计算方法采用了 Pillow 库来读取由爬虫获取的街景图像数据。首先,每张图像被打开并获取其宽度和高度,然后初始化一个绿色像素计数器。接下来,代码遍历图像中的每个像素点,并获取其 RGB 值。

在判断像素是否为绿色时,我们使用了一个简单的条件:绿色通道值(g)大于红色(r)和蓝色(b)通道值。这是一个基本的方法来识别绿色像素,当然,你可以根据需要调整这个阈值或采用更复杂的颜色空间转换和阈值处理方法。

在获取了所有绿色像素后,我们计算绿色像素占总像素数的比例,以得到绿视率。这个比例乘以 100,以便以百分比的形式表示。

对于一个给定的地点,我们获取其四个不同方向(0 度、90 度、180 度和 270 度)的街景图像,并对每张图像都进行上述的绿视率计算,从而估算出该地区的整体绿视率。

代码语言:javascript
复制
# 计算绿色像素比例函数
def calculate_green_ratio(image_path):
    # 打开图像
    img = Image.open(image_path)

    # 获取图像的宽度和高度
    width, height = img.size

    # 初始化绿色像素计数
    green_pixels = 0

    # 遍历每个像素
    for x in range(width):
        for y in range(height):
            # 获取像素的RGB值
            r, g, b = img.getpixel((x, y))

            # 判断是否为绿色像素(可以根据需要调整阈值)
            if g > r and g > b:
                green_pixels += 1

    # 计算绿色像素占比
    total_pixels = width * height
    green_ratio = (green_pixels / total_pixels) * 100

    return green_ratio

"visualize_green_pixels"函数首先打开指定路径的图像文件,然后将其转换为 NumPy 数组。这个 NumPy 数组表示了图像的像素数据,使我们能够更容易地进行像素级的操作。

接着,我们定义了一个布尔掩码("green_mask"),用于筛选出绿色像素。掩码基于一个简单的条件:绿色通道值(索引为 1 的通道,基于 0 索引的 RGB)大于红色和蓝色通道值。这个掩码用于选择所有绿色像素,并将其他像素设置为黑色。

然后,我们在一个 12x6 英寸大小的图形中可视化原始图像和仅包含绿色像素的图像。在左侧,我们展示了原始图像,而在右侧则是高亮显示了绿色像素的图像。

这个函数的目的是提供一个直观的方式来查看图像中的绿色像素,从而帮助我们查看目前绿视率计算效果。

代码语言:javascript
复制
# 定义一个函数用于可视化图像中的绿色像素
def visualize_green_pixels(image_path):
    # 打开指定路径的图像文件
    img = Image.open(image_path)

    # 将图像转换为NumPy数组以便处理
    img_np = np.array(img)

    # 创建一个布尔掩码来标识绿色像素
    # 绿色通道值(索引为1)大于红色和蓝色通道值时,掩码为True
    green_mask = (img_np[:,:,1] > img_np[:,:,0]) & (img_np[:,:,1] > img_np[:,:,2])

    # 使用掩码复制原始图像数组,非绿色像素设为黑色
    green_pixels = img_np.copy()
    green_pixels[~green_mask] = [0, 0, 0]

    # 创建一个12x6英寸的新图形
    plt.figure(figsize=(12, 6))

    # 在第一个子图中显示原始图像
    plt.subplot(1, 2, 1)
    plt.imshow(img)
    plt.title('Original Image')  # 设置标题
    plt.axis('off')  # 隐藏坐标轴

    # 在第二个子图中显示只包含绿色像素的图像
    plt.subplot(1, 2, 2)
    plt.imshow(green_pixels)
    plt.title('Green Pixels')  # 设置标题
    plt.axis('off')  # 隐藏坐标轴

    # 显示图形
    plt.show()

主要的两个函数我们已经写好,接下来搭建主函数,就可以开始批量进行绿视率计算啦。

代码语言:javascript
复制
# 指定图像文件夹的路径
folder_path = r"/home/mw/project/wuhan"  # 替换为你的文件夹路径

# 使用列表推导式获取文件夹中所有以.jpg、.jpeg或.png结尾的图像文件
image_files = [f for f in os.listdir(folder_path) if f.endswith(('.jpg', '.jpeg', '.png'))]

# 初始化结果列表和计数器
results = []
i = 0

# 获取一个示例图像的完整路径并进行绿色像素可视化
image_example = os.path.join(folder_path, image_files[0])
visualize_green_pixels(image_example)

# 遍历文件夹中的每个图像文件
for image_file in image_files:
    # 获取当前图像文件的完整路径
    image_path = os.path.join(folder_path, image_file)

    # 调用calculate_green_ratio函数计算当前图像的绿色像素比例
    green_ratio = calculate_green_ratio(image_path)

    # 将图像文件名和计算出的绿色像素比例添加到结果列表中
    results.append({'Image Name': image_file, 'Green Ratio (%)': green_ratio})

    # 更新处理计数器
    i += 1
    print("当前处理到第{}张图片".format(i))

# 将结果列表转换为DataFrame,并保存为CSV文件
df = pd.DataFrame(results)
df.to_csv('/home/mw/project/wuhan/green_ratio_results.csv', index=False)

# 输出完成信息
print("Green ratio calculation completed. Results saved to green_ratio_results.csv.")

处理得到的 csv 文件,第一列为街景图像名称,而名称含有经纬度信息,所以我们需要将经纬度提取出来,方便后续可视化。

代码语言:javascript
复制
代码语言:javascript
复制
# 输入CSV文件路径
input_csv_file = '/home/mw/project/wuhan/green_ratio_results.csv'
output_csv_file = '/home/mw/project/wuhan/green_ratio_results1.csv'

# 检查输出文件夹是否存在,如果不存在则创建
output_folder = 'output_folder'
if not os.path.exists(output_folder):
    os.makedirs(output_folder)

# 读取CSV文件
df = pd.read_csv(input_csv_file)

# 将'Image Name'列按照'_'进行分割,并扩展为新的列
a = df['Image Name'].str.split('_', expand=True)

# 选择新的数据框中的前两列和原始数据框中的第二列,然后合并成新的数据框
new_df = pd.concat([a.iloc[:, :2], df.iloc[:, 1]], axis=1)

# 设置新的列名
new_df.columns = ['x', 'y', 'Green Ratio (%)']

# 保存处理后的数据到新的CSV文件
new_df.to_csv(os.path.join(output_folder, output_csv_file), index=False)

# 打印保存成功的消息
print(f"Data written to {output_csv_file} in {output_folder} successfully!")
代码语言:javascript
复制

3.2 武汉市绿化率可视化

首先下载安装 QGIS。

利用输出的 csv 文件,结合 QGIS 软件,将街景图像可视化在武汉市矢量图后的最终效果图是这样:

1、打开 QGIS--->图层--->文件名选择刚刚生成得到的"green_ratio_results1.csv"文件--->x 轴为 x 列,y 轴为 y 列--->点击添加

2、将绿化率矢量文件导入后,导入武汉市矢量图,进行矢量叠加分析,具体参数设置如图所示。

3、在左下角图层列表中,双击被连接图层,对连接数据进行符号化,符号化设置参数如图所示。

4、双击打开武汉市行政区划矢量图,打开符号化与标注,参数设置如下图所示。

5、左上角 工程--->新建打印布局

6、依次添加地图、指北针、图例、比例尺

7、左上角 布局--->导出为图像--->参数设置

3.3 总结

在课程中,我们学习了城市绿视率的计算方法以及如何利用街景图像技术来评估城市的绿化水平。绿视率是通过对街景图像进行分析,提取绿地和植被覆盖等绿化要素,并计算它们在整体城市面积中的比例来衡量。街景图像的爬取是获取评估城市绿化水平所需数据的重要步骤,需要通过爬虫技术获取大量的街景图像数据,以支持后续的分析和计算。

在作业中,我们首先要使用百度地图API接口爬取2013年的武汉市街景图像数据,其中街景图像的fov为60。这涉及到破解反爬虫机制、分析爬取数据以及对数据的简单处理。接着,我们要对爬取得到的街景图像进行语义分割,计算街景图像的天空率,即天空像素占比。这需要对图像进行处理和分析,理解图像的本质数据类型以及如何通过这种数据类型进行显示。最后,我们要根据街景图像的经纬度信息生成POI点,并在武汉市的矢量图上进行可视化,这涉及到将POI点的csv文件生成矢量文件,并在QGIS中进行可视化。

通过这些作业,我们将掌握爬虫技术、图像处理和分析方法,以及矢量数据的处理和可视化技术,从而更好地理解城市绿化水平,并为城市规划和设计提供科学依据。

4. 总结

本项目来自【和鲸社区】的活动【武汉大学——聚焦前沿对话未来:地理空间智能(GeoAI)最新研究进展及落地应用】中的一篇教案

代码语言:javascript
复制
【学习链接】https://www.heywhale.com/home/activity/detail/662b317aa2141d2b0bf33b49

其他教案【基于多源数据融合的土地利用分类模型】,【面向复杂城市系统的大规模物流优化算法】,【基于街景图像的武汉城市绿化空间分析】大家可以了免费报名在线学习。

可以在线运行代码哦

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-05-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 点点GIS 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • 1.1 项目背景
      • 1.2 任务简介
      • 街景爬虫和实现
        • 2.1 任务准备
          • 2.2 街景爬取
          • 武汉市街景图像绿视率计算
            • 3.1 武汉市绿视率计算
              • 3.2 武汉市绿化率可视化
                • 1、打开 QGIS--->图层--->文件名选择刚刚生成得到的"green_ratio_results1.csv"文件--->x 轴为 x 列,y 轴为 y 列--->点击添加
                  • 2、将绿化率矢量文件导入后,导入武汉市矢量图,进行矢量叠加分析,具体参数设置如图所示。
                    • 3、在左下角图层列表中,双击被连接图层,对连接数据进行符号化,符号化设置参数如图所示。
                      • 4、双击打开武汉市行政区划矢量图,打开符号化与标注,参数设置如下图所示。
                        • 5、左上角 工程--->新建打印布局
                          • 6、依次添加地图、指北针、图例、比例尺
                            • 7、左上角 布局--->导出为图像--->参数设置
                              • 3.3 总结
                                • 4. 总结
                                相关产品与服务
                                API 网关
                                腾讯云 API 网关(API Gateway)是腾讯云推出的一种 API 托管服务,能提供 API 的完整生命周期管理,包括创建、维护、发布、运行、下线等。
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档