zoukankan      html  css  js  c++  java
  • Windows下用Caffe跑自己的数据(遥感影像)

    1 前言

    Caffe对于像我这样的初学者来说是一款非常容易上手的深度学习框架。关于用Caffe跑自己的数据这样的博客已经非常多,感谢前辈们为我们提供的这么好的学习资源。这里我主要结合我所在的行业,说下如何对跑通具有多通道多格式的遥感数据。

    2 数据准备

    Caffe封装的非常好,要想将我们的数据运用于Caffe上,我们唯一要做的工作就是准备好Caffe支持的数据输入格式(leveldb/lmdb)。

    Caffe解决方案下有一个工程convert_imageset为我们提供了接口,主要是将图像文件转化为 Caffe支持的两种数据格式。工程实现数据格式转换主要经过以下几个步骤:

    在细读这个工程文件是会发现,其数据读取函数用的是OpenCV 的imread函数,在io.cpp。关于OpenCV的imread函数,这里不做详细介绍,只说出其存在的问题:

    1 对于图像文件,imread不能读取多波段数据(遥感图像),超过4个波段的;

    2 读取的数据格式默认是CV_8UC(n),遥感数据明显不符合要求。

    因此,要想通过Caffe自带的数据集转换接口将多波段多数据类型的遥感图像输出为Caffe支持的leveldb和lmdb格式存在明显的不合理问题。

    关于遥感图像的读取,我想大家第一反应就是GDAL库。因此,我尝试在Caffe的解决方案下重写数据转换接口,利用GDAL库来读图像,并将读取的数据转换为OpenCV的Mat数据格式,从而和图1中流程的第二步接轨(GDAL数据读取转换为Mat格式可参看前面的博客)。但是后来一想,感觉这样做有点多余,为啥不通过GDAL读取的数据直接写入到Caffe::Datum中呢。

    后来仔细看了Caffe::Datum类,发现其存储数据目前只支持uchar和float,如果读者愿意,我想还可以给Datum类添加其他的支持数据格式。但是,我觉得float格式已经满足我的要求了。因此,我写了一个简单的函数,实现从GDAL读取的数据到Datum的转化,其代码如下:

     1 bool ReadImageToDatum(const std::string &imgfilename,
     2     const int label,
     3     Datum &datum)
     4 {
     5     GDALAllRegister();
     6     GDALDataset *poRemoteSensingImageDS = (GDALDataset*)GDALOpen(imgfilename.c_str(),GA_ReadOnly);
     7     datum.set_channels(kRemoteSensingBandNums);
     8     datum.set_height(kRemoteSensingSize);
     9     datum.set_width(kRemoteSensingSize);
    10     datum.set_label(label);
    11     datum.clear_data();
    12     datum.clear_float_data();
    13     datum.set_encoded(false);
    14     int *data = new int[kRemoteSensingImageNBytes];
    15     int *pBandMap = new int[kRemoteSensingBandNums];
    16     for (int b = 0; b < kRemoteSensingBandNums; b++){
    17         pBandMap[b] = b + 1;
    18     }
    19     GDALDataType ty = poRemoteSensingImageDS->GetRasterBand(1)->GetRasterDataType();
    20     poRemoteSensingImageDS->RasterIO(GF_Read, 0, 0, kRemoteSensingSize, kRemoteSensingSize,
    21         data, kRemoteSensingSize, kRemoteSensingSize, ty, kRemoteSensingBandNums,
    22         pBandMap, sizeof(int), kRemoteSensingSize*sizeof(int), 
    23         kRemoteSensingSize*kRemoteSensingSize*sizeof(int));
    24     for (int i = 0; i < kRemoteSensingImageNBytes; i++){
    25         datum.add_float_data((float)data[i]);
    26     }
    27     delete[]data; data = nullptr;
    28     delete[]pBandMap; pBandMap = nullptr;
    29     GDALClose((GDALDatasetH)poRemoteSensingImageDS);
    30     return 1;
    31 };

    这里有个细节问题需要说下:因为我不大算动图1第三步中的Caffe::Datum--》leveldb/lmdb这个过程,所以GDAL读取的数据顺序需要与Mat中图像的存储格式一样。Mat默认数据存储格式是:BIP,及按像元保存,即先保存第一个波段的第一个像元,之后保存第二波段的第一个像元,依次保存存储。因此,在用GDAL读取图像的时候也应该用BIP格式读取,确保一致。到此遥感数据集的转换工作基本完成。我们可以将具有多波段和多数据类型的遥感数据顺利的保存为leveldb或者lmdb。我想其他的数据类型也可参考类似的方法。可以自己制作一个统一的二进制文件格式,然后轻松实现转换。

    3 均值计算

    这一步没有需要改动的地方,compute_image_mean 工程提供的接口完全可以支持之前Datum中的uchar和float两种数据格式。

     1     if (data.size() != 0) {
     2       CHECK_EQ(data.size(), size_in_datum);
     3       for (int i = 0; i < size_in_datum; ++i) {
     4         sum_blob.set_data(i, sum_blob.data(i) + (uint8_t)data[i]);
     5       }
     6     } else {
     7       CHECK_EQ(datum.float_data_size(), size_in_datum);
     8       for (int i = 0; i < size_in_datum; ++i) {
     9         sum_blob.set_data(i, sum_blob.data(i) +
    10             static_cast<float>(datum.float_data(i)));
    11       }
    12     }

    4 模型训练

    这个过程也没有需要改动的,设置好网络参数,利用Caffe.exe提供的接口就可以顺利的完成模型的训练工作。

    5 分类

    分类同样存在之前数据准备中出现的问题,因此,还是要重写classification工程,主要在于图像的读取部分,并将用GDAL读取的数据,转化为Mat的多通道数据。具体不说了,上传部分代码供大家参考:

                float *readPatchImage = new float[kRemoteSensingSize*kRemoteSensingSize*bandNums];
                int leftX = colIndex - constWidth;
                if (leftX + kRemoteSensingSize > width) leftX = width - kRemoteSensingSize - 1;
                poRemoteSensingImageDS->RasterIO(GF_Read, leftX, leftY, kRemoteSensingSize, kRemoteSensingSize,
                    readPatchImage, kRemoteSensingSize, kRemoteSensingSize, GDT_Float32, bandNums,
                    pBandMap, bandNums*sizeof(float), bandNums*kRemoteSensingSize*sizeof(float),sizeof(float));
                cv::Mat img = cv::Mat(kRemoteSensingSize, kRemoteSensingSize, CV_32FC(bandNums), readPatchImage);
                std::vector<Prediction> predictions = classifier.Classify(img);
                if (predictions[0].first == "0")
                {
                    std::cout << "-----Find Suspicious Chinmey:    Probability:" << predictions[0].second << std::endl;
                    std::cout << "                                 Position:leftX:" << leftX << "  leftY:" << leftY << std::endl;
                    std::cout << std::endl;
                    unsigned char* buf = new unsigned char[kRemoteSensingSize*kRemoteSensingSize];
                    int i = 0;
                    while (i < kRemoteSensingSize*kRemoteSensingSize) 
                        buf[i++] = 1;
                    poOutBand->RasterIO(GF_Write, leftX, leftY, kRemoteSensingSize, kRemoteSensingSize, buf,
                        kRemoteSensingSize, kRemoteSensingSize, GDT_Byte, 0, 0);
                    delete[]buf; buf = nullptr;
                
                }
                delete[]readPatchImage; readPatchImage = nullptr;
            }
  • 相关阅读:
    移动平台开发需要的记录(续)
    移动平台开发需要的记录
    Chrome浏览器被劫持
    记录一个GO安装问题
    记录开发环境配置 MySql(5.7.17)+EF6.x+VS2017(15.7.1)
    win7(x64)下安装cocos2d并编译安卓项目
    Xocde一次版本升级遇到的问题 (Code Sign Error)
    【转】SqlServer将没有log文件的数据库文件附加到服务器中
    [转]IIS6.0迁移至IIS7.0
    重置VS设置
  • 原文地址:https://www.cnblogs.com/zyore2013/p/6080675.html
Copyright © 2011-2022 走看看