福州大学《嵌入式系统综合设计》实验四:边缘检测

一、实验目的

BMCV 提供了一套基于 Sophon AI 芯片优化的机器视觉库,通过利用芯片的 TPU 和 VPP模块,可以完成色彩空间转换、尺度变换、仿射变换、透射变换、线性变换、画框、JPEG 编解码、BASE64 编解码、NMS、排序、特征匹配等操作。

本实验的目的是掌握算能的BMCV接口使用方法,掌握bmcv_sobel,bmcv_canny边缘检测函数的使用方法。

二、实验内容

基于套接字、多线程、同步锁机制实现多媒体文件的收发;

发送端Ubuntu的PC机读取文件,每1024个字节组成一个包通过TCP报文发送到接收端;接收端SE5上启动2个线程,线程1接收报文并将报文存入缓存;线程2通过缓存读取报文存入文件中;要求线程1和线程2之间通过同步锁进行线程同步。

  1. 编写代码,通过OpenCV读取图片文件,并调用BMCV的bmcv_sobel、bmcv_canny函数来实现对图片的边缘检测,最后输出检测结果。
  2. 直接利用OpenCV的边缘检测接口,实现边缘检测功能;
  3. 对比OpenCV与BMCV边缘检测所需要的时间;

三、开发环境

开发主机:Ubuntu 22.04 LTS

硬件:算能SE5

本地如果有SE5硬件,则可以PC机作为客户端,SE5作为服务器端。本地如果没有SE5硬件,只有云空间,则可以直接将客户端和服务器端都通过云空间实现,机在云空间的SE5模拟环境中实现。

四、实验器材

开发主机 + 云平台

五、实验过程与结论

5.1 BMCV关键函数解析

请参考算能BMCV开发资料:《BMCV User Guide》,也可以通过以下网址下载:

https://doc.sophgo.com/docs/2.7.0/docs_latest_release/bmcv/BMCV_User_Guide_zh.pdf

OpenCV的开发资料可参考《OpenCV官方文档》。

算能BMCV提供了bmcv_image_sobelbmcv_image_canny函数用于进行边缘检测。

bmcv_image_sobel

bm_status_t bmcv_image_sobel (
bm_handle_t handle,       //BMCV句柄
bm_image input,           //输入的BMI图片(待处理)
bm_image output,          //输出的BMI图片(处理结果)
int dx,                   //x 方向上的差分阶数
int dy)                   //y 方向上的差分阶数

具体函数接口说明如下:

(1)第二个参数和第三个参数图像的格式为bm_image,bm_image 需要外部调用 bmcv_image_creat创建。image 内存可以使用 bm_image_alloc_dev_mem 或者 bm_image_copy_host_to_device来开辟新的内存,或者使用 bmcv_image_attach 来 attach 已有的内存。

(2)dx, dy取值皆为1或0。 其中,dx=1,dy=0,表示计算X方向的导数,检测出的是垂直方向上的边缘;dx=0,dy=1,表示计算Y方向的导数,检测出的是垂直方向上的边缘。

(3) Sobel 核的大小,必须是-1,1,3,5 或7。其中特殊地,如果是-1 则使用3×3 Scharr 滤波器,如果是1 则使用3×1 或者1×3 的核。默认值为3。scale 为对求出的差分结果乘以的系数,默认值为1。Delta为在输出最终结果之前加上该偏移量,默认值为0。通常不需要对scale和Delta进行设置。

bmcv_image_canny

bm_status_t bmcv_image_canny (
bm_handle_t handle,
bm_image input,
bm_image output,
float threshold1,
float threshold2,
int aperture_size = 3,
bool l2gradient = false);

具体函数接口说明如下:

(1)第二个参数和第三个参数图像的格式为bm_image,bm_image 需要外部调用 bmcv_image_create 创建。image 内存可以使用 bm_image_alloc_dev_mem 或者 bm_image_copy_host_to_device来开辟新的内存,或者使用 bmcv_image_attach 来 attach 已有的内存。

(2)threshold1 和threshold2 为双阈值法的第一、第二个阈值。aperture_size 为 其中Sobel 核的大小,目前仅支持3。l2gradient 表示是否使用L2 范数来求图像梯度, 默认值为false,默认为由L1范数来求解图像梯度。

注意,BMCV的函数接口都是基于BMI格式进行图像处理。如上面的函数说明,其中第二个参数和第三个参数都是基于bm_image格式的。因此,需要首先通过OpenCV读取图片,并将图片格式转换为BMI格式后,才可以调用bmcv_image_sobel和bmcv_image_canny函数进行边缘检测。

本实验及实验5,实验6,实验7中使用BMCV相关函数的基本处理流程如下图所示,仅需调整红框模块中所调用的API即可实现不同实验功能:

4-1 实验流程框图

首先,本实例为了利用BMCV接口,需要引用相关的BMCV相关头文件:

#include "bmcv_api.h"

创建Mat类对象并读取图片数据:

# 创建OpenCV类对象
cv::Mat Input,Out;
# 读取第二个命令行参数存入mat对象中(读取数据)
Input = cv::imread(argv[1], 0);

注意,这里OpenCV类读取到的图片文件输出的格式是MAT格式,而BMCV处理的图片是bm_image格式,即BMCV对象。因此,我们需要先创建BMCV对象,然后将OpenCV类读取到的图片通过toBMI接口转换为BMCV对象。

# 创建BMCV对象
bm_image input, output;
bm_image_create(handle,height,width,FORMAT_GRAY,DATA_TYPE_EXT_1N_BYTE,&input);
# 以下是c++智能指针:划分一块内存区域并获取其信息
std::unique_ptr<unsigned char[]> src_data(new unsigned char[width * height]);
std::unique_ptr<unsigned char[]> res_data(new unsigned char[width * height]);

BMCV对象操作要求,在对象创建后,需要为该对象申请内部管理内存。如下函数所示:

bm_image_alloc_contiguous_mem(1, &input);
bm_image_alloc_contiguous_mem(1, &output);

也可以通过bm_image_alloc_dev_mem(input)函数申请内存:

bm_image_alloc_dev_mem(input)
bm_image_alloc_dev_mem(output);

然后通过toBMI函数将OpenCV读取的图片mat类数据转化为BMCV类数据,再调用bmcv_image_sobel函数进行处理:

cv::bmcv::toBMI(Input,&input);
# Sobel边缘检测
bmcv_image_sobel(handle, input, output, 0, 1)

需要注意的是这里用了toBMI函数实际内部做了一个内存同步的操作。也就是OpenCV读取的mat格式图片实际处于系统内存中,通过toBMI转换后同步到设备内存中。这里也可以通过bm_image_copy_host_to_device函数完成内存的同步。具体见上述的《BMCV User Guide110页中的示例代码所采用的方法。

将处理结果转化为mat数据格式保存

cv::bmcv::toMAT(&output, Out);
cv::imwrite("out.jpg", Out);

销毁内存

bm_image_free_contiguous_mem(1, &input);
bm_image_free_contiguous_mem(1, &output);
bm_image_destroy(input);
bm_image_destroy(output);
bm_dev_free(handle);

综上,我们可以得到利用BMCV sobel函数进行图像边缘检测的关键代码如下:

#include <iostream>
#include <vector>
#include "bmcv_api.h"
#include "common.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <memory>

using namespace cv;
using namespace std;

int main(int argc, char *argv[]) {	
bm_handle_t handle;					  //获取句柄 
	bm_dev_request(&handle, 0);
	int width =  600;					   	  //定义图片数据 
	int height = 600;
	cv::Mat Input,Out,Test; 				   
	Input = cv::imread(argv[1], 0);	      //opencv读取图片,通过命令行参数传入
		
	// 智能指针获取分配内存数据 
	std::unique_ptr<unsigned char[]> src_data(
		  new unsigned char[width * height]);
	std::unique_ptr<unsigned char[]> res_data(
		  new unsigned char[width * height]);

	// bmcv处理 
	bm_image input, output;
	bm_image_create(handle,height,width,FORMAT_GRAY, DATA_TYPE_EXT_1N_BYTE,&input);
	bm_image_alloc_contiguous_mem(1, &input, 1); 	// 分配device memory 
	unsigned char * input_img_data = src_data.get();
	bm_image_copy_host_to_device(input, (void **)&input_img_data);
    bm_image_create(handle,height,width,FORMAT_GRAY,DATA_TYPE_EXT_1N_BYTE,&output);
	bm_image_alloc_contiguous_mem(1, &output, 1);	
		
	cv::bmcv::toBMI(Input,&input);                  //自动进行内存同步
    // bmcv图像处理:ca
	if (BM_SUCCESS != bmcv_image_sobel(handle, input, output, 0, 1)) {
		std::cout << "bmcv sobel error !!!" << std::endl;
		bm_image_destroy(input);
		bm_image_destroy(output);
		bm_dev_free(handle);
		return -1;
	}// 将输出结果转成mat数据并保存 
	cv::bmcv::toMAT(&output, Out);
	cv::imwrite("out.jpg", Out);
		
	bm_image_free_contiguous_mem(1, &input);
	bm_image_free_contiguous_mem(1, &output);
	bm_image_destroy(input);
	bm_image_destroy(output);
	bm_dev_free(handle);
    return 0; 
}

如果采用bmcv_image_canny函数进行边缘检测,只需要将上述代码中的bmcv_image_sobel函数改为bmcv_image_canny函数即可:

// bmcv图像处理:canny 
if (BM_SUCCESS != bmcv_image_canny(handle, input, output, 0, 200)) {
	    td::cout << "bmcv canny error !!!" << std::endl;
		bm_image_destroy(input);
		bm_image_destroy(output);
		bm_dev_free(handle);
		exit(-1);
}

编写makfile文件:

DEBUG        ?= 0
PRODUCTFORM  ?= soc
BM_MEDIA_ION ?= 0

INSTALL_DIR    ?= release

//注意:这个地方一定要根据自己的目录路径进行设置
top_dir :=../../..

ifeq ($(PRODUCTFORM),x86) # pcie mode
    CROSS_CC_PREFIX = x86_64-linux-
else # pcie_arm64 and soc mode
    CROSS_CC_PREFIX = aarch64-linux-gnu-
endif

CC  = $(CROSS_CC_PREFIX)gcc
CXX = $(CROSS_CC_PREFIX)g++

CPPFLAGS := -std=gnu++11 -fPIC -Wall -Wl,--fatal-warning
ifeq ($(DEBUG), 0)
    CPPFLAGS += -O2
else
    CPPFLAGS += -g
endif

# NATIVE API SDK
NATIVE_SDK_HEADERS:=-I$(top_dir)/include/decode
NATIVE_SDK_LDFLAGS:=-L$(top_dir)/lib/decode/${PRODUCTFORM}
NATIVE_SDK_LDLIBS :=-lbmion -lbmjpulite -lbmjpuapi -lbmvpulite -lbmvpuapi -lbmvideo -lbmvppapi -lyuv

# FFMPEG SDK
FF_SDK_HEADERS := -I$(top_dir)/include/ffmpeg
FF_SDK_LDFLAGS := -L$(top_dir)/lib/ffmpeg/$(PRODUCTFORM)
FF_SDK_LDLIBS  := -lavcodec -lavformat -lavutil -lswresample -lswscale

# OpenCV SDK
OCV_SDK_HEADERS := -I$(top_dir)/include/opencv/opencv4
OCV_SDK_LDFLAGS := -L$(top_dir)/lib/opencv/$(PRODUCTFORM)
OCV_SDK_LDLIBS  := -lopencv_core -lopencv_imgcodecs -lopencv_imgproc -lopencv_videoio

# BMCV SDK
BMCV_SDK_HEADERS := -I$(top_dir)/include/bmlib
BMCV_SDK_LDFLAGS := -L$(top_dir)/lib/bmnn/$(PRODUCTFORM)
ifeq (${PRODUCTFORM}, x86)
BMCV_SDK_LDFLAGS :=  -L$(top_dir)/lib/bmnn/pcie
endif
BMCV_SDK_LDLIBS  := -lbmcv -lbmlib

CPPFLAGS += $(NATIVE_SDK_HEADERS) $(FF_SDK_HEADERS) $(OCV_SDK_HEADERS) $(BMCV_SDK_HEADERS)
LDFLAGS  := $(NATIVE_SDK_LDFLAGS) $(FF_SDK_LDFLAGS) $(OCV_SDK_LDFLAGS)

LDLIBS   := $(NATIVE_SDK_LDLIBS) $(FF_SDK_LDLIBS) $(OCV_SDK_LDLIBS) $(BMCV_SDK_LDLIBS) -lpthread -lstdc++

TARGET=bmcv_sobel
MAKEFILE=Makefile
ALLOBJS=*.o
ALLDEPS=*.dep
RM=rm -rf
CP=cp -f

SOURCES := bmcv_sobel.cpp

OBJECTPATHS:=$(patsubst %.cpp,%.o,$(SOURCES))

.phony: all clean

all: $(TARGET)

$(TARGET): $(OBJECTPATHS)
	$(CC) -o $@ $(OBJECTPATHS) $(LDFLAGS) $(LDLIBS)

install: $(TARGET)
	install -d $(INSTALL_DIR)/bin
	install $(TARGET) $(INSTALL_DIR)/bin

uninstall:
	$(RM) $(INSTALL_DIR)/bin/$(TARGET) 

clean:
	$(RM) $(TARGET)
	$(RM) $(ALLDEPS)
	$(RM) $(ALLOBJS)

bmcv_sobel.o : bmcv_sobel.cpp $(MAKEFILE)
	$(CXX) $(CPPFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)
LDLIBS   := $(NATIVE_SDK_LDLIBS) $(FF_SDK_LDLIBS) $(OCV_SDK_LDLIBS) $(BMCV_SDK_LDLIBS) -lpthread -lstdc++

TARGET=bmcv_sobel
MAKEFILE=Makefile
ALLOBJS=*.o
ALLDEPS=*.dep
RM=rm -rf
CP=cp -f

SOURCES := bmcv_sobel.cpp

OBJECTPATHS:=$(patsubst %.cpp,%.o,$(SOURCES))

.phony: all clean

all: $(TARGET)

$(TARGET): $(OBJECTPATHS)
	$(CC) -o $@ $(OBJECTPATHS) $(LDFLAGS) $(LDLIBS)

install: $(TARGET)
	install -d $(INSTALL_DIR)/bin
	install $(TARGET) $(INSTALL_DIR)/bin

uninstall:
	$(RM) $(INSTALL_DIR)/bin/$(TARGET) 

clean:
	$(RM) $(TARGET)
	$(RM) $(ALLDEPS)
	$(RM) $(ALLOBJS)

bmcv_sobel.o : bmcv_sobel.cpp $(MAKEFILE)
	$(CXX) $(CPPFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)
5.2 BMCV执行结果

向云平台或SE5上传待检测的图片,并执行如下代码:

./bmcv_sobel greycat.jpeg bmcv

运行程序后,对同一张图片进行处理所得出的sobelcanny边缘检测的两个结果:

Sobel:

Canny:

如上图所示,两种边缘检测都能大概检测出图像边缘,但精细程度不同。在实际应用时可选择自己所适合的方式选择合适的边缘检测方式。

5.3 OpenCV关键函数解析

OpenCV也提供了SobelCanny边缘检测算子,具体函数原型如下:

void cv::Canny(InputArray image,
    OutputArray  edges,
    double  threshold1,
    double 	threshold2,
    int 	apertureSize = 3,
    bool    L2gradient = false 
)

	
void cv::Sobel(InputArray src,
    OutputArray dst, //输出图像,与输入图像src具有相同的尺寸和通道数,数据类型由第三个参数ddepth控制。
    int ddepth,    // ddepth:输出图像的数据类型(深度), 为-1时,输出图像的数据类型自动选择。
    int dx,
    int dy,
    int ksize = 3,
    double scale = 1,
    double delta = 0,
    int borderType = BORDER_DEFAULT) //像素外推法选择标志,默认为//BORDER_DEFAULT,表示不包含边界值倒序填充。

同名参数的含义与BMCV中参数含义相同。OpenCV下,不需要进行BMI转换,直接可以将读取到的MAT格式的图片通过sobel Canny接口进行处理。如下图所示:

//头文件
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
....
//关键代码
cv::Mat srcImage = cv::imread(argv[1], 1);
cv::Mat grayImage; 
cv::Mat srcImage1 = srcImage.clone();
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
Mat dstImage, edge;
dstImage.create(srcImage1.size(), srcImage1.type());
dstImage = Scalar::all(0);
srcImage1.copyTo(dstImage, edge);

5.4 硬件加速性能对比

此外,在算能云平台上,基于BMCVsobel函数,因为使用了硬件加速,所以可以提升速率。

为验证执行程序所需的时间,须在运行时通过time命令来实现,如下图所示:

第一张图为用OpenCVsobel函数所需时间,第二张图为用bmcvsobel函数时所需的时间。经硬件加速后,程序所需的运行时间明显减少。

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

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

相关文章

【EI会议征稿】2024年智慧城市与信息系统国际学术会议 (ICSCIS 2024)

2024年智慧城市与信息系统国际学术会议 (ICSCIS 2024) 2024 International Conference on Smart City and Information System 随着互联网技术的发展&#xff0c;城市化进程的深入&#xff0c;智慧城市的研究与发展越来越普遍&#xff0c;运用物联网、云计算、大数据等先进信…

HackTheBox-Machines--Codify

文章目录 1 端口扫描2 80端口探测3 利用CVE-2023-30547获取权限4 权限提升 Codify 测试过程 1 端口扫描 nmap -sC -sV 10.129.59.150端口扫描&#xff0c;发现开启了22、80、3000三个端口&#xff1a;   22端口&#xff1a;没有账号密码&#xff0c;暂时不考虑进行测试   8…

LVS+keepalived——高可用集群

lvskeepalived&#xff1a;高可用集群 keepalived为lvs应运而生的高可用服务。lvs的调度器无法做高可用&#xff0c;于是keepalived这个软件。实现的是调度器的高可用。但是&#xff1a;keepalived不是专门为lvs集群服务的&#xff0c;也可以做其他代理服务器的高可用。 lvs的…

VSCode新建Vue项目

前言 Vue.js 是一款流行的 JavaScript 前端框架&#xff0c;它可以帮助开发者轻松构建高性能、可扩展的 Web 应用程序。而 VSCode 则是一款功能强大的开源代码编辑器&#xff0c;它提供了许多有用的工具和插件&#xff0c;可以大幅提高开发效率。 在本文中&#xff0c;我们将…

HTML+CSS+ElementUI搭建个人博客页面(纯前端)

网站演示 搭建过程 html部分 首先下载Vue2&#xff0c;ElementUI等插件&#xff0c;放在你的本地。我这里为了运行方便&#xff0c;把代码放在了一个框架里运行。 下载后引入部分 <link rel"stylesheet" href"{{URL::asset(elementui/lib/theme-chalk/ind…

门店数字化系统功能有哪些?门店数字化系统介绍

随着科技的发展&#xff0c;门店也在逐步实现数字化转型。一个完整的门店数字化系统应包含预约功能、商城系统、会员系统、供应链系统以及合伙人系统等功能。以下是一个门店数字化系统的功能框架。 一、门店预约功能 预约功能是门店数字化系统的核心之一&#xff0c;它可以帮助…

【Mycat2实战】五、Mycat实现分库分表【实践篇】

Mycat2本系列文章 一、Mycat简介 二、Mycat安装部署 三、Mycat实现读写分离 四、Mycat实现分库分表【概念篇】 五、Mycat实现分库分表【实践篇】 【番外】Mysql主从复制搭建 1. 前言 上篇文章讲述了Mycat分库分表的概念&#xff0c;本文主要讲如何使用Mycat来实现分库分表。…

Android Studio 引入Xui框架-简单应用

Android Studio Flamingo | 2022.2.1 Patch 2 Android 11开发、Gradle Version 8.0、 jdk17 源代码&#xff1a;GitHub - xuexiangjys/XUI: &#x1f48d;A simple and elegant Android native UI framework, free your hands! (一个简洁而优雅的Android原生UI框架&#xff…

python tkinter 使用

python tkinter 使用 ython可以使用多种GUI库来创建窗口页面&#xff0c;例如Tkinter、PyQt、wxPython等。 本篇文章主要讲述如何使用tkinter。 1&#xff1a;导入 import tkinter as tk这时如果运行的话会提示&#xff1a; ModuleNotFoundError: No module named ‘tkint…

卷?中学生开始学习人工智能和大模型,附课件!

卷&#xff1f;中学生开始学习人工智能和大模型&#xff0c;附课件&#xff01; 大家好&#xff0c;我是老章 发现一个面向11-14岁人群的AI课程&#xff0c;还附加了大模型内容&#xff0c;浏览了一遍它们的课件&#xff08;还有面向教师的资源&#xff09;&#xff0c;感觉非…

SAP 重复制造简介

重复制造是制造模块三大制造模式&#xff08;离散、流程、重复&#xff09;之一&#xff0c;是专门用于产品大批量输出的生产环境。 重复制造与JIT管理思想、拉式生产理念紧密结合&#xff0c;首先在日本企业中得到广泛且成功的运用。与重复制造密切相关的功能&#xff0c;如看…

【力扣】 209. 长度最小的子数组

【力扣】 209. 长度最小的子数组 文章目录 【力扣】 209. 长度最小的子数组1. 题目介绍2. 解法2.1 暴力求解2.2 前缀和 二分查找2.3 滑动窗口2.4 贪心回溯 3. Danger参考 1. 题目介绍 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 …

负载均衡Ribbon和Feign的使用与区别

Ribbon 的介绍 Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具。主要功能是提供客户端的软件负载均衡和服务调用。Ribbon 客户端组件提供一系列完善的配置项如连接超时&#xff0c;重试等。简单的说&#xff0c;就是在配置文件中列出Load Balancer…

“茶叶创新:爆改营销策略,三个月狂销2300万“

我的朋友去年制作了一款白茶&#xff0c;并在短短三个月内将其销售量推到了2300万的高峰。你相信吗&#xff1f; 这位朋友并没有任何茶叶方面的经验&#xff0c;他只是一个有着冒险精神的企业家。他先找到了一家代工厂&#xff0c;帮助他把他的茶叶理念转化为现实。 当他把茶叶…

小趴菜教你如何用Python开发手机App..

Python语言虽然很万能&#xff0c;但用它来开发app还是显得有点不对路&#xff0c;因此用Python开发的app应当是作为编码练习、或者自娱自乐所用&#xff0c;加上目前这方面的模块还不是特别成熟&#xff0c;bug比较多&#xff0c;总而言之&#xff0c;劝君莫轻入。 准备工作 …

Java修仙记之记录一次与前端女修士论道的经历

文章开始之前&#xff0c;想跟我念一句&#xff1a;福生无量天尊&#xff0c;无量寿佛&#xff0c;阿弥陀佛 第一场论道&#xff1a;id更新之争 一个天气明朗的下午&#xff0c;前端的小美女长发姐告诉我&#xff1a;嘿&#xff0c;小后端&#xff0c;你的代码报错了 我答道&am…

【旅游行业】Axure旅游社交平台APP端原型图,攻略门票酒店民宿原型案例

作品概况 页面数量&#xff1a;共 110 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;旅游平台&#xff0c;酒店住宿 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本作品为「旅游社交平台」移动端…

每日一练 | 华为认证真题练习Day134

1、开启标准STP协议的交换机可能存在哪些端口状态&#xff1f;&#xff08;多选&#xff09; A. Discarding B. Listening C. Disabled D. Forwarding 2、下列路由协议中优先级最高的是&#xff1f; A. Direct B. RIP C. OSPF D. Static 3、参考如图所示的输出结果&…

如何使用API接口对接淘宝获取店铺销量排序,店铺名称等参数

要接入淘宝官方开放平台API接口获取店铺销量排序&#xff0c;店铺名称等参数&#xff0c;需要按照以下步骤进行操作&#xff1a; 找到可用的API接口&#xff1a;首先&#xff0c;需要找到支持查询店铺信息的API接口。可以在电商数据平台的开放平台上查找相应的API接口。注册并…

PHP 进阶之路 - 亿级 pv 网站架构实战之性能优化

PHP 进阶之路 - 亿级 pv 网站架构实战之性能优化 缘起 PV和PU&#xff08;数据分析—业务指标&#xff09; PV即访问次数——用户每访问一次可以看作一次PV。 PU即访问人数——在同一天内&#xff0c;一个用户无论访问了多少次都算一个访客。 通过PV和PU可以分析出用户喜欢…