前言

很高兴,收到了一份新款 OrangePi AIpro 开发板,这是香橙派第一次与华为昇腾合作,使用昇腾系列 AI 处理器来设计这款高性价比的 AI 开发板。这块开发板不仅性能强大,还支持丰富的硬件接口,为AI开发者提供了一个理想的实验平台。作为一名会点AI的软件工程师,我迫不及待地想要尝试这款设备,将其应用到实际项目中。*^____^*

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_开发板

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_人工智能_02

OrangePi AIpro 开发板简介

OrangePi AIpro 拥有8-12 TOPS(每秒万亿次操作)的AI算力,就像你拥有了一台超级计算机,可以快速处理各种复杂的AI任务,比如识别图像、分析视频等。无论是开发智能家居、智能安防,这款开发板都能轻松胜任。其次, AIpro 还配备了8GB或16GB的高速内存,这意味着你可以运行更多、更复杂的AI应用程序,体验流畅的性能。即使是较大规模的数据处理和深度学习模型训练,这款开发板也能轻松应对,毫不费力。

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_windows_03

此外,OrangePi AIpro 提供了许多便捷的接口,支持双HDMI输出,能够连接两个4K高清显示屏,带来更广阔的视觉体验;千兆网口保证你在大数据传输和实时联网应用中享有快速、稳定的网络连接;Wi-Fi 5和蓝牙4.2让你可以随时随地进行无线连接,非常适合物联网项目;多种USB接口和Type-C支持高速数据传输,连接外部设备更加方便;M.2插槽支持大容量SSD硬盘,为你提供更多存储空间,存储和读取数据速度更快。

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_开发语言_04

远程连接及相关设置

这次收到的 OrangePi AIpro 开发板带了一张TF卡,内置了官方的 Ubuntu 系统,省去了很多烧录带来的麻烦,所以烧录这部分就略过了,如果大家对烧录系统到 TF 卡以及烧录到 SATA SSD 的过程,可以参考我的这篇博客。

OrangePi Ai Pro 开箱及镜像烧录指南

第一次使用开发板,链接wifi到开发板是我使用开发板必不可少的操作,这样就可以使用 SSH、VNC 等工具来对开发板进行操作,省去了每次使用时需要链接开发板到显示器的步骤。



OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_开发语言_05

至于连接工具,官方是有提供 MobaXterm 安装包的,但我更习惯使用 XShell,这里就使用 XShell 进行连接。配置好我们的 SSH 连接信息,点击连接即可进行连接。

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_人工智能_06

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_开发语言_07

为了避免算力恐慌的问题,这里我们刷入官方的“超频”固件,据说这款固件是从CANN软件层面进行优化,将我们的 OrangePi AIpro 开发板从8T的算力优化到了8~12T的算力,但是能不能到12T似乎是个随机数,不同的板子的上限貌似不同 (*^____^*)

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_数据库_08

使用 Xftp 将最新的固件文件 Ascend310B-firmware-7.3.t10.0.b528-rc-signed-opiaipro-12t-1.6ghz-20240605.run 传输到我们的开发板上,然后对固件进行升级。

# 给固件权限
chmod +x Ascend310B-firmware-7.3.t10.0.b528-rc-signed-opiaipro-12t-1.6ghz-20240605.run
 
# 运行固件
./Ascend310B-firmware-7.3.t10.0.b528-rc-signed-opiaipro-12t-1.6ghz-20240605.run --full
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_数据库_09

升级后断电重启即可体验到8~12T的算力啦!

开放 JupyterLab 端口

为了更为方便的在我的电脑上使用 OrangePi AIpro 的环境来调试 AI 模型,这里配置并开放 JupyterLab 的访问端口,从而使得我的电脑可以访问到 OPI AIpro 的 JupyterLab。(如果你想得到一个可以随时使用 OrangePi AIpro 算力的 JupyterLab,完全可以向我一样,打开 JupyterLab 的端口,然后搭配内网穿透服务,风扇开到最大,这样就可以得到一个带有算力资源且随时可以敲代码的网页服务。)

安装防火墙

首先,我们需要确保系统的安全性。防火墙是保护系统免受未经授权访问的重要工具。我们需要先安装防火墙:

sudo apt install firewalld
  • 1.
启动防火墙并开放端口

安装完成后,我们需要启动防火墙并确保它在每次系统启动时自动运行。同时,为了方便后续访问 Jupyter Notebook,我们还需要开放 8888 端口。以下是具体步骤:

# 切换到 root 用户
su root

# 启动防火墙并设置开机自启
sudo systemctl start firewalld
sudo systemctl enable firewalld

# 开放 8888 端口,允许通过该端口进行访问
firewall-cmd --add-port=8888/tcp --permanent

# 重新加载防火墙配置
sudo firewall-cmd --reload
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

通过这些步骤,我们已经成功启动了防火墙,并开放了 8888 端口,为我们通过浏览器访问 Jupyter Notebook 提供了便利。

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_人工智能_10

创建启动 Jupyter Notebook 的脚本

为了更加方便地启动 Jupyter Notebook,我们可以创建一个启动脚本,从而简化启动过程,还可以自动设置环境变量,确保 Jupyter Notebook 能够正常运行。以下是脚本的具体内容:

. /usr/local/Ascend/ascend-toolkit/set_env.sh
export PYTHONPATH=/usr/local/Ascend/thirdpart/aarch64/acllite:$PYTHONPATH

if [ $# -eq 1 ]; then
    jupyter lab --ip $1 --port 8888 --allow-root --no-browser
else
    jupyter lab --ip 0.0.0.0 --port 8888 --allow-root --no-browser
fi
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

将以上内容保存为 start_notebook_all_ports.sh 文件。

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_开发语言_11

赋予脚本运行权限

为了使脚本能够执行,我们需要赋予它运行权限:

chmod +x start_notebook_all_ports.sh
  • 1.
运行脚本

现在,我们可以运行这个脚本来启动 Jupyter Notebook:

./start_notebook_all_ports.sh
  • 1.

运行这个脚本后,你可以在浏览器中通过开发板的 IP 地址和 8888 端口访问 Jupyter Notebook。例如,如果开发板的 IP 地址是 192.168.1.100,那么你可以在浏览器中输入 http://192.168.1.100:8888 来访问,我这里是 192.168.8.131。

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_windows_12

获取 Jupyter Notebook Token

为了确保 Jupyter Notebook 的安全性,每次启动时都会生成一个访问令牌(Token)。我们可以通过以下命令来获取这个令牌:

jupyter server list
  • 1.

将输出中的 Token 复制到浏览器中,即可成功登录 Jupyter Lab。

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_人工智能_13

成功打开 Jupyter Lab

成功打开 Jupyter Lab 后,你可以开始在 OrangePi AIpro 开发板上进行 AI 项目的开发与实验。这款开发板的强大性能和丰富接口将大大提升你的开发效率和体验。

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_数据库_14


OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_开发板_15

通过以上步骤,我们就可以随时可以在 OrangePi AIpro 开发板上进行 AI 开发和实验。

搭建卡通图像生成服务

安装 python-aclite 等依赖包

打开 ~/.bashrc 并在最后添加下面内容,并 source ~/.bashrc 使其生效:

export CPU_ARCH=`arch`
export THIRDPART_PATH=${HOME}/Ascend/thirdpart/${CPU_ARCH}  #代码编译时链接第三方库
export PYTHONPATH=${THIRDPART_PATH}/acllite:$PYTHONPATH #设置pythonpath为固定目录
export INSTALL_DIR=${HOME}/Ascend/ascend-toolkit/latest #CANN软件安装后文件存储路径
  • 1.
  • 2.
  • 3.
  • 4.

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_数据库_16

获取ascend公共文件,并拷贝到第三方依赖文件夹:

cd ${HOME} 
git clone https://gitee.com/ascend/samples.git
mkdir -p ${THIRDPART_PATH}
cp -r ${HOME}/samples/common ${THIRDPART_PATH}
  • 1.
  • 2.
  • 3.
  • 4.

然后拷贝 python acllite 库到三方依赖文件夹就行了:

cp -r ${HOME}/samples/python/common/acllite ${THIRDPART_PATH}
  • 1.
体验图片卡通化AI

打开JupyterLab,按照操作手册找到 demo5,找到 main.ipynb 文件,并点击上面的 restart 双箭头,然后样例就开始运行了。

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_人工智能_17

运行后,我们就可以看到卡通化后的图片了。

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_数据库_18

打造图片卡通化 AI 服务器

但我们想要的不仅仅于此。我想将其打造成网页服务器或者API,这样配合内网穿透服务,我们就可以随时使用我们的 OrangePi AIpro 中所含有的 AI 服务啦!这里我主要使用 flask 来构建一个 OrangePi AIpro 图片卡通化的网页服务。下面是简单实现步骤和代码实现:

为了在上传图片后显示处理后的结果,我们需要在上传和处理图片后,将处理后的图片路径传递给模板,并在模板中显示该图片。

下面是完整的实现步骤:

1. 创建 Flask 应用

创建一个新的目录,例如 cartoonization_app,并在其中创建以下文件:

  • app.py: 主 Flask 应用文件。
  • templates/: 目录,存放 HTML 模板。
  • static/: 目录,存放生成的图片。
2. 编写 app.py

app.py 中编写 Flask 应用:

import os
import sys
import numpy as np
import cv2
from flask import Flask, request, render_template, send_from_directory, redirect, url_for

# 添加模型路径
path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(path, ".."))
sys.path.append(os.path.join(path, "../../../common/acllite/"))

import acl
import acllite_utils as utils
import constants as const
from acllite_imageproc import AclLiteImageProc
from acllite_model import AclLiteModel
from acllite_image import AclLiteImage
from acllite_resource import AclLiteResource

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'static/uploads'
app.config['PROCESSED_FOLDER'] = 'static/processed'
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['PROCESSED_FOLDER'], exist_ok=True)

class Cartoonization(object):
    def __init__(self, model_path, model_width, model_height):
        self._model_path = model_path
        self._model_width = model_width
        self._model_height = model_height
        self.device_id = 0
        self._dvpp = None
        self._model = None

    def init(self):
        self._dvpp = AclLiteImageProc()
        self._model = AclLiteModel(self._model_path)
        return const.SUCCESS

    @utils.display_time
    def pre_process(self, image):
        image_dvpp = image.copy_to_dvpp()
        yuv_image = self._dvpp.jpegd(image_dvpp)
        crop_and_paste_image = self._dvpp.crop_and_paste_get_roi(yuv_image, image.width, image.height, \
                                    self._model_width, self._model_height)
        return crop_and_paste_image

    @utils.display_time
    def inference(self, resized_image):
        return self._model.execute(resized_image)

    @utils.display_time
    def post_process(self, infer_output, origin_image):
        data = ((np.squeeze(infer_output[0]) + 1) * 127.5)
        img = cv2.cvtColor(data, cv2.COLOR_RGB2BGR)
        img = cv2.resize(img, (origin_image.width, origin_image.height))
        return img

# 初始化Cartoonization对象
currentPath = os.path.join(path, "..")
MODEL_PATH = os.path.join(currentPath, "model/cartoonization.om")
MODEL_WIDTH = 256
MODEL_HEIGHT = 256

acl_resource = AclLiteResource()
acl_resource.init()
cartoonization = Cartoonization(MODEL_PATH, MODEL_WIDTH, MODEL_HEIGHT)
ret = cartoonization.init()
utils.check_ret("Cartoonization.init ", ret)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/upload', methods=['POST'])
def upload_file():
    if 'file' not in request.files:
        return 'No file part'
    file = request.files['file']
    if file.filename == '':
        return 'No selected file'
    if file:
        filename = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
        file.save(filename)

        # 处理图片
        image = AclLiteImage(filename)
        crop_and_paste_image = cartoonization.pre_process(image)
        result = cartoonization.inference([crop_and_paste_image])
        processed_image = cartoonization.post_process(result, image)

        # 保存处理后的图片
        processed_filename = os.path.join(app.config['PROCESSED_FOLDER'], file.filename)
        cv2.imwrite(processed_filename, processed_image)

        return redirect(url_for('processed_file', filename=file.filename))

@app.route('/processed/<filename>')
def processed_file(filename):
    return render_template('result.html', filename=filename)

@app.route('/static/processed/<filename>')
def send_processed_file(filename):
    return send_from_directory(app.config['PROCESSED_FOLDER'], filename)

if __name__ == '__main__':
    app.run(debug=True, host="0.0.0.0", port=5000)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
3. 创建模板

templates 目录下创建两个 HTML 文件:

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Cartoonization</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f0f0f0;
            margin: 0;
            padding: 20px;
        }
        .container {
            max-width: 600px;
            margin: 0 auto;
            background-color: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        h1 {
            text-align: center;
            margin-bottom: 20px;
            color: #333;
        }
        form {
            text-align: center;
        }
        input[type="file"] {
            display: none;
        }
        .custom-file-upload {
            border: 1px solid #ccc;
            display: inline-block;
            padding: 6px 12px;
            cursor: pointer;
            background-color: #f0f0f0;
            color: #333;
            border-radius: 4px;
        }
        .custom-file-upload:hover {
            background-color: #e0e0e0;
        }
        input[type="submit"] {
            margin-top: 10px;
            background-color: #4CAF50;
            color: white;
            border: none;
            padding: 10px 20px;
            text-align: center;
            text-decoration: none;
            display: inline-block;
            font-size: 16px;
            cursor: pointer;
            border-radius: 4px;
        }
        input[type="submit"]:hover {
            background-color: #45a049;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Upload an Image to Cartoonize</h1>
        <form action="/upload" method="post" enctype="multipart/form-data">
            <label for="file-upload" class="custom-file-upload">
                Choose File
            </label>
            <input id="file-upload" type="file" name="file">
            <input type="submit" value="Upload">
        </form>
    </div>
</body>
</html>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_人工智能_19

result.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Processed Image</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f0f0f0;
            margin: 0;
            padding: 20px;
            text-align: center;
        }
        .container {
            max-width: 800px;
            margin: 0 auto;
            background-color: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        h1 {
            color: #333;
        }
        img {
            max-width: 100%;
            height: auto;
            margin-top: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        a {
            display: inline-block;
            margin-top: 20px;
            padding: 10px 20px;
            background-color: #4CAF50;
            color: white;
            text-decoration: none;
            border-radius: 4px;
            transition: background-color 0.3s ease;
        }
        a:hover {
            background-color: #45a049;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Processed Image</h1>
        <img src="{{ url_for('send_processed_file', filename=filename) }}" alt="Processed Image">
        <br>
        <a href="/">Upload another image</a>
    </div>
</body>
</html>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_windows_20

打开浏览器,访问 http://<香橙派IP地址>:5000/,你应该能看到上传图片的表单。上传图片后,处理后的图片将会展示在结果页面上,并提供返回主页的链接以便上传另一张图片。

注意事项
  • 确保 static/uploadsstatic/processed 目录存在并且可写。
  • 根据实际情况修改模型路径和其他配置。
  • 可能需要根据实际情况调整 AclLiteImage 的初始化和使用方法。

使用体验总结

当你收到一款像 OrangePi AIpro 这样强大的开发板时,你不仅仅获得了昇腾系列 AI 处理器的强大性能支持,还拥有了丰富的硬件接口和便捷的开发环境。在使用 OrangePi AIpro 的过程中,我深刻体验到了它为 AI 开发者带来的诸多优势:

  1. 强大的计算能力:搭载8-12 TOPS的 AI 算力,OrangePi AIpro 让你能够快速处理复杂的 AI 任务。
  2. 丰富的硬件接口:支持双HDMI输出、千兆网口、Wi-Fi 5 和蓝牙 4.2,以及多种 USB 接口和 Type-C,为你的项目提供了灵活多样的连接方式,适合各种物联网和数据传输需求。
  3. 完善的昇腾生态:生态系统不仅包括硬件和软件支持,还涵盖了多种工具和资源,极大地丰富了开发者在使用这款开发板时的选择和便利性。特别是昇腾社区的AI案例,给了我这种AI小白很多灵感。

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务_人工智能_21

总的来说,这是国产开发板香橙派 OrangePi AIpro 以及国产计算框架 CANN 的应用典范,不仅在性能上有了质的飞跃,而且在使用体验和开发效率上都极大地提升,是两者的巨大进步。唯一的不足可能就是散热问题,也可以说是风扇的问题,风扇调大就会特别响,不过OrangePi AIpro 20T版本好像用到了铜管散热,大大降低了这个问题,希望能兼容一下8T的散热。

非常期待华为昇腾和香橙派后续的继续合作,带来更好的国产AI开发板。国产加油!