当前位置: 首页> 娱乐> 明星 > 军事最新新闻头条_英文网站建设公司报价_百度搜索引擎优化详解_查网址

军事最新新闻头条_英文网站建设公司报价_百度搜索引擎优化详解_查网址

时间:2025/7/13 17:13:30来源:https://blog.csdn.net/m0_57010556/article/details/145873701 浏览次数:0次
军事最新新闻头条_英文网站建设公司报价_百度搜索引擎优化详解_查网址

概述

本文将详细介绍如何在Linux环境下部署MTCNN模型进行人脸检测,并使用NCNN框架进行推理。

1. CMake的安装与配置

下载CMake源码

前往CMake官网下载,找到适合您系统的最新版本tar.gz文件链接,或者直接通过wget下载:CMake官方下载页面https://cmake.org/download/

cd ~
wget https://github.com/Kitware/CMake/releases/download/v3.x.x/cmake-3.x.x.tar.gz

请将3.x.x替换为您想要安装的具体版本号。

解压并进入解压后的目录

tar -xzvf cmake-3.x.x.tar.gz
cd cmake-3.x.x

编译与安装

  1. 配置编译选项:使用bootstrap脚本进行配置:

    ./bootstrap
  2. 编译:使用所有可用的核心进行并行编译:

    make -j$(nproc)
  3. 安装:将CMake安装到系统中:

    sudo make install
  4. 刷新共享库缓存(如果需要):

    sudo ldconfig

安装完成后,再次运行以下命令验证安装是否成功:

cmake --version

配置环境变量(可选)

如果您选择自定义安装路径(例如/usr/local/bin以外的路径),可能需要手动配置环境变量以确保系统能够找到新安装的CMake。

编辑~/.bashrc~/.zshrc文件(取决于您使用的shell),添加以下行:

export PATH=/path/to/cmake/bin:$PATH

其中/path/to/cmake/bin是您指定的CMake安装路径下的bin目录。

保存文件后,运行以下命令使更改生效:

source ~/.bashrc  # 对于Bash用户
# 或者
source ~/.zshrc   # 对于Zsh用户

2. Protobuf的安装

更新系统软件包

首先,更新您的系统软件包列表,确保所有现有的包都是最新的:

sudo apt-get update
sudo apt-get upgrade

安装依赖项

安装构建Protobuf所需的各种工具和库:

sudo apt-get install autoconf automake libtool curl make g++ unzip

下载Protobuf源码

您可以从GitHub上克隆官方Protobuf仓库,或者直接下载特定版本的压缩包。这里我们使用Git进行操作:

cd ~
git clone https://github.com/protocolbuffers/protobuf.git
cd protobuf
git submodule update --init --recursive

编译Protobuf

创建并进入构建目录

为了保持源代码目录的整洁,建议在一个新的目录中进行编译:

cd ~/protobuf
mkdir -p build && cd build

使用CMake配置项目

CMake是一个跨平台的构建系统生成器,支持多种IDE和构建工具。

  1. 配置CMake:

    cmake .. -DCMAKE_BUILD_TYPE=Release
  2. 编译Protobuf:

    make -j$(nproc)
  3. 安装Protobuf:

    sudo make install
    sudo ldconfig  # 刷新共享库缓存

验证安装

检查版本信息

验证Protobuf是否正确安装,并检查其版本号:

protoc --version

您应该看到类似如下的输出:

libprotoc 3.x.x

其中3.x.x是具体的版本号。

配置环境变量(可选)

如果希望在任何位置都能直接运行protoc命令,而无需指定完整路径,可以将Protobuf的bin目录添加到系统的PATH环境变量中。编辑~/.bashrc~/.zshrc文件,根据您的shell类型,添加以下行:

export PATH=$PATH:/usr/local/bin

保存文件后,运行以下命令使更改生效:

source ~/.bashrc  # 对于Bash用户
# 或者
source ~/.zshrc   # 对于Zsh用户

3. OpenCV库的安装与配置

更新系统软件包

首先,更新您的系统软件包列表:

sudo apt-get update
sudo apt-get upgrade

安装依赖项

安装构建OpenCV所需的各种工具和库:

sudo apt-get install build-essential cmake git pkg-config libgtk-3-dev
sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev
sudo apt-get install python3-dev python3-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev
sudo apt-get install libdc1394-22-dev libopenblas-dev liblapack-dev gfortran
sudo apt-get install libprotobuf-dev protobuf-compiler

下载OpenCV源码

您可以通过Git克隆OpenCV的GitHub仓库来获取最新的稳定版本:

cd ~
git clone https://github.com/opencv/opencv.git
cd opencv
git checkout 4.x # 替换为所需的版本号# 克隆contrib仓库(可选)
cd ~
git clone https://github.com/opencv/opencv_contrib.git
cd opencv_contrib
git checkout 4.x # 确保与主仓库版本一致

编译OpenCV

创建并进入构建目录

cd ~/opencv
mkdir -p build && cd build

使用CMake配置项目

运行CMake以配置构建选项。这里我们指定一些常用的选项,例如启用Python支持、设置安装路径等。如果您不需要这些功能或使用了不同的路径,请相应地调整命令。

cmake -D CMAKE_BUILD_TYPE=Release \-D CMAKE_INSTALL_PREFIX=/usr/local \-D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules \-D BUILD_opencv_python3=ON \-D PYTHON3_EXECUTABLE=$(which python3) \-D PYTHON3_INCLUDE_DIR=$(python3 -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())") \-D PYTHON3_PACKAGES_PATH=$(python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") ..

编译

make -j$(nproc)

这可能需要一些时间,具体取决于您的硬件性能。

安装

完成编译后,使用以下命令安装OpenCV到系统中:

sudo make install
sudo ldconfig

验证安装

查找头文件和库文件

安装完成后,OpenCV的头文件通常位于/usr/local/include/opencv4/,而库文件则位于/usr/local/lib/

您可以检查这些位置是否包含必要的文件:

ls /usr/local/include/opencv4/
ls /usr/local/lib/

4. ncnn库在Linux环境下的编译

更新系统软件包

首先,更新您的系统软件包列表,确保所有现有的包都是最新的:

sudo apt-get update
sudo apt-get upgrade

安装依赖项

安装构建NCNN所需的各种工具和库:

sudo apt-get install build-essential cmake git libprotobuf-dev protobuf-compiler
sudo apt-get install libvulkan-dev vulkan-utils  # 如果需要Vulkan支持

libprotobuf-devprotobuf-compiler 是用于处理模型文件(如 .param.bin 文件)的必要依赖项。

下载NCNN源码

您可以通过Git克隆NCNN的GitHub仓库来获取最新的稳定版本:

cd ~
git clone https://github.com/Tencent/ncnn.git
cd ncnn

如果您想要特定的版本,可以切换到对应的分支或标签:

git checkout <branch_or_tag_name>

例如,切换到最新稳定版:

git checkout master

编译NCNN

创建并进入构建目录

为了保持源代码目录的整洁,建议在一个新的目录中进行编译:

mkdir -p build && cd build

使用CMake配置项目

运行CMake以配置构建选项。这里我们指定一些常用的选项,例如启用Vulkan支持、设置安装路径等。如果您不需要这些功能或使用了不同的路径,请相应地调整命令。

cmake .. \-DCMAKE_BUILD_TYPE=Release \-DNCNN_VULKAN=ON \  # 如果你希望使用Vulkan加速,请启用此选项-DNCNN_PROTOBUF_USE_SYSTEM=ON \-DProtobuf_DIR=/usr/lib/x86_64-linux-gnu/pkgconfig \  # 手动指定 Protobuf 的路径-DProtobuf_INCLUDE_DIR=/usr/include \-DProtobuf_LIBRARY=/usr/lib/x86_64-linux-gnu/libprotobuf.so \-DProtobuf_PROTOC_EXECUTABLE=/usr/bin/protoc

    编译

    使用所有可用的核心进行并行编译:

    make -j$(nproc)

    这可能需要一些时间,具体取决于您的硬件性能。

    安装

    完成编译后,使用以下命令安装NCNN到系统中:

    sudo make install
    sudo ldconfig  # 刷新共享库缓存

    默认情况下,头文件会被安装到 /usr/local/include/ncnn,库文件会被安装到 /usr/local/lib

    验证安装

    查找头文件和库文件

    安装完成后,NCNN的头文件通常位于 /usr/local/include/ncnn,而库文件则位于 /usr/local/lib

    您可以检查这些位置是否包含必要的文件:

    ls /usr/local/include/ncnn/
    ls /usr/local/lib/

    5. MTCNN源码

    ncnn框架实现的mtcnn主要包含两个核心代码文件mtcnn.h,mtcnn.cpp

    mtcnn.h代码如下:

    #pragma once#ifndef __MTCNN_NCNN_H__
    #define __MTCNN_NCNN_H__
    #include <ncnn/net.h>
    #include <string>
    #include <vector>
    #include <time.h>
    #include <algorithm>
    #include <map>
    #include <iostream>using namespace std;
    struct Bbox
    {float score;int x1;int y1;int x2;int y2;bool exist;float area;float ppoint[10];float regreCoord[4];
    };class MTCNN {public:MTCNN(const string& model_path);MTCNN(const std::vector<std::string> param_files, const std::vector<std::string> bin_files);~MTCNN();void configure_ncnn(ncnn::Net& net, int num_threads);void SetMinFace(int minSize);void detect(ncnn::Mat& img_, std::vector<Bbox>& finalBbox);
    private:void generateBbox(ncnn::Mat score, ncnn::Mat location, vector<Bbox>& boundingBox_, float scale);void nms(vector<Bbox>& boundingBox_, const float overlap_threshold, string modelname = "Union");void refine(vector<Bbox>& vecBbox, const int& height, const int& width, bool square);void PNet();void RNet();void ONet();ncnn::Net Pnet, Rnet, Onet;ncnn::Mat img;const float nms_threshold[3] = { 0.5f, 0.7f, 0.7f };const float mean_vals[3] = { 127.5, 127.5, 127.5 };const float norm_vals[3] = { 0.0078125, 0.0078125, 0.0078125 };const int MIN_DET_SIZE = 12;std::vector<Bbox> firstBbox_, secondBbox_, thirdBbox_;int img_w, img_h;private://部分可调参数const float threshold[3] = { 0.8f, 0.8f, 0.6f };int minsize = 20;const float pre_facetor = 0.709f;};#endif //__MTCNN_NCNN_H__
    

    mtcnn.cpp代码如下:

    #include "mtcnn.h"bool cmpScore(Bbox lsh, Bbox rsh) {if (lsh.score < rsh.score)return true;elsereturn false;
    }bool cmpArea(Bbox lsh, Bbox rsh) {if (lsh.area < rsh.area)return false;elsereturn true;
    }MTCNN::MTCNN(const std::string& model_path) {std::vector<std::string> param_files = {model_path + "/det1.param",model_path + "/det2.param",model_path + "/det3.param"};std::vector<std::string> bin_files = {model_path + "/det1.bin",model_path + "/det2.bin",model_path + "/det3.bin"};// 配置多线程int num_threads = 4; // 设置线程数configure_ncnn(Pnet, num_threads);configure_ncnn(Rnet, num_threads);configure_ncnn(Onet, num_threads);// 加载模型Pnet.load_param(param_files[0].data());Pnet.load_model(bin_files[0].data());Rnet.load_param(param_files[1].data());Rnet.load_model(bin_files[1].data());Onet.load_param(param_files[2].data());Onet.load_model(bin_files[2].data());
    }MTCNN::~MTCNN(){Pnet.clear();Rnet.clear();Onet.clear();
    }void MTCNN::configure_ncnn(ncnn::Net& net, int num_threads) {ncnn::Option opt;opt.num_threads = num_threads; // 设置线程数opt.use_vulkan_compute = false; // 如果不使用 Vulkan,设置为 falsenet.opt = opt;
    }void MTCNN::SetMinFace(int minSize){minsize = minSize;
    }void MTCNN::generateBbox(ncnn::Mat score, ncnn::Mat location, std::vector<Bbox>& boundingBox_, float scale){const int stride = 2;const int cellsize = 12;//score pfloat *p = score.channel(1);//score.data + score.cstep;//float *plocal = location.data;Bbox bbox;float inv_scale = 1.0f/scale;for(int row=0;row<score.h;row++){for(int col=0;col<score.w;col++){if(*p>threshold[0]){bbox.score = *p;bbox.x1 = round((stride*col+1)*inv_scale);bbox.y1 = round((stride*row+1)*inv_scale);bbox.x2 = round((stride*col+1+cellsize)*inv_scale);bbox.y2 = round((stride*row+1+cellsize)*inv_scale);bbox.area = (bbox.x2 - bbox.x1) * (bbox.y2 - bbox.y1);const int index = row * score.w + col;for(int channel=0;channel<4;channel++){bbox.regreCoord[channel]=location.channel(channel)[index];}boundingBox_.push_back(bbox);}p++;//plocal++;}}
    }void MTCNN::nms(std::vector<Bbox> &boundingBox_, const float overlap_threshold, string modelname){if(boundingBox_.empty()){return;}sort(boundingBox_.begin(), boundingBox_.end(), cmpScore);float IOU = 0;float maxX = 0;float maxY = 0;float minX = 0;float minY = 0;std::vector<int> vPick;int nPick = 0;std::multimap<float, int> vScores;const int num_boxes = boundingBox_.size();vPick.resize(num_boxes);for (int i = 0; i < num_boxes; ++i){vScores.insert(std::pair<float, int>(boundingBox_[i].score, i));}while(vScores.size() > 0){int last = vScores.rbegin()->second;vPick[nPick] = last;nPick += 1;for (std::multimap<float, int>::iterator it = vScores.begin(); it != vScores.end();){int it_idx = it->second;maxX = (std::max)(boundingBox_.at(it_idx).x1, boundingBox_.at(last).x1);maxY = (std::max)(boundingBox_.at(it_idx).y1, boundingBox_.at(last).y1);minX = (std::min)(boundingBox_.at(it_idx).x2, boundingBox_.at(last).x2);minY = (std::min)(boundingBox_.at(it_idx).y2, boundingBox_.at(last).y2);//maxX1 and maxY1 reuse maxX = ((minX-maxX+1)>0)? (minX-maxX+1) : 0;maxY = ((minY-maxY+1)>0)? (minY-maxY+1) : 0;//IOU reuse for the area of two bboxIOU = maxX * maxY;if(!modelname.compare("Union"))IOU = IOU/(boundingBox_.at(it_idx).area + boundingBox_.at(last).area - IOU);else if(!modelname.compare("Min")){IOU = IOU/((boundingBox_.at(it_idx).area < boundingBox_.at(last).area)? boundingBox_.at(it_idx).area : boundingBox_.at(last).area);}if(IOU > overlap_threshold){it = vScores.erase(it);}else{it++;}}}vPick.resize(nPick);std::vector<Bbox> tmp_;tmp_.resize(nPick);for(int i = 0; i < nPick; i++){tmp_[i] = boundingBox_[vPick[i]];}boundingBox_ = tmp_;
    }void MTCNN::refine(vector<Bbox> &vecBbox, const int &height, const int &width, bool square){if(vecBbox.empty()){cout<<"Bbox is empty!!"<<endl;return;}float bbw=0, bbh=0, maxSide=0;float h = 0, w = 0;float x1=0, y1=0, x2=0, y2=0;for(vector<Bbox>::iterator it=vecBbox.begin(); it!=vecBbox.end();it++){bbw = (*it).x2 - (*it).x1 + 1;bbh = (*it).y2 - (*it).y1 + 1;x1 = (*it).x1 + (*it).regreCoord[0]*bbw;y1 = (*it).y1 + (*it).regreCoord[1]*bbh;x2 = (*it).x2 + (*it).regreCoord[2]*bbw;y2 = (*it).y2 + (*it).regreCoord[3]*bbh;if(square){w = x2 - x1 + 1;h = y2 - y1 + 1;maxSide = (h>w)?h:w;x1 = x1 + w*0.5 - maxSide*0.5;y1 = y1 + h*0.5 - maxSide*0.5;(*it).x2 = round(x1 + maxSide - 1);(*it).y2 = round(y1 + maxSide - 1);(*it).x1 = round(x1);(*it).y1 = round(y1);}//boundary checkif((*it).x1<0)(*it).x1=0;if((*it).y1<0)(*it).y1=0;if((*it).x2>width)(*it).x2 = width - 1;if((*it).y2>height)(*it).y2 = height - 1;it->area = (it->x2 - it->x1)*(it->y2 - it->y1);}
    }void MTCNN::PNet(){firstBbox_.clear();float minl = img_w < img_h? img_w: img_h;float m = (float)MIN_DET_SIZE/minsize;minl *= m;float factor = pre_facetor;vector<float> scales_;while(minl>MIN_DET_SIZE){scales_.push_back(m);minl *= factor;m = m*factor;}for (size_t i = 0; i < scales_.size(); i++) {int hs = (int)ceil(img_h*scales_[i]);int ws = (int)ceil(img_w*scales_[i]);ncnn::Mat in;resize_bilinear(img, in, ws, hs);ncnn::Extractor ex = Pnet.create_extractor();//ex.set_num_threads(2);ex.set_light_mode(true);ex.input("data", in);ncnn::Mat score_, location_;ex.extract("prob1", score_);ex.extract("conv4-2", location_);std::vector<Bbox> boundingBox_;generateBbox(score_, location_, boundingBox_, scales_[i]);nms(boundingBox_, nms_threshold[0]);firstBbox_.insert(firstBbox_.end(), boundingBox_.begin(), boundingBox_.end());boundingBox_.clear();}
    }
    void MTCNN::RNet(){secondBbox_.clear();int count = 0;for(vector<Bbox>::iterator it=firstBbox_.begin(); it!=firstBbox_.end();it++){ncnn::Mat tempIm;copy_cut_border(img, tempIm, (*it).y1, img_h-(*it).y2, (*it).x1, img_w-(*it).x2);ncnn::Mat in;resize_bilinear(tempIm, in, 24, 24);ncnn::Extractor ex = Rnet.create_extractor();//ex.set_num_threads(2);ex.set_light_mode(true);ex.input("data", in);ncnn::Mat score, bbox;ex.extract("prob1", score);ex.extract("conv5-2", bbox);if ((float)score[1] > threshold[1]) {for (int channel = 0; channel<4; channel++) {it->regreCoord[channel] = (float)bbox[channel];//*(bbox.data+channel*bbox.cstep);}it->area = (it->x2 - it->x1)*(it->y2 - it->y1);it->score = score.channel(1)[0];//*(score.data+score.cstep);secondBbox_.push_back(*it);}}
    }
    void MTCNN::ONet(){thirdBbox_.clear();for(vector<Bbox>::iterator it=secondBbox_.begin(); it!=secondBbox_.end();it++){ncnn::Mat tempIm;copy_cut_border(img, tempIm, (*it).y1, img_h-(*it).y2, (*it).x1, img_w-(*it).x2);ncnn::Mat in;resize_bilinear(tempIm, in, 48, 48);ncnn::Extractor ex = Onet.create_extractor();//ex.set_num_threads(2);ex.set_light_mode(true);ex.input("data", in);ncnn::Mat score, bbox, keyPoint;ex.extract("prob1", score);ex.extract("conv6-2", bbox);ex.extract("conv6-3", keyPoint);if ((float)score[1] > threshold[2]) {for (int channel = 0; channel < 4; channel++) {it->regreCoord[channel] = (float)bbox[channel];}it->area = (it->x2 - it->x1) * (it->y2 - it->y1);it->score = score.channel(1)[0];for (int num = 0; num<5; num++) {(it->ppoint)[num] = it->x1 + (it->x2 - it->x1) * keyPoint[num];(it->ppoint)[num + 5] = it->y1 + (it->y2 - it->y1) * keyPoint[num + 5];}thirdBbox_.push_back(*it);}}
    }void MTCNN::detect(ncnn::Mat& img_, std::vector<Bbox>& finalBbox_){img = img_;img_w = img.w;img_h = img.h;img.substract_mean_normalize(mean_vals, norm_vals);PNet();//the first stage's nmsif(firstBbox_.size() < 1) return;nms(firstBbox_, nms_threshold[0]);refine(firstBbox_, img_h, img_w, true);//second stageRNet();if(secondBbox_.size() < 1) return;nms(secondBbox_, nms_threshold[1]);refine(secondBbox_, img_h, img_w, true);//third stage ONet();if(thirdBbox_.size() < 1) return;refine(thirdBbox_, img_h, img_w, true);nms(thirdBbox_, nms_threshold[2], "Min");finalBbox_ = thirdBbox_;
    }
    

    6. Linux下进行推理

    在Linux下配置完Opencv和ncnn的环境后编写简单的main.cpp进行模型的推理,代码如下:

    #include "mtcnn.h"
    #include <opencv2/opencv.hpp>
    #include <chrono>using namespace cv;int main()
    {std::string model_path = "./models"; //根据模型权重所在位置修改路径MTCNN mm(model_path);mm.SetMinFace(20);cv::VideoCapture video("./video/video.mp4"); //根据测试视频所在位置修改路径if (!video.isOpened()) {std::cerr << "failed to load video" << std::endl;return -1;}std::vector<Bbox> finalBbox;cv::Mat frame;// 记录开始时间auto start = std::chrono::high_resolution_clock::now();do {finalBbox.clear();video >> frame;if (!frame.data) {std::cerr << "Capture video failed" << std::endl;break;}ncnn::Mat ncnn_img = ncnn::Mat::from_pixels(frame.data, ncnn::Mat::PIXEL_BGR2RGB, frame.cols, frame.rows);mm.detect(ncnn_img, finalBbox);for (vector<Bbox>::iterator it = finalBbox.begin(); it != finalBbox.end(); it++) {if ((*it).exist) {cv::rectangle(frame, cv::Point((*it).x1, (*it).y1), cv::Point((*it).x2, (*it).y2), cv::Scalar(0, 0, 255), 2, 8, 0);}}} while (1);// 释放资源video.release();// 记录结束时间auto end = std::chrono::high_resolution_clock::now();// 计算持续时间std::chrono::duration<double> duration = end - start;// 输出结果(秒)std::cout << "Time taken: " << duration.count() << " seconds" << std::endl;return 0;
    }
    

    测试项目的目录结构如下:

    mtcnn/
    ├── Makefile
    ├── video
    │   └── video.mp4
    ├── include/
    │   └── mtcnn.h
    ├── src/
    │   ├── mtcnn.cpp
    │   └── main.cpp
    └── models├── det1.bin├── det1.param├── det2.bin├── det2.param├── det3.bin└── det3.param
    

    ncnn架构的mtcnn模型权重下载链接如下:

    ncnn架构的mtcnn模型权重下载https://download.csdn.net/download/m0_57010556/90433089Makefile的内容如下:

    # 编译器
    CXX = g++# 编译选项
    CXXFLAGS = -Wall -I./include -O2 -fopenmp `pkg-config --cflags opencv4`# 目标可执行文件名
    TARGET = face_detection# 源文件目录
    SRCDIR = src# 头文件目录
    INCDIR = include# 链接库路径
    OPENCV_LIBS = `pkg-config --libs opencv4`
    OPENCV_CFLAGS = `pkg-config --cflags opencv4`
    NCNN_CFLAGS = -I/home/ncnn/build/install/include
    NCNN_LIBS = -L/home/ncnn/build/install/lib -lncnn# 找到所有源文件
    SOURCES := $(wildcard $(SRCDIR)/*.cpp)# 生成目标文件列表
    OBJECTS := $(patsubst $(SRCDIR)/%.cpp, %.o, $(SOURCES))# 默认目标
    all: $(TARGET)# 链接目标文件生成可执行文件
    $(TARGET): $(OBJECTS)$(CXX) $(CXXFLAGS) $^ -o $@ $(OPENCV_LIBS) $(NCNN_LIBS) -lpthread -ldl -lgomp# 规则:从源文件生成目标文件
    %.o: $(SRCDIR)/%.cpp$(CXX) $(CXXFLAGS) $(OPENCV_CFLAGS) $(NCNN_CFLAGS) -c $< -o $@# 清理生成的文件
    clean:rm -f $(OBJECTS) $(TARGET).PHONY: all clean

    编译和运行

    在项目目录下运行以下命令来编译和运行你的程序:

    编译

    make

    这将编译 src/mtcnn.cppsrc/main.cpp 并生成可执行文件 face_detection

    运行

    ./face_detection

    你应该会看到输出:

    Capture video failed
    Time taken: 22.336 seconds

    此时说明在linux下模型推理成功

    关键字:军事最新新闻头条_英文网站建设公司报价_百度搜索引擎优化详解_查网址

    版权声明:

    本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

    我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

    责任编辑: