【OpenCV】C++红绿灯轮廓识别+ROS话题实现

目录

前言

一、背景知识

Opencv轮廓检测

ROS相关知识

二、环境依赖

三、具体实现

Step1:初始化ROS,订阅话题

Step2:接收话题,进入回调

1. 帧处理 

2. 膨胀腐蚀处理

Step3:红绿特征处理

1. 提取绘制轮廓

2. 转换矩形、排序

3. 显示检测结果

四、完整代码

五、使用方法

CMakeLists.txt

 package.xml

detect.launch

六、后续改进思路 


前言

根据需求需要使用Opencv实现红绿灯检测的功能,于是在猿力猪大佬的【OpenCV】红绿灯识别 轮廓识别 C++ OpenCV 案例实现 文章的基础上,将Opencv 3中的写法改成了Opencv 4,在具体图片处理的部分也按照我自己的逻辑进行了一些改动,并写成ROS工作空间包含了完整的话题读取,图片处理及检测结果显示。

一、背景知识

Opencv轮廓检测

这个部分主要用到Opencv中的findContours函数,具体介绍可以参考:findContours函数参数详解,关于轮廓检测的基本概念参考上面提到的猿力猪大佬的博文即可。

ROS相关知识

ROS编译方式:[详细教程]使用ros编译运行自己写的程序

ROS多节点运行:ROS中的roslaunch命令和launch文件(ROS入门学习笔记四)

ROS话题订阅:ROS消息发布(publish)与订阅(subscribe)(C++代码详解)

二、环境依赖

  • OpenCV 4
  • cv_bridge

三、具体实现

Step1:初始化ROS,订阅话题

int main(int argc, char **argv)
{
    ros::init(argc, argv, "tld_cv_node");
    ros::NodeHandle nh;

    std::string image_topic;
    nh.param<std::string>("sub_topic", image_topic, "/src_rgb/compressed");
    std::cout << "image_topic: " << image_topic << std::endl;

    ros::Subscriber camera_sub =
        nh.subscribe(image_topic, 2, camera_callback);
    ros::spin();
    ros::waitForShutdown();
    return 0;
}

Step2:接收话题,进入回调

1. 帧处理 

  • 从图像话题中读取图像并转换为BGR格式,调整亮度,而后转为YCrCb格式,提取ROI,根据红绿阈值拆分红色和绿色分量
cv_bridge::CvImagePtr cv_ptr =
            cv_bridge::toCvCopy(msg_pic, sensor_msgs::image_encodings::BGR8);
        if (rotated)
        {
            cv::flip(cv_ptr->image, src_image, -1);
        }
        else
        {
            src_image = cv_ptr->image;
        }
        // std::cout << "src_image" << src_image.size() << std::endl;

        // 亮度参数
        double a = 0.3;
        double b = (1 - a) * 125;

        // 统计检测用时
        clock_t start, end;

        start = clock();

        src_image.copyTo(frame);
        // 调整亮度
        src_image.convertTo(img, img.type(), a, b);
        // cv::imshow("img",img);

        // 使用ROI(感兴趣区域)方式截取图像
        cv::Rect roi(0, 0, 2048, 768); // 定义roi,图片尺寸2048*1536
        // std::cout << "img size:" << img.size() << std::endl;
        cv::Mat roi_image = img(roi);

        // 转换为YCrCb颜色空间
        cvtColor(roi_image, imgYCrCb, cv::COLOR_BGR2YCrCb);
        // cvtColor(img, imgYCrCb, cv::COLOR_BGR2YCrCb);
        imgRed.create(imgYCrCb.rows, imgYCrCb.cols, CV_8UC1);
        imgGreen.create(imgYCrCb.rows, imgYCrCb.cols, CV_8UC1);

        // 分解YCrCb的三个成分
        std::vector<cv::Mat> planes;
        split(imgYCrCb, planes);

        // 遍历以根据Cr分量拆分红色和绿色
        cv::MatIterator_<uchar> it_Cr = planes[1].begin<uchar>(),
                                it_Cr_end = planes[1].end<uchar>();
        cv::MatIterator_<uchar> it_Red = imgRed.begin<uchar>();
        cv::MatIterator_<uchar> it_Green = imgGreen.begin<uchar>();

        for (; it_Cr != it_Cr_end; ++it_Cr, ++it_Red, ++it_Green)
        {
            // RED, 145<Cr<470 红色
            // if (*it_Cr > 145 && *it_Cr < 470)
            if (*it_Cr > 140 && *it_Cr < 470)
                *it_Red = 255;
            else
                *it_Red = 0;

            // GREEN 95<Cr<110 绿色
            if (*it_Cr > 95 && *it_Cr < 110)
                *it_Green = 255;
            else
                *it_Green = 0;
            // YELLOW 黄色
        }

PS:ROI选取这里只是随意截取了图片的上半部分。

2. 膨胀腐蚀处理

  • 膨胀的第三个参数:膨胀操作的内核,我根据实际场景的检测效果进行了修改
// 膨胀和腐蚀
        dilate(imgRed, imgRed, cv::Mat(8, 8, CV_8UC1), cv::Point(-1, -1));
        erode(imgRed, imgRed, cv::Mat(1, 1, CV_8UC1), cv::Point(-1, -1));
        dilate(imgGreen, imgGreen, cv::Mat(12, 12, CV_8UC1), cv::Point(-1, -1));
        erode(imgGreen, imgGreen, cv::Mat(1, 1, CV_8UC1), cv::Point(-1, -1));

Step3:红绿特征处理

  • 这是我改动最大的一个函数,只保留了原作者提取轮廓转换为矩形的思路。先提取、绘制轮廓、显示检测结果,然后对得到的矩形进行位置排序,再对轮廓依次进行显示。

1. 提取绘制轮廓

// 提取轮廓
    findContours(tmp_Red, contours_Red, hierarchy_Red, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
    findContours(tmp_Green, contours_Green, hierarchy_Green, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

    // 绘制轮廓
    drawContours(frame, contours_Red, -1, cv::Scalar(0, 0, 255), cv::FILLED); // Red
    std::cout << "提取到的红色轮廓数量:" << contours_Red.size() << std::endl;
    drawContours(frame, contours_Green, -1, cv::Scalar(0, 255, 0), cv::FILLED); // Green
    std::cout << "提取到的绿色轮廓数量:" << contours_Green.size() << std::endl;

    // 显示轮廓
    //  imshow("contours", frame);

    trackBox_Red = new cv::Rect[contours_Red.size()];
    trackBox_Green = new cv::Rect[contours_Green.size()];

2. 转换矩形、排序

  • 此处需特别注意trackBox指针的清空
  • 对结构体的排序方式参考了用sort对结构体某个元素排序的方法
// 确定要跟踪的区域
    for (int i = 0; i < contours_Red.size(); i++)
    {
        // Yi opencv4 不支持 CvSeq
        trackBox_Red[i] = cv::boundingRect(contours_Red[i]);
    }

    for (int i = 0; i < contours_Green.size(); i++)
    {
        // Yi opencv4 不支持 CvSeq
        trackBox_Green[i] = cv::boundingRect(contours_Green[i]);
    }

    // imshow("contours", tmp);

    // Rect.tl() 返回矩形左上顶点的坐标
    for (int i = 0; i < contours_Red.size(); i++)
    {
        Store_x_color a;
        a.x = trackBox_Red[i].tl().x;
        a.y = trackBox_Red[i].tl().y;
        a.color = 0;
        x_color.push_back(a);
    }

    for (int i = 0; i < contours_Green.size(); i++)
    {
        Store_x_color a;
        a.x = trackBox_Green[i].tl().x;
        a.y = trackBox_Green[i].tl().y;
        a.color = 1;
        x_color.push_back(a);
    }

    // 清空指针
    delete[] trackBox_Red;
    delete[] trackBox_Green;

    // 对左上顶点横坐标进行排序
    sort(x_color.begin(), x_color.end(), CompareByX);

3. 显示检测结果

// 显示结果
    for (int i = 0; i < x_color.size(); i++)
    {
        if (0 == x_color[i].color)
            cv::putText(frame, "Red", cv::Point(x_color[i].x, x_color[i].y - 25), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 2, 8, 0);
        else if (1 == x_color[i].color)
            cv::putText(frame, "Green", cv::Point(x_color[i].x, x_color[i].y - 25), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0), 2, 8, 0);
        else if (2 == x_color[i].color)
            cv::putText(frame, "Yellow", cv::Point(x_color[i].x, x_color[i].y - 25), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 255), 2, 8, 0);
        else
            cv::putText(frame, "Lights off", cv::Point(x_color[i].x, x_color[i].y - 25), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 255, 255), 2, 8, 0);
    }

    cv::namedWindow("tld_result", 0);
    cv::resizeWindow("tld_result", 1920, 1080);
    cv::imshow("tld_result", frame);
    cv::waitKey(1);

实际检测结果如下图所示: 

  

四、完整代码

/*
 * @CopyRight: All Rights Reserved by Plusgo
 * @Author: Yi
 * @E-mail: waterwinsor@gmail.com
 * @Date: 2023年 05月 06日 星期六 15:44:35
 * @LastEditTime: 2023年 05月 08日 星期一 10:07:30
 */

// requirements: opencv 4

#include <iostream>
#include <fstream>
#include <time.h>
#include <algorithm>

#include <cv_bridge/cv_bridge.h>
#include <image_transport/image_transport.h>
#include <ros/ros.h>
#include <sensor_msgs/Image.h>
#include <sensor_msgs/PointCloud2.h>

#include <opencv2/opencv.hpp>
#include "opencv2/imgproc.hpp"
#include <opencv2/imgproc/types_c.h>

struct Store_x_color
{
    int x;     // 存储左上顶点横坐标
    int y;     // 存储左上顶点纵坐标
    int color; // 存储当前点颜色
};

// Function headers
void processImg(cv::Mat, cv::Mat); // 前红后绿
bool CompareByX(const Store_x_color &, const Store_x_color &);

// Global variables
cv::Mat src_image;
bool rotated = true; // rotate 180

cv::Mat frame;
cv::Mat img;
cv::Mat imgYCrCb;
cv::Mat imgGreen;
cv::Mat imgRed;
cv::Mat imgYellow;
std::vector<Store_x_color> x_color;

void camera_callback(const sensor_msgs::CompressedImageConstPtr &msg_pic)
{
    try
    {
        cv_bridge::CvImagePtr cv_ptr =
            cv_bridge::toCvCopy(msg_pic, sensor_msgs::image_encodings::BGR8);
        if (rotated)
        {
            cv::flip(cv_ptr->image, src_image, -1);
        }
        else
        {
            src_image = cv_ptr->image;
        }
        // std::cout << "src_image" << src_image.size() << std::endl;

        // 亮度参数
        double a = 0.3;
        double b = (1 - a) * 125;

        // 统计检测用时
        clock_t start, end;

        start = clock();

        src_image.copyTo(frame);
        // 调整亮度
        src_image.convertTo(img, img.type(), a, b);
        // cv::imshow("img",img);

        // 使用ROI(感兴趣区域)方式截取图像
        cv::Rect roi(0, 0, 2048, 768); // 定义roi,图片尺寸2048*1536
        // std::cout << "img size:" << img.size() << std::endl;
        cv::Mat roi_image = img(roi);

        // 转换为YCrCb颜色空间
        cvtColor(roi_image, imgYCrCb, cv::COLOR_BGR2YCrCb);
        // cvtColor(img, imgYCrCb, cv::COLOR_BGR2YCrCb);
        imgRed.create(imgYCrCb.rows, imgYCrCb.cols, CV_8UC1);
        imgGreen.create(imgYCrCb.rows, imgYCrCb.cols, CV_8UC1);

        // 分解YCrCb的三个成分
        std::vector<cv::Mat> planes;
        split(imgYCrCb, planes);

        // 遍历以根据Cr分量拆分红色和绿色
        cv::MatIterator_<uchar> it_Cr = planes[1].begin<uchar>(),
                                it_Cr_end = planes[1].end<uchar>();
        cv::MatIterator_<uchar> it_Red = imgRed.begin<uchar>();
        cv::MatIterator_<uchar> it_Green = imgGreen.begin<uchar>();

        for (; it_Cr != it_Cr_end; ++it_Cr, ++it_Red, ++it_Green)
        {
            // RED, 145<Cr<470 红色
            // if (*it_Cr > 145 && *it_Cr < 470)
            if (*it_Cr > 140 && *it_Cr < 470)
                *it_Red = 255;
            else
                *it_Red = 0;

            // GREEN 95<Cr<110 绿色
            if (*it_Cr > 95 && *it_Cr < 110)
                *it_Green = 255;
            else
                *it_Green = 0;
            // YELLOW 黄色
        }

        // 膨胀和腐蚀
        dilate(imgRed, imgRed, cv::Mat(8, 8, CV_8UC1), cv::Point(-1, -1));
        erode(imgRed, imgRed, cv::Mat(1, 1, CV_8UC1), cv::Point(-1, -1));
        dilate(imgGreen, imgGreen, cv::Mat(12, 12, CV_8UC1), cv::Point(-1, -1));
        erode(imgGreen, imgGreen, cv::Mat(1, 1, CV_8UC1), cv::Point(-1, -1));

        // 检测和显示
        processImg(imgRed, imgGreen);

        // 清空x_color
        x_color.clear();

        end = clock();
        std::cout << "检测时间:" << (double)(end - start) / CLOCKS_PER_SEC << std::endl; // 打印检测时间
    }
    catch (cv_bridge::Exception e)
    {
        ROS_ERROR_STREAM("cant't get image");
    }
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "tld_cv_node");
    ros::NodeHandle nh;

    std::string image_topic;
    nh.param<std::string>("sub_topic", image_topic, "/src_rgb/compressed");
    std::cout << "image_topic: " << image_topic << std::endl;

    ros::Subscriber camera_sub =
        nh.subscribe(image_topic, 2, camera_callback);
    ros::spin();
    ros::waitForShutdown();
    return 0;
}

void processImg(cv::Mat src_Red, cv::Mat src_Green)
{
    cv::Mat tmp_Red;
    cv::Mat tmp_Green;

    std::vector<std::vector<cv::Point>> contours_Red;
    std::vector<std::vector<cv::Point>> contours_Green;

    std::vector<cv::Vec4i> hierarchy_Red;
    std::vector<cv::Vec4i> hierarchy_Green;

    cv::Rect *trackBox_Red;
    cv::Rect *trackBox_Green;

    src_Red.copyTo(tmp_Red);
    src_Green.copyTo(tmp_Green);

    // 提取轮廓
    findContours(tmp_Red, contours_Red, hierarchy_Red, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
    findContours(tmp_Green, contours_Green, hierarchy_Green, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

    // 绘制轮廓
    drawContours(frame, contours_Red, -1, cv::Scalar(0, 0, 255), cv::FILLED); // Red
    std::cout << "提取到的红色轮廓数量:" << contours_Red.size() << std::endl;
    drawContours(frame, contours_Green, -1, cv::Scalar(0, 255, 0), cv::FILLED); // Green
    std::cout << "提取到的绿色轮廓数量:" << contours_Green.size() << std::endl;

    // 显示轮廓
    //  imshow("contours", frame);

    trackBox_Red = new cv::Rect[contours_Red.size()];
    trackBox_Green = new cv::Rect[contours_Green.size()];

    // 确定要跟踪的区域
    for (int i = 0; i < contours_Red.size(); i++)
    {
        // Yi opencv4 不支持 CvSeq
        trackBox_Red[i] = cv::boundingRect(contours_Red[i]);
    }

    for (int i = 0; i < contours_Green.size(); i++)
    {
        // Yi opencv4 不支持 CvSeq
        trackBox_Green[i] = cv::boundingRect(contours_Green[i]);
    }

    // imshow("contours", tmp);

    // Rect.tl() 返回矩形左上顶点的坐标
    for (int i = 0; i < contours_Red.size(); i++)
    {
        Store_x_color a;
        a.x = trackBox_Red[i].tl().x;
        a.y = trackBox_Red[i].tl().y;
        a.color = 0;
        x_color.push_back(a);
    }

    for (int i = 0; i < contours_Green.size(); i++)
    {
        Store_x_color a;
        a.x = trackBox_Green[i].tl().x;
        a.y = trackBox_Green[i].tl().y;
        a.color = 1;
        x_color.push_back(a);
    }

    // 清空指针
    delete[] trackBox_Red;
    delete[] trackBox_Green;

    // 对左上顶点横坐标进行排序
    sort(x_color.begin(), x_color.end(), CompareByX);

    // 显示结果
    for (int i = 0; i < x_color.size(); i++)
    {
        if (0 == x_color[i].color)
            cv::putText(frame, "Red", cv::Point(x_color[i].x, x_color[i].y - 25), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 2, 8, 0);
        else if (1 == x_color[i].color)
            cv::putText(frame, "Green", cv::Point(x_color[i].x, x_color[i].y - 25), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0), 2, 8, 0);
        else if (2 == x_color[i].color)
            cv::putText(frame, "Yellow", cv::Point(x_color[i].x, x_color[i].y - 25), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 255), 2, 8, 0);
        else
            cv::putText(frame, "Lights off", cv::Point(x_color[i].x, x_color[i].y - 25), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 255, 255), 2, 8, 0);
    }

    cv::namedWindow("tld_result", 0);
    cv::resizeWindow("tld_result", 1920, 1080);
    cv::imshow("tld_result", frame);
    cv::waitKey(1);

    return;
}

bool CompareByX(const Store_x_color &a, const Store_x_color &b)
{
    return a.x < b.x;
}

五、使用方法

编译所需的CMakeLists.txt、package.xml和运行所需roslaunch文件如下

  • CMakeLists.txt

cmake_minimum_required(VERSION 2.8.3)
project(tld_cv)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_BUILD_TYPE "Release")  # Debug Release RelWithDebInfo

add_definitions(-O2 -pthread)
add_compile_options(-std=c++11)

find_package(OpenCV REQUIRED)
find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
  sensor_msgs
  cv_bridge
  image_transport
)

catkin_package(
    CATKIN_DEPENDS
    roscpp
    std_msgs
    sensor_msgs
    cv_bridge
    image_transport
)

include_directories(
# include
  ${catkin_INCLUDE_DIRS}
  ${OpenCV_INCLUDE_DIRS}
)

add_executable(tld_cv src/main.cpp)
target_link_libraries(tld_cv
        ${catkin_LIBRARIES}
        ${OpenCV_LIBRARIES}
        )

  •  package.xml

<?xml version="1.0"?>
<package format="2">
  <name>tld_cv</name>
  <version>0.0.0</version>
  <description>The tld_cv package</description>

  <maintainer email="sunyuzhe@plusgo.com.cn">sunyuzhe</maintainer>

  <license>TODO</license>

  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>cv_bridge</build_depend>
  <build_depend>image_transport</build_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>sensor_msgs</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_export_depend>cv_bridge</build_export_depend>
  <build_export_depend>image_transport</build_export_depend>
  <build_export_depend>roscpp</build_export_depend>
  <build_export_depend>sensor_msgs</build_export_depend>
  <build_export_depend>std_msgs</build_export_depend>
  <exec_depend>cv_bridge</exec_depend>
  <exec_depend>image_transport</exec_depend>
  <exec_depend>roscpp</exec_depend>
  <exec_depend>sensor_msgs</exec_depend>
  <exec_depend>std_msgs</exec_depend>


  <!-- The export tag contains other, unspecified, tags -->
  <export>
    <!-- Other tools can request additional information be placed here -->

  </export>
</package>
  • detect.launch

<launch>

    <arg name="sub_image_topic" value="/camera/image_color/compressed"/>
    
    <param name="sub_topic" value="$(arg sub_image_topic)"/>

    <node pkg="tld_cv" type="tld_cv" name="tld_cv" output="screen" />

</launch>

六、后续改进思路 

改进可有如下几个方向:

  • ROI

根据具体自动驾驶场景,可以通过红绿灯位置、高度、相机安装方式、车辆定位和IMU信息实时计算出一个更为精确的ROI,检测再进行排序即可确定红绿灯的个数和顺序,方便编写后处理中的逻辑。

  • 筛选面积

根据检测后的结果筛选较大的几个轮廓,可以排除掉某些较小物体的误检干扰 

本人接触OpenCV时间尚短、经验尚浅,如果有任何疏漏、错误还请大家指出~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/21518.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【网络协议详解】——数据链路层协议(学习笔记)

&#x1f4d6; 前言&#xff1a;数据链路层是 OSI 模型中的第二层&#xff0c;位于物理层之上&#xff0c;是通信网络中的重要组成部分之一。数据链路层协议负责将网络层传输的数据分组封装成帧&#xff0c;传输到物理层&#xff0c;并通过物理介质进行传输。同时&#xff0c;数…

算法笔记:A2-A4-RSRQ切换算法

1 LTE 切换 LTE切换是移动通信网络中的一个过程&#xff0c;移动设备在保持无间断服务的情况下&#xff0c;将其连接从一个基站切换到另一个基站。当移动设备离开当前基站的覆盖范围或网络资源拥塞时&#xff0c;就需要进行切换。LTE切换通常是基于特定的条件触发的&#xff0…

makefile 学习(1):C/C++ 编译过程

1. GCC 介绍 1.1 介绍 GCC 官方文档 https://gcc.gnu.org/onlinedocs/ 官方文档是最权威的&#xff0c;网上所有的答案都来自官方文档国内论坛参差不齐&#xff0c;找到好的答案比较花时间&#xff0c;并且很容易被错误的文档误导。所以推荐看官方文档靠谱点&#xff0c;并且…

二、数据字典开发

文章目录 二、数据字典开发1、搭建service-cmn模块1.1 搭建service-cmn模块1.2 修改配置1.3 启动类 2、数据字典列表2.1 数据字典列表接口2.1.1 model模块添加数据字典实体2.1.2 添加数据字典mapper2.1.4 添加数据字典controller 2.2 数据字典列表前端2.2.1 添加路由2.2.2 定义…

【Java算法题】剑指offer_01数据结构

前言 刷题链接&#xff1a; https://www.nowcoder.com/exam/oj/ta?page2&tpId13&type265 1. 链表 JZ24 反转链表 思路&#xff1a;基本操作&#xff0c;如下所示。 /* public class ListNode {int val;ListNode next null;ListNode(int val) {this.val val;} }…

ad18学习笔记一

如何自学altium designer如何自学altium designer&#xff1f; - 知乎如何自学altium designer 这里面有ad官方推荐的b站的视频&#xff1a;可以直接去b站关注ad官方账号 AltiumChina&#xff0c;它本身就发布了很多实用教程。 在知乎的这个界面也有Altium Designer Ver18_官…

c++ 11标准模板(STL) std::set(六)

定义于头文件 <set> template< class Key, class Compare std::less<Key>, class Allocator std::allocator<Key> > class set;(1)namespace pmr { template <class Key, class Compare std::less<Key>> using se…

如何使用SCQA模型提高表达能力

SCQA架构是“结构化表达”工具。 一、什么是“SCQA架构”&#xff1f;‍ S&#xff08;Situation&#xff09;情景——由熟悉的情境或事实引入 C&#xff08;Complication&#xff09;冲突——指出实际面临的困境或冲突 Q&#xff08;Question&#xff09;疑问——你如何分析…

文本三剑客正则表达式3

文章目录 文本三剑客&正则表达式31 awk工作原理2 awk的基本格式及其内置变量2.1 基本格式2.2 内置变量2.3 示例2.3.1 直接打印所有内容2.3.2 取每一行的第一列2.3.3 打印行号&#xff0c;及所有内容2.3.4 打印第三行2.3.5 打印2-4行2.3.6 打印第2行和第4行2.3.7 用正则表达…

基于harbor安装私有镜像仓库

目录 Harbor介绍 Harbor安装 下载完成后&#xff0c;在压缩包解压到/usr/local目录下&#xff1a; 修改Harbor配置文件 推送本地镜像到harbor上 1、给本地镜像打一个标签 2、 设置docker的daemon.json 3、重启docker 4、使用docker登录harbor 5、把本地的镜像push到harbor…

银豆信息张雪灿:钻石级合作伙伴的增长秘诀

编者按&#xff1a; 杭州银豆信息技术有限公司&#xff08;简称“银豆”&#xff09;&#xff0c;是一家专注于云计算服务的高科技企业&#xff0c;目前已为2000家企业级客户提供了专业的行业解决方案, 与人民网、光大银行、长安汽车金融、vivo金融、浙江省农科院、淄博市大数…

MediaPipe虹膜检测:实时虹膜跟踪和深度估计

包括计算摄影(例如,人像模式和闪光反射)和增强现实效果(例如,虚拟化身)在内的大量实际应用都依赖于通过跟踪虹膜来估计眼睛位置。一旦获得了准确的光圈跟踪,我们就可以确定从相机到用户的公制距离,而无需使用专用的深度传感器。反过来,这可以改善各种用例,从计算摄影…

机器学习之SVM分类器介绍——核函数、SVM分类器的使用

系类文章目录 机器学习算法——KD树算法介绍以及案例介绍 机器学习的一些常见算法介绍【线性回归&#xff0c;岭回归&#xff0c;套索回归&#xff0c;弹性网络】 文章目录 一、SVM支持向量机介绍 1.1、SVM介绍 1.2、几种核函数简介 a、sigmoid核函数 b、非线性SVM与核函…

从内网护卫到零信任尖兵:腾讯iOA炼成记

腾讯既是企业产品的服务商又是使用者&#xff0c;很多产品最原始的出发点最早只是为了解决腾讯自身某一个需求&#xff0c;经过不断地发展完善和业务场景锤炼&#xff0c;最终进化成一个成熟的企服产品。本系列文章讲述的是这样一组Made in Tencent故事&#xff0c;这是系列的第…

Word批量更改图片环绕方式与=尺寸大小

前提&#xff1a;一份Word文档里面有100张图片&#xff0c;有大有小&#xff0c;需要将100张图片更改为统一大小&#xff0c;宽度与高度均为5厘米&#xff0c;同时环绕方式也需要改成四周型。 默认Word图片的默认环绕方式为嵌入型&#xff0c;需要统一更改为四周型&#xff0c;…

linux 安装 maven 3.8 版本

文章目录 1&#xff1a;maven 仓库官网 2、下载安装包 3、使用&#xff1a;Xftp 上传到你想放的目录 4、解压文件 ​编辑 5、配置环境变量 ​编辑 6、刷新 /etc/profile 文件 7、查看maven 版本 1&#xff1a;maven 仓库官网 Maven – Download Apache Mavenhttps://mave…

Java 基础进阶篇(十五):IO 流总结(全网最全面)

文章目录 前置内容&#xff1a;字符集一、IO 流概述二、字节流2.1 文件字节输入流 FileInputStream2.1.1 案例&#xff1a;每次读取一个字节2.1.2 案例&#xff1a;每次读取一个字节数组2.1.3 案例&#xff1a;读取文件的全部字节 2.2 文件字节输出流 FileOutputStream2.3 文件…

使用Docker Dockerfile构建php LNMP集成开发环境,并运行Thinkphp5

宿主机环境 系统&#xff1a;MAC、Windows10 Docker版本&#xff1a;Docker version 23.0.5 Docker Desktop:Dockerdesktop官方地址 前言 这篇主要介绍如何在Mac、Windows10使用docker搭建LNMP集成开发环境。下面我会写Dockerfile编译安装Nginxphp基础环境。mysql、redis基…

pynvme操作流程

如下操作pynvme运行在fedora上&#xff0c;在其他操作系统尚未做尝试。 步骤一&#xff1a;检查本地windows是否安装ssh 检查方式&#xff1a;windows本地打开windows powershell&#xff0c;输入ssh&#xff0c;若打印usage &#xff1a;ssh等一些信息&#xff0c;则已安装s…

8.防火墙

文章目录 防火墙iptables防火墙介绍基础操作高级操作通用匹配隐含匹配端口匹配&#xff1a;--sport 源端口、--dport 目的端口 TCP标志位匹配&#xff1a;--tcp-flags TCP标志位ICMP类型匹配&#xff1a;--icmp-type ICMP类型 显式匹配多端口匹配IP范围匹配&#xff1a;-m ipra…