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;
            }
  • 相关阅读:
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 454 四数相加 II
    Java实现 LeetCode 454 四数相加 II
    Java实现 LeetCode 454 四数相加 II
    FFmpeg解码H264及swscale缩放详解
    linux中cat more less head tail 命令区别
    C语言字符串操作总结大全(超详细)
    如何使用eclipse进行嵌入式Linux的开发
  • 原文地址:https://www.cnblogs.com/zyore2013/p/6080675.html
Copyright © 2011-2022 走看看