当前位置:网站首页>Opencv installation (x86/tx2 cuda/ shared library)
Opencv installation (x86/tx2 cuda/ shared library)
2022-06-22 03:17:00 【JanKin_ BY】
Catalog
2.1. Previously installed opencv, use jtop nothing CUDA Speed up , Unload and reload .
2.2. download opencv edition https://opencv.org/releases/
2.3. download opencv_contrib.git
2.4. Set compilation item :(-D CUDA_ARCH_BIN=xx This jtop Inquire about cuda arch 6.2)
2.5. Configuration environment
2.5.1. sudo gedit /etc/ld.so.conf.d/opencv.conf add to /usr/local/lib
2.5.2.sudo gedit /etc/bash.bashrc add to
3. take opencv Install into file , quote
6.Opencv Point cloud realsense Point cloud
6.1. install realsense library
6.2. In the source code example.h Kao to include file
1.opencv x86 install
sudo apt-get install build-essential libgtk2.0-dev libgtk-3-dev libavcodec-dev libavformat-dev libjpeg-dev libswscale-dev libtiff5-dev# python3 Support
sudo apt install python3-dev python3-numpy# streamer Support
sudo apt install libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev# Optional dependencies
sudo apt install libpng-dev libopenexr-dev libtiff-dev libwebp-dev Source code download
https://opencv.org/releases/
unzip opencv-4.1.0.zip
cd opencv-4.1.0
mkdir build
cd build/Compile
cmake -D CMAKE_BUILD_TYPE=Release -D OPENCV_GENERATE_PKGCONFIG=ON -D WITH_GTK=ON -D WITH_OPENGL=ON -D WITH_V4L=ON -D WITH_VTK=ON -D WITH_TBB=ON -D CMAKE_INSTALL_PREFIX=/usr/local ..
make
sudo make install Configuration environment
1. sudo gedit /etc/ld.so.conf.d/opencv.conf
add to /usr/local/lib
2.sudo gedit /etc/bash.bashrc
add to
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
export PKG_CONFIG_PATH
source /etc/bash.bashrc # Update source link
sudo updatedb # Update file library 2.opencv tx2 cuda
2.1. Previously installed opencv, use jtop nothing CUDA Speed up , Unload and reload .

OpenCV Can make CUDA Speed up _ Lost little bookboy's Note-CSDN Blog
Linux install OpenCV4( Optional GPU Speed up ) - You know
uninstall opencv
cd opencv/build
sudo make uninstall && make clean
sudo rm -r /usr/local/include/opencv2 /usr/local/include/opencv /usr/include/opencv /usr/include/opencv2 /usr/local/share/opencv /usr/local/share/OpenCV /usr/share/opencv /usr/share/OpenCV /usr/local/bin/opencv* /usr/local/lib/libopencv*
2.1.1 Not installed opencv
sudo apt-get install build-essential libgtk2.0-dev libgtk-3-dev libavcodec-dev libavformat-dev libjpeg-dev libswscale-dev libtiff5-dev
sudo apt install python3-dev python3-numpy
sudo apt install libgstreamer-plugins-base1.0-dev libgstreamer1.0-devopencv_cuda: opencv opencv_contrib
2.2. download opencv edition https://opencv.org/releases/
mkdir opencv_cuda
cd opencv_cuda
git clone https://github.com/opencv/opencv.git2.3. download opencv_contrib.git
git clone https://github.com/opencv/opencv_contrib.git2.4. Set compilation item :(-D CUDA_ARCH_BIN=xx This jtop Inquire about cuda arch 6.2)
cd ./opencv
mkdir build
cd build
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D CUDA_CUDA_LIBRARY=/usr/local/cuda/lib64/stubs/libcuda.so -D CUDA_ARCH_BIN=6.2 -D CUDA_ARCH_PTX="" -D WITH_CUDA=ON -D WITH_TBB=ON -D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=ON -D INSTALL_C_EXAMPLES=ON -D INSTALL_PYTHON_EXAMPLES=ON -D BUILD_EXAMPLES=ON -D WITH_QT=ON -D WITH_OPENGL=ON -D ENABLE_FAST_MATH=1 -D CUDA_FAST_MATH=1 -D WITH_CUBLAS=1 -D WITH_NVCUVID=ON -D BUILD_opencv_cudacodec=OFF -D OPENCV_GENERATE_PKGCONFIG=ON -DOPENCV_EXTRA_MODULES_PATH= ../../opencv_contrib/modules ../../opencv
sudo make -j4
sudo make install2.5. Configuration environment
2.5.1. sudo gedit /etc/ld.so.conf.d/opencv.conf
add to /usr/local/lib
2.5.2.sudo gedit /etc/bash.bashrc add to
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
export PKG_CONFIG_PATH
source /etc/bash.bashrc
sudo updatedb
2.6. Possible mistakes
opencv_contrib Wrong position ,OPENCV_EXTRA_MODULES_PATH The following path must not have spaces .
CMake Error at modules/core/CMakeLists.txt:52 (message):
CUDA: OpenCV requires enabled 'cudev' module from 'opencv_contrib'
repository: https://github.com/opencv/opencv_contrib
GitHub - opencv/opencv_contrib: Repository for OpenCV's extra modules
cd <opencv_build_directory>
cmake -DOPENCV_EXTRA_MODULES_PATH=<opencv_contrib>/modules <opencv_source_directory>
3.opencv Compile separately , Install into file
cd opencv-3.2.0
mkdir build
cd build
mkdir source
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/sd_card/opencv-3.2.0/build/source -D WITH_GTK=ON -DWITH_CUDA=OFF -DENABLE_PRECOMPILED_HEADERS=OFF ..
make
make installmaincpp
#include "opencv2/opencv.hpp"
using namespace cv;
int main()
{
printf(CV_VERSION);
return 0;
}Printed version
CMakeLists.txt Use :
cmake_minimum_required (VERSION 2.8)
project (demo)
set(OpenCV_DIR /sd_card/opencv-3.2.0/build/source/share/OpenCV)
find_package( OpenCV 3 REQUIRED )
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(main main.cpp)
target_link_libraries(main ${OpenCV_LIBS} )build Add environment variables under OpenCV_DIR , The value is that you can find OpenCVConfig.cmake perhaps OpenCVConfig-version.cmake Of opencv route
4.opencv Picture verification
Open at the terminal to opencv-4.1.0/sample/cpp/example_cmake Catalog , Execute the following code :
cmake .
make
./opencv_example
mkdir ~/opencv-test
cd ~/opencv-test
gedit DisplayImage.cpp
#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char** argv )
{
if ( argc != 2 )
{
printf("usage: DisplayImage.out <Image_Path>\n");
return -1;
}
Mat image;
image = imread( argv[1], 1 );
if ( !image.data )
{
printf("No image data \n");
return -1;
}
namedWindow("Display Image", WINDOW_AUTOSIZE );
imshow("Display Image", image);
waitKey(0);
return 0;
}gedit CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project( DisplayImage )
find_package( OpenCV REQUIRED )
add_executable( DisplayImage DisplayImage.cpp )
target_link_libraries( DisplayImage ${OpenCV_LIBS} )cd ~/opencv-test
cmake .
make
perform
here opencv-test The executable file has been generated in the folder DisplayImage, Just download a picture from the Internet and put it in opencv-test Under the folder , Download here opencv.jpg, And then run
./DisplayImage 1.jpg 
Reference resources :ubuntu opencv Failed to turn on the camera _shadowsland The column -CSDN Blog
5.opencv Camera acquisition
sudo apt install pkg-config v4l-utilsView camera :
v4l2-ctl --list-devices # View camera devices
ls /dev/ # View all devices video0 video1
ll /dev/video* # View camera devices
v4l2-ctl -d /dev/video0 --all # View all information about the camera
v4l2-ctl -d /dev/video0 --list-formats-ext Use --list-formats-ext Get supported resolutions and encoding formats
Camera device number
find /lib/modules/ -name "*v4l2*.ko"
sudo modprobe adv7511-v4l2establish camera.cpp
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main( int argc, char** argv )
{
cv::VideoCapture capture(atoi(argv[1]));
if (!capture.isOpened())
return 1;
cv::Mat Frame;
bool stop = false;
while (!stop)
{
capture >> Frame;
cv::imshow(" video ", Frame);
waitKey(10);
}
return 0;
}establish CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project( DisplayCamera )
find_package( OpenCV REQUIRED )
add_executable( DisplayCamera camera.cpp )
target_link_libraries(DisplayCamera ${OpenCV_LIBS})cmake .&&make
sudo ./DisplayCamera 4

6.Opencv Point cloud realsense Point cloud
6.1. install realsense library
6.2. In the source code example.h Kao to include file
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2017 Intel Corporation. All Rights Reserved.
#include <librealsense2/rs.h>
#include <stdio.h>
#include <stdlib.h>
/* Function calls to librealsense may raise errors of type rs_error*/
void check_error(rs2_error* e)
{
if (e)
{
printf("rs_error was raised when calling %s(%s):\n", rs2_get_failed_function(e), rs2_get_failed_args(e));
printf(" %s\n", rs2_get_error_message(e));
exit(EXIT_FAILURE);
}
}
void print_device_info(rs2_device* dev)
{
rs2_error* e = 0;
printf("\nUsing device 0, an %s\n", rs2_get_device_info(dev, RS2_CAMERA_INFO_NAME, &e));
check_error(e);
printf(" Serial number: %s\n", rs2_get_device_info(dev, RS2_CAMERA_INFO_SERIAL_NUMBER, &e));
check_error(e);
printf(" Firmware version: %s\n\n", rs2_get_device_info(dev, RS2_CAMERA_INFO_FIRMWARE_VERSION, &e));
check_error(e);
}6.3. modify rs-depth.cpp
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2017 Intel Corporation. All Rights Reserved.
/* Include the librealsense C header files */
#include <librealsense2/rs.h>
#include <ctime>
#include <librealsense2/h/rs_pipeline.h>
#include <librealsense2/h/rs_option.h>
#include <librealsense2/h/rs_frame.h>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <vector>
#include <fstream>
#include <iostream>
#include <string>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include "./include/example.h"
using namespace std;
using namespace cv;
// These parameters are reconfigurable //
#define STREAM RS2_STREAM_DEPTH // rs2_stream is a types of data provided by RealSense device //
#define FORMAT RS2_FORMAT_Z16 // rs2_format identifies how binary data is encoded within a frame //
#define WIDTH 640 // Defines the number of columns for each frame or zero for auto resolve//
#define HEIGHT 480 // Defines the number of lines for each frame or zero for auto resolve //
#define FPS 30 // Defines the rate of frames per second //
#define STREAM_INDEX 0 // Defines the stream index, used for multiple streams of the same type //
#define HEIGHT_RATIO 20 // Defines the height ratio between the original frame to the new frame //
#define WIDTH_RATIO 10 // Defines the width ratio between the original frame to the new frame //
#define CV_COLOR_DARKGRAY cv::Scalar(169,169,169)
#define CV_COLOR_BLACK cv::Scalar(0,0,0)
// The number of meters represented by a single depth unit
float get_depth_unit_value(const rs2_device* const dev)
{
rs2_error* e = 0;
rs2_sensor_list* sensor_list = rs2_query_sensors(dev, &e);
check_error(e);
int num_of_sensors = rs2_get_sensors_count(sensor_list, &e);
check_error(e);
float depth_scale = 0;
int is_depth_sensor_found = 0;
int i;
for (i = 0; i < num_of_sensors; ++i)
{
rs2_sensor* sensor = rs2_create_sensor(sensor_list, i, &e);
check_error(e);
// Check if the given sensor can be extended to depth sensor interface
is_depth_sensor_found = rs2_is_sensor_extendable_to(sensor, RS2_EXTENSION_DEPTH_SENSOR, &e);
check_error(e);
if (1 == is_depth_sensor_found)
{
depth_scale = rs2_get_option((const rs2_options*)sensor, RS2_OPTION_DEPTH_UNITS, &e);
check_error(e);
rs2_delete_sensor(sensor);
break;
}
rs2_delete_sensor(sensor);
}
rs2_delete_sensor_list(sensor_list);
if (0 == is_depth_sensor_found)
{
printf("Depth sensor not found!\n");
exit(EXIT_FAILURE);
}
return depth_scale;
}
int main()
{
const Size sizepx(640, 860);
Mat screen(sizepx.width, sizepx.height, CV_32FC3);
screen.setTo(Scalar(255, 0, 0));// The background color
clock_t startTime,endTime;
float timefps=0;
char str[10];// String used to store frame rate
rs2_error* e = 0;
struct color_point
{
int x;
int y;
double color;
}color_Point;
// Create a context object. This object owns the handles to all connected realsense devices.
// The returned object should be released with rs2_delete_context(...)
rs2_context* ctx = rs2_create_context(RS2_API_VERSION, &e); //realsense edition
check_error(e);
/* Get a list of all the connected devices. */
// The returned object should be released with rs2_delete_device_list(...)
rs2_device_list* device_list = rs2_query_devices(ctx, &e); // The equipment list
check_error(e);
int dev_count = rs2_get_device_count(device_list, &e);// Number of equipment
check_error(e);
printf("There are %d connected RealSense devices.\n", dev_count);
if (0 == dev_count)
return EXIT_FAILURE;
// Get the first connected device
// The returned object should be released with rs2_delete_device(...)
rs2_device* dev = rs2_create_device(device_list, 0, &e);// Connecting device
check_error(e);
print_device_info(dev);// Print device information
/* Determine depth value corresponding to one meter */
uint16_t one_meter = (uint16_t)(1.0f / get_depth_unit_value(dev));// Calculate the depth information distance , Take the opposite , result >1 stay 1m Inside
// Create a pipeline to configure, start and stop camera streaming
// The returned object should be released with rs2_delete_pipeline(...)
rs2_pipeline* pipeline = rs2_create_pipeline(ctx, &e);// Create pipes Start or end camera data ctx It means device
check_error(e);
// Create a config instance, used to specify hardware configuration
// The retunred object should be released with rs2_delete_config(...)
rs2_config* config = rs2_create_config(&e);
check_error(e);
// Request a specific configuration
rs2_config_enable_stream(config, STREAM, STREAM_INDEX, WIDTH, HEIGHT, FORMAT, FPS, &e);
check_error(e);
// Start the pipeline streaming
// The retunred object should be released with rs2_delete_pipeline_profile(...)
rs2_pipeline_profile* pipeline_profile = rs2_pipeline_start_with_config(pipeline, config, &e);
if (e)
{
printf("The connected device doesn't support depth streaming!\n");
exit(EXIT_FAILURE);
}
rs2_stream_profile_list* stream_profile_list = rs2_pipeline_profile_get_streams(pipeline_profile, &e);
if (e)
{
printf("Failed to create stream profile list!\n");
exit(EXIT_FAILURE);
}
rs2_stream_profile* stream_profile = (rs2_stream_profile*)rs2_get_stream_profile(stream_profile_list, 0, &e);
if (e)
{
printf("Failed to create stream profile!\n");
exit(EXIT_FAILURE);
}
rs2_stream stream; rs2_format format; int index; int unique_id; int framerate;
rs2_get_stream_profile_data(stream_profile, &stream, &format, &index, &unique_id, &framerate, &e);
if (e)
{
printf("Failed to get stream profile data!\n");
exit(EXIT_FAILURE);
}
int width; int height;
rs2_get_video_stream_resolution(stream_profile, &width, &height, &e); // Get width, height and data flow
if (e)
{
printf("Failed to get video stream resolution data!\n");
exit(EXIT_FAILURE);
}
int rows = height / HEIGHT_RATIO;
int row_length = width / WIDTH_RATIO;
int display_size = (rows + 1) * (row_length + 1);
int buffer_size = display_size * sizeof(char);
char* buffer = (char *)calloc(display_size, sizeof(char));
char* out = NULL;
while (1)
{
// This call waits until a new composite_frame is available
// composite_frame holds a set of frames. It is used to prevent frame drops
// The returned object should be released with rs2_release_frame(...)
rs2_frame* frames = rs2_pipeline_wait_for_frames(pipeline, RS2_DEFAULT_TIMEOUT, &e);
check_error(e);
// Returns the number of frames embedded within the composite frame
int num_of_frames = rs2_embedded_frames_count(frames, &e);
check_error(e);
int i;
startTime = clock();// Timing begins
for (i = 0; i < num_of_frames; ++i)
{
// The retunred object should be released with rs2_release_frame(...)
rs2_frame* frame = rs2_extract_frame(frames, i, &e);
check_error(e);
// Check if the given frame can be extended to depth frame interface
// Accept only depth frames and skip other frames
if (0 == rs2_is_frame_extendable_to(frame, RS2_EXTENSION_DEPTH_FRAME, &e))
{
rs2_release_frame(frame);
continue;
}
/* Retrieve depth data, configured as 16-bit depth values */
const uint16_t* depth_frame_data = (const uint16_t*)(rs2_get_frame_data(frame, &e));// Point cloud data 1
check_error(e);
/* Print a simple text-based representation of the image, by breaking it into 10x5 pixel regions and approximating the coverage of pixels within one meter */
out = buffer;
int x, y, i;
int* coverage = (int*)calloc(row_length, sizeof(int));
vector<color_point> vectorp;// Storage point container
for (y = 0; y < height; ++y)
{
for (x = 0; x < width; ++x)
{
// Create a depth histogram to each row
int coverage_index = x / WIDTH_RATIO;
int depth = *depth_frame_data++; // Pointer to data
if (depth > 0 && depth < one_meter*3&&x%2==0)
{
++coverage[coverage_index];
color_Point.x=x;
color_Point.y=y;
color_Point.color=(double)depth/one_meter/3.0;
vectorp.push_back(color_Point);
}
}
if ((y % HEIGHT_RATIO) == (HEIGHT_RATIO-1))
{
for (i = 0; i < (row_length); ++i)
{
static const char* pixels = " .:nhBXWW";
int pixel_index = (coverage[i] / (HEIGHT_RATIO * WIDTH_RATIO / sizeof(pixels)));
*out++ = pixels[pixel_index];
coverage[i] = 0;
}
*out++ = '\n';
}
}
screen.setTo(Scalar(1, 1, 1));// The background is blue
for(int point_num=0;point_num<vectorp.size();point_num++)
{
Point point_center;
point_center=Point(vectorp[point_num].x,vectorp[point_num].y);
circle(screen,point_center,1, Scalar(vectorp[point_num].color,vectorp[point_num].color/3.0,1-vectorp[point_num].color),3,8,0);// The dot is green
}
// According to the text
string text = "FPS:";
sprintf(str, "%.2f", timefps); // The frame rate is kept to two decimal places
text += str; // stay "FPS:" Then add the frame rate value string
int font_face = cv::FONT_HERSHEY_COMPLEX;
double font_scale =0.5;
int thickness = 2,baseline;
// Get the length and width of the text box
Size text_size = getTextSize(text, font_face, font_scale, thickness, &baseline);
// Center the text box
Point origin;
//origin.x = screen.cols / 2 - text_size.width / 2;
//origin.y = screen.rows / 2 + text_size.height / 2;
origin.x =text_size.width / 2;
origin.y = 50 + text_size.height / 2;
putText(screen, text, origin, font_face, font_scale, cv::Scalar(0, 0, 0), thickness, 8, 0);
// According to the text
imshow(" The screen ", screen);
waitKey(10);
*out++ = 0;
//printf("\n%s", buffer);
free(coverage);
rs2_release_frame(frame);
}
rs2_release_frame(frames);
endTime = clock();// End of the timing
timefps=1.0/((float)(endTime - startTime)/CLOCKS_PER_SEC);
}
// Stop the pipeline streaming
rs2_pipeline_stop(pipeline, &e);
check_error(e);
// Release resources
free(buffer);
rs2_delete_pipeline_profile(pipeline_profile);
rs2_delete_stream_profiles_list(stream_profile_list);
rs2_delete_stream_profile(stream_profile);
rs2_delete_config(config);
rs2_delete_pipeline(pipeline);
rs2_delete_device(dev);
rs2_delete_device_list(device_list);
rs2_delete_context(ctx);
return EXIT_SUCCESS;
}
modify CMakeLists.txt
# License: Apache 2.0. See LICENSE file in root directory.
# Copyright(c) 2019 Intel Corporation. All Rights Reserved.
# minimum required cmake version: 3.1.0
cmake_minimum_required(VERSION 3.1.0)
project(RealsenseExamples-Depth)
find_package( OpenCV REQUIRED )
if(NOT WIN32)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread")
endif()
set(DEPENDENCIES realsense2 ${PCL_LIBRARIES})
find_package(OpenGL)
if(NOT OPENGL_FOUND)
message(FATAL_ERROR "\n\n OpenGL package is missing!\n\n")
endif()
list(APPEND DEPENDENCIES ${OPENGL_LIBRARIES})
if(WIN32)
list(APPEND DEPENDENCIES glfw3)
else()
find_package(glfw3 REQUIRED)
list(APPEND DEPENDENCIES glfw)
endif()
add_executable(PointDepth rs-depth.cpp )
target_link_libraries(PointDepth ${DEPENDENCIES} ${OpenCV_LIBS})

Use it directly apt install
sudo apt-get install python-opencv
sudo apt-get install python-numpy
test :
open python,importcv If the module succeeds .
import cv 
边栏推荐
- Irregular layout of table label
- 交通标志分类
- 【NVMe2.0b 8】NVMe 队列仲裁机制
- 【Percona-Toolkit】系列之pt-table-checksum和pt-table-sync 数据校验修复神器
- 【NVMe2.0b 6】NVMe 队列模型
- 【NVMe2.0b 9】控制器初始化过程
- Force buckle 102 Sequence traversal of binary tree
- JS special effects in the construction of animated web pages
- Jz59. print binary tree in zigzag order
- C1-qt idea of realizing simple calculator 2021.10.15
猜你喜欢
![[6. high precision multiplication]](/img/83/1659503e62839c395ca7849596d6fc.png)
[6. high precision multiplication]

C3-qt realize Gobang games (I) November 7, 2021

360edr planing

tag动态规划-刷题预备知识-1.动态规划五部曲解题法 + lt.509. 斐波那契数/ 剑指Offer 10.I + lt.70. 爬楼梯彻底解惑 + 面试真题扩展

图书馆管理系统(PHP期末报告)

uv_loop_init()流程

深度学习期末复习

Use of day19qpushbutton 2021-10-30

Wxml template syntax, wxss template style, global configuration, page configuration, and network data request

Figure database ongdb release v-1.0.2
随机推荐
关于mysql的int型主键自增问题
Force buckle 239 Sliding window Max
uv_loop_init()流程
Implementation of epoll+threadpool high concurrency network IO model
web框架概述与程序开发
Library management system (PHP final report)
torch.max()使用讲解
[4. high precision addition]
The most popular massive Alibaba cloud disk resource sharing
[kubernetes series] what is kubernetes?
Moorish voting
Sword finger offer 58 Symmetric binary tree
Using JMeter for web side automated testing
Force buckle 141 Circular linked list
最热门海量的阿里云盘资源分享
Primary key in efcore
Tag dynamic programming - preliminary knowledge for question brushing -1 Dynamic programming five part problem solving method + lt.509 Fibonacci number / Sword finger offer 10 I + lt.70. Climbing stai
Redis configuration and optimization
BOM 属性、方法、事件应用案例
Horizontal comparison of domestic API management platforms, which one is stronger?