百度地图街景图片爬取

20220601超简单百度地图街景图片爬取+绿视率计算 真的不难咯。 阅读详情
AI摘要

本文详述了百度街景地图的爬虫实现过程,包括解析街景请求、递归爬取街景图片、存储至数据库的完整逻辑,以及在实际应用中遇到的问题和解决方案。

1 需求

       抓取整个杭州市的百度/腾讯街景地图及其时光机功能(实时图片和历史图片),进行图像分析。

2 分析

       百度地图街景模式下,点击向前可发现,街景图片是异步加载的,我们可以打开百度地图的街景模式,f12打开开发者模式,清空所有响应,并点击向前,可以看到产生了很多的图片请求


在这里插入图片描述

2.1 街景request简要分析

       本文以杭州市余杭区文一西路海创园附近处(由西向东)的街景为例仔细分析这些请求的作用:
       首先,第一条请求:

https://mapsv0.bdimg.com/?qt=qsdata&x=13361258.73664768&y=3518572.1440338427&time=201709&mode=day&px=1336124156&py=351856780&pz=14.89&roadid=eeaa41-bd79-2cf9-3491-2ca35d&type=street&action=0&pc=1&auth=8Nv8G8DBLbO2F1vLvHNAgXOTz4UFPbxHuxHLxBLVHTRt1qo6DF%3D%3DCcvY1SGpuztAFwWv1GgvPUDZYOYIZuVt1cv3uHxtOmm0mEb1PWv3GuxNVt%3DErpTgZp1GHJMP6V8%40aDcEWe1GD8zv7u%40ZPuVteuEthjzgjyBKOBEEUWWOxtx77INHu%3D%3D8x35&udt=20190619&fn=jsonp.p30899897

       此请求也可简化为(为简便,以下请求均不带auth参数,不影响结果获取):

https://mapsv0.bdimg.com/?qt=qsdata&x=13361258.73664768&y=3518572.1440338427&time=201709&mode=day

       作用:根据地图上点击的位置,生成百度地图坐标x和y值,再得到服务器的json响应,(如下id即为该位置的百度街景ID,也就是后续要用到的panoID)
在这里插入图片描述
       第二条请求:

https://mapsv0.bdimg.com/?qt=sdata&sid=09025200121709031616142855K&pc=1

       作用:该请求包含了panoId参数,返回该位置的街景相关信息(附近panoID,以及该位置的历史图片,拍摄时期等等),下文会详细分析。json响应如下图所示:
在这里插入图片描述
       第三条请求:

https://mapsv0.bdimg.com/?qt=guide&sid=09025200121709031616142855K&fn=jsonp29109114

       作用: 获取该位置附近的公司或学校。json响应如下图所示:
在这里插入图片描述
       第四条请求:

https://mapsv0.bdimg.com/?qt=pdata&sid=09025200121709031616142855K&pos=0_0&z=1&udt=20190619

       作用: 该请求返回该位置街景的全景图片。如下所示:
在这里插入图片描述
       第五条请求:

https://mapsv0.bdimg.com/?qt=pr3d&fovy=35&quality=80&panoid=09025200121709031616142855K&heading=72.801&pitch=0&width=198&height=108

       作用:该请求返回一个当前视角的小尺寸图片,如图所示:
在这里插入图片描述
       其中的参数解释,网上找到了一个图片,有些参数未亲自验证。
在这里插入图片描述
       由于小尺寸图片放大之后比较模糊,所以不对其进行获取。

       第六条请求(这是一组大图请求):

https://mapsv0.bdimg.com/?qt=pdata&sid=09025200121709031616142855K&pos=1_4&z=4
https://mapsv0.bdimg.com/?qt=pdata&sid=09025200121709031616142855K&pos=2_4&z=4
https://mapsv0.bdimg.com/?qt=pdata&sid=09025200121709031616142855K&pos=1_5&z=4
https://mapsv0.bdimg.com/?qt=pdata&sid=09025200121709031616142855K&pos=2_5&z=4
https://mapsv0.bdimg.com/?qt=pdata&sid=09025200121709031616142855K&pos=1_6&z=4
https://mapsv0.bdimg.com/?qt=pdata&sid=09025200121709031616142855K&pos=2_6&z=4
https://mapsv0.bdimg.com/?qt=pdata&sid=09025200121709031616142855K&pos=1_7&z=4
https://mapsv0.bdimg.com/?qt=pdata&sid=09025200121709031616142855K&pos=2_7&z=4

在这里插入图片描述
       作用:获取从左至右的街景大图,其中参数pos=1_*表示高角度(就是我们需要获取的),从左至右一共是4张(高角度),这样就拼成了我们在百度街景中所看到的一整张大图;pos=2_*表示低角度。

       因此我们要得到一座城市、一条街道的街景图片,首先要获取该城市、该街道所有位置的街景ID,然后通过模拟上述图片请求即可,但是如何获取杭州市所有街道的街景ID呢?

       网上查了些资料,有人提出的想法是:

  • 1、暴力循环panoID,错误就忽略,正确就返回结果。
  • 2、在一条道路寻找一个种子panoID,然后爬取整条道路的所有图片。
  • 3、根据百度地图的坐标,设置一个区域,遍历整个区域的所有坐标,正确就返回panoID,错误就不处理。

       粗略一看感觉很有道理,但仔细一分析就知扯淡! 首先,第一条,暴利循环相当低效,由于百度没有提供整条街道的经纬度接口,也没有提供获得整条街道的所有panoID值的接口(至少我没找到),所以如果根据网友的意思,在给定起始panoID情况下循环试错获取下一个panoID需要发送上万次、数十万次请求,很可能没获取到下一个ID就已经被百度封IP了,第三条,我确实想设置一个区域,但如何遍历该区域所有坐标呢?如何保证街景图片是同一条街道且连续的呢?有大牛想到解决办法的话烦请告知!再来看第二条,似乎可取,重点是如何获取下一个panoID。答案还是在上述"请求二"中。

2.2 请求2详细分析

       我们可以看到,Roads[0].Panos标签下,包含有多个panoID,我们可以在街景模式下点击向前10米,浏览器又加载了下个panoID(09025200121709031616155125K)的图片,这里有一个规律,向前20米左右会加载当前panoID的下下个panoID图片,以此类推。
在这里插入图片描述
       当再点击向前,加载到panoID为09025200121709031616211215K的图片时,我们再看它的"请求二"的json响应如下图所示:
在这里插入图片描述
       如图所示,Roads[0]为当前位置节点信息(IsCurrent: 1),Roads是一个数组,通常会有多个元素作为候选节点,这里仅有一个,即Roads[1],Roads[1].ID和Links[0].RID对应,继续点击向前,发现加载的正是我们从图中所见的panoID:09025200121709031616224035K。我们再观察09025200121709031616224035K这个位置的"请求二"响应,看到又有当前位置下的前向panoID集合了。我们再放心大胆地尝试几次点击,发现是有这个规律。紧锁的眉头渐渐舒缓,是时候喝杯咖啡了。在这里插入图片描述
       coffee归来,思路再捋一捋。由上,如何根据给定的起始panoID爬取所在街道的所有街景图片?
业务逻辑流程梳理大致如下:

  1. 选择合适的起始panoID(可将其存入配置文件或数据库);
  2. 根据panoID拼接上述"请求二",并发送请求获取响应;
  3. json解析该响应,获取当前位置附近的前向panoID集合;
  4. 遍历该集合,拼接每个位置的图片下载链接(4张图片)并下载;
  5. 当遍历到集合最后一个元素时,还需解析Roads数组中的非当前元素,并将其对应到Links数组中的panoID,我们姑且称之为锚节点;
  6. 用该锚节点的panoID代入STEP 2中,这样整个逻辑就形成了递归函数。

2.3 图片存储

       在这里可以根据不同的业务需求采用不同的数据库,关系型或非关系型均可。某种程度上非关系型列式存储可能更好,因为当我们爬取一个城市所有街道的街景时,有的位置有历史数据,有的则没有,因此大规模数据集下可利用列式存储的弹性伸缩实现数据的高效存取。但考虑到后续图像分析时,可能需要建立空间连续(离散)位置的图像模型,这又更适合采用关系型数据库来存取。以mysql与Hbase为例,再回顾一下典型的关系型数据库和非关系型数据库之间的区别:

2.3.1 HBase与MySQL的区别
属性HBaseMySQL
存储按列存储,可灵活增加列,列为空时不占存储空间按行存储
伸缩扩展性支持需要第三方中间层支持
高并发读写支持不支持
条件查询只支持按rowkey查询支持
数据类型字符串类型多种类型
数据操作只有查询、插入、删除、清空等还包括各种连接操作等
数据更新实际是插入新的数据,多版本替换修改
2.3.2 二进制流的存储

       下载几张图片看下大小,然后决定选用何种字段类型,经比较分析,得如下要点:

  • 图片转为二进制字节流
  • 表中图片字段设为blob型

       MySQL 存储blob型数据可参阅此处

3 代码

       对应上述业务逻辑,编写不同步骤的代码

3.1 建表及读表记录

       建表,并将起始panoID存入数据库。建表语句如下:

CREATE TABLE `test`.`baidu_pano`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `pano_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '街景ID',
  `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '街道名称',
  `lati` double(32, 0) NULL DEFAULT NULL COMMENT '纬度',
  `lonti` double(32, 0) NULL DEFAULT NULL COMMENT '经度',
  `direction` bit(1) NULL DEFAULT NULL COMMENT '0:由西向东;1:由东向西',
  PRIMARY KEY USING BTREE (`id`)
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'InnoDB free: 30720 kB' ROW_FORMAT = Compact;

       插入panoID如图所示:
在这里插入图片描述
       在coding之前先在builder.gradle文件中一并引入所有依赖:

ext {
    commonsDbutilsVersion = '1.6'
    druidVersion = '1.0.18'
    mysqlConnectorVersion = '5.1.37'
}

dependencies {
    compile "com.cetiti.ddc:ddc-core:0.1.11-alpha1"
    compile "commons-dbutils:commons-dbutils:${commonsDbutilsVersion}"
    compile 'org.apache.logging.log4j:log4j-core:2.8.2'
    compile "com.alibaba:druid:${druidVersion}"
    compile "mysql:mysql-connector-java:${mysqlConnectorVersion}"
    compile 'org.postgresql:postgresql:42.2.5'
    testCompile 'junit:junit:4.9'
    compile 'org.apache.commons:commons-pool2:2.4.2'
    compile 'redis.clients:jedis:2.9.0'
}
// 除此之外,还要引入snakeyaml-1.18.jar、fasjson、okhttp等jar包,对应版本可以自行搜索,本项目因依赖了公司自研的jar包,所以未显式说明

       我们需编写一个从MySQL读取记录并封装结果集的工具类MySQLHelper ,源码如下:

public class MySQLHelper {

    private static final Logger logger = Logger.getLogger(MySQLHelper.class);
    private static QueryRunner runner;

    private static ResultSetHandler<HashMap<String,Object>> h = new ResultSetHandler<HashMap<String,Object>>() {
        @Override
        public HashMap<String,Object> handle(ResultSet rs) throws SQLException {
            if (!rs.next()) {
                return null;
            }
            ResultSetMetaData meta = rs.getMetaData();
            int cols = meta.getColumnCount();
            HashMap<String,Object> result = new HashMap<>(16);

            for (int i = 0; i < cols; i++) {
                result.put(meta.getColumnLabel(i + 1),rs.getObject(i + 1));
            }
            return result;
        }
    };

    public MySQLHelper(){
        runner = MySQLPool.getInstance().getRunner();
    }

    @SuppressWarnings("unchecked")
    public List<Pano> getAllPanoFromDB(){
        try {
            String qSql =  "select pano_id as panoId, name from baidu_pano limit 10";
            @SuppressWarnings("rawtypes")
            BeanListHandler blh = new BeanListHandler(Pano.class);
            return (ArrayList<Pano>) runner.query(qSql,blh);
        } catch (SQLException e) {
            logger.error("getAllIpFromDB", e);
            return null;
        }
    }
}

       其中连接池MySQLPool代码如下:

public class MySQLPool {
    private static MySQLPool instance = null;
    private static final Logger logger = Logger.getLogger(MySQLPool.class);
    private DruidDataSource dds;
    private QueryRunner runner;
    private Properties properties;

    public QueryRunner getRunner() {
        return this.runner;
    }

    private MySQLPool() {
        ConfigParser parser = ConfigParser.getInstance();
        String dbAlias = "mysql-data";
        Map<String, Object> dbConfig = parser.getModuleConfig("database");
        Map<String, Object> mysqlConfig = (Map)parser.assertKey(dbConfig, dbAlias, "database");
        Properties properties = new Properties();
        String url = (String)parser.assertKey(mysqlConfig, "url", "database." + dbAlias);
        String username = (String)parser.assertKey(mysqlConfig, "username", "database." + dbAlias);
        String password = (String)parser.assertKey(mysqlConfig, "password", "database." + dbAlias);
        properties.setProperty("url", url);
        properties.setProperty("username", username);
        properties.setProperty("password", password);
        properties.setProperty("maxActive", "20");
        this.properties = properties;

        try {
            this.dds = (DruidDataSource)DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception var10) {
            logger.error("Failed to connect data MySQL db,Exception:{}", var10);
        }

        this.runner = new QueryRunner(this.dds);
    }

    public static MySQLPool getInstance() {
        if (instance == null) {
            Class var0 = MySQLPool.class;
            synchronized(MySQLPool.class) {
                if (instance == null) {
                    instance = new MySQLPool();
                }
            }
        }

        return instance;
    }
}

       ConfigParser类为自定义的配置文件读取解析类,源码如下:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Map;
import org.yaml.snakeyaml.Yaml;

public class ConfigParser {
    private static final Logger logger = Logger.getLogger(ConfigParser.class);
    private static ConfigParser instance = new ConfigParser();
    private static final String CONFIG_FILENAME = "config.yml";
    private Yaml yaml = null;
    private Object config;

    private ConfigParser() {
        if (this.yaml == null) {
            this.yaml = new Yaml();
        }

        File f = ResourceUtils.loadResouces("config.yml");

        try {
            this.config = this.yaml.load(new FileInputStream(f));
            logger.info("file {} is loaded", f.getAbsoluteFile());
        } catch (FileNotFoundException var3) {
            var3.printStackTrace();
        }

    }

    public static ConfigParser getInstance() {
        return instance;
    }

    public Object getConfig() {
        return this.config;
    }

    public Map<String, Object> getModuleConfig(String name) {
        return this.getModuleConfig(name, this.config);
    }

    public Map<String, Object> getModuleConfig(String name, Object parent) {
        Map<String, Object> rtn = (Map)((Map)parent).get(name);
        return rtn;
    }

    public Object assertKey(Map<String, Object> config, String key, String parent) {
        Object value = config.get(key);
        if (value == null) {
            logger.error("{}.{} is a mandatory configuration", new Object[]{parent, key});
            System.exit(0);
        }

        return value;
    }

    public Object getValue(Map<String, Object> config, String key, Object def, String parent) {
        Object value = config.get(key);
        if (value == null) {
            logger.warn("{}.{} is't configured, default value {} is used", new Object[]{parent, key, def});
            config.put(key, def);
            return def;
        } else {
            return value;
        }
    }

    public void dumpConfig() {
        System.out.println(this.yaml.dump(this.config));
    }
}

3.2 配置文件

       配置文件config.yml中配置如下:

apps:
  ## 基本属性
  spider-baidupano:
    common:
      group: ipproxy-xundaili-zhg
      cron: "0 0 0 */1 * ?"
      firstpage: 1
      totalpages: 1
      distribute: false
      fixed: true
      order: desc
    ## 数据源
    source:
      baseurl: https://mapsv0.bdimg.com/?qt=pdata&sid=09024600011606211814253666L&pos=1_4&z=4
      listpageregex: "https://mapsv0\\.bdimg\\.com/\\?qt\\=sdata"
    storage:
      ## dbType: MySQL HBase Hive MongoDB Kafka PostgreSQL
      dbtype: MySQL
      dbalias: mysql-data
      ## 图片存储位置
      piclocation: \baidupano
    filter:
      searchfilter: true
      contentfilter: false
    ## 反爬虫
    antirobot:
      ipproxy: false
      listipproxy: false
      sleeptime: 900000
    analysis:
      sentiment: false

distribute:
  scheduler: com.demo.ddc.scheduler.MemberScheduler
#  dbtype: redis
#  dbalias: redis

database:
  mysql-data:
    url: "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8"
    username: root
    password: '123456'

3.2 图片下载及存储逻辑

       首先编写请求发送工具类:

public class OkHttpUtils {

    private static volatile OkHttpClient okHttpClient;

    private OkHttpUtils(){
    }

    public static OkHttpClient getInstance(){
        if (null==okHttpClient){
            synchronized (OkHttpUtils.class){
                if (okHttpClient==null){
                    okHttpClient = new OkHttpClient();
                    return okHttpClient;
                }
            }
        }
        return okHttpClient;
    }
}

       图片下载及文件路径处理工具类:

public class PicLoadUtils {
    private static SpiderConfig spiderConfig;
    private final static String WINDOWS_DISK_SYMBOL = ":";
    private final static String WINDOWS_PATH_SYMBOL = "\\";

    public PicLoadUtils(){
        String spiderId = "spider-baidupano";
        spiderConfig = new SpiderConfig(spiderId);
    }

    private String getFileLocation(String storeDirName){
        String separator = "/";
        ConfigParser parser = ConfigParser.getInstance();
        String spiderId = "spider-googlemap";
        SpiderConfig spiderConfig = new SpiderConfig(spiderId);
        Map<String,Object> storageConfig = (Map<String, Object>) parser.assertKey(spiderConfig.getSpiderConfig(),"storage", spiderConfig.getConfigPath());
        String fileLocation = (String) parser.getValue(storageConfig,"piclocation",null,spiderConfig.getConfigPath()+".storage");
        String pathSeparator = getSeparator();
        String location;
        if(fileLocation!=null){
            //先区分系统环境,再判断是否为绝对路径
            if (separator.equals(pathSeparator)){
                //linux
                if(fileLocation.startsWith(separator)){
                    location = fileLocation + pathSeparator + "data";
                }else {
                    location = System.getProperty("user.dir") + pathSeparator + fileLocation;
                }
                location = location.replace("//", pathSeparator);
                return location;
            }else {
                //windows
                if (fileLocation.contains(WINDOWS_DISK_SYMBOL)){
                    //绝对路径
                    location = fileLocation + pathSeparator + "data";
                }else {
                    //相对路径
                    location = System.getProperty("user.dir") + pathSeparator + fileLocation;
                }
                location = location.replace("\\\\",pathSeparator);
            }
        }else{
            //默认地址
            location = System.getProperty("user.dir") + pathSeparator + storeDirName;
        }
        return location;
    }

    public String dateToPath(long timestamp) {
        String pathSeparator = getSeparator();
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(timestamp*1000);
        String year = String.format("%04d",calendar.get(Calendar.YEAR));
        String month = String.format("%02d",calendar.get(Calendar.MONTH)+1);
        String date = String.format("%02d",calendar.get(Calendar.DATE));
        return year + pathSeparator + month + pathSeparator + date;
    }

    private String getSeparator(){
        String pathSeparator = File.separator;
        if(!WINDOWS_PATH_SYMBOL.equals(File.separator)){
            pathSeparator = "/";
        }
        return pathSeparator;
    }

    public void mkDir(File file){
        String directory = file.getParent();
        File myDirectory = new File(directory);
        if (!myDirectory.exists()) {
            myDirectory.mkdirs();
        }
    }

    public String downloadPic(String url, String panoId){
        okhttp3.Request request = new okhttp3.Request.Builder()
                .url(url)
                .build();
        Response response = null;
        InputStream inputStream = null;
        FileOutputStream out = null;
        String localLocation = null;
        String relativePath = null;
        try {
            response = OkHttpUtils.getInstance().newCall(request).execute();
            //将响应数据转化为输入流数据
            inputStream = response.body().byteStream();
            byte[] buffer = new byte[2048];
            localLocation = this.getFileLocation("baidupano");
            Date nowTime = new Date(System.currentTimeMillis());
            relativePath = this.dateToPath(nowTime.getTime()/1000) + File.separator + panoId + File.separator + nowTime.getTime()+".jpg";
            File myPath = new File(localLocation + File.separator + relativePath);
            this.mkDir(myPath);
            out = new FileOutputStream(myPath);
            int len;
            while ((len = inputStream.read(buffer)) != -1){
                out.write(buffer,0,len);
            }
            //刷新文件流
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (inputStream!=null){
                try {
                    inputStream.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if (null!=out){
                try {
                    out.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if (null!=response){
                response.body().close();
            }
        }
        return localLocation + File.separator + relativePath;
    }

    public String downloadPic(String url, String curPanoId, String hisPanoId){
        okhttp3.Request request = new okhttp3.Request.Builder()
                .url(url)
                .build();
        Response response = null;
        InputStream inputStream = null;
        FileOutputStream out = null;
        String localLocation = null;
        String relativePath = null;
        try {
            response = OkHttpUtils.getInstance().newCall(request).execute();
            //将响应数据转化为输入流数据
            inputStream = response.body().byteStream();
            byte[] buffer = new byte[2048];
            localLocation = this.getFileLocation("baidupano");
            Date nowTime = new Date(System.currentTimeMillis());
            relativePath = this.dateToPath(nowTime.getTime()/1000) + File.separator + curPanoId + File.separator + hisPanoId + File.separator + nowTime.getTime()+".jpg";
            File myPath = new File(localLocation + File.separator + relativePath);
            this.mkDir(myPath);
            out = new FileOutputStream(myPath);
            int len;
            while ((len = inputStream.read(buffer)) != -1){
                out.write(buffer,0,len);
            }
            //刷新文件流
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (inputStream!=null){
                try {
                    inputStream.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if (null!=out){
                try {
                    out.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if (null!=response){
                response.body().close();
            }
        }
        return localLocation + File.separator + relativePath;
    }
}

       根据上述分析的流程编写核心业务逻辑代码,由于递归的使用,为防止栈溢出,设定一个层级level:

public void getBaiduPanoPics(String curPanoId, int level){
    // 递归层级控制
    if (level == 0){
        logger.info("此街道爬取完毕!");
        return;
    }
    //发送json请求爬取街景图片存库
    JSONObject jsonObject = sendPanoJsonRequest(curPanoId);
    processStorePanoByID(curPanoId,jsonObject,tableName);

    //获取这一段路接下来的panoId
    List<String> forwardNodes = getForwardNodes(curPanoId, jsonObject);
    if (forwardNodes!=null&&!forwardNodes.isEmpty()){
        //遍历爬取
        int forwardNodeSize = forwardNodes.size();
        JSONObject tempJsonObject;
        String id;
        for(int i = 0; i < forwardNodeSize-1; i++){
            id = forwardNodes.get(i);
            tempJsonObject = sendPanoJsonRequest(id);
            processStorePanoByID(id,tempJsonObject,tableName);
        }
        //单独处理集合中最后一个元素,因为需在此得到锚节点进而递归
        id = forwardNodes.get(forwardNodeSize-1);
        tempJsonObject = sendPanoJsonRequest(id);
        processStorePanoByID(id, tempJsonObject, tableName);
        String anchorNode = getEasyAnchorNode(tempJsonObject);
        if (StringUtils.isBlank(anchorNode)){
            return;
        }
        //递归调用
        getBaiduPanoPics(anchorNode,level-1);
    }else {
        //若返回为空,则切换为获取links
        String anchorNode = getEasyAnchorNode(jsonObject);
        if (StringUtils.isBlank(anchorNode)){
            return;
        }
        //递归调用
        getBaiduPanoPics(anchorNode,level-1);
    }
}

       上述方法中调用的其他方法完整代码如下:

public class BaiduPanoPics {

    private final Logger logger  =  Logger.getLogger(BaiduPanoPics.class);
    private static final String REQUEST_PREX_FORWARD_PANOS = "https://mapsv0.bdimg.com/?qt=sdata&sid=";
    private static final String REQUEST_PREX_PICS = "https://mapsv1.bdimg.com/?qt=pdata&sid=";
    private static final int PIC_NUM = 8;
    private String tableName;

    public BaiduPanoPics(String tableName){
        this.tableName = tableName;
    }

    private List<String> getPicsUrl(String panoId){
        // 不添加方向判断逻辑
        final String param = "&z=4";
        String picUrl;
        List<String> picUrlList = new ArrayList<>(4);
        for (int i = 4; i < PIC_NUM; i++){
            picUrl = REQUEST_PREX_PICS + panoId + "&pos=1_"+ i + param;
            picUrlList.add(picUrl);
        }
        return picUrlList;
    }

    public void getBaiduPanoPics(String curPanoId, int level){
        // 递归层级控制
        if (level == 0){
            logger.info("此街道爬取完毕!");
            return;
        }
        //发送json请求爬取街景图片存库
        JSONObject jsonObject = sendPanoJsonRequest(curPanoId);
        processStorePanoByID(curPanoId,jsonObject,tableName);

        //获取这一段路接下来的panoId
        List<String> forwardNodes = getForwardNodes(curPanoId, jsonObject);
        if (forwardNodes!=null&&!forwardNodes.isEmpty()){
            //遍历爬取
            int forwardNodeSize = forwardNodes.size();
            JSONObject tempJsonObject;
            String id;
            for(int i = 0; i < forwardNodeSize-1; i++){
                id = forwardNodes.get(i);
                tempJsonObject = sendPanoJsonRequest(id);
                processStorePanoByID(id,tempJsonObject,tableName);
            }
            //单独处理集合中最后一个元素,因为需在此得到锚节点进而递归
            id = forwardNodes.get(forwardNodeSize-1);
            tempJsonObject = sendPanoJsonRequest(id);
            processStorePanoByID(id, tempJsonObject, tableName);
            String anchorNode = getEasyAnchorNode(tempJsonObject);
            if (StringUtils.isBlank(anchorNode)){
                return;
            }
            //递归调用
            getBaiduPanoPics(anchorNode,level-1);
        }else {
            //若返回为空,则切换为获取links
            String anchorNode = getEasyAnchorNode(jsonObject);
            if (StringUtils.isBlank(anchorNode)){
                return;
            }
            //递归调用
            getBaiduPanoPics(anchorNode,level-1);
        }
    }


    /**
     * @param curPanoId id
     * @param jsonObject json
     * @return list
     * 根据当前panoID、json响应,提取出前向道路中的panoId
     * 若遇到分叉(也可能是人行道),返回null
     */
    private List<String> getForwardNodes(String curPanoId, JSONObject jsonObject){
        JSONArray roadJsonArray = jsonObject.getJSONArray("Roads");
        JSONArray panoJsonArray = roadJsonArray.getJSONObject(0).getJSONArray("Panos");
        //总数组大小
        int nearByPanoIdSize = panoJsonArray.size();
        List<String> panoJsonIdList = new ArrayList<>();
        String panoJsonId;
        if (nearByPanoIdSize>1){
            for (int i=0; i < nearByPanoIdSize; i++){
                panoJsonId = panoJsonArray.getJSONObject(i).getString("PID");
                //将返回的最近路段panoId存入list
                panoJsonIdList.add(panoJsonId);
            }
            int currentPanoIdIndex = panoJsonIdList.indexOf(curPanoId);
            if (currentPanoIdIndex >= 0){
                return panoJsonIdList.subList(currentPanoIdIndex+1,nearByPanoIdSize);
            }else{
                System.out.println("当前节点不在附近节点集中!");
                // 返回什么?
            }
        }else if(nearByPanoIdSize==1){
            //前方遇到分叉路
            panoJsonId = panoJsonArray.getJSONObject(0).getString("PID");
            if (curPanoId.equals(panoJsonId)){
                return null;
            }else {
                panoJsonIdList.add(panoJsonId);
            }
        }else {
            logger.info("页面异常!");
        }
        return panoJsonIdList;
    }

    /**
     * @param jsonObject json
     * @return list
     * 根据指定panoId,获取下一段路各方向的起始panoId
     * 若路有分叉,返回多个,否则返回一个
     */
    private List<String> getAnchorNode(JSONObject jsonObject){
        //暂不判断方向
        //获取link字段中的数组
        JSONArray anchorIdArray = jsonObject.getJSONArray("Links");
        List<String> anchorLinks = new ArrayList<>();
        for (Object linkJson:anchorIdArray){
            JSONObject anchor = (JSONObject)linkJson;
            String anchorId = anchor.getString("PID");
            anchorLinks.add(anchorId);
        }
        return anchorLinks;
    }

    /**
     * 易出现环链
     * @param jsonObject
     * @return string
     */
    private String getEasyAnchorNode(JSONObject jsonObject){
        JSONArray anchorIdArray = jsonObject.getJSONArray("Links");
        int size = anchorIdArray.size();
        if (size==0){
            return null;
        }else if (size ==1){
            return ((JSONObject)anchorIdArray.get(0)).getString("PID");
        }else {
            int index = new Random().nextInt(anchorIdArray.size());
            return ((JSONObject)anchorIdArray.get(index)).getString("PID");
        }

    }

    /**
     * 也会产生环链
     * @param jsonObject
     * @return panoId
     */
    private String getAnchorNodeByDir(JSONObject jsonObject){
        JSONArray anchorIdArray = jsonObject.getJSONArray("Links");
        if (anchorIdArray.size()==1){
            return ((JSONObject)anchorIdArray.get(0)).getString("PID");
        }else{
            //选择dir值最小的那个,很可能是前向节点
            Map<Integer,String> map = new HashMap<>(4);
            int dirTemp = 400;
            for (Object linkJson:anchorIdArray){
                JSONObject anchor = (JSONObject)linkJson;
                Integer anchorDir = anchor.getInteger("DIR");
                String anchorPid = anchor.getString("PID");
                map.put(anchorDir,anchorPid);
                if (dirTemp>anchorDir){
                    dirTemp = anchorDir;
                }
            }
            return map.get(dirTemp);

        }
    }

    /**
     * 获取锚节点,尽量沿着同一条路走,但有时路况不能保证一定
     * 易出现环链
     * @param jsonObject
     * @return string
     */
    private String getAdvancedAnchorNode(JSONObject jsonObject){
        JSONArray anchorIdArray = jsonObject.getJSONArray("Links");
        if (anchorIdArray.isEmpty()){
            return null;
        }
        //若只有一个,直接返回
        if(anchorIdArray.size()==1){
            return ((JSONObject)anchorIdArray.get(0)).getString("PID");
        }
        //若有多个,为后续提取方便,用map存储诸锚节点
        Map<String,String> anchorMap = new HashMap<>(8);
        for (Object linkJson:anchorIdArray){
            JSONObject anchor = (JSONObject)linkJson;
            String anchorPid = anchor.getString("PID");
            String anchorRid = anchor.getString("RID");
            anchorMap.put(anchorRid, anchorPid);
        }
        //用list存储RoadBean信息
        JSONArray roadJsonArray = jsonObject.getJSONArray("Roads");
        List<RoadBean> roadBeanList = new ArrayList<>(8);
        for (Object roadJson:roadJsonArray){
            JSONObject road = (JSONObject)roadJson;
            String roadId = road.getString("ID");
            boolean isCurrent = road.getBoolean("IsCurrent");
            String roadName = road.getString("Name");
            RoadBean roadBean = new RoadBean(roadId,isCurrent,roadName);
            roadBeanList.add(roadBean);
        }
        if (roadBeanList.size()>1&&roadBeanList.get(0).isCurrent()){
            //当前位置节点所属街道名
            String currentStreetName = roadBeanList.get(0).getRoadName();
            for (int i = 1; i < roadBeanList.size(); i++){
                RoadBean roadBean = roadBeanList.get(i);
                // 尽量沿着同一条路前行
                if (currentStreetName.equals(roadBean.getRoadName())){
                    return anchorMap.get(roadBean.getRid());
                }
            }
            //若遍历完无同名道路, 选择links中的第一个
            return ((JSONObject)anchorIdArray.get(0)).getString("PID");
        }else {
            logger.info("道路异常或解析异常");
        }
        return null;
    }


    /**
     *
     * @param panoId id
     * @return JSONObject
     * 发送json请求,获取服务器json响应
     */
    private JSONObject sendPanoJsonRequest(String panoId){
        String suffixParam = "&pc=1";
        String url = REQUEST_PREX_FORWARD_PANOS + panoId + suffixParam;
        //发送json请求(可从中获取历史panoId及附近路段的panoId)
        String jsonPanoResponse = sendGetRequest(url);
        JSONObject jsonObject =  JSON.parseObject(jsonPanoResponse);
        // json数组对象
        JSONArray jsonArray = JSON.parseArray(jsonObject.get("content").toString());
        return jsonArray.getJSONObject(0);
    }

    /**
     * @param curPanoId
     * 根据curPanoId爬取街景图片下载到本地并存储于数据库
     */
    private void processStorePanoByID(String curPanoId,JSONObject jsonObject,String tableName){
        String curPanoDate = jsonObject.getString("Time");
        //下载当前街景当前图片存储于本地
        List<String> curPanoPicPath = downloadCurPanoPics(curPanoId);
        PanoPic panoPicBean = new PanoPic(curPanoId,curPanoDate,curPanoPicPath);
        //下载当前街景历史图片
        JSONArray timeLineJsonArray = jsonObject.getJSONArray("TimeLine");
        if (timeLineJsonArray.size()>1) {
            //为简便,有历史街景则选择第一项
            JSONObject timeLineJson = timeLineJsonArray.getJSONObject(1);
            if (!timeLineJson.getBooleanValue("IsCurrent")){
                String historyPanoId = timeLineJson.getString("ID");
                String timeLine = timeLineJson.getString("TimeLine");
                List<String> hisPanoPicPath = downloadHisPanoPics(curPanoId,historyPanoId);
                panoPicBean.setHisPanoId(historyPanoId);
                panoPicBean.setHisShootDate(timeLine);
                panoPicBean.setHisPicPath(hisPanoPicPath);
            }
        }
        //将本地图片存于数据库
        storePanoPicsToDb(panoPicBean,tableName);
    }


    private List<String> downloadCurPanoPics(String panoId){
        String localPath;
        List<String> picsRequestList = this.getPicsUrl(panoId);
        PicLoadUtils picLoadUtils = new PicLoadUtils();
        List<String> localPathList = new ArrayList<>(4);
        for (String picRequest:picsRequestList){
            localPath = picLoadUtils.downloadPic(picRequest, panoId);
            localPathList.add(localPath);
        }
        return localPathList;
    }

    private List<String> downloadHisPanoPics(String panoId,String hisPanoId){
        String localPath;
        List<String> picsRequestList = this.getPicsUrl(hisPanoId);
        PicLoadUtils picLoadUtils = new PicLoadUtils();
        List<String> localPathList = new ArrayList<>(4);
        for (String picRequest:picsRequestList){
            localPath = picLoadUtils.downloadPic(picRequest, panoId, hisPanoId);
            localPathList.add(localPath);
        }
        return localPathList;
    }

    private void storePanoPicsToDb(PanoPic panoPic,String tableName){
        //读本地图片存数据库
        BlobInsertUtils blobInsertUtils = new BlobInsertUtils(tableName);
        blobInsertUtils.insertAllImage2DBWithNoCheck(panoPic);
    }

    public String sendGetRequest(String url){
        okhttp3.Request request = new okhttp3.Request.Builder()
                .url(url).build();
        Response response;
        String result = null;
        try {
            response = OkHttpUtils.getInstance().newCall(request).execute();
            result = response.body().string();
            response.body().close();
        } catch (IOException e) {
            logger.error("发送请求失败--"+url);
            e.printStackTrace();
        }
        return result;
    }
}

       再编写测试代码:

public class ImageStoreTest {

    private static final Logger logger = Logger.getLogger(ImageStoreTest.class);

    public static void main(String[] args) {
        // 读取数据库记录存入list
        List<Pano> panoList = new MySQLHelper().getAllPanoFromDB();
        BaiduPanoPics baiduPanoPics = new BaiduPanoPics("baidu_pano_pics");
        for (Pano pano:panoList){
            String panoID = pano.getPanoId();
            logger.info("----------开始爬取起始id:"+ panoID+"-----------");
            baiduPanoPics.getBaiduPanoPics(panoID,50);
            logger.info("-----------"+panoID+"爬取结束!"+"-----------");
        }
    }
}

上述代码块中存储二进制流至MySQL相关逻辑BlobInsertUtils可查阅另一篇博客-----百度街景图片存MySQL

private void storePanoPicsToDb(PanoPic panoPic,String tableName){
        //读本地图片存数据库
        BlobInsertUtils blobInsertUtils = new BlobInsertUtils(tableName);
        blobInsertUtils.insertAllImage2DBWithNoCheck(panoPic);
    }

       这样就可以看到爬取结果存入数据库了,在这过程中,如果发生请求报错,很可能是因为JDK版本太低导致的发送https请求存在bug,需要升级到1.8.0_211以上版本即可。整个百度街景图片爬取就写到这里,欢迎留言交流。

4 遗留问题

  1. 在递归遍历的方法中会产生类似环形链表情况,针对此只能多做尝试了,选择可递归层次更多的起始id,这样也能满足本项目的基本需求;
  2. 理想情况下,锚节点的选择可以帮助我们只凭一个起始panoID就可沿着一条路走到底,但现实情况不是这样,锚节点选择需再优化;
  3. 大部分图片下载链接参数范围均为4-7,有些则不是(由西向东、由南至北有的是0-3),这个跟前进方向有关,因此最好根据不同的方向和路况信息设计更优的链接拼接规则,这就需要再更深层次的分析百度地图街景的相关接口了,但从爬取效果看,4-7的范围仍可获取不同角度的街景图片。

    1和2本质上还是因为没有获取百度地图各街道街景ID或百度坐标所致,经和GIS开发组同事讨论后终于找到了解决办法,即:通过OpenStreetMap获取城市路网数据,得到城市路网的谷歌坐标后,再进行百度坐标转换(这里提供一个坐标转换的测试网址),得到百度坐标后,即可通过发送上述请求1就能获取百度坐标系下的位置街景ID。有兴趣的朋友可移步另一篇路网数据获取的博客。
       若大牛有更好的方法解决街道坐标问题,欢迎交流!

参考

  • http://www.siyuanblog.com/?p=632
  • https://www.jianshu.com/p/3a0fa1e57ff6
【街景绿视率计算】基于百度街景和DeepLabv3Plus-Pytorch的街景绿视率计算,手把手教学 来了,今天给大家带了基于百度街景和DeepLabv3Plus-Pytorch的街景绿视率计算,过程比较复杂,但是现在这个应该是最简单而且准确率比较高的方式了,并且不需要标注样本,直接用模型的就可以~~ 阅读详情

相关推荐

百度地图街景图像批量获取

由于个人研究需要在最近试了一下使用API获取百度地图街景图片,在此介绍一下基于我个人需求的百度街景批量获取方法,本文以Python编码作为主要方法,如果对零编码工具有需要的可以关注一下UA城市分析小组刚刚发布的街景图片获取工具。 街景图像用作区域细粒度空间指标测度需要解决几个问题。 1. 首先就是调用额度的限制,现在百度对于个人认证开发者每天可用的街景图像调用额度为100张,在不考虑购买额度上限(1万张600元,其实有经费的前提下可以直接考虑钞能力)的情况下动辄几千上万张的爬取数量会大大增加需要爬取的时间周

boycetoon29的博客 1万+

基于GIS和Python的百度地图街景爬取

最近导师要求在一个城市设计项目里应用街景分析,于是学习并梳理了一下爬取百度地图街景的流程,简单做个总结。 1 百度全景API简介 先附上百度官方文档。 简单分析一下全景API的请求构成: "http://api.map.baidu.com/panorama/v2?ak=E4805d16520de693a3fe707cdc962045&width=512&height=256&amp...

flora的博客 1万+

基于python3的百度街景地图爬虫

# _*_ coding: utf-8 _*_ __author__ = 'Minty' import urllib from urllib.request import urlopen import threading from optparse import OptionParser from bs4 import BeautifulSoup import sys import re imp...

ZMT1849101245的专栏 7274

python百度地图API爬取街景

QGIS导入数据教程 一、在openstreetmap官网中获取目标区域的四个经纬度值 1、打开OSM官网,将地图定位在需要下载的区域 2、选择 导出----手动选择不同区域,在地图上会出现选择框,根据需要选好区域 二、用QGIS获取目标区域的开放街道数据,取得路网数据 1、打开QGIS选择 矢量----开放街道图----下载数据 2、范围----手动,复制OSM上的坐标,然后点击...

Mr_JK1031的博客 8513

街景图计算绿视率:百度地图爬取与语义分割实战

绿视率(Green View Index, GVI)是衡量城市街道绿色感知的核心指标,本质是图像中植被像素占有效街道区域的比例。其计算依赖两大基础:稳定获取高分辨率街景图像,以及鲁棒的像素级植被语义分割。传统HSV色彩阈值法在玻璃反射、阴影干扰、光照变化等真实场景下失效;而通用分割模型未经街景特化适配,易漏检苔藓、垂直绿化等小目标。本文聚焦工程落地瓶颈,详解如何绕过百度地图反爬机制获取高清街景URL,结合Deeplabv3轻量化改造、鱼眼畸变矫正、CRF后处理及多维度可信度验证,构建端到端GVI计算流水线。

weixin_33834137的博客 347

图像 存储csv_百度地图街景图像批量获取

由于个人研究需要在最近试了一下使用API获取百度地图街景图片,在此介绍一下基于我个人需求的百度街景批量获取方法,本文以Python编码作为主要方法,如果对零编码工具有需要的可以关注一下UA城市分析小组刚刚发布的街景图片获取工具。街景图片怎么获取?无需写代码,免费获取街景图片的软件来了街景图像用作区域细粒度空间指标测度需要解决几个问题。首先就是调用额度的限制,现在百度对于个人认证开发者每天...

weixin_35836501的博客 846

百度街景自动爬取,并自动检测没有影像的位置

今天百度街景的权限申请成功了,所以就试了下之前找到的一个爬取街景的代码,我把原作者导入数据时文件格式txt改成了xlsx,因为表格比较方便批量修改一些内容。 爬取到的街景影像有很多都没有数据,返回的是一些固定的图片,一个一个筛选太麻烦了,所以写了一段代码可以检测到这些影像,并且改名为特定格式,方便后续的一些筛选工作。 直接上代码 import urllib.request #打开网页模块 import urllib.parse #转码模块 import pand...

qq_41882857的博客 3355

从街景爬取到网页漫游:实战构建全景图应用

本文详细介绍了从街景爬取到网页漫游的全景图应用构建实战,包括数据获取、预处理、前端集成方案对比及企业级解决方案。重点解析了使用Photo Sphere Viewer和Three.js实现全景展示的技术细节,并提供了性能优化和移动端适配的实用技巧,帮助开发者快速构建高质量的全景漫游应用。

weixin_33719619的博客 203

百度地图街景获取

2️⃣ 可爬取所有历史街景、最新街景、或指定具体日期。街景图片命名方式一般为:日期_经度_纬度。1️⃣单个城市历史街景打包 300元/个(北上广深等一线城市除外),一个区50元。包括历年图片,每个采样点爬取前、后、左、右4个角度。3⃣️您无需提供任何材料,等待一到两天即可。全国街景图片实时采集(bdmap)

qq_45621904的博客 1438

精通百度地图街景图片提取脚本

百度地图街景提供了丰富的城市街道全景视图,这些图片数据通常是以高分辨率和高质量呈现的。它们通过一系列经纬度坐标以及相机方向参数进行标识,形成了一个连续的、可交互的街道景观。这些数据在互联网上以图片的形式展现,但百度地图并没有直接提供官方的API用于批量下载这些图片。Python使用def关键字定义函数。函数可以带有参数,并且可以返回一个值。# 示例代码:定义一个简单的函数超文本传输协议(HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。

weixin_28933797的博客 1444

百度地图街景图片提取脚本

输入经纬高即可提取百度地图中存在的街景图片

全景图的获取以及HTML页面显示全景图

全景图的获取(含爬虫)以及利用Photo Sphere Viewer在HTML页面浏览全景图。

m0_60404402的博客 6690

TwinCAT3入门教程3——PLC程序变量定义和硬件IO关联

目录 1、IO输入输出变量定义和硬件关联 1.1 IO输入 1.2 IO输出 1.3 PLC变量和IO模块关联 1.4 测试IO输出点 2、伺服变量关联 1、IO输入输出变量定义和硬件关联 1.1 IO输入 添加全局变量IO_Global 变量定义 (*==========================================IO输入===============================================================*)

panjinliang066333的博客 1万+

MIT Place Pulse数据集及google街景图片爬取

1、项目背景 1.1 使用谷歌街景图片的必要性 如果想要做 “建立街景图片与人主观感受的联系”相关场景,需使用 MIT Place Pulse 数据集。该数据集文件可直接下载,但没有提供街景图片本身,只提供了街景的坐标,需通过谷歌街景开放API 获取对应的街景图片。 MIT Place Pulse数据集中的街景图片大多在国外,因此需翻墙爬取。 1.2 使用谷歌街景图片的目标 “建立街景图片与...

Vincent2014Linux 9717

若依(RuoYi)项目里,Quartz定时任务踩坑实录:并发控制、事务回滚与异常处理

本文深入解析若依(RuoYi)项目中Quartz定时任务的并发控制、事务回滚与异常处理三大核心问题。通过实战案例揭示@DisallowConcurrentExecution注解的局限性和Redis分布式锁的应用,详细讲解Spring事务集成方案及多数据源事务管理,并提供生产级监控体系搭建指南,帮助开发者规避Quartz在生产环境中的常见陷阱。

weixin_28731223的博客 413

基于单片机的自动喷淋系统设计【毕业设计】

随着人民生活质量提高与生活节奏的加快,城市居民对于花草绿植的家庭培养与装饰需求增加,随之因浇灌植物的需要,智能浇花系统的市场逐渐扩大。现有自动浇花系统大部分只能通过设置固定浇水时间而难以实现适时、适量浇水,或在植物环境土壤含水量低时进行报警而并不能实际补水。为克服局限性而设计以单片机为控制中心的自动浇花系统。通过湿度传感器检测目标植物环境土壤湿度数据,再经ADCO832将其放大转换为数字量,由单片机读取输出向LCD1602 显示屏显示并通过比对键盘向单片机所设置的湿度范围而利用中断服务程序进行补水需求判断。

qq_48771816的博客 1712

网络时空大数据爬取与分析DAS系统(街景图片采集与绿视率分析)

手把手教 | 网络时空大数据爬取与分析DAS系统(街景图片采集与绿视率分析) 原创 DAS Team 双评价DAS 7月2日 地理计算语言,为大众赋能地理智慧。 ——DAS Team 爬取分析DAS系统下载 地址: https://pan.baidu.com/s/12MLx3vt-ctNPr6HZ3CumbA 提取码:3vxx Tips:目前的DAS系统支持ArcGIS 10.2-10.8、Word 2010以上的所有版本。 什么是街景图片 街景图片是一种实景地图服务,通过城市街道的360度全景图像为用户提

zhangjisong的博客 7334

从零到一:用Verilog和Quartus构建智能交通灯系统的实战指南

本文详细介绍了如何使用Verilog和Quartus Prime从零开始构建智能交通灯系统。通过分步指导,包括状态机设计、时序控制、数码管显示等核心模块实现,并结合ModelSim仿真验证,帮助FPGA初学者掌握数字电路设计的关键技术。文章还提供了实际部署到FPGA开发板的调试技巧和进阶优化方案。

weixin_29172201的博客 477

百度街景图片存MySQL

1 引言        本文记录百度街景图片的存储过程,主要逻辑是:读取本地图片,存mysql,代码直接下载稍作修改就可运行,存储相关使用的是Java原生接口。更多代码以及百度街景图片相关背景参考这里。 2 建表        表字段主要分为两部分:当前街景部分和历史街景部分,每部分都包含4大属性:id、拍摄日期、图片...

Vincent2014Linux 1861

YOLOv13手把手添加YOLOv13注意力机制:Biformer(BiLevelRoutingAttentio)YOLOv13双层路由注意力 革命性的高效注意力机制深度解析YOLOv13YOLOv1

YOLOv13 BiLevelRoutingAttention是一种革命性的高效注意力机制,YOLOv13通过双层路由策略显著降低了计算复杂度。YOLO它将注意力计算分为区域级路由和令牌级注意力两个层次,前者选择最相关的K个区域,后者仅在选定区域内执行注意力计算。这种设计借鉴了人类视觉系统的分层处理机制,在保持长距离依赖建模能力的同时,将复杂度从O(N²)降至O(NK + P⁴)。该机制还集成了可微分路由、自适应参数和LePE位置编码等创新技术,YOLOv13 YOLOv13 YOLOv13 YOLOv13

FJN110的博客 660

18006.STM32通过SPI读取LAN9253数据

【代码】18006.STM32通过SPI读取LAN9253数据。

写给自己的笔记 556

街景爬虫全流程解析(无需AK,无限制爬取!!)

详细拆解街景爬虫的全部流程,从爬虫到全景图投影,仅用于学习交流!!!

2301_76257981的博客 1363

环保 HJ212协议解析

由于是做环保相关的,有时需要对212协议进行拆包和解包。HJ212协议是一种字符串协议,数据传输通讯包主要由包头、数据段长度、数据段、CRC校验、包尾组成,其中“数据段”内容包括请求编码、系统编码、命令编码、密码、设备唯一标识、总包数、包号、指令参数。请求编码为请求的时间戳,系统编码ST统一规定为22,命令编码CN为该数据包的时间类型,访问密码、设备唯一标识在对接时由平台提供,指令参数为数据内容。通讯协议的数据结构如图4所示。 图4 通讯协议的数据结构 6.1.1通讯包结构组成 名称 类型 长度

ccf19881030的专栏 2万+

python调用百度地图可视化_python百度地图API爬取街景-Go语言中文社区

QGIS导入数据教程一、在openstreetmap官网中获取目标区域的四个经纬度值1、打开OSM官网,将地图定位在需要下载的区域2、选择 导出----手动选择不同区域,在地图上会出现选择框,根据需要选好区域二、用QGIS获取目标区域的开放街道数据,取得路网数据1、打开QGIS选择 矢量----开放街道图----下载数据2、范围----手动,复制OSM上的坐标,然后点击确定3、添加矢量,导入数据,...

weixin_39639919的博客 700

【AutoSAR】【WdgM】WdgM安全管理模块

一、WdgM在AutoSAR里的整体架构 WdgM是在autosar架构里位于服务层的一个基础软件模块;主要监督程序的执行; 主要包括三个模块: 看门狗管理器(WdgM) 看门狗接口(WdgIf) 看门狗驱动(Wdg) 二、三种监督机制 Alive监控-------主要监控周期性软件程序的时间 DeadLine监控-------主要监控非周期软件程序 程序流监控-------主要监控程序流执行的正确性 三、基于全局状态的检测流程 受监管实体(SE):Supervised Ent

专注汽车软件开发、AutoSAR、车载以太网、SOA、EE架构。 2993

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

上一篇: 百度街景图片存MySQL
下一篇: springboot 对HDFS的文件操作
zhg_vincent
博客等级 码龄12年 93粉丝 45原创
评论 43
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值