Atlas 500 Docker镜像安装ffmpeg+OpenCV环境

举报
jackwangcumt 发表于 2021/11/23 16:02:48 2021/11/23
【摘要】 本文重点讲解如何在Atlas 500 智能小站上基于Docker镜像来安装ffmpeg和OpenCV环境,并运行从rstp获取视频流,用om模型进行分类和输出分类结果的示例。

1 ffmpeg概述


     根据百度百科的定义,ffmpeg是开源的音视频处理库,它可以用来记录、转换数字音频、视频,并能将其转化为流的计算机程序。它包含了非常先进的音频/视频编解码库libavcodec。ffmpeg视频采集功能非常强大,不仅可以采集视频采集卡或USB摄像头的图像,还可以进行屏幕录制,同时还支持以RTP方式将视频流传送给支持RTSP的流媒体服务器,支持直播应用。ffmpeg库主要包含如下的核心组件:
ffmpeg:用于格式转换、解码或电视卡即时编码等
ffsever:一个 HTTP 多媒体即时广播串流服务器
ffplay:简单的播放器,使用ffmpeg 库解析和解码
libavformat:用于各种音视频封装格式的生成和解析
libavcodec:用于各种类型声音/图像编解码
libavutil:公共的工具函数
libswscale:用于视频场景比例缩放、色彩映射转换
libpostproc:用于后期效果处理
      下面给出常见的ffmpeg命令:

#图片序列合成视频
ffmpeg -f images -i img%d.jpg myvideo.mpg
#将视频分解成图片序列
ffmpeg -i myvideo.mpg image%d.jpg
#从视频提取声音,存为demo.mp3
ffmpeg -i source_video.avi -vn -ar 44100 -ac 2 -ab 192 -f mp3 demo.mp3
#视频格式转换
ffmpeg -i video_src.mpg video_dest.avi
#将.avi转成gif动画
ffmpeg -i video_dest.avi anime.gif
#合成视频和音频
ffmpeg -i demo.wav -i video_src.avi video_dest.mpg

2 OpenCV概述


      根据百度百科的定义,OpenCV是一个基于Apache2.0协议的开源跨平台计算机视觉和机器学习软件库,它可以运行在Linux、Windows、Android和Mac OS操作系统上。它提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。OpenCV用C++语言编写,轻量且高效,主要倾向于实时视觉应用。 当前在计算机视觉项目中,经常需要用到OpenCV,特别是Python版的接口。

3 Docker镜像安装ffmpeg+OpenCV环境


     之前的一篇博客《华为Atlas 500小站Docker镜像制作》介绍了如何制作Atlas 500 Docker镜像,下面的安装是基于之前的Docker镜像。首先以admin登录Atlas 500 智能小站,进入开发者模式(develop)。这里我们需要启动Docker镜像,在启动之前,需要查看一下当前的Docker镜像列表,命令如下:

Euler:~ # docker images

    输出结果如下:
1.jpg
     从上图输出结果可以看出,制作的Docker 镜像 workload-image:v1.0 在列表中。下面将基于此镜像来创建容器,命令如下:

docker run \
--device=/dev/davinci0 \
--device=/dev/davinci_manager \
--device=/dev/hisi_hdc \
--device /dev/devmm_svm \
-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
-v /home/data/miniD/driver/lib64:/home/data/miniD/driver/lib64 \
-v /run/board_cfg.ini:/run/board_cfg.ini \
-it workload-image:v1.0 bash

     执行结果如下所示:

2.jpg

    如果执行成功,会进入Docker容器的交互环境,这里可以执行Linux相关命令,其中的用户为root,而容器ID为e9c222179267 。这个ID后续的启动和停止容器等操作都会用到。此时容器中的Python环境还未安装,下面首先安装Python环境和相关依赖项,命令如下:

apt-get update
apt-get install python3.7
apt-get install python3-pip
apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk

    执行成功后,系统中会有python3.6的命令,没错就是python3.6 。继续执行如下命令安装:

python3.6 -m pip install --upgrade pip --user -i https://mirrors.huaweicloud.com/repository/pypi/simple
python3.6 -m pip install Cython numpy pillow tornado==5.1.0 protobuf  \
--user -i https://mirrors.huaweicloud.com/repository/pypi/simple

     安装编辑器vim,命令如下:

apt-get install vim

     安装Python OpenCV 库,命令如下:

apt-get install python3-opencv

      下面介绍如何安装ffmpeg,这里的安装可以按照如下网址从源码进行编译安装,这个过程比较耗时,估计要1~2小时左右。参考网址如下:  https://gitee.com/ascend/samples/blob/master/cplusplus/environment/opencv_install/README_300_CN.md

      下面是按照上述文档将编译后的库进行打包成 ascend_ddk.tar.gz ,它将开发环境安装的ffmpeg、opencv库导入运行环境中,以提供运行使用。执行的操作如下:

mkdir $HOME/ascend_ddk
scp -r HwHiAiUser@X.X.X.X:/home/HwHiAiUser/ascend_ddk/x86 $HOME/ascend_ddk
scp -r HwHiAiUser@X.X.X.X:/usr/lib/x86_64-linux-gnu/lib* $HOME/ascend_ddk/x86/lib

     下面给出依赖性文件,如下所示:

PyAV.tar.gz
ascend_ddk.tar.gz (编译好的库打包)

     其中的PyAV可以从下面的网址进行克隆源码,命令如下:

git clone https://gitee.com/mirrors/PyAV.git

     将文件从Atlas 500小站拷贝到当前容器中,命令如下:

docker cp /opt/mount/docker05/ascend_ddk.tar.gz  e9c222179267:/root/
docker cp /opt/mount/docker05/PyAV.tar.gz  e9c222179267:/root/

    上传成功后,可以在容器/root目录下查看:

3.jpg

    解压安装,命令如下:

tar zxvf ascend_ddk.tar.gz
#################################################
pip3 install Cython
apt-get install pkg-config libxcb-shm0-dev libxcb-xfixes0-dev
#使opencv能找到ffmpeg
cp /root/ascend_ddk/x86/lib/pkgconfig/* /usr/share/pkgconfig
#################################################
tar zxvf PyAV.tar.gz
cd PyAV
python3.6 setup.py build --ffmpeg-dir=/root/ascend_ddk/x86
python3.6 setup.py install

   如果编译安装PyAV过程中报如下错误,则可以尝试的解决方案如下:

编译PyAv报错:
   Could not find libavdevice with pkg-config.
   Could not find libavfilter with pkg-config.
解决方法:
   步骤1.  cp /root/ascend_ddk/x86/lib/pkgconfig/* /usr/share/pkgconfig/ 
   步骤2.  export PKG_CONFIG_PATH=/usr/share/pkgconfig/

   编译PyAV稍微有点耗时,请耐心等待。等成功安装完成后,输出信息如下所示:

#xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Installing pyav script to /usr/local/bin

Installed /usr/local/lib/python3.6/dist-packages/av-8.0.4.dev0-py3.6-linux-aarch64.egg
Processing dependencies for av==8.0.4.dev0
Finished processing dependencies for av==8.0.4.dev0

成功安装后,还需要配置环境变量,具体操作如下所示:

vim /etc/ld.so.conf.d/ffmpeg.conf

添加如下内容:

/root/ascend_ddk/x86/lib

命令行执行如下命令,配置生效:

ldconfig

继续配置环境变量:

vim /etc/profile

在末尾追加如下内容:

export PATH=$PATH:/root/ascend_ddk/x86/bin
export PYTHONPATH=/home/data/miniD/driver/lib64:$PYTHONPATH
export LD_LIBRARY_PATH=/home/data/miniD/driver/lib64:$LD_LIBRARY_PATH

命令行执行如下命令,配置生效:

source /etc/profile

下面需要验证一下是否可以正常运行,命令如下:

cd ~
python3.6
################Python环境下############
import av
import cv2

 正确执行,界面如下所示:

5.jpg

如果执行import av报如下错误,则检查环境变量是否配置正确,执行如下操作:

ImportError: libavcodec.so.58: cannot open shared object file: No such file or directory

如果import命令都可以正确执行,没有报错信息,则说明ffmpeg 、OpenCV和PyAV都已经安装成功。首先退出当前Docker容器,重新启动容器:

root@e9c222179267:~/dist# exit
exit
Euler:~ # docker ps -a
CONTAINER ID        IMAGE                 COMMAND 
e9c222179267        workload-image:v1.0   "bash"     

Euler:~ # docker start e9c222179267
e9c222179267
Euler:~ # docker attach e9c222179267
root@e9c222179267:~#

在容器中输入如下命令,查看当前npu信息:

root@e9c222179267:~# npu-smi info

界面如下所示:

6.jpg

下面给出官网示例的修改版,从摄像头用rstp协议拉取视频流,并通过resnet50.om 模型的分类,来确定当前的视频中是否有dog,且识别dog的种类。 可以代码如下所示:

# Copyright 2020 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import cv2
import datetime

import argparse
import numpy as np
import acl
import os
from PIL import Image
from constant import ACL_MEM_MALLOC_HUGE_FIRST, \
    ACL_MEMCPY_HOST_TO_DEVICE, ACL_MEMCPY_DEVICE_TO_HOST, \
    ACL_ERROR_NONE, IMG_EXT, NPY_FLOAT32

buffer_method = {
    "in": acl.mdl.get_input_size_by_index,
    "out": acl.mdl.get_output_size_by_index
    }


def check_ret(message, ret):
    if ret != ACL_ERROR_NONE:
        raise Exception("{} failed ret={}"
                        .format(message, ret))


class Net(object):
    def __init__(self, device_id, model_path):
        self.device_id = device_id      # int
        self.model_path = model_path    # string
        self.model_id = None            # pointer
        self.context = None             # pointer

        self.input_data = []
        self.output_data = []
        self.model_desc = None          # pointer when using
        self.load_input_dataset = None
        self.load_output_dataset = None

        self.init_resource()

    def __del__(self):
        print("Releasing resources stage:")
        ret = acl.mdl.unload(self.model_id)
        check_ret("acl.mdl.unload", ret)
        if self.model_desc:
            acl.mdl.destroy_desc(self.model_desc)
            self.model_desc = None

        while self.input_data:
            item = self.input_data.pop()
            ret = acl.rt.free(item["buffer"])
            check_ret("acl.rt.free", ret)

        while self.output_data:
            item = self.output_data.pop()
            ret = acl.rt.free(item["buffer"])
            check_ret("acl.rt.free", ret)

        if self.context:
            ret = acl.rt.destroy_context(self.context)
            check_ret("acl.rt.destroy_context", ret)
            self.context = None

        ret = acl.rt.reset_device(self.device_id)
        check_ret("acl.rt.reset_device", ret)
        ret = acl.finalize()
        check_ret("acl.finalize", ret)
        print('Resources released successfully.')

    def init_resource(self):
        print("init resource stage:")
        ret = acl.init()
        check_ret("acl.init", ret)
        ret = acl.rt.set_device(self.device_id)
        check_ret("acl.rt.set_device", ret)

        self.context, ret = acl.rt.create_context(self.device_id)
        check_ret("acl.rt.create_context", ret)

        # load_model
        self.model_id, ret = acl.mdl.load_from_file(self.model_path)
        check_ret("acl.mdl.load_from_file", ret)
        print("model_id:{}".format(self.model_id))

        self.model_desc = acl.mdl.create_desc()
        self._get_model_info()
        print("init resource success")

    def _get_model_info(self,):
        ret = acl.mdl.get_desc(self.model_desc, self.model_id)
        check_ret("acl.mdl.get_desc", ret)
        input_size = acl.mdl.get_num_inputs(self.model_desc)
        output_size = acl.mdl.get_num_outputs(self.model_desc)
        self._gen_data_buffer(input_size, des="in")
        self._gen_data_buffer(output_size, des="out")

    def _gen_data_buffer(self, size, des):
        func = buffer_method[des]
        for i in range(size):
            # check temp_buffer dtype
            temp_buffer_size = func(self.model_desc, i)
            temp_buffer, ret = acl.rt.malloc(temp_buffer_size,
                                             ACL_MEM_MALLOC_HUGE_FIRST)
            check_ret("acl.rt.malloc", ret)

            if des == "in":
                self.input_data.append({"buffer": temp_buffer,
                                        "size": temp_buffer_size})
            elif des == "out":
                self.output_data.append({"buffer": temp_buffer,
                                         "size": temp_buffer_size})

    def _data_interaction(self, dataset, policy=ACL_MEMCPY_HOST_TO_DEVICE):
        temp_data_buffer = self.input_data \
            if policy == ACL_MEMCPY_HOST_TO_DEVICE \
            else self.output_data
        if len(dataset) == 0 and policy == ACL_MEMCPY_DEVICE_TO_HOST:
            for item in self.output_data:
                temp, ret = acl.rt.malloc_host(item["size"])
                if ret != 0:
                    raise Exception("can't malloc_host ret={}".format(ret))
                dataset.append({"size": item["size"], "buffer": temp})

        for i, item in enumerate(temp_data_buffer):
            if policy == ACL_MEMCPY_HOST_TO_DEVICE:
                ptr = acl.util.numpy_to_ptr(dataset[i])
                ret = acl.rt.memcpy(item["buffer"],
                                    item["size"],
                                    ptr,
                                    item["size"],
                                    policy)
                check_ret("acl.rt.memcpy", ret)

            else:
                ptr = dataset[i]["buffer"]
                ret = acl.rt.memcpy(ptr,
                                    item["size"],
                                    item["buffer"],
                                    item["size"],
                                    policy)
                check_ret("acl.rt.memcpy", ret)

    def _gen_dataset(self, type_str="input"):
        dataset = acl.mdl.create_dataset()

        temp_dataset = None
        if type_str == "in":
            self.load_input_dataset = dataset
            temp_dataset = self.input_data
        else:
            self.load_output_dataset = dataset
            temp_dataset = self.output_data

        for item in temp_dataset:
            data = acl.create_data_buffer(item["buffer"], item["size"])
            _, ret = acl.mdl.add_dataset_buffer(dataset, data)

            if ret != ACL_ERROR_NONE:
                ret = acl.destroy_data_buffer(data)
                check_ret("acl.destroy_data_buffer", ret)

    def _data_from_host_to_device(self, images):
        print("data interaction from host to device")
        # copy images to device
        self._data_interaction(images, ACL_MEMCPY_HOST_TO_DEVICE)
        # load input data into model
        self._gen_dataset("in")
        # load output data into model
        self._gen_dataset("out")
        print("data interaction from host to device success")

    def _data_from_device_to_host(self):
        print("data interaction from device to host")
        res = []
        # copy device to host
        self._data_interaction(res, ACL_MEMCPY_DEVICE_TO_HOST)
        print("data interaction from device to host success")
        result = self.get_result(res)
        self._print_result(result)

    def run(self, images):
        self._data_from_host_to_device(images)
        self.forward()
        self._data_from_device_to_host()

    def forward(self):
        print('execute stage:')
        ret = acl.mdl.execute(self.model_id,
                              self.load_input_dataset,
                              self.load_output_dataset)
        check_ret("acl.mdl.execute", ret)
        self._destroy_databuffer()
        print('execute stage success')

    def _print_result(self, result):
        vals = np.array(result).flatten()
        top_k = vals.argsort()[-1:-6:-1]
        print("======== top5 inference results: =============")

        for j in top_k:
            if vals[j] >= 0.5 :
                print(">>>>>>>>>>>>>>>>> find dog >>>>>>>>>>>>>")
            print("[%d]: %f" % (j, vals[j]))

    def _destroy_databuffer(self):
        for dataset in [self.load_input_dataset, self.load_output_dataset]:
            if not dataset:
                continue
            number = acl.mdl.get_dataset_num_buffers(dataset)
            for i in range(number):
                data_buf = acl.mdl.get_dataset_buffer(dataset, i)
                if data_buf:
                    ret = acl.destroy_data_buffer(data_buf)
                    check_ret("acl.destroy_data_buffer", ret)
            ret = acl.mdl.destroy_dataset(dataset)
            check_ret("acl.mdl.destroy_dataset", ret)

    def get_result(self, output_data):
        result = []
        dims, ret = acl.mdl.get_cur_output_dims(self.model_desc, 0)
        check_ret("acl.mdl.get_cur_output_dims", ret)
        out_dim = dims['dims']
        for temp in output_data:
            ptr = temp["buffer"]
            # 转化为float32类型的数据
            data = acl.util.ptr_to_numpy(ptr, tuple(out_dim), NPY_FLOAT32)
            result.append(data)
        return result


def transfer_pic(input_path):
    input_path = os.path.abspath(input_path)
    with Image.open(input_path) as image_file:
        image_file = image_file.resize((256, 256))
        img = np.array(image_file)
    height = img.shape[0]
    width = img.shape[1]
    # 对图片进行切分,取中间区域
    h_off = (height - 224) // 2
    w_off = (width - 224) // 2
    crop_img = img[h_off:height - h_off, w_off:width - w_off, :]
    # rgb to bgr,改变通道顺序
    img = crop_img[:, :, ::-1]
    shape = img.shape
    img = img.astype("float16")
    img[:, :, 0] -= 104
    img[:, :, 1] -= 117
    img[:, :, 2] -= 123
    img = img.reshape([1] + list(shape))
    img = img.transpose([0, 3, 1, 2])
    result = np.frombuffer(img.tobytes(), np.float16)
    return result


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--device', type=int, default=0)
    parser.add_argument('--model_path', type=str,
                        default="./model/resnet50.om")
    parser.add_argument('--images_path', type=str, default="./data")
    args = parser.parse_args()
    print("Using device id:{}\nmodel path:{}\nimages path:{}"
          .format(args.device, args.model_path, args.images_path))

    net = Net(args.device, args.model_path)
    cap = cv2.VideoCapture('rtsp://xxxxxx:554/Streaming/Channels/101')
    print(cap)
    ret,frame = cap.read()
    while ret:
        ret,frame = cap.read()
        cv2.imwrite(args.images_path+'/frame.jpg', frame)


        images_list = [os.path.join(args.images_path, img)
                    for img in os.listdir(args.images_path)
                    if os.path.splitext(img)[1] in IMG_EXT]

        for image in images_list:
            print("images:{}".format(image))
            img = transfer_pic(image)
            net.run([img])



        if cv2.waitKey(1) & 0xFF == ord('q'):
            print("===break=========")
            break
    print("cv2.destroyAllWindows()")
    cv2.destroyAllWindows()
    cap.release()
    print("*****run finish******")

执行如下命令,启动示例:

root@e9c222179267:~/dist# python3 ./src/main.py
Using device id:0
model path:./model/resnet50.om
images path:./data
init resource stage:
model_id:1
init resource success
<VideoCapture 0xffff91d938b0>
images:./data/dog1_1024_683.jpg
data interaction from host to device
data interaction from host to device success
execute stage:
execute stage success
data interaction from device to host
data interaction from device to host success
======== top5 inference results: =============
>>>>>>>>>>>>>>>>> find dog >>>>>>>>>>>>>
[549]: 0.808105
[868]: 0.103577
[611]: 0.032349
[584]: 0.013908
[679]: 0.009338
images:./data/dog2_1024_683.jpg
data interaction from host to device
data interaction from host to device success
execute stage:
execute stage success
data interaction from device to host
data interaction from device to host success
======== top5 inference results: =============
[108]: 0.332764
[611]: 0.284668
[973]: 0.103943
[549]: 0.096863
[620]: 0.027756
images:./data/frame.jpg
data interaction from host to device
data interaction from host to device success
execute stage:
execute stage success
data interaction from device to host
data interaction from device to host success
======== top5 inference results: =============
>>>>>>>>>>>>>>>>> find dog >>>>>>>>>>>>>
[904]: 0.610352
[390]: 0.064880
[137]: 0.052521
[741]: 0.034454
[489]: 0.024231
images:./data/dog1_1024_683.jpg
data interaction from host to device
data interaction from host to device success
execute stage:
execute stage success
data interaction from device to host
data interaction from device to host success
======== top5 inference results: =============
>>>>>>>>>>>>>>>>> find dog >>>>>>>>>>>>>
[549]: 0.808105
[868]: 0.103577
[611]: 0.032349
[584]: 0.013908
[679]: 0.009338
images:./data/dog2_1024_683.jpg
data interaction from host to device
data interaction from host to device success
execute stage:
execute stage success
data interaction from device to host
data interaction from device to host success
======== top5 inference results: =============
[108]: 0.332764
[611]: 0.284668
[973]: 0.103943
[549]: 0.096863
[620]: 0.027756
images:./data/frame.jpg

关于示例项目其他文件和模型,可以参考官网samples示例【基于 Caffe ResNet-50 网络实现图片分类(同步推理)】。参考网址为:

https://gitee.com/ascend/samples/tree/master/python/level2_simple_inference/1_classification/resnet50_imagenet_classification

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。