caffe2 ios部署

一、caffe2 ios 工程编译配置

关于caffe2的ios编译及部署,github上已经有比较成熟的代码,这里选择基于该作者的工作进行修改部署。
https://github.com/KleinYuan/Caffe2-iOS
按照作者给出的步骤,进行git clone和编译

brew install git-lfs
git lfs install
git lfs clone https://github.com/KleinYuan/Caffe2-iOS

完成后进入src目录,调用setup.sh,将会自动下载caffe2并编译caffe2的ios库

cd Caffe2-iOS/src/caffe2-ios
bash ./setup.sh

上个步骤大概耗费20~30分钟左右。
完成后,会发现在src目录中,新建了一个caffe2的目录,里面包含caffe2原始文件和编译好的库。由于作者已经在工程中配置好了一切,所以只需要打开xcode工程进行测试即可。
打开src/caffe2-ios/caffe2-ios.xcodeproj进行编译,我这里出现错误,提示caffe2.pb.h和predictor_consts.pb.h找不到。如果上面的安装过程正常的话,应该会在src/caffe2/caffe2/proto路径下调用protobuf的protoc进行生成的,但是我上面的安装过程里跳过了这一步,没关系,可以手动生成。不管你系统是否已经安装了protobuf,建议直接使用src/caffe2/build_host_protoc/bin里的protoc进行生成,否则可能会有版本不一致的错误:

cd src/caffe2/caffe2/proto
../../build_host_protoc/bin/protoc caffe2.proto --cpp_out=./
../../build_host_protoc/bin/protoc predictor_consts.proto --cpp_out=./

生成后,重新编译xcode工程,编译成功即可运行。
demo里作者给出了两个caffe2模型:squeezenet和tinyyolo,我在iphone6sp上测时间分别为35ms/frame及52ms/frame

二、转换自己的caffe模型并部署到ios上

2.1 编译安装caffe2

在上一步中,测试了作者给出的caffe2模型,但如果要将自己的caffe模型部署到ios上呢?
caffe2很方便地提供了转换的接口,在使用该接口前,需要编译安装caffe2(上一步中只是编译了caffe2的iOS库)
进入caffe2目录并新建build文件夹

cd src/caffe2
mkdir build && cd build
cmake -DUSE_CUDA=OFF ..
sudo make install

安装成功后,caffe2相关的库和python库将被安装在/usr/local中,当然可以在cmake的时候指定安装目录
将/usr/local添加到python环境变量中

export PYTHONPATH=/usr/local

完成后可以测试一下看是否有错误

cd ~ && python -c 'from caffe2.python import core' 2>/dev/null && echo "Success" || echo "Failure"

2.2 转换caffe模型到caffe2模型

调用python接口caffe_translator转换caffe2模型

python caffe2/python/caffe_translator.py deploy.prototxt test_iter_83000.caffemodel --init_net=testinit.pb --predict_net=testpredict.pb

最终需要用到的就是转换后的模型testinit.pb和testpredict.pb

2.3 在ios上应用caffe2模型

在工程中加入转换好的模型
声明caffe2对象并

@property (nonatomic, strong) Caffe2 *caffe2;
self.caffe2 = [[Caffe2 alloc] init:@"init" predict:@"predict" error:&err];

预测一副图像

NSMutableArray *ret = [self.caffe2 predictwithmat:resimg withMean: &mean_val withScale:1.f];

这里的predict接口函数是自己实现的,可以支持输入opencv图像和均值减除和归一化等预处理

- (nullable NSArray*) predictwithmat:(cv::Mat) image withMean: (float*)mean_val withScale: (float) scale
{
    NSMutableArray* result = nil;
    caffe2::Predictor::TensorVector output_vec;

    if (self.busyWithInference) {
        return nil;
    } else {
        self.busyWithInference = true;
    }

    UInt8* pixels = (UInt8*) image.data;
    caffe2::TensorCPU input;
    size_t  w = image.cols, h = image.rows ;
    // Reasonable dimensions to feed the predictor.
    const int predHeight = (int)CGSizeEqualToSize(self.imageInputDimensions, CGSizeZero) ? int(h) : self.imageInputDimensions.height;
    const int predWidth = (int)CGSizeEqualToSize(self.imageInputDimensions, CGSizeZero) ? int(w) : self.imageInputDimensions.width;
    const int crops = 1;
    const int channels = image.channels();
    const int size = predHeight * predWidth;

    std::vector<float> inputPlanar(crops * channels * predHeight * predWidth);

    if (channels > 1) //bgr img
    {
        for (int c = 0; c < channels; c++)
        {
            for (int i = 0; i < predHeight; i++)
            {
                for (int j = 0; j < predWidth; j++)
                {
                    inputPlanar[i * predWidth + j + c * size] = scale * float(image.at(i, j)[c] - mean_val[c]);
                }
            }
        }
    }
    else{
        for (int i = 0; i < predHeight; i++)
        {
            for (int j = 0; j < predWidth; j++)
            {
                inputPlanar[i * predWidth + j ] = scale * float(image.at(i, j) - mean_val[0]);
            }
        }

    }

    input.Resize(std::vector<int>({crops, channels, predHeight, predWidth}));
    input.ShareExternalPointer(inputPlanar.data());

    caffe2::Predictor::TensorVector input_vec{&input};
    NSTimeInterval start = [[NSDate date] timeIntervalSince1970];
    _predictor->run(input_vec, &output_vec);
    NSTimeInterval end = [[NSDate date] timeIntervalSince1970];

    NSLog(@"elapsed time %f" , (end-start)*1000);
    if (output_vec.capacity() > 0) {
        for (auto output : output_vec) {
            // currently only one dimensional output supported
            result = [NSMutableArray arrayWithCapacity:output_vec.size()];
            for (auto i = 0; i < output->size(); ++i) {
                result[i] = @(output->template data<float>()[i]);
                NSLog(@"%@", result[i]);
            }
        }
    }

    self.busyWithInference = false;
    return result;
}

你可能感兴趣的:(iOS,学习,深度学习)