# 关于舰船检测项目的调试记录
项目要求:
- 通过摄像头图像采集,在海面状态下实现对船只的精准检测
- 嵌入式平台的制板尺寸要严格符合要求
- 将检测到的结果通过以太网传输到客户端
- 通过控制GPIO,PWM,ADC实现对直流电机的角度控制
1、选择目标检测网络(Yolov5、Yolov5_lite)、训练
将训练后的模型已经转换,并使用onnxsim进行简化
python export.py --weights 'weights/best.pt' --batch-size 1 --img_size 320
pip install onnxsimplifier
python -m onnxsim best.onnx e.onnx
2、通过NCNN将Yolov5_lite部署
在树莓派4b、香橙派zero3、香橙派zero2上进行测试、验证可行性
sudo apt-get install git cmake
sudo apt-get install -y gfortran
sudo apt-get install -y libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install --no-install-recommends libboost-all-dev
sudo apt-get install -y libgflags-dev libgoogle-glog-dev liblmdb-dev libatlas-base-dev
#!/bin/bash
sudo apt-get install -y g++
sudo apt-get install -y libncurses5-dev
sudo apt-get install -y zlib1g-dev
sudo apt-get install -y bison
sudo apt-get install -y flex
sudo apt-get install -y unzip
sudo apt-get install -y autoconf
sudo apt-get install -y gawk
sudo apt-get install -y make
sudo apt-get install -y gettext
sudo apt-get install -y gcc
sudo apt-get install -y binutils
sudo apt-get install -y patch
sudo apt-get install -y bzip2
sudo apt-get install -y libz-dev
sudo apt-get install -y asciidoc
sudo apt-get install -y subversion
sudo apt-get install -y sphinxsearch
sudo apt-get install -y libtool
sudo apt-get install -y sphinx-common
sudo apt-get install -y libc6:i386 libgcc1:i386 libstdc++6:i386 -y
sudo apt-get install -y git cmake
sudo apt-get install -y gfortran
sudo apt-get install -y libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install -y --no-install-recommends libboost-all-dev
sudo apt-get install -y libgflags-dev libgoogle-glog-dev liblmdb-dev libatlas-base-dev
在Ubuntu虚拟机上进行交叉编译
- 下载交叉编译工具
toolchain 工具: gcc-arm-8.2-2018.11-x86_64-aarch64-linux-gnu.tar.xz - 配置环境变量
export PATH=$PATH:/home/alpha/gcc-arm-8.2-2018.11-x86_64-aarch64-linux-gnu/bin
source ~/.bashrc
git clone https://github.com/Tencent/ncnn.git
cd ncnn
mkdir build-aarch64
cd build-aarch64
cmake -DCMAKE_TOOLCHAIN_FILE=../toolchains/aarch64-linux-gnu.toolchain.cmake .. #可选择开启vulkan -DNCNN_VULKAN=ON
make -j8
make install
之后将build-aarch64文件夹转移到香橙派上,在 CMakeLists.txt 中添加静态库路径如下,就可以编译自己的工程了。
INCLUDE_DIRECTORIES(/home/ds-h616/ncnn/install/include/ncnn/)
LINK_DIRECTORIES(/home/ds-h616/ncnn/install/lib/)
target_link_libraries(${PROJECT_NAME} ncnn ${OpenCV_LIBS} /home/ds-h616/ncnn/install/lib/libncnn.a)
新建工程文件夹boat_detect,并将onnx模型转换为ncnn,并添加到model文件下
cd ncnn/build
./tools/onnx/onnx2ncnn e.onnx e.param e.bin
优化为fp16精度
./tools/onnxoptimize e.param e.bin eopt.param eopt.bin 65536
将代码添加到src中,示例代码如下
// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// 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.
#include "layer.h"
#include "net.h"
#if defined(USE_NCNN_SIMPLEOCV)
#include "simpleocv.h"
#else
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#endif
#include <float.h>
#include <stdio.h>
#include <vector>
#include <sys/time.h>
// 0 : FP16
// 1 : INT8
#define USE_INT8 0
// 0 : Image
// 1 : Camera
#define USE_CAMERA 1
struct Object
{
cv::Rect_<float> rect;
int label;
float prob;
};
static inline float intersection_area(const Object& a, const Object& b)
{
cv::Rect_<float> inter = a.rect & b.rect;
return inter.area();
}
static void qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right)
{
int i = left;
int j = right;
float p = faceobjects[(left + right) / 2].prob;
while (i <= j)
{
while (faceobjects[i].prob > p)
i++;
while (faceobjects[j].prob < p)
j--;
if (i <= j)
{
// swap
std::swap(faceobjects[i], faceobjects[j]);
i++;
j--;
}
}
#pragma omp parallel sections
{
#pragma omp section
{
if (left < j) qsort_descent_inplace(faceobjects, left, j);
}
#pragma omp section
{
if (i < right) qsort_descent_inplace(faceobjects, i, right);
}
}
}
static void qsort_descent_inplace(std::vector<Object>& faceobjects)
{
if (faceobjects.empty())
return;
qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}
static void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
{
picked.clear();
const int n = faceobjects.size();
std::vector<float> areas(n);
for (int i = 0; i < n; i++)
{
areas[i] = faceobjects[i].rect.area();
}
for (int i = 0; i < n; i++)
{
const Object& a = faceobjects[i];
int keep = 1;
for (int j = 0; j < (int)picked.size(); j++)
{
const Object& b = faceobjects[picked[j]];
// intersection over union
float inter_area = intersection_area(a, b);
float union_area = areas[i] + areas[picked[j]] - inter_area;
// float IoU = inter_area / union_area
if (inter_area / union_area > nms_threshold)
keep = 0;
}
if (keep)
picked.push_back(i);
}
}
static inline float sigmoid(float x)
{
return static_cast<float>(1.f / (1.f + exp(-x)));
}
// unsigmoid
static inline float unsigmoid(float y) {
return static_cast<float>(-1.0 * (log((1.0 / y) - 1.0)));
}
static void generate_proposals(const ncnn::Mat &anchors, int stride, const ncnn::Mat &in_pad,
const ncnn::Mat &feat_blob, float prob_threshold,
std::vector <Object> &objects) {
const int num_grid = feat_blob.h;
float unsig_pro = 0;
if (prob_threshold > 0.6)
unsig_pro = unsigmoid(prob_threshold);
int num_grid_x;
int num_grid_y;
if (in_pad.w > in_pad.h) {
num_grid_x = in_pad.w / stride;
num_grid_y = num_grid / num_grid_x;
} else {
num_grid_y = in_pad.h / stride;
num_grid_x = num_grid / num_grid_y;
}
const int num_class = feat_blob.w - 5;
const int num_anchors = anchors.w / 2;
for (int q = 0; q < num_anchors; q++) {
const float anchor_w = anchors[q * 2];
const float anchor_h = anchors[q * 2 + 1];
const ncnn::Mat feat = feat_blob.channel(q);
for (int i = 0; i < num_grid_y; i++) {
for (int j = 0; j < num_grid_x; j++) {
const float *featptr = feat.row(i * num_grid_x + j);
// find class index with max class score
int class_index = 0;
float class_score = -FLT_MAX;
float box_score = featptr[4];
if (prob_threshold > 0.6) {
// while prob_threshold > 0.6, unsigmoid better than sigmoid
if (box_score > unsig_pro) {
for (int k = 0; k < num_class; k++) {
float score = featptr[5 + k];
if (score > class_score) {
class_index = k;
class_score = score;
}
}
float confidence = sigmoid(box_score) * sigmoid(class_score);
if (confidence >= prob_threshold) {
float dx = sigmoid(featptr[0]);
float dy = sigmoid(featptr[1]);
float dw = sigmoid(featptr[2]);
float dh = sigmoid(featptr[3]);
float pb_cx = (dx * 2.f - 0.5f + j) * stride;
float pb_cy = (dy * 2.f - 0.5f + i) * stride;
float pb_w = pow(dw * 2.f, 2) * anchor_w;
float pb_h = pow(dh * 2.f, 2) * anchor_h;
float x0 = pb_cx - pb_w * 0.5f;
float y0 = pb_cy - pb_h * 0.5f;
float x1 = pb_cx + pb_w * 0.5f;
float y1 = pb_cy + pb_h * 0.5f;
Object obj;
obj.rect.x = x0;
obj.rect.y = y0;
obj.rect.width = x1 - x0;
obj.rect.height = y1 - y0;
obj.label = class_index;
obj.prob = confidence;
objects.push_back(obj);
}
} else {
for (int k = 0; k < num_class; k++) {
float score = featptr[5 + k];
if (score > class_score) {
class_index = k;
class_score = score;
}
}
float confidence = sigmoid(box_score) * sigmoid(class_score);
if (confidence >= prob_threshold) {
float dx = sigmoid(featptr[0]);
float dy = sigmoid(featptr[1]);
float dw = sigmoid(featptr[2]);
float dh = sigmoid(featptr[3]);
float pb_cx = (dx * 2.f - 0.5f + j) * stride;
float pb_cy = (dy * 2.f - 0.5f + i) * stride;
float pb_w = pow(dw * 2.f, 2) * anchor_w;
float pb_h = pow(dh * 2.f, 2) * anchor_h;
float x0 = pb_cx - pb_w * 0.5f;
float y0 = pb_cy - pb_h * 0.5f;
float x1 = pb_cx + pb_w * 0.5f;
float y1 = pb_cy + pb_h * 0.5f;
Object obj;
obj.rect.x = x0;
obj.rect.y = y0;
obj.rect.width = x1 - x0;
obj.rect.height = y1 - y0;
obj.label = class_index;
obj.prob = confidence;
objects.push_back(obj);
}
}
}
}
}
}
}
static int detect_yolov5(const cv::Mat& bgr, std::vector<Object>& objects)
{
ncnn::Net yolov5;
#if USE_INT8
yolov5.opt.use_int8_inference=true;
#else
yolov5.opt.use_vulkan_compute = true;
yolov5.opt.use_bf16_storage = true;
#endif
// original pretrained model from https://github.com/ultralytics/yolov5
// the ncnn model https://github.com/nihui/ncnn-assets/tree/master/models
#if USE_INT8
yolov5.load_param("../model/eopt.param");
yolov5.load_model("../model/eopt.bin");
#else
yolov5.load_param("../model/eopt.param");
yolov5.load_model("../model/eopt.bin");
#endif
const int target_size = 320;
const float prob_threshold = 0.60f;
const float nms_threshold = 0.60f;
int img_w = bgr.cols;
int img_h = bgr.rows;
// letterbox pad to multiple of 32
int w = img_w;
int h = img_h;
float scale = 1.f;
if (w > h)
{
scale = (float)target_size / w;
w = target_size;
h = h * scale;
}
else
{
scale = (float)target_size / h;
h = target_size;
w = w * scale;
}
ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR2RGB, img_w, img_h, w, h);
// pad to target_size rectangle
// yolov5/utils/datasets.py letterbox
int wpad = (w + 31) / 32 * 32 - w;
int hpad = (h + 31) / 32 * 32 - h;
ncnn::Mat in_pad;
ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 114.f);
const float norm_vals[3] = {1 / 255.f, 1 / 255.f, 1 / 255.f};
in_pad.substract_mean_normalize(0, norm_vals);
ncnn::Extractor ex = yolov5.create_extractor();
ex.input("images", in_pad);
std::vector<Object> proposals;
// stride 8
{
ncnn::Mat out;
ex.extract("451", out);
ncnn::Mat anchors(6);
anchors[0] = 10.f;
anchors[1] = 13.f;
anchors[2] = 16.f;
anchors[3] = 30.f;
anchors[4] = 33.f;
anchors[5] = 23.f;
std::vector<Object> objects8;
generate_proposals(anchors, 8, in_pad, out, prob_threshold, objects8);
proposals.insert(proposals.end(), objects8.begin(), objects8.end());
}
// stride 16
{
ncnn::Mat out;
ex.extract("479", out);
ncnn::Mat anchors(6);
anchors[0] = 30.f;
anchors[1] = 61.f;
anchors[2] = 62.f;
anchors[3] = 45.f;
anchors[4] = 59.f;
anchors[5] = 119.f;
std::vector<Object> objects16;
generate_proposals(anchors, 16, in_pad, out, prob_threshold, objects16);
proposals.insert(proposals.end(), objects16.begin(), objects16.end());
}
// stride 32
{
ncnn::Mat out;
ex.extract("507", out);
ncnn::Mat anchors(6);
anchors[0] = 116.f;
anchors[1] = 90.f;
anchors[2] = 156.f;
anchors[3] = 198.f;
anchors[4] = 373.f;
anchors[5] = 326.f;
std::vector<Object> objects32;
generate_proposals(anchors, 32, in_pad, out, prob_threshold, objects32);
proposals.insert(proposals.end(), objects32.begin(), objects32.end());
}
// sort all proposals by score from highest to lowest
qsort_descent_inplace(proposals);
// apply nms with nms_threshold
std::vector<int> picked;
nms_sorted_bboxes(proposals, picked, nms_threshold);
int count = picked.size();
objects.resize(count);
for (int i = 0; i < count; i++)
{
objects[i] = proposals[picked[i]];
// adjust offset to original unpadded
float x0 = (objects[i].rect.x - (wpad / 2)) / scale;
float y0 = (objects[i].rect.y - (hpad / 2)) / scale;
float x1 = (objects[i].rect.x + objects[i].rect.width - (wpad / 2)) / scale;
float y1 = (objects[i].rect.y + objects[i].rect.height - (hpad / 2)) / scale;
// clip
x0 = std::max(std::min(x0, (float)(img_w - 1)), 0.f);
y0 = std::max(std::min(y0, (float)(img_h - 1)), 0.f);
x1 = std::max(std::min(x1, (float)(img_w - 1)), 0.f);
y1 = std::max(std::min(y1, (float)(img_h - 1)), 0.f);
objects[i].rect.x = x0;
objects[i].rect.y = y0;
objects[i].rect.width = x1 - x0;
objects[i].rect.height = y1 - y0;
}
return 0;
}
static void draw_objects(const cv::Mat& bgr, const std::vector<Object>& objects)
{
static const char* class_names[] = {
"face","face_mask"
};
cv::Mat image = bgr.clone();
for (size_t i = 0; i < objects.size(); i++)
{
const Object& obj = objects[i];
fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob,
obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height);
cv::rectangle(image, obj.rect, cv::Scalar(0, 255, 0));
char text[256];
sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);
int baseLine = 0;
cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
int x = obj.rect.x;
int y = obj.rect.y - label_size.height - baseLine;
if (y < 0)
y = 0;
if (x + label_size.width > image.cols)
x = image.cols - label_size.width;
cv::rectangle(image, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),
cv::Scalar(255, 255, 255), -1);
cv::putText(image, text, cv::Point(x, y + label_size.height),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
}
#if USE_CAMERA
imshow("camera", image);
cv::waitKey(1);
#else
cv::imwrite("result.jpg", image);
#endif
}
#if USE_CAMERA
int main(int argc, char** argv)
{
cv::VideoCapture capture;
capture.open(0, cv::CAP_V4L2);
capture.set(cv::CAP_PROP_FRAME_WIDTH,640);
capture.set(cv::CAP_PROP_FRAME_HEIGHT,640);
cv::Mat frame;
while (true)
{
capture >> frame;
cv::Mat m = frame;
std::vector<Object> objects;
detect_yolov5(frame, objects);
draw_objects(m, objects);
if (cv::waitKey(30) >= 0)
break;
}
}
#else
int main(int argc, char** argv)
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s [imagepath]\n", argv[0]);
return -1;
}
const char* imagepath = argv[1];
struct timespec begin, end;
long time;
clock_gettime(CLOCK_MONOTONIC, &begin);
cv::Mat m = cv::imread(imagepath, 1);
if (m.empty())
{
fprintf(stderr, "cv::imread %s failed\n", imagepath);
return -1;
}
std::vector<Object> objects;
detect_yolov5(m, objects);
clock_gettime(CLOCK_MONOTONIC, &end);
time = (end.tv_sec - begin.tv_sec) + (end.tv_nsec - begin.tv_nsec);
printf(">> Time : %lf ms\n", (double)time/1000000);
draw_objects(m, objects);
return 0;
}
#endif
#CMakeList.txt
cmake_minimum_required(VERSION 2.8)
project(boat_detect)
#opencv
find_package(OpenCV REQUIRED)
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})
#ncnn
find_package(OpenMP REQUIRED)
if(OPENMP_FOUND)
message("OPENMP_FOUND")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()
INCLUDE_DIRECTORIES(/home/ds-h616/ncnn/install/include/ncnn/)
LINK_DIRECTORIES(/home/ds-h616/ncnn/install/lib/)
add_executable(${PROJECT_NAME}
src/boat_detect.cpp)
target_link_libraries(
${PROJECT_NAME}
ncnn
${OpenCV_LIBS}
/home/ds-h616/ncnn/install/lib/libncnn.a
)
cd build
cmake ..
make
./boat_detect
不断更新数据集并产生新的权重(不断测试、缺啥补啥)
降低虚警概率,加入分类网络(ShuffleNet)
通过先分类,分辨是否为海面场景后在选择是否进行目标检测来降低虚警概率
#include "layer.h"
#include "net.h"
#if defined(USE_NCNN_SIMPLEOCV)
#include "simpleocv.h"
#else
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#endif
#include <float.h>
#include <stdio.h>
#include <vector>
#include <sys/time.h>
#include<fstream>
#include <iostream>
#include <chrono>
#include <opencv2/opencv.hpp>
using namespace cv;
static ncnn::UnlockedPoolAllocator g_blob_pool_allocator;
static ncnn::PoolAllocator g_workspace_pool_allocator;
struct Object
{
cv::Rect_<float> rect;
int label;
float prob;
};
static inline float intersection_area(const Object& a, const Object& b)
{
cv::Rect_<float> inter = a.rect & b.rect;
return inter.area();
}
static void qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right)
{
int i = left;
int j = right;
float p = faceobjects[(left + right) / 2].prob;
while (i <= j)
{
while (faceobjects[i].prob > p)
i++;
while (faceobjects[j].prob < p)
j--;
if (i <= j)
{
// swap
std::swap(faceobjects[i], faceobjects[j]);
i++;
j--;
}
}
#pragma omp parallel sections
{
#pragma omp section
{
if (left < j) qsort_descent_inplace(faceobjects, left, j);
}
#pragma omp section
{
if (i < right) qsort_descent_inplace(faceobjects, i, right);
}
}
}
static void qsort_descent_inplace(std::vector<Object>& faceobjects)
{
if (faceobjects.empty())
return;
qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}
//nms
static void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
{
picked.clear();
const int n = faceobjects.size();
std::vector<float> areas(n);
for (int i = 0; i < n; i++)
{
areas[i] = faceobjects[i].rect.area();
}
for (int i = 0; i < n; i++)
{
const Object& a = faceobjects[i];
int keep = 1;
for (int j = 0; j < (int)picked.size(); j++)
{
const Object& b = faceobjects[picked[j]];
// intersection over union
float inter_area = intersection_area(a, b);
float union_area = areas[i] + areas[picked[j]] - inter_area;
// float IoU = inter_area / union_area
if (inter_area / union_area > nms_threshold)
keep = 0;
}
if (keep)
picked.push_back(i);
}
}
static inline float sigmoid(float x)
{
return static_cast<float>(1.f / (1.f + exp(-x)));
}
static inline float unsigmoid(float y) {
return static_cast<float>(-1.0 * (log((1.0 / y) - 1.0)));
}
//生成置信�?
static void generate_proposals(const ncnn::Mat &anchors, int stride, const ncnn::Mat &in_pad,
const ncnn::Mat &feat_blob, float prob_threshold,
std::vector <Object> &objects) {
const int num_grid = feat_blob.h;
float unsig_pro = 0;
if (prob_threshold > 0.6)
unsig_pro = unsigmoid(prob_threshold);
int num_grid_x;
int num_grid_y;
if (in_pad.w > in_pad.h) {
num_grid_x = in_pad.w / stride;
num_grid_y = num_grid / num_grid_x;
} else {
num_grid_y = in_pad.h / stride;
num_grid_x = num_grid / num_grid_y;
}
const int num_class = feat_blob.w - 5;
const int num_anchors = anchors.w / 2;
for (int q = 0; q < num_anchors; q++) {
const float anchor_w = anchors[q * 2];
const float anchor_h = anchors[q * 2 + 1];
const ncnn::Mat feat = feat_blob.channel(q);
for (int i = 0; i < num_grid_y; i++) {
for (int j = 0; j < num_grid_x; j++) {
const float *featptr = feat.row(i * num_grid_x + j);
// find class index with max class score
int class_index = 0;
float class_score = -FLT_MAX;
float box_score = featptr[4];
if (prob_threshold > 0.6) {
// while prob_threshold > 0.6, unsigmoid better than sigmoid
if (box_score > unsig_pro) {
for (int k = 0; k < num_class; k++) {
float score = featptr[5 + k];
if (score > class_score) {
class_index = k;
class_score = score;
}
}
float confidence = sigmoid(box_score) * sigmoid(class_score);
if (confidence >= prob_threshold) {
float dx = sigmoid(featptr[0]);
float dy = sigmoid(featptr[1]);
float dw = sigmoid(featptr[2]);
float dh = sigmoid(featptr[3]);
float pb_cx = (dx * 2.f - 0.5f + j) * stride;
float pb_cy = (dy * 2.f - 0.5f + i) * stride;
float pb_w = pow(dw * 2.f, 2) * anchor_w;
float pb_h = pow(dh * 2.f, 2) * anchor_h;
float x0 = pb_cx - pb_w * 0.5f;
float y0 = pb_cy - pb_h * 0.5f;
float x1 = pb_cx + pb_w * 0.5f;
float y1 = pb_cy + pb_h * 0.5f;
Object obj;
obj.rect.x = x0;
obj.rect.y = y0;
obj.rect.width = x1 - x0;
obj.rect.height = y1 - y0;
obj.label = class_index;
obj.prob = confidence;
objects.push_back(obj);
}
} else {
for (int k = 0; k < num_class; k++) {
float score = featptr[5 + k];
if (score > class_score) {
class_index = k;
class_score = score;
}
}
float confidence = sigmoid(box_score) * sigmoid(class_score);
if (confidence >= prob_threshold) {
float dx = sigmoid(featptr[0]);
float dy = sigmoid(featptr[1]);
float dw = sigmoid(featptr[2]);
float dh = sigmoid(featptr[3]);
float pb_cx = (dx * 2.f - 0.5f + j) * stride;
float pb_cy = (dy * 2.f - 0.5f + i) * stride;
float pb_w = pow(dw * 2.f, 2) * anchor_w;
float pb_h = pow(dh * 2.f, 2) * anchor_h;
float x0 = pb_cx - pb_w * 0.5f;
float y0 = pb_cy - pb_h * 0.5f;
float x1 = pb_cx + pb_w * 0.5f;
float y1 = pb_cy + pb_h * 0.5f;
Object obj;
obj.rect.x = x0;
obj.rect.y = y0;
obj.rect.width = x1 - x0;
obj.rect.height = y1 - y0;
obj.label = class_index;
obj.prob = confidence;
objects.push_back(obj);
}
}
}
}
}
}
}
int classify(const cv::Mat& bgr, std::vector<float>& cls_scores)
{
printf("h:%d,w:%d\n",bgr.rows,bgr.cols);
cv::Mat image = bgr.clone();
ncnn::Net cls_net;
cls_net.opt.use_vulkan_compute = true;
cls_net.opt.use_bf16_storage = false;
cls_net.opt.lightmode = true;
cls_net.opt.num_threads = 2;
cls_net.opt.blob_allocator = &g_blob_pool_allocator;
cls_net.opt.workspace_allocator = &g_workspace_pool_allocator;
cls_net.load_param("/home/ds-h616/boat_detect/model/sea_sim_1710228-sim-opt.param");
cls_net.load_model("/home/ds-h616/boat_detect/model/sea_sim_1710228-sim-opt.bin");
ncnn::Mat in = ncnn::Mat::from_pixels_resize(image.data, ncnn::Mat::PIXEL_BGR2RGB, image.cols, image.rows, 224, 224);
const float norm_255[3] = { 1 / 255.0f, 1 / 255.0f, 1 / 255.0f };
// in.substract_mean_normalize(mean_vals, std_vals);
in.substract_mean_normalize(0, norm_255);
// in.substract_mean_normalize(mean_vals, std_vals);
// fprintf(stderr, "input shape: %d %d %d %d\n", in.dims, in.h, in.w, in.c);
ncnn::Extractor ex = cls_net.create_extractor();
ex.input("input.1", in);//input �?.param鏂囦欢涓緭鍏ヨ妭鐐瑰悕�?
ncnn::Mat out;
ex.extract("1491", out);
{
ncnn::Layer* softmax = ncnn::create_layer("Softmax");
ncnn::ParamDict pd;
softmax->load_param(pd);
softmax->forward_inplace(out, cls_net.opt);
delete softmax;
}
out = out.reshape(out.w * out.h * out.c);
cls_scores.resize(out.w);
for (int j = 0; j < out.w; j++) {
cls_scores[j] = out[j];
printf("cls_scores[%d]=%f\n", j, cls_scores[j]);
std::string text = "cls_scores[" + std::to_string(j) + "]=" + std::to_string(cls_scores[j]);
cv::Point textOrg(50, (j + 1) * 30);
cv::putText(image, text, textOrg, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 255, 255), 1);
}
if (cls_scores[0] >= 0.5) {
return 1;
}
else {
return -1;
}
return 0;
}
static int detect_yolov5(const cv::Mat& bgr, std::vector<Object>& objects,float prob_threshold = 0.65f)
{
//printf("yolo\n");
//printf("prob_threshold:%f\n",prob_threshold);
ncnn::Net yolov5;
//printf("h:%d,w:%d\n",bgr.rows,bgr.cols);
yolov5.opt.use_vulkan_compute = true;
yolov5.opt.use_bf16_storage = false;
yolov5.opt.lightmode = true;
yolov5.opt.num_threads = 2;
yolov5.opt.blob_allocator = &g_blob_pool_allocator;
yolov5.opt.workspace_allocator = &g_workspace_pool_allocator;
yolov5.load_param("/home/ds-h616/boat_detect/model/eopt316.param");
yolov5.load_model("/home/ds-h616/boat_detect/model/eopt316.bin");
const int target_size = 320;
//const float prob_threshold = 0.60f;
const float nms_threshold = 0.60f;
int img_w = bgr.cols;
int img_h = bgr.rows;
// letterbox pad to multiple of 32
int w = img_w;
int h = img_h;
float scale = 1.f;
if (w > h)
{
scale = (float)target_size / w;
w = target_size;
h = h * scale;
}
else
{
scale = (float)target_size / h;
h = target_size;
w = w * scale;
}
ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR2RGB, img_w, img_h, w, h);
// pad to target_size rectangle
// yolov5/utils/datasets.py letterbox
int wpad = (w + 31) / 32 * 32 - w;
int hpad = (h + 31) / 32 * 32 - h;
ncnn::Mat in_pad;
ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 114.f);
const float norm_vals[3] = {1 / 255.f, 1 / 255.f, 1 / 255.f};
in_pad.substract_mean_normalize(0, norm_vals);
ncnn::Extractor ex = yolov5.create_extractor();
ex.input("images", in_pad);
std::vector<Object> proposals;
// stride 8
{
ncnn::Mat out;
ex.extract("/model.21/Transpose_output_0", out);
ncnn::Mat anchors(6);
anchors[0] = 10.f;
anchors[1] = 13.f;
anchors[2] = 16.f;
anchors[3] = 30.f;
anchors[4] = 33.f;
anchors[5] = 23.f;
std::vector<Object> objects8;
generate_proposals(anchors, 8, in_pad, out, prob_threshold, objects8);
proposals.insert(proposals.end(), objects8.begin(), objects8.end());
}
// stride 16
{
ncnn::Mat out;
ex.extract("/model.21/Transpose_1_output_0", out);
ncnn::Mat anchors(6);
anchors[0] = 30.f;
anchors[1] = 61.f;
anchors[2] = 62.f;
anchors[3] = 45.f;
anchors[4] = 59.f;
anchors[5] = 119.f;
std::vector<Object> objects16;
generate_proposals(anchors, 16, in_pad, out, prob_threshold, objects16);
proposals.insert(proposals.end(), objects16.begin(), objects16.end());
}
// stride 32
{
ncnn::Mat out;
ex.extract("/model.21/Transpose_2_output_0", out);
ncnn::Mat anchors(6);
anchors[0] = 116.f;
anchors[1] = 90.f;
anchors[2] = 156.f;
anchors[3] = 198.f;
anchors[4] = 373.f;
anchors[5] = 326.f;
std::vector<Object> objects32;
generate_proposals(anchors, 32, in_pad, out, prob_threshold, objects32);
proposals.insert(proposals.end(), objects32.begin(), objects32.end());
}
// sort all proposals by score from highest to lowest
qsort_descent_inplace(proposals);
// apply nms with nms_threshold
std::vector<int> picked;
nms_sorted_bboxes(proposals, picked, nms_threshold);
int count = picked.size();
objects.resize(count);
for (int i = 0; i < count; i++)
{
objects[i] = proposals[picked[i]];
// adjust offset to original unpadded
float x0 = (objects[i].rect.x - (wpad / 2)) / scale;
float y0 = (objects[i].rect.y - (hpad / 2)) / scale;
float x1 = (objects[i].rect.x + objects[i].rect.width - (wpad / 2)) / scale;
float y1 = (objects[i].rect.y + objects[i].rect.height - (hpad / 2)) / scale;
// clip
x0 = std::max(std::min(x0, (float)(img_w - 1)), 0.f);
y0 = std::max(std::min(y0, (float)(img_h - 1)), 0.f);
x1 = std::max(std::min(x1, (float)(img_w - 1)), 0.f);
y1 = std::max(std::min(y1, (float)(img_h - 1)), 0.f);
objects[i].rect.x = x0;
objects[i].rect.y = y0;
objects[i].rect.width = x1 - x0;
objects[i].rect.height = y1 - y0;
}
return 0;
}
static void draw_objects(const cv::Mat& bgr, const std::vector<Object>& objects, const int frame_count)
{
static const char* class_names[] = {
"boat"
};
cv::Mat image = bgr.clone();
for (size_t i = 0; i < objects.size(); i++)
{
const Object& obj = objects[i];
fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob,
obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height);
cv::rectangle(image, obj.rect, cv::Scalar(0, 255, 0));
char text[256];
sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);
int baseLine = 0;
cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
int x = obj.rect.x;
int y = obj.rect.y - label_size.height - baseLine;
if (y < 0)
y = 0;
if (x + label_size.width > image.cols)
x = image.cols - label_size.width;
cv::rectangle(image, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),
cv::Scalar(255, 255, 255), -1);
cv::putText(image, text, cv::Point(x, y + label_size.height),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
//std::vector<int> compression_params;
//compression_params.push_back(cv::IMWRITE_JPEG_QUALITY);
//compression_params.push_back(90);
//std::vector<uchar> jpeg_buffer;
//cv::imencode(".jpg", image, jpeg_buffer, compression_params);
//std::ofstream file("/home/orangepi/Desktop/detect/result_" + std::to_string(frame_count) +".dat", std::ios::out | std::ios::binary);
//file.write(reinterpret_cast<const char*>(jpeg_buffer.data()), jpeg_buffer.size());
//file.close();
//cv::imwrite(save_path, image);
cv::imwrite("/home/ds-h616/boat_detect/detect_images/result_" + std::to_string(frame_count) +".jpg", image);
}
//imshow("camera", image);
cv::waitKey(1);
}
int main(int argc, char** argv){
cv::VideoCapture capture;
capture.open(0, cv::CAP_V4L2);
capture.set(cv::CAP_PROP_FRAME_WIDTH,320);
capture.set(cv::CAP_PROP_FRAME_HEIGHT,320);
int total_frames = 0;
cv::Mat frame;
while (true)
{
capture >> frame;
std::vector<Object> objects;
cv::Mat m = frame;
//imshow("camera", m);
cv::waitKey(1);
std::vector<float> cls_score;
/*
int ret = classify(frame, cls_score);
if (ret == 1) {
detect_yolov5(frame, objects,0.5f);
draw_objects(frame,objects,total_frames);
}else{
detect_yolov5(frame, objects,0.7f);
draw_objects(frame,objects,total_frames);
}
*/
int ret = classify(frame, cls_score);
if (ret == 1) {
detect_yolov5(frame, objects);
draw_objects(m,objects,total_frames++);
}
}
capture.release();
cv::destroyAllWindows();
return 0;
}
在香橙派上利用wiringpi控制GPIO,PWM,通过tb6612实现电机控制
利用Socket实现以太网的图片传输
client.cpp(windows)
#include <iostream>
#include <fstream>
#include <cstring>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#define SERVER_PORT 5678
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
using namespace std;
// 函数用于从完整路径中提取文件名
string extractFileName(const string& filePath) {
// 查找最后一个路径分隔符
size_t pos = filePath.find_last_of("/\\");
if (pos != string::npos) {
// 如果找到,返回分隔符之后的部分
return filePath.substr(pos + 1);
} else {
// 如果没有找到分隔符,假设整个路径就是一个文件名
return filePath;
}
}
int main(int argc, char **argv)
{
if (argc != 2) {
cout << "Usage: ./" << argv[0] << " ServerIPAddress\n";
exit(1);
}
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "WSAStartup Failed!" << endl;
return 1;
}
// 设置一个socket地址结构client_addr, 代表客户机的ip地址和端口
SOCKADDR_IN client_addr;
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sin_family = AF_INET; // internet协议族IPv4
client_addr.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY表示自动获取本机地址
client_addr.sin_port = htons(0); // auto allocated, 让系统自动分配一个空闲端口
// 创建用于internet的流协议(TCP)类型socket,用client_socket代表客户端socket
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client_socket == INVALID_SOCKET) {
cout << "Create Socket Failed!" << endl;
WSACleanup();
return 1;
}
else
cout << "Create Socket Success." << endl;
// 把客户端的socket和客户端的socket地址结构绑定
if (bind(client_socket, reinterpret_cast<SOCKADDR*>(&client_addr), sizeof(client_addr)) != 0) {
cout << "Client Bind Port Failed!" << endl;
closesocket(client_socket);
WSACleanup();
return 1;
}
else
cout << "Client Bind Port Success." << endl;
// 设置一个socket地址结构server_addr,代表服务器的internet地址和端口
SOCKADDR_IN server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
// 服务器的IP地址来自程序的参数
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
if (server_addr.sin_addr.s_addr == INADDR_NONE) {
cout << "Server IP Address Error!" << endl;
closesocket(client_socket);
WSACleanup();
return 1;
}
server_addr.sin_port = htons(SERVER_PORT);
int server_addr_length = sizeof(server_addr);
// 向服务器发起连接请求,连接成功后client_socket代表客户端和服务器端的一个socket连接
if (connect(client_socket, reinterpret_cast<SOCKADDR*>(&server_addr), server_addr_length) != 0) {
cout << "Can Not Connect To " << argv[1] << "!" << endl;
closesocket(client_socket);
WSACleanup();
return 1;
}
else
cout << "Already Connected To " << argv[1] << "." << endl;
char file_name[FILE_NAME_MAX_SIZE + 1];
memset(file_name, 0, sizeof(file_name));
cout << "Please Input File Name On Server: ";
cin >> file_name;
char buffer[BUFFER_SIZE];//缓存区
memset(buffer, 0, sizeof(buffer));
strncpy_s(buffer, sizeof(buffer), file_name, strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name));
// 向服务器发送buffer中的数据,此时buffer中存放的是客户端需要接收的文件的名字
send(client_socket, buffer, BUFFER_SIZE, 0);
string fileName = extractFileName(file_name);
ofstream file(fileName, ios::binary);
if (!file.is_open()) {
cout << "File: " << fileName << " Can Not Open To Write!" << endl;
closesocket(client_socket);
WSACleanup();
return 1;
}
// 从服务器端接收数据到buffer中
memset(buffer, 0, sizeof(buffer));
int length = 0;
while ((length = recv(client_socket, buffer, BUFFER_SIZE, 0)) > 0) {
file.write(buffer, length);
memset(buffer, 0, sizeof(buffer));
}
if (length < 0) {
cout << "Receive Data From Server " << argv[1] << " Failed!" << endl;
}
cout << "Receive File: " << fileName << " From Server[" << argv[1] << "] Finished!" << endl;
// 传输完毕,关闭socket
file.close();
closesocket(client_socket);
WSACleanup();
return 0;
}
server.cpp(linux)
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define SERVER_PORT 5678 // 端口号
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
using namespace std;
int main(int argc, char **argv)
{
// 设置一个socket地址结构server_addr,代表服务器ip地址和端口
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(SERVER_PORT);
// 创建用于流协议(TCP)socket,用server_socket代表服务器向客户端提供服务的接口
int server_socket = socket(PF_INET, SOCK_STREAM, 0);
if (server_socket < 0)
{
cout << "Create Socket Failed!" << endl;
exit(1);
}
else
cout << "Create Socket Success." << endl;
// 把socket和socket地址结构绑定
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))
{
cout << "Server Bind Port: " << SERVER_PORT << " Failed!" << endl;
exit(1);
}
else
cout << "Client Bind Port Success." << endl;
// server_socket用于监听
if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE))
{
cout << "Server Listen Failed!" << endl;
exit(1);
}
else
cout << "Listening...." << endl;
// 服务器始终监听
while(1)
{
// 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept
// 接受此请求,同时将client端的地址和端口等信息写入client_addr中
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
// 接受一个从client端到达server端的连接请求,将客户端的信息保存在client_addr中
// 如果没有连接请求,则一直等待直到有连接请求为止,这是accept函数的特性
// accpet返回一个新的socket,这个socket用来与此次连接到server的client进行通信
// 这里的new_server_socket代表了这个通信通道
int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);
if (new_server_socket < 0)
{
cout << "Server Accept Failed!" << endl;
break;
}
else
cout << "Server Accept Success." << endl;
char buffer[BUFFER_SIZE];
bzero(buffer, sizeof(buffer));
length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);
if (length < 0)
{
cout << "Server Recieve Data Failed!" << endl;
break;
}
else
cout << "Server Recieve Data Success." << endl;
char file_name[FILE_NAME_MAX_SIZE + 1];
bzero(file_name, sizeof(file_name));
strncpy(file_name, buffer,
strlen(buffer) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer));
ifstream fp(file_name, ios::binary); //获取文件操作符
if (!fp.is_open())
{
cout << "File:\t" << file_name << " Not Found!" << endl;
}
else
{
bzero(buffer, BUFFER_SIZE);
int file_block_length = 0;
while( (file_block_length = fp.readsome(buffer, BUFFER_SIZE)) > 0)
{
// 发送buffer中的字符串到new_server_socket,实际上就是发送给客户端
if (send(new_server_socket, buffer, file_block_length, 0) < 0)
{
cout << "Send File:\t" << file_name << " Failed!" << endl;
break;
}
bzero(buffer, sizeof(buffer));
}
fp.close();
cout << "File:\t" << file_name << " Transfer Finished!" << endl;
}
close(new_server_socket);
}
close(server_socket);
return 0;
}
芯片选型(H616核心板方案)
按接口需求绘制底板并焊接_V1.0
烧录系统固件出现问题(USB口速率问题,已解决)
按接口需求绘制底板并焊接_V1.0
测试新板子
厂家提供的ubuntu镜像系统未开启摄像头设备支持,按照厂家技术支持的方式,通过在虚拟机上配置系统内核menuconfig、重新编译并烧录。
ls /dev/video*
显示 /dev/video0,说明摄像头设备正常,检查opencv摄像头图像采集是否正常
在新板卡上配置opencv、NCNN环境
运行程序,出现系统崩溃、segmentation fault的错误
崩溃日志显示
- 通过查阅资料,崩溃日志显示是文件系统错误,通过查看磁盘占用,我们怀疑是inodes太小造成的,尝试更改inodes大小,或者重新挂载文件系统,未果。
- 不断gdb,查看程序中止的位置,发现在程序大概率卡死在读取模型后/推理前,分析大概率是硬件资源的问题。
尝试开启vulkan,在虚拟机上交叉编译打开vulkan的ncnn
编译后,在板子上安装vulkan驱动,vulkaninfo
发现只起用了CPU模拟GPU,并没有调用起硬件GPU,查阅资料发现H616仅支持vulkan1.1,但我们并没有找到Vulkan的1.1版本。。。。。
尝试opencv的DNN推理
- 只能作为备用方案,速度较慢、而且模型推理结果并不正常,dnn已经三年没有更新维护了,可能存在网络的支持问题
#include <iostream>
#include <vector>
using namespace std;
#include <opencv2/opencv.hpp>
using namespace cv;
#define SIGMOID(x) (float)(1.0f/(1+exp(-x)))
#define USE_LETTERBOX 1
typedef struct {
int left;
int top;
int right;
int bottom;
float score;
int type;
} BBox;
class YOLOv5
{
private:
int mInputWidth;
int mInputHeight;
int mClassNum;
vector<int> mScales;
vector<int> mOutputWidth;
vector<int> mOutputHeight;
vector<int> mAnchors;
dnn::Net mNet;
public:
YOLOv5(char *model, int width, int height, int classNum, int *anchors);
~YOLOv5();
Mat preProcess(Mat img);
vector<BBox> process(Mat img);
vector<BBox> postProcess(vector<Mat> outputs);
};
std::vector<cv::String> getOutputsNames(const cv::dnn::Net& net)
{
static std::vector<cv::String> names;
if (names.empty())
{
//Get the indices of the output layers, i.e. the layers with unconnected outputs
std::vector<int> outLayers = net.getUnconnectedOutLayers();
//get the names of all the layers in the network
std::vector<cv::String> layersNames = net.getLayerNames();
// Get the names of the output layers in names
names.resize(outLayers.size());
for (size_t i = 0; i < outLayers.size(); ++i)
names[i] = layersNames[outLayers[i] - 1];
}
// for (auto a=names.begin();a!=names.end();a++)
// cout << *a <<endl;
return names;
}
bool cmp(const BBox& a, const BBox& b)
{
return a.score > b.score;
}
float iou(BBox a, BBox b)
{
Rect r1 = Rect(Point(a.left, a.top),Point(a.right, a.bottom));
Rect r2 = Rect(Point(b.left, b.top),Point(b.right, b.bottom));
Rect inter = r1 & r2;
if (inter.area() <= 0)
return 0.;
return inter.area()*1. / (r1.area() + r2.area() - inter.area());
}
void doNMS(vector<BBox>& boxes)
{
for (size_t i=0;i<boxes.size();i++)
for (size_t j=i+1;j<boxes.size();j++)
if (iou(boxes[i],boxes[j]) > 0.85)
{
boxes.erase(boxes.begin()+j--);
}
}
void dumpBBoxes(vector<BBox> boxes)
{
for (size_t i=0;i<boxes.size();i++)
printf("%d %d %d %d\n", boxes[i].left, boxes[i].top,boxes[i].right,boxes[i].bottom);
}
YOLOv5::YOLOv5(char *model, int width, int height, int classNum, int *anchors)
{
mNet = dnn::readNetFromONNX(model);
for (int i=0;i<18;i++)
mAnchors.push_back(anchors[i]);
mInputWidth = width;
mInputHeight = height;
mClassNum = classNum;
mScales.push_back(8);
mScales.push_back(16);
mScales.push_back(32);
for (int i=0;i<3;i++)
{
mOutputWidth.push_back(mInputWidth/mScales[i]);
mOutputHeight.push_back(mInputHeight/mScales[i]);
}
}
YOLOv5::~YOLOv5()
{
}
Mat YOLOv5::preProcess(Mat img)
{
Mat dst = Mat::zeros(mInputHeight,mInputWidth,CV_8UC3);;
#if USE_LETTERBOX
float scaleSize = min(1.*mInputWidth/img.cols, 1.*mInputHeight/img.rows);
int dstWidth = img.cols * scaleSize;
int dstHeight = img.rows*scaleSize;
int wIndex = (mInputWidth - dstWidth)/2;
int hIndex = (mInputHeight - dstHeight)/2;
Mat resized;
resize(img,resized, Size(dstWidth,dstHeight));
dst.setTo(Scalar(127,127,127));
Rect r = Rect(wIndex,hIndex,dstWidth,dstHeight);
resized.copyTo(dst(r));
#else
resize(img,dst,Size(mInputWidth,mInputHeight));
#endif
return dst;
}
vector<BBox> YOLOv5::postProcess(vector<Mat> outputs)
{
vector<BBox> boxes;
//第i个输出层
for (size_t i=0;i<outputs.size();i++)
{
float *data = (float*)(outputs[i].data);
size_t index = 0;
//第d个维度
for (size_t d= 0;d<3;d++)
{
//列
for (size_t m=0;m<mOutputHeight[i];m++)
{
//行
for (size_t n=0;n<mOutputWidth[i];n++)
{
float objConf = SIGMOID(data[index+4]);
if (objConf >= 0.01)
{
// printf("%u %u %u %u\n", i,d,m,n);
float x = (float)(SIGMOID(data[index+0]) * 2 - 0.5 + n) * mScales[i];
float y = (float)(SIGMOID(data[index+1]) * 2 - 0.5 + m) * mScales[i];
float w = (float)pow(SIGMOID(data[index+2])*2,2) * mAnchors[6*i+2*d];
float h = (float)pow(SIGMOID(data[index+3])*2,2) * mAnchors[6*i+2*d+1];
//类别
for (size_t c = 0;c<mClassNum;c++)
{
float score = SIGMOID(data[index+5+c]) * objConf;
if (score >= 0.01)
{
BBox box;
box.left = x-w/2.;
box.right = x+w/2;
box.top = y-h/2.;
box.bottom = y+h/2.;
box.score = score;
box.type = c;
boxes.push_back(box);
}
}
}
index += (5+mClassNum);
}
}
}
}
if (boxes.size() > 1)
{
std::sort(boxes.begin(),boxes.end(), cmp);
doNMS(boxes);
}
return boxes;
}
vector<BBox> YOLOv5::process(Mat img)
{
Mat resized = preProcess(img);
Mat blob;
dnn::blobFromImage(resized, blob, 1/255., Size(mInputWidth, mInputHeight), Scalar(0.,0.,0.), true, false);
mNet.setInput(blob);
vector<Mat> outputs;
mNet.forward(outputs, getOutputsNames(mNet));
vector<BBox> boxes = postProcess(outputs);
#if USE_LETTERBOX
float scaleSize = min(1.*mInputWidth/img.cols, 1.*mInputHeight/img.rows);
int dstWidth = img.cols * scaleSize;
int dstHeight = img.rows*scaleSize;
int wIndex = (mInputWidth - dstWidth)/2;
int hIndex = (mInputHeight - dstHeight)/2;
for (size_t i=0;i<boxes.size();i++)
{
boxes[i].left = (boxes[i].left - wIndex) * img.cols / dstWidth;
boxes[i].right = (boxes[i].right - wIndex) * img.cols / dstWidth;
boxes[i].top = (boxes[i].top - hIndex) * img.rows / dstHeight;
boxes[i].bottom = (boxes[i].bottom - hIndex) * img.rows / dstHeight;
}
#else
for (size_t i=0;i<boxes.size();i++)
{
boxes[i].left = boxes[i].left * img.cols/mInputWidth;
boxes[i].right = boxes[i].right * img.cols/mInputWidth;
boxes[i].top = boxes[i].top * img.rows / mInputHeight;
boxes[i].bottom = boxes[i].bottom * img.rows / mInputHeight;
}
#endif
return boxes;
}
int main(int argc, char** argv)
{
int anchors[] = {10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326};
YOLOv5 *v5 = new YOLOv5(argv[1], 640,480,10,anchors);
Mat frame = imread(argv[2]);
vector<BBox> results = v5->process(frame);
for (size_t i=0;i<results.size();i++)
{
int left = results[i].left;
int right = results[i].right;
int top = results[i].top;
int bottom = results[i].bottom;
rectangle(frame, Point(left,top),Point(right,bottom),Scalar(0,0,255),4,4,0);
}
resize(frame,frame,Size(1920,1080));
imshow("result",frame);
waitKey(0);
return 0;
}
尝试直接移植pytorch使用onnx或其他推理框架MNN等
对NCNN进行参数配置,内存优化,规定线程成功解决系统崩溃问题
- 线程为2时,CPU占用稳定在45%左右
cls_net.opt.use_vulkan_compute = true;
cls_net.opt.use_bf16_storage = false;
//H616貌似不支持bf16精度计算,如果设为true检测结果不正常,在香橙派zero2(h616)上也如此
cls_net.opt.lightmode = true;
cls_net.opt.num_threads = 2;
cls_net.opt.blob_allocator = &g_blob_pool_allocator;
cls_net.opt.workspace_allocator = &g_workspace_pool_allocator;
cls_net.load_param("/home/ds-h616/boat_detect/model/sea_sim_1710228-sim-opt.param");
cls_net.load_model("/home/ds-h616/boat_detect/model/sea_sim_1710228-sim-opt.bin");
操作GPIO、PWM、LRADC
修改设备树文件,将PI11引脚复用为PWM1,与uart3引脚冲突,注释掉uart3。
vim /root/tina/device/config/chips/h616/configs/p2/board.dts
vim /root/tina/lichee/linux-4.9/arch/arm64/boot/dts/sunxi/sun50iw9p1.dtsi
- 在sun50iw9p1.dtsi文件中添加或修改如下代码
pwm: pwm@0300a000 {
compatible = "allwinner,sunxi-pwm";
reg = <0x0 0x0300a000 0x0 0x3ff>;
clocks = <&clk_pwm>;
pwm-number = <6>;
pwm-base = <0x0>;
pwms = <&pwm0>, <&pwm1>,<&pwm2>, <&pwm3>, <&pwm4>, <&pwm5>;
};
pwm0: pwm0@0300a010 {
compatible = "allwinner,sunxi-pwm0";
pinctrl-names = "active", "sleep";
reg_base = <0x0300a000>;
};
pwm1: pwm1@0300a011 {
compatible = "allwinner,sunxi-pwm1";
pinctrl-names = "active", "sleep";
reg_base = <0x0300a000>;
};
pwm2: pwm2@0300a012 {
compatible = "allwinner,sunxi-pwm2";
pinctrl-names = "active", "sleep";
reg_base = <0x0300a000>;
};
pwm3: pwm3@0300a013 {
compatible = "allwinner,sunxi-pwm3";
pinctrl-names = "active", "sleep";
reg_base = <0x0300a000>;
};
pwm4: pwm4@0300a014 {
compatible = "allwinner,sunxi-pwm4";
pinctrl-names = "active", "sleep";
reg_base = <0x0300a000>;
};
pwm5: pwm5@0300a015 {
compatible = "allwinner,sunxi-pwm5";
pinctrl-names = "active", "sleep";
reg_base = <0x0300a000>;
};
- 在board.dts中添加如下代码
&pio {
...............//省略其他模块的引脚设置
pwm1_pin_a: pwm1@0 {
pins = "PI11";#引脚名
function = "pwm1";
allwinner,muxsel = <0x05>;#复用为PWM1
allwinner,drive = <0xffffffff>;#驱动能力
allwinner,pull = <0>;
allwinner,data = <0xffffffff>;
};
pwm1_pin_b: pwm1@1 {
pins = "PI11";
allwinner,function = "io_disabled";
allwinner,muxsel = <0x07>;
allwinner,drive = <0xffffffff>;
allwinner,pull = <0>;
allwinner,data = <0xffffffff>;
};
...............//省略其他模块的引脚设置
};
pwm1: pwm1@0300a011 {
pinctrl-names = "active", "sleep";
pinctrl-0 = <&pwm1_pin_a>;
pinctrl-1 = <&pwm1_pin_b>;
status = "okay";
clk_bypass_output = <0x00>;#禁用clk旁路输出
};
- 设置PWM工作频率和占空比
echo 1 > /sys/class/pwm/pwmchip0/export
echo 1000 > /sys/class/pwm/pwmchip0/pwm1/period
echo 500 > /sys/class/pwm/pwmchip0/pwm1/duty_cycle
echo 1 > /sys/class/pwm/pwmchip0/pwm1/enable
cat /sys/kernel/debug/pwm #查看PWM节点
cat /sys/kernel/debug/pinctrl/pinctrl-handles #查看IO节点
- 设置GPIO属性
echo PI12 > /sys/kernel/debug/sunxi_pinctrl/sunxi_pin
echo PI12 1 > /sys/kernel/debug/sunxi_pinctrl/function
echo PI12 1 > /sys/kernel/debug/sunxi_pinctrl/data
echo PI13 > /sys/kernel/debug/sunxi_pinctrl/sunxi_pin
echo PI13 1 > /sys/kernel/debug/sunxi_pinctrl/function
echo PI13 0 > /sys/kernel/debug/sunxi_pinctrl/data
echo PI14 > /sys/kernel/debug/sunxi_pinctrl/sunxi_pin
echo PI14 1 > /sys/kernel/debug/sunxi_pinctrl/function
echo PI14 1 > /sys/kernel/debug/sunxi_pinctrl/data
- 读取LRADC上报数据
sudo apt-get install bsdmainutils
hexdump /dev/input/event0
c语言操作读写PWM模块
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#define HIGH "1"
#define LOW "0"
int write_to_file(const char *filepath, const char *value) {
int fd = open(filepath, O_WRONLY);
if (fd < 0) {
perror("open");
return -1;
}
ssize_t len = strlen(value);
ssize_t ret = write(fd, value, len);
if (ret < 0) {
perror("write");
close(fd);
return -1;
} else if (ret < len) {
fprintf(stderr, "Incomplete write\n");
close(fd);
return -1;
}
close(fd);
return 0;
}
int setup_pwm() {
if (write_to_file("/sys/class/pwm/pwmchip0/export", "1") < 0) {
fprintf(stderr, "Failed to export PWM chip\n");
return -1;
}
if (write_to_file("/sys/class/pwm/pwmchip0/pwm1/period", "1000") < 0) {
fprintf(stderr, "Failed to set PWM period\n");
return -1;
}
if (write_to_file("/sys/class/pwm/pwmchip0/pwm1/duty_cycle", "500") < 0) {
fprintf(stderr, "Failed to set PWM duty cycle\n");
return -1;
}
if (write_to_file("/sys/class/pwm/pwmchip0/pwm1/enable", "1") < 0) {
fprintf(stderr, "Failed to enable PWM\n");
return -1;
}
return 0;
}
int main() {
const int delay = 1000000;
if (setup_pwm() < 0) {
return -1;
}
if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/sunxi_pin", "PI12") < 0) {
fprintf(stderr, "Failed to set pin\n");
return -1;
}
if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/function", "PI12 1") < 0) {
fprintf(stderr, "Failed to set pin function\n");
return -1;
}
if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/sunxi_pin", "PI13") < 0) {
fprintf(stderr, "Failed to set pin\n");
return -1;
}
if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/function", "PI13 1") < 0) {
fprintf(stderr, "Failed to set pin function\n");
return -1;
}
if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/sunxi_pin", "PI14") < 0) {
fprintf(stderr, "Failed to set pin\n");
return -1;
}
if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/function", "PI14 1") < 0) {
fprintf(stderr, "Failed to set pin function\n");
return -1;
}
if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/data", "PI14 1") < 0) {
fprintf(stderr, "Failed to set pin output\n");
return -1;
}
while (1) {
if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/data", "PI12 1") < 0) {
fprintf(stderr, "Failed to set pin output\n");
return -1;
}
if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/data", "PI13 0") < 0) {
fprintf(stderr, "Failed to set pin output\n");
return -1;
}
usleep(delay);
if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/data", "PI12 0") < 0) {
fprintf(stderr, "Failed to set pin output\n");
return -1;
}
if (write_to_file("/sys/kernel/debug/sunxi_pinctrl/data", "PI13 1") < 0) {
fprintf(stderr, "Failed to set pin output\n");
return -1;
}
usleep(delay);
}
return 0;
}
做开机自动登录
在root下创建.bash_profile文件,写入你要执行的程序脚本。再设置板卡root用户自启动。
vim /etc/systemd/system/getty.target.wants/serial-getty@ttyS0.service #串口登录
vim /etc/systemd/system/getty.target.wants/getty@tty1.service
修改下面的语句
# ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear %I $TERM
ExecStart=-/sbin/agetty --autologin root --noclear %I $TERM
环境安装
#!/bin/bash
sudo apt-get update
sudo apt-get -y upgrade
sudo apt-get install -y g++
sudo apt-get install -y libncurses5-dev
sudo apt-get install -y make
sudo apt-get install -y build-essential
sudo apt-get install -y git cmake
sudo apt-get install -y gfortran
sudo apt-get install -y libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install -y --no-install-recommends libboost-all-dev
sudo apt-get install -y libgflags-dev libgoogle-glog-dev liblmdb-dev libatlas-base-dev
sudo apt-get install -y bsdmainutils
TCP以太网传输20240508
实现输入服务器IP,连接成功后,提示是否申请发送图片y/n。按提示输入y,detect_images文件夹下会保存最新识别到的舰船图片,按提示反复输入y,最新的的舰船图片会按序号依次保存。输入n,退出程序。
client.cpp
#include <iostream>
#include <fstream>
#include <cstring>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#define SERVER_PORT 5678
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
using namespace std;
const string SERVER_FILE_NAME = "/home/ds-h616/boat_detect/detect_images/result.jpg"; // 服务器上的文件路径
const string LOCAL_SAVE_PATH = "detect_images/"; // 本地保存路径
// 函数用于从完整路径中提取文件名并去掉扩展名
string extractFileNameWithoutExt(const string& fileName) {
size_t lastDot = fileName.find_last_of('.');
size_t lastSlash = fileName.find_last_of("/\\");
if (lastDot != string::npos) {
return fileName.substr(lastSlash + 1, lastDot - lastSlash - 1);
}
return fileName.substr(lastSlash + 1);
}
int main()
{
static int fileCounter = 0; // 文件计数器
string SERVER_IP_ADDRESS; // 服务器的 IP 地址
cout << "Enter the server IP address: ";
cin >> SERVER_IP_ADDRESS; // 获取服务器 IP 地址
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "WSAStartup Failed!" << endl;
return 1;
}
while (true) {
cout << "Do you want to transfer a file? (y/n): ";
string response;
cin >> response;
if (response != "y") break;
SOCKADDR_IN client_addr, server_addr;
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
client_addr.sin_port = htons(0);
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client_socket == INVALID_SOCKET) {
cout << "Create Socket Failed!" << endl;
WSACleanup();
return 1;
}
if (bind(client_socket, reinterpret_cast<SOCKADDR*>(&client_addr), sizeof(client_addr)) != 0) {
cout << "Client Bind Port Failed!" << endl;
closesocket(client_socket);
WSACleanup();
return 1;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP_ADDRESS.c_str());
server_addr.sin_port = htons(SERVER_PORT);
if (connect(client_socket, reinterpret_cast<SOCKADDR*>(&server_addr), sizeof(server_addr)) != 0) {
cout << "Cannot Connect To " << SERVER_IP_ADDRESS << "!" << endl;
closesocket(client_socket);
WSACleanup();
return 1;
}
cout << "Connection Established." << endl;
char buffer[BUFFER_SIZE];
strncpy_s(buffer, SERVER_FILE_NAME.c_str(), BUFFER_SIZE);
send(client_socket, buffer, BUFFER_SIZE, 0);
string baseFileName = extractFileNameWithoutExt(SERVER_FILE_NAME);
string fullFileName = LOCAL_SAVE_PATH + baseFileName + "_" + to_string(fileCounter++) + ".jpg";
ofstream file(fullFileName, ios::binary);
if (!file.is_open()) {
cout << "File: " << fullFileName << " Cannot Open To Write!" << endl;
closesocket(client_socket);
WSACleanup();
return 1;
}
int length = 0;
while ((length = recv(client_socket, buffer, BUFFER_SIZE, 0)) > 0) {
file.write(buffer, length);
}
if (length < 0) {
cout << "Receive Data From Server " << SERVER_IP_ADDRESS << " Failed!" << endl;
} else {
cout << "Receive File: " << fullFileName << " From Server [" << SERVER_IP_ADDRESS << "] Finished!" << endl;
}
file.close();
closesocket(client_socket);
}
WSACleanup();
return 0;
}
server.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define SERVER_PORT 5678 // 端口号
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
using namespace std;
int main(int argc, char **argv)
{
// 设置一个socket地址结构server_addr,代表服务器ip地址和端口
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(SERVER_PORT);
// 创建用于流协议(TCP)socket,用server_socket代表服务器向客户端提供服务的接口
int server_socket = socket(PF_INET, SOCK_STREAM, 0);
if (server_socket < 0)
{
cout << "Create Socket Failed!" << endl;
exit(1);
}
else
cout << "Create Socket Success." << endl;
// 把socket和socket地址结构绑定
if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))
{
cout << "Server Bind Port: " << SERVER_PORT << " Failed!" << endl;
exit(1);
}
else
cout << "Client Bind Port Success." << endl;
// server_socket用于监听
if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE))
{
cout << "Server Listen Failed!" << endl;
exit(1);
}
else
cout << "Listening...." << endl;
// 服务器始终监听
while(1)
{
// 定义客户端的socket地址结构client_addr,当收到来自客户端的请求后,调用accept
// 接受此请求,同时将client端的地址和端口等信息写入client_addr中
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
// 接受一个从client端到达server端的连接请求,将客户端的信息保存在client_addr中
// 如果没有连接请求,则一直等待直到有连接请求为止,这是accept函数的特性
// accpet返回一个新的socket,这个socket用来与此次连接到server的client进行通信
// 这里的new_server_socket代表了这个通信通道
int new_server_socket = accept(server_socket, (struct sockaddr*)&client_addr, &length);
if (new_server_socket < 0)
{
cout << "Server Accept Failed!" << endl;
break;
}
else
cout << "Server Accept Success." << endl;
char buffer[BUFFER_SIZE];
bzero(buffer, sizeof(buffer));
length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);
if (length < 0)
{
cout << "Server Recieve Data Failed!" << endl;
break;
}
else
cout << "Server Recieve Data Success." << endl;
char file_name[FILE_NAME_MAX_SIZE + 1];
bzero(file_name, sizeof(file_name));
strncpy(file_name, buffer,
strlen(buffer) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer));
ifstream fp(file_name, ios::binary); //获取文件操作符
if (!fp.is_open())
{
cout << "File:\t" << file_name << " Not Found!" << endl;
}
else
{
bzero(buffer, BUFFER_SIZE);
int file_block_length = 0;
while( (file_block_length = fp.readsome(buffer, BUFFER_SIZE)) > 0)
{
// 发送buffer中的字符串到new_server_socket,实际上就是发送给客户端
if (send(new_server_socket, buffer, file_block_length, 0) < 0)
{
cout << "Send File:\t" << file_name << " Failed!" << endl;
break;
}
bzero(buffer, sizeof(buffer));
}
fp.close();
cout << "File:\t" << file_name << " Transfer Finished!" << endl;
}
close(new_server_socket);
}
close(server_socket);
return 0;
}
配置静态IP
nano /etc/network/interfaces
source /etc/network/interfaces.d/*
# Network is managed by Network manager
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.137.36
netmask 255.255.255.0
gateway 192.168.137.1
# dns-nameservers 4.4.4.4
# dns-nameservers 8.8.8.8
nano /etc/resolv.conf
这个文件可以不用改
# Generated by NetworkManager
nameserver 192.168.137.1
# nameserver 8.8.8.8 # Replace with your nameserver ip
# nameserver 4.4.4.4 # Replace with your nameserver ip
sudo systemctl restart NetworkManager
板卡作为客户端,主机作为服务端,服务端接收多个客户端发送的图片数据20240513
首先考虑持续握手,长连接/短链接。实现客户端每100ms发送一次连接请求,并判断文件是否存在且稳定,如果满足要求则发送,完成后断开。
当我们尝试先判断文件存在且稳定,再建立连接的逻辑时,会经常出现文件传输失败的问题。
因为当你先判断文件是否存在然后再建立连接,这中间可能会有一段时间延迟。在这段时间内,文件状态可能会发生变化,导致不一致的问题。例如:
cpp复制代码if (file_exists_and_stable(FILE_PATH)) {
// 这里可能有一段时间延迟
int sock = socket(AF_INET, SOCK_STREAM, 0);
// 建立连接
}
这种情况下,文件在你判断其稳定之后到你真正建立连接并发送文件之间,可能被其他进程修改或删除,导致文件传输失败。而先建立连接再判断文件状态可以减少这种时间间隔,保证文件状态的一致性。
20240515
采用短链接方式,传一张图像就断开,再次等待连接。然后为了保证图片唯一性,识别的图片按序号排列生成,不是采用同一文件名命名,避免造成写入文件错误。客户端监听文件夹内是否有jpg文件,并排序,将最早被创建或修改的文件发送,完成后删除,关闭socket,等待下一次连接。
client.cpp
#include <iostream>
#include <fstream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <dirent.h>
#include <vector>
#include <algorithm>
#define SERVER_IP "192.168.137.1"
#define SERVER_PORT 5678
#define FILE_DIR "/home/ds-h616/boat_detect/detect_images/"
using namespace std;
bool file_exists_and_stable(string& file_path) {
DIR *dir;
struct dirent *ent;
vector<string> jpg_files;
if ((dir = opendir(FILE_DIR)) != nullptr) {
while ((ent = readdir(dir)) != nullptr) {
string file_name = ent->d_name;
if (file_name.size() > 4 && file_name.substr(file_name.size() - 4) == ".jpg") {
jpg_files.push_back(file_name);
}
}
closedir(dir);
} else {
cerr << "Could not open directory: " << FILE_DIR << endl;
return false;
}
if (jpg_files.empty()) {
return false;
}
sort(jpg_files.begin(), jpg_files.end(), [](const string &a, const string &b) {
struct stat stat_a, stat_b;
stat((FILE_DIR + a).c_str(), &stat_a);
stat((FILE_DIR + b).c_str(), &stat_b);
return stat_a.st_mtime < stat_b.st_mtime;
});
file_path = FILE_DIR + jpg_files.front();
struct stat file_stat;
if (stat(file_path.c_str(), &file_stat) != 0) {
return false;
}
time_t initial_time = file_stat.st_mtime;
usleep(100000); // Wait for a short period to ensure no more writes to the file
stat(file_path.c_str(), &file_stat); // Re-check the modification time
return initial_time == file_stat.st_mtime;
}
void send_file(const string& file_path, int sock) {
ifstream file(file_path, ios::binary);
if (!file.is_open()) {
cerr << "Failed to open file: " << file_path << endl;
return;
}
string filename = file_path.substr(file_path.find_last_of("/") + 1);
send(sock, filename.c_str(), filename.length(), 0);
char buffer[1024];
while (!file.eof()) {
file.read(buffer, sizeof(buffer));
int bytes_read = file.gcount();
int total_sent = 0;
while (total_sent < bytes_read) {
int bytes_sent = send(sock, buffer + total_sent, bytes_read - total_sent, 0);
if (bytes_sent < 0) {
cerr << "Failed to send file data." << endl;
file.close();
return;
}
total_sent += bytes_sent;
}
}
file.close();
remove(file_path.c_str()); // Delete the file after sending
cout << "File sent and deleted: " << file_path << endl;
}
int main() {
while (true) {
string file_path;
if (!file_exists_and_stable(file_path)) {
cout << "No more files to send." << endl;
sleep(1); // Wait for a while before checking for new files
continue; // No more files to send, continue to next iteration
}
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
cerr << "Socket creation failed." << endl;
sleep(1);
continue;
}
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
cerr << "Could not connect to server." << endl;
close(sock);
sleep(1);
continue;
}
cout << "Connected to server at " << SERVER_IP << ":" << SERVER_PORT << endl;
send_file(file_path, sock);
close(sock);
usleep(100000); // Short delay before next connection attempt
}
return 0;
}
server.cpp
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <thread>
#include <mutex>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <direct.h>
#pragma comment(lib, "Ws2_32.lib")
#define SERVER_PORT 5678
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
using namespace std;
mutex file_count_mutex;
void handle_client(SOCKET client_socket) {
char buffer[BUFFER_SIZE];
char file_name[FILE_NAME_MAX_SIZE + 1];
sockaddr_in client_info = {};
int addr_size = sizeof(client_info);
getpeername(client_socket, (sockaddr*)&client_info, &addr_size);
char* client_ip = inet_ntoa(client_info.sin_addr);
string directory = "./" + string(client_ip);
_mkdir(directory.c_str());
ZeroMemory(file_name, sizeof(file_name));
int bytes = recv(client_socket, file_name, FILE_NAME_MAX_SIZE, 0);
if (bytes > 0) {
file_name[bytes] = '\0';
string final_file_name = string(file_name);
string file_path = directory + "/" + final_file_name;
ofstream out_file(file_path, ios::binary);
if (!out_file) {
cerr << "Failed to open file for writing: " << file_path << endl;
closesocket(client_socket);
return;
}
do {
ZeroMemory(buffer, BUFFER_SIZE);
bytes = recv(client_socket, buffer, BUFFER_SIZE, 0);
if (bytes > 0) {
out_file.write(buffer, bytes);
}
} while (bytes > 0);
out_file.close();
if (bytes == 0) {
cout << "File received and saved: " << file_path << endl;
} else {
cerr << "Failed to receive the file data or connection lost." << endl;
}
} else {
cerr << "Failed to receive the file name." << endl;
}
closesocket(client_socket);
}
int main() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == INVALID_SOCKET) {
cerr << "Create Socket Failed!" << endl;
WSACleanup();
return 1;
}
sockaddr_in server_addr;
ZeroMemory(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(SERVER_PORT);
if (bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr))) {
cerr << "Server Bind Port: " << SERVER_PORT << " Failed!" << endl;
WSACleanup();
return 1;
}
if (listen(server_socket, 20)) {
cerr << "Server Listen Failed!" << endl;
WSACleanup();
return 1;
}
cout << "Server is listening on port " << SERVER_PORT << endl;
while (true) {
sockaddr_in client_addr;
int client_addr_len = sizeof(client_addr);
SOCKET client_socket = accept(server_socket, (sockaddr*)&client_addr, &client_addr_len);
if (client_socket == INVALID_SOCKET) {
cerr << "Server Accept Failed!" << endl;
continue;
}
thread(handle_client, client_socket).detach();
}
closesocket(server_socket);
WSACleanup();
return 0;
}
通过systemd
服务文件,自启动程序sudo nano /etc/systemd/system/djqd.service
[Unit]
Description=djqd Service
After=network.target
[Service]
ExecStart=/home/ds-h616/boat_detect/obj/djqd
WorkingDirectory=/home/ds-h616/boat_detect/obj/
User=root
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable djqd.service
# sudo systemctl start djqd.service
sudo systemctl status djqd.service
reboot