bazel を使用してプロジェクトを構築します。これには、外部ライブラリの導入方法 (opencv およびコンパイルされた tensorflow lite ライブラリがプロジェクトに導入されます)、動的ライブラリと静的ライブラリにコンパイルする方法、コンパイルされたライブラリを呼び出す方法が含まれます。
プロジェクトのルート ディレクトリ内のすべてのファイルの構造を次の図に示します。プロジェクトの構築は、図の赤い枠で囲まれたファイルによって説明されます。青の枠は、生成された静的ライブラリと動的ライブラリです (これらは他のディレクトリからコピーされます)。
1. 外部ライブラリ ファイルをビルドする
まず、WORKSPACE をビルドします。つまり、次のようにインポートする必要がある外部ライブラリを決定します。
# 确定外部的opencv库路径
new_local_repository(
name = "opencv4",
path = "/usr/local", # opencv安装的目录
build_file = "opencv4.BUILD",
)
# 确定外部的tensorflow lite库路径
new_local_repository(
name = "tensorflow_lite",
path = "/home/chentao/tensorflowlite", # tensorflow lite编译成功所在目录
build_file = "tensorflowLite.BUILD",
)
下の図はopencvとtensorflow liteのライブラリのディレクトリ構造です。実際の図を見ると分かりやすいです(ファイルが多すぎてツリーで表示すると見苦しくなります。手描きのものを見てください)。
次に、WORKSPACE で説明されている opencv4.BUILD および tensorflowLite.BUILD ファイルをビルドしますが、これはエラーが発生しやすいものです。より深く理解するために、上の図に基づいてパスの問題を注意深く明確にすることができます。
opencv4.BUILD ファイル:
# 构建opencv外部引入目标
cc_library(
name = "opencv4",
srcs = glob(["lib/libopencv*.so*"]),
hdrs = glob(["include/opencv4/opencv2/**/*.h*"]),
includes = ["include/opencv4/"],
visibility = ["//visibility:public"],
linkstatic = 0, # 默认值是1,表示链接的是静态库,否则就是动态库(有疑问不确定)
)
tensorflowLite.BUILD ファイル:
# 构建tensorflow lite外部引入库目标
cc_library(
name = "tensorflow_lite",
srcs = glob(["libs/libtensorflowlite.so"]),
hdrs = glob(["include/**/*.h"]), # 等同于glob(["include/tensorflow/lite/**/*.h"]) + glob(["include/flatbuffers/**/*.h"])
includes = ["include/"],
visibility = ["//visibility:public"],
linkstatic = 0,
)
この時点で、外部ライブラリ ファイルが決定され、それらを直接使用できるようになります。
2. srcディレクトリにBUILDファイルを作成
このファイルの BUILD ファイルには、opencv ライブラリと tensorflow lite ライブラリを使用して、動的ライブラリ、静的ライブラリ、およびその他のターゲットにコンパイルすることが含まれています。一般的に使用される構文を提供すると役立ちます。詳細については、注記を参照してください。
# 构建facepose目标
cc_library(
name = "facepose",
srcs = glob(["*.cpp"]), # 遍历当前目录所有.cpp文件
hdrs = glob(["*.h*"]), # 遍历当前目录所有含有.h字符的文件
deps = [
"@tensorflow_lite//:tensorflow_lite",
"@opencv4//:opencv4",
],
visibility = ["//visibility:public"], # 完全公开,本worksapce及其他workspace都可以访问
#visibility = ["//visibility:private"], # 私有,仅同一package内可访问
#visibility = ["@my_repo//foo/bar:__pkg__"], # 表示指定的my_repo项目的//foo/bar的package内所有Targets可以访问
#visibility = ["//foo/bar:__subpackages__"], # 表示指定的//foo/bar的package及其内部所有package的所有Targets可以访问
)
# 构建facepose动态库目标
cc_binary(
name = "libfacepose.so", # name一定要命名成lib*.so形式,否则会出现gcc定位错误(查到资料看到的,未验证)
srcs = glob(["*.cpp"]) + glob(["*.h*"]),
# hdrs = glob(["*.h*"]), # cc_binary中没有hdrs属性
deps = [
"@tensorflow_lite//:tensorflow_lite",
"@opencv4//:opencv4",
],
linkshared=True, # 由这个属性指定生成动态库目标文件
visibility = ["//visibility:public"],
)
# 构建facepose静态库目标
cc_library(
name = "faceposestatic", # 生成结果会自动在前面加上lib,在后面加上.a
srcs = glob(["*.cpp"]),
hdrs = glob(["*.h*"]),
deps = [
"@tensorflow_lite//:tensorflow_lite",
"@opencv4//:opencv4",
],
linkstatic=True, # 由这个属性指定生成静态库目标文件
visibility = ["//visibility:public"],
)
# 构建facepose头文件目标
cc_library(
name = "faceposeHeader",
hdrs = glob(["*.h*"]),
visibility = ["//visibility:public"],
)
# 将当前目录的所有含有.h文件变成可以看见属性
#exports_files(glob(["*.h*"]))
3. デモディレクトリ内のBUILDファイル
このディレクトリは、bazel で実行ファイルにコンパイルする方法、動的/静的ライブラリの使用方法などを示します。詳細については、注記を参照してください。
# 构建引用facepose动态库目标:如果在showAll目标中使用,需要先在程序根目录运行:
# bazel build //src:libfacepose.so命令,将生成的.so文件拷贝到对应路径(参考libfacepose.so的路径)
cc_import(
name="dllfacepose",
shared_library="libfacepose.so",
deps = ["//src:faceposeHeader"],
)
# 构建引用facepose静态库目标:如果在showAll目标中使用,需要先在程序根目录运行:
# bazel build //src:faceposestatic命令,将生成的.a文件拷贝到对应路径(参考libfaceposestatic.a的路径)
cc_import(
name="staticFacepose",
static_library="libfaceposestatic.a",
deps = ["//src:faceposeHeader"],
# 文件所在BUILD中添加了exports_files(glob(["*.h*"])),下面属性和deps属性作用相同
#hdrs = ["@//src:DetectionPostProcess.hpp","@//src:FaceDetection.hpp",其他头文件也要加],
)
# 构建可执行程序目标
cc_binary(
name = "showAll",
srcs = ["showAll.cpp"],
deps = [
"//src:facepose", # 引入其他Packages的构建的目标
#":dllfacepose", # 使用自己编译的动态库文件
#":staticFacepose", # 使用自己编译的静态库文件。上述三种只须选其中一种即可
"@opencv4//:opencv4", # 引入外部opencv库
"@tensorflow_lite//:tensorflow_lite"
],
linkopts = ["-lpthread"], # 链接多线程库
copts = ["-Isrc"], # 添加src路径:不添加,则showAll.cpp文件中就需要#include "src/IrisLandmark.hpp"方式
#copts = ["-std=c++14"]+["-Iinclude"], # 指定C++14标准编译,并在当前或者依赖包的include目录下寻找头文件
)
# 构建可执行文件,用于测试opencv
cc_binary(
name = "test_opencv",
srcs = ["test_opencv.cpp"],
deps = [
"@opencv4//:opencv4",
],
)
4. 上記のターゲットコマンドをコンパイルします。
次のコマンドはすべて、WORKSPACE と同じディレクトリであるルート ディレクトリのターミナルから実行されます。
最も単純なプロジェクトは、test_opencv.cpp を使用して、opencv 呼び出しが成功したかどうかをテストすることです。コマンドは次のとおりです。
# 编译
bazel build //demo:test_opencv
# 执行
./bazel-bin/demo/test_opencv
次のように動的/静的コマンドをコンパイルします。
# 动态库
bazel build //src:libfacepose.so
# 静态库
bazel build //src:faceposestatic
opencv および tensorflow lite ライブラリ ファイルを呼び出すことができる実行可能ファイルをコンパイルします。コマンドは次のとおりです。
# 编译
bazel build //demo:showAll
# 执行
./bazel-bin/demo/showAll
投稿された test_opencv.cpp および showAll.cpp ファイルには #include メモ項目が含まれており、騙されています。
test_opencv.cpp
#include <iostream>
#include "opencv2/highgui.hpp"
int main(int argc, char* argv[]) {
cv::VideoCapture cap("../face_pose.avi");
bool success = cap.isOpened();
if (success == false)
{
std::cerr << "Cannot open the camera." << std::endl;
return 1;
}
while (success)
{
cv::Mat rframe, frame;
success = cap.read(rframe); // read a new frame from video
if (success == false)
break;
cv::imshow("Face detector", rframe);
if (cv::waitKey(1) == 27)
break;
}
cap.release();
cv::destroyAllWindows();
return 0;
}
showAll.cpp
#include <iostream>
#include "opencv2/highgui.hpp"
#include "IrisLandmark.hpp" // 这里注意是否是 #include "src/IrisLandmark.hpp" ,看编译选项是否有copts = ["-Isrc"]
#define SHOW_FPS (1)
#if SHOW_FPS
#include <chrono>
#endif
int main(int argc, char* argv[]) {
my::IrisLandmark irisLandmarker("./models");
cv::VideoCapture cap("./face_pose.avi");
bool success = cap.isOpened();
if (success == false)
{
std::cerr << "Cannot open the camera." << std::endl;
return 1;
}
#if SHOW_FPS
float sum = 0;
int count = 0;
#endif
while (success)
{
cv::Mat rframe, frame;
success = cap.read(rframe); // read a new frame from video
if (success == false)
break;
cv::flip(rframe, rframe, 1);
#if SHOW_FPS
auto start = std::chrono::high_resolution_clock::now();
#endif
irisLandmarker.loadImageToInput(rframe);
irisLandmarker.runInference();
for (auto landmark: irisLandmarker.getAllFaceLandmarks()) {
cv::circle(rframe, landmark, 2, cv::Scalar(0, 255, 0), -1);
}
for (auto landmark: irisLandmarker.getAllEyeLandmarks(true, true)) {
cv::circle(rframe, landmark, 2, cv::Scalar(0, 0, 255), -1);
}
for (auto landmark: irisLandmarker.getAllEyeLandmarks(false, true)) {
cv::circle(rframe, landmark, 2, cv::Scalar(0, 0, 255), -1);
}
#if SHOW_FPS
auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
float inferenceTime = duration.count() / 1e3;
sum += inferenceTime;
count += 1;
int fps = (int) 1e3/ inferenceTime;
cv::putText(rframe, std::to_string(fps), cv::Point(20, 70), cv::FONT_HERSHEY_PLAIN, 3, cv::Scalar(0, 196, 255), 2);
#endif
cv::imshow("Face detector", rframe);
if (cv::waitKey(10) == 27)
break;
}
#if SHOW_FPS
std::cout << "Average inference time: " << sum / count << "ms " << std::endl;
#endif
cap.release();
cv::destroyAllWindows();
return 0;
}