CUDA小白 - NPP(2) -图像处理-算数和逻辑操作

cuda小白
原文链接 NPP

GPU架构近些年也有不少的变化,具体的可以参考别的博主的介绍,都比较详细。还有一些cuda中的专有名词的含义,可以参考《详解CUDA的Context、Stream、Warp、SM、SP、Kernel、Block、Grid》

常见的NppStatus,可以看这里。

如有问题,请指出,谢谢

Arithmetic Operations

当前模块主要是加减乘除,abs,平方,矩阵相乘,开根,ln,exp等。不同相同功能,以其中一个为例进行介绍。

AddC

针对图像中每一个像素添加一个常量值。与之类似的还有MulC,SubC,DivC,AbsDiffC,MulScale
大概接口样式:

// 两个结果的区别在于,有I的结果可以就原始的图像地址进行操作,无需进行拷贝
// Sfs的含义 表示可以对图像的数值范围进行约束操作。
NppStatus nppiAddC_[数据类型]_C[通道数]RSfs_[是否使用流]
NppStatus nppiAddC_[数据类型]_C[通道数]IRSfs_[是否使用流]

以为三通道的uint8_t的图像数据为例子:

NppStatus nppiAddC_8u_C3RSfs(const Npp8u * pSrc1,
							 int nSrc1Step,
							 const Npp8u aConstants[3],
							 Npp8u *pDst,
							 int nDstStep,
							 NppiSize oSizeROI,
							 int nScaleFactor);
NppStatus nppiAddC_8u_C3RSfs(const Npp8u aConstants[3],
							 Npp8u *pDst,
							 int nDstStep,
							 NppiSize oSizeROI,
							 int nScaleFactor);	
code
#include <iostream>
#include <cuda_runtime.h>
#include <npp.h>
#include <opencv2/opencv.hpp>

#define CUDA_FREE(ptr) { if (ptr != nullptr) { cudaFree(ptr); ptr = nullptr; } }

int main() {
  std::string directory = "../";
  // =============== load image ===============
  cv::Mat image = cv::imread(directory + "dog.png");
  if (image.empty()) {
    std::cout << "Load image error!" << std::endl;
    return -1;
  }

  int image_width = image.cols;
  int image_height = image.rows;
  int image_size = image_width * image_height * 3 * sizeof(uint8_t);
  std::cout << "Image info : image_width = " << image_width
            << ", image_height = " << image_height << std::endl;

  // =============== malloc && cpy ===============
  uint8_t *in_ptr, *in_ptr2, *out_ptr, *roi_out_ptr;
  cudaMalloc((void**)&in_ptr, image_size);
  cudaMalloc((void**)&in_ptr2, image_size);
  cudaMalloc((void**)&out_ptr, image_size);
  cudaMalloc((void**)&roi_out_ptr, image_size);
  cudaMemcpy(in_ptr, image.data, image_size, cudaMemcpyHostToDevice);
  cudaMemcpy(in_ptr2, image.data, image_size, cudaMemcpyHostToDevice);

  uint8_t host_constant[3] = { (uint8_t)0, (uint8_t)20, (uint8_t)0 };

  NppiSize roi1, roi2;
  roi1.width = image_width;
  roi1.height = image_height;
  roi2.width = image_width / 2;
  roi2.height = image_height / 2;
  
  // nppiAddC_8u_C3RSfs
  cv::Mat out_image = cv::Mat::zeros(image_height, image_width, CV_8UC3);
  NppStatus status;
  status = nppiAddC_8u_C3RSfs(in_ptr, image_width * 3, host_constant, out_ptr, 
                              image_width * 3, roi1, 0);
  if (status != NPP_SUCCESS) {
    std::cout << "[GPU] ERROR nppiAddC_8u_C3RSfs failed, status = " << status << std::endl;
    return false;
  }
  cudaMemcpy(out_image.data, out_ptr, image_size, cudaMemcpyDeviceToHost);
  cv::imwrite(directory + "add_constant.jpg", out_image);

  status = nppiAddC_8u_C3RSfs(in_ptr, image_width * 3, host_constant, out_ptr, image_width * 3, 
                              roi1, 1);
  if (status != NPP_SUCCESS) {
    std::cout << "[GPU] ERROR nppiAddC_8u_C3RSfs failed, status = " << status << std::endl;
    return false;
  }
  cudaMemcpy(out_image.data, out_ptr, image_size, cudaMemcpyDeviceToHost);
  cv::imwrite(directory + "add_constant_scale.jpg", out_image);

  status = nppiAddC_8u_C3RSfs(in_ptr, image_width * 3, host_constant, out_ptr, image_width * 3, 
                              roi2, 0);
  if (status != NPP_SUCCESS) {
    std::cout << "[GPU] ERROR nppiAddC_8u_C3RSfs failed, status = " << status << std::endl;
    return false;
  }
  cudaMemcpy(out_image.data, out_ptr, image_size, cudaMemcpyDeviceToHost);
  cv::imwrite(directory + "add_constant_roi.jpg", out_image);

  // free
  CUDA_FREE(in_ptr)
  CUDA_FREE(in_ptr2)
  CUDA_FREE(out_ptr)
  CUDA_FREE(roi_out_ptr)
}
make
cmake_minimum_required(VERSION 3.20)
project(test)

find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})

find_package(CUDA REQUIRED)
include_directories(${CUDA_INCLUDE_DIRS})
file(GLOB CUDA_LIBS "/usr/local/cuda/lib64/*.so")

add_executable(test test.cpp)
target_link_libraries(test
                      ${OpenCV_LIBS}
                      ${CUDA_LIBS}
)
result

请添加图片描述
注意点:

  1. 对图像的进行值的范围进行一定的约束,最初的图像RGB的值都是[0, 255],如果对应的scale设置为1的时候,相当于将数值的范围变为2的-nScaleFactor倍数,即[0, 128],超过128的会变成128,因此整体的图像色调会变暗;同样的,如果设置为-1,则整体的会变为2倍,因为存图的限制只能是[0, 255],所以整体看起来会变亮。
  2. 如果指定的roi不是整张图的时候,由于roi的类型是NppiSize(width, height),因此输入的指针不是指向图像的起始位置,而是roi的起始位置。
  3. MulScale相较于MulC来说,默认nScaleFactor为0;
  4. AbsDiffC中主要功能就是得到整张图像与host_constant的绝对差值。
  5. 每个结果都有一个对应的包含cudastream的版本,按需使用。

Add

与AddC不同的是,Add输入的是两张图像。同样的还有Mul,MulScale,Sub,Div,Div_round,Abs,AbsDiff,Sqr,Sqrt,Ln,Exp。(由于Abs,AbsDiff,Sqr,Sqrt,Ln,Exp在图像出列方面使用的不是很多,就不细述)。
以uint8_t的三通道图像为例:

// 命名规则与nppiAddC*类似
NppStatus nppiAdd_8u_C3RSfs(const Npp8u * pSrc1,
							int nSrc1Step,
						    const Npp8u *pSrc2,
							int nSrc2Step,
							Npp8u * pDst,
							int nDstStep,
							NppiSize oSizeROI,
							int nScaleFactor);
NppStatus nppiAdd_8u_C3IRSfs(const Npp8u *pSrc,
							 int nSrcStep,
							 Npp8u *pSrcDst,
							 int nSrcDstStep,
							 NppiSize oSizeROI,
							 int nScaleFactor);
code
#include <iostream>
#include <cuda_runtime.h>
#include <npp.h>
#include <opencv2/opencv.hpp>

#define PRINT_VALUE(value) {  \
  std::cout << "[GPU] " << #value << " = " << value << std::endl; }

#define CUDA_FREE(ptr) { if (ptr != nullptr) { cudaFree(ptr); ptr = nullptr; } }

int main() {
  std::string directory = "../";
  // =============== load image ===============
  cv::Mat image = cv::imread(directory + "dog.png");
  if (image.empty()) {
    std::cout << "Load image error!" << std::endl;
    return -1;
  }

  int image_width = image.cols;
  int image_height = image.rows;
  int image_size = image_width * image_height * 3 * sizeof(uint8_t);
  std::cout << "Image info : image_width = " << image_width
            << ", image_height = " << image_height << std::endl;

  // =============== malloc && cpy ===============
  uint8_t *in_ptr, *in_ptr2, *out_ptr, *roi_out_ptr;
  cudaMalloc((void**)&in_ptr, image_size);
  cudaMalloc((void**)&in_ptr2, image_size);
  cudaMalloc((void**)&out_ptr, image_size);
  cudaMalloc((void**)&roi_out_ptr, image_size);
  cudaMemcpy(in_ptr, image.data, image_size, cudaMemcpyHostToDevice);
  cudaMemcpy(in_ptr2, image.data, image_size, cudaMemcpyHostToDevice);

  NppiSize roi1, roi2;
  roi1.width = image_width;
  roi1.height = image_height;
  roi2.width = image_width / 2;
  roi2.height = image_height / 2;
  
  // nppiAdd_8u_C3RSfs
  cv::Mat out_image = cv::Mat::zeros(image_height, image_width, CV_8UC3);
  NppStatus status;
  status = nppiAdd_8u_C3RSfs(in_ptr, image_width * 3, in_ptr2, image_width * 3, out_ptr, 
                             image_width * 3, roi1, 0);
  if (status != NPP_SUCCESS) {
    std::cout << "[GPU] ERROR nppiAdd_8u_C3RSfs failed, status = " << status << std::endl;
    return false;
  }
  cudaMemcpy(out_image.data, out_ptr, image_size, cudaMemcpyDeviceToHost);
  cv::imwrite(directory + "add.jpg", out_image);

  status = nppiAdd_8u_C3RSfs(in_ptr, image_width * 3, in_ptr2, image_width * 3, out_ptr, 
                             image_width * 3, roi1, 1);
  if (status != NPP_SUCCESS) {
    std::cout << "[GPU] ERROR nppiAdd_8u_C3RSfs failed, status = " << status << std::endl;
    return false;
  }
  cudaMemcpy(out_image.data, out_ptr, image_size, cudaMemcpyDeviceToHost);
  cv::imwrite(directory + "add_scale.jpg", out_image);

  status = nppiAdd_8u_C3RSfs(in_ptr, image_width * 3, in_ptr2, image_width * 3, out_ptr, 
                             image_width * 3, roi2, 0);
  if (status != NPP_SUCCESS) {
    std::cout << "[GPU] ERROR nppiAdd_8u_C3RSfs failed, status = " << status << std::endl;
    return false;
  }
  cudaMemcpy(out_image.data, out_ptr, image_size, cudaMemcpyDeviceToHost);
  cv::imwrite(directory + "add_roi.jpg", out_image);

  // free
  CUDA_FREE(in_ptr)
  CUDA_FREE(in_ptr2)
  CUDA_FREE(out_ptr)
  CUDA_FREE(roi_out_ptr)
}
make
cmake_minimum_required(VERSION 3.20)
project(test)

find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})

find_package(CUDA REQUIRED)
include_directories(${CUDA_INCLUDE_DIRS})
file(GLOB CUDA_LIBS "/usr/local/cuda/lib64/*.so")

add_executable(test test.cpp)
target_link_libraries(test
                      ${OpenCV_LIBS}
                      ${CUDA_LIBS}
)
result

请添加图片描述
注意点:

  1. nScaleFactor与AddC中的功能一致。roi的操作也与AddC中的一致。
  2. 由于使用的是两个相同的图片进行相加,因此在nScaleFactor为1的时候,所有的数值都变成原来的值,因此保存的图像与原图一致。
  3. 同样有cudastream版本,按需使用。

AddWeighted

将特定区域的图像进行填充weight

NppStatus nppiAddWeighted_8u32f_C1IR(const Npp8u *pSrc,
									 int nSrcStep,
									 Npp32f * pSrcDst,
									 int nSrcDstStep,
									 NppiSize oSizeROI,
								     Npp32f nAlpha);
NppStatus nppiAddWeighted_8u32f_C1IMR(const Npp8u *pSrc,
									  int nSrcStep,
									  const Npp8u *pMask,
									  int nMaskStep,
									  Npp32f * pSrcDst,
									  int nSrcDstStep,
									  NppiSize oSizeROI,
									  Npp32f nAlpha);	
code
#include <iostream>
#include <cuda_runtime.h>
#include <npp.h>
#include <opencv2/opencv.hpp>

#define PRINT_VALUE(value) {  \
  std::cout << "[GPU] " << #value << " = " << value << std::endl; }

#define CUDA_FREE(ptr) { if (ptr != nullptr) { cudaFree(ptr); ptr = nullptr; } }

int main() {
  std::string directory = "../";
  // =============== load image ===============
  cv::Mat image = cv::imread(directory + "dog.png");
  if (image.empty()) {
    std::cout << "Load image error!" << std::endl;
    return -1;
  }

  cv::Mat gray;
  cv::cvtColor(image, gray, CV_BGR2GRAY);
  cv::imwrite(directory + "gray.jpg", gray);

  int image_width = gray.cols;
  int image_height = gray.rows;
  int image_size = image_width * image_height;
  std::cout << "Image info : image_width = " << image_width
            << ", image_height = " << image_height << std::endl;

  cv::Mat mat_mask = cv::Mat::ones(image_height, image_width, CV_8UC1);

  cv::Rect rc_center = cv::Rect(image_width / 4, image_height / 4, 
                                image_width / 2, image_height / 2);
  mat_mask(rc_center) = cv::Mat::ones(image_height / 2, image_width / 2, CV_8UC1) * 255;
  cv::imwrite(directory + "mask.jpg", mat_mask);

  // =============== malloc && cpy ===============
  uint8_t *in_ptr, *mask;
  cudaMalloc((void**)&in_ptr, image_size * sizeof(uint8_t));
  cudaMalloc((void**)&mask, image_size * sizeof(uint8_t));
  cudaMemcpy(in_ptr, gray.data, image_size, cudaMemcpyHostToDevice);
  cudaMemcpy(mask, mat_mask.data, image_size, cudaMemcpyHostToDevice);

  float *out_ptr, *out_ptr1, *out_ptr2;
  cudaMalloc((void**)&out_ptr, image_size * sizeof(float));
  cudaMalloc((void**)&out_ptr1, image_size * sizeof(float));
  cudaMalloc((void**)&out_ptr2, image_size * sizeof(float));
  
  NppiSize roi1, roi2;
  roi1.width = image_width;
  roi1.height = image_height;
  roi2.width = image_width / 2;
  roi2.height = image_height / 2;
  
  // nppiAdd_8u_C3RSfs
  cv::Mat out_image = cv::Mat::zeros(image_height, image_width, CV_32FC1);
  NppStatus status;
  status = nppiAddWeighted_8u32f_C1IMR(in_ptr, image_width * sizeof(uint8_t), 
                                       mask, image_width * sizeof(uint8_t), 
                                       out_ptr, image_width * sizeof(float), 
                                       roi1, 1.0);
  if (status != NPP_SUCCESS) {
    std::cout << "[GPU] ERROR nppiAddWeighted_8u32f_C1IMR failed, status = " << status << std::endl;
    return false;
  }
  cudaMemcpy(out_image.data, out_ptr, image_size * sizeof(float), cudaMemcpyDeviceToHost);
  cv::imwrite(directory + "addweight.jpg", out_image);

  status = nppiAddWeighted_8u32f_C1IMR(in_ptr, image_width * sizeof(uint8_t), 
                                       mask, image_width * sizeof(uint8_t), 
                                       out_ptr1, image_width * sizeof(float), 
                                       roi1, 0.5);
  if (status != NPP_SUCCESS) {
    std::cout << "[GPU] ERROR nppiAddWeighted_8u32f_C1IMR failed, status = " << status << std::endl;
    return false;
  }
  cudaMemcpy(out_image.data, out_ptr1, image_size * sizeof(float), cudaMemcpyDeviceToHost);
  cv::imwrite(directory + "addweight_scale.jpg", out_image);

  status = nppiAddWeighted_8u32f_C1IMR(in_ptr, image_width * sizeof(uint8_t), 
                                       mask, image_width * sizeof(uint8_t), 
                                       out_ptr2, image_width * sizeof(float), 
                                       roi2, 0.5);
  if (status != NPP_SUCCESS) {
    std::cout << "[GPU] ERROR nppiAddWeighted_8u32f_C1IMR failed, status = " << status << std::endl;
    return false;
  }
  cudaMemcpy(out_image.data, out_ptr2, image_size * sizeof(float), cudaMemcpyDeviceToHost);
  cv::imwrite(directory + "addweight_roi_scale.jpg", out_image);

  // free
  CUDA_FREE(in_ptr)
  CUDA_FREE(mask)
  CUDA_FREE(out_ptr)
  CUDA_FREE(out_ptr1)
  CUDA_FREE(out_ptr2)
}
make
cmake_minimum_required(VERSION 3.20)
project(test)

find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})

find_package(CUDA REQUIRED)
include_directories(${CUDA_INCLUDE_DIRS})
file(GLOB CUDA_LIBS "/usr/local/cuda/lib64/*.so")

add_executable(test test.cpp)
target_link_libraries(test
                      ${OpenCV_LIBS}
                      ${CUDA_LIBS}
)
result

请添加图片描述
注意点:
1.nAlpha是针对原图中的每一个像素的值需要添加的权重,mask仅影响目标位置中那些部分需要输出。
2. roi表示输入的区域约束。

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

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

相关文章

docker部署前端项目保姆级教程

本地启动docker&#xff08;有不会启动的吗&#xff1f;下载docker&#xff08;小海豚&#xff09;双击起来就行&#xff09; 准备阿里云账号&#xff08;免费&#xff09; 没有就去注册一个&#xff0c;记住密码后面要用到 官网地址&#xff1a;阿里云登录 - 欢迎登录阿里云…

字符设备驱动(内核态用户态内存交互)

前言 内核驱动&#xff1a;运行在内核态的动态模块&#xff0c;遵循内核模块框架接口&#xff0c;更倾向于插件。 应用程序&#xff1a;运行在用户态的进程。 应用程序与内核驱动交互通过既定接口&#xff0c;内核态和用户态访问依然遵循内核既定接口。 环境搭建 系统&#…

华为云Stack的学习(三)

四、华为云Stack公共组件 1.华为云Stack公共负载均衡方案介绍 1.1 LVS原理 LVS是四层负载均衡&#xff0c;建立在OSI模型的传输层之上&#xff0c;所以效率非常高。 LVS有两种转发模式&#xff1a; NAT模式的转发主要通过修改IP地址&#xff08;位于OSI模型的第三层网络层&…

Linux安装Docker

文章目录 先决条件开始安装1.卸载旧版本2.安装依赖3.切换数据源4.安装Docker5.启动服务6.查看版本7.查看端口8.测试拉取镜像 同系列文章 先决条件 Linux内核版本高于3.1 开始安装 1.卸载旧版本 yum remove docker \docker-client \docker-client-latest \docker-common \doc…

【算法与数据结构】404、LeetCode左叶子之和

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;思路比较简单&#xff0c;遍历所有节点然后判断该节点是否为左叶子节点&#xff0c;如果是&#xff0c…

Zblog博客网站搭建与上线发布:在Windows环境下利用cpolar内网穿透实现公网访问的指引

文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 想要成为一个合格的技术宅或程序员&#xff0c;自己搭建网站制作网页是绕…

新手将最简单的springboot部署上tomcat出现的意外问题

现阶段springboot部署到tomcat的文章一抓一大把且都相同,便贴一个地址以展示流程: SpringBoot打war包部署Tomcat(最全)_spring boot war 部署tomcat_聊Java的博客-CSDN博客 那么就说一下我出现的问题: 在完整复现流程且确认代码无误的情况下,部署到tomcat,此时问题出现了:启动…

PHPEXCEL 导出excel

$styleArray [alignment > [horizontal > Alignment::HORIZONTAL_CENTER,vertical > Alignment::VERTICAL_CENTER],];$border_style [borders > [allborders > [style > \PHPExcel_Style_Border::BORDER_THIN ,//细边框]]];$begin_date $request->beg…

损失函数介绍

用softmax&#xff0c;就可以将一个输出值转换到概率取值的一个范围。 交叉熵损失CrossEntropyLoss 第一个参数weight&#xff0c; 各类别的loss设置权值&#xff0c; 如果类别不均衡的时候这个参数很有必要了&#xff0c;加了之后损失函数变成这样&#xff1a; 第二个参数ign…

Leetcode 191.位1的个数

编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 1 的个数&#xff08;也被称为汉明重量&#xff09;。 提示&#xff1a; 请注意&#xff0c;在某些语言&#xff08;如 Java&#xff09;中…

微服务之Nacos

1 版本说明 官网地址&#xff1a; https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E 1.1 2021.x 分支 适配 SpringBoot 2.4, Spring Cloud 2021.x 版本及以上的Spring Cloud Alibaba 版本如下表&#xff08;最新版本用*标记&am…

036 - timezone

timestamp随着mysql的time_zone变化而变化&#xff0c;但是datetime不会&#xff1b; -- 查询mysql的变量&#xff1a; show variables;-- 模糊查询变量中带有time_zone的变量&#xff1a; show variables like %time_zone%; -- 创建表 create table test_time_zone (a dateti…

小蜗语音工具1.9、文本,小说,字幕生成语音、多角色对话,语音识别、读取音频字幕

小蜗语音免费工具 一、文本转字幕文本内容和TXT文件 二、文本转语音1、文本内容生成语音2、字幕生成语音3、多角色对话4、选择文件5、批量处理 三、语音识别、音频MP31、语音识别2、下载模型下载地址 一、文本转字幕 可以把正本小说&#xff0c;生成字幕文件。不限制文件的大小…

用NeRFMeshing精确提取NeRF网络中的3D网格

准确的 3D 场景和对象重建对于机器人、摄影测量和 AR/VR 等各种应用至关重要。 NeRF 在合成新颖视图方面取得了成功&#xff0c;但在准确表示底层几何方面存在不足。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 我们已经看到了最新的进展&#xff0c;例如 NVIDIA 的…

汽车制造行业,配电柜如何实施监控?

工业领域的生产过程依赖于高效、稳定的电力供应&#xff0c;而配电柜作为电力分配和控制的关键组件&#xff0c;其监控显得尤为重要。 配电柜监控通过实时监测、数据收集和远程控制&#xff0c;为工业企业提供了一种有效管理电能的手段&#xff0c;从而确保生产的连续性、安全性…

针对论坛系统进行功能测试和性能测试

项目链接:飞鸽论坛 目录 一. 项目背景 二. 项目功能 三. 功能测试 注册: 登录: 更改用户信息: 发布帖子: 更新帖子信息: 点赞: 评论: 发送私信: 测试报告 四. 性能测试 Virtual User Generator Controller Analysis 测试报告: 一. 项目背景 该论坛系统采用前…

二级MySQL(二)——编程语言,函数

SQL语言又称为【结构化查询语言】 请使用FLOOR&#xff08;x&#xff09;函数求小于或等于5.6的最大整数 请使用TRUNCATE&#xff08;x&#xff0c;y&#xff09;函数将数字1.98752895保留到小数点后4位 请使用UPPER&#xff08;&#xff09;函数将字符串‘welcome’转化为大写…

Oracle创建控制列表ACL(Access Control List)

Oracle创建控制列表ACL&#xff08;Access Control List&#xff09; Oracle ACL简介一、先登陆163邮箱设置开启SMTP。二、Oracle ACL控制列表处理&#xff08;一&#xff09;创建ACL&#xff08;create_acl&#xff09;&#xff08;二&#xff09;添加ACL权限&#xff08;add_…

研磨设计模式day11代理模式

目录 场景 代码实现 ​编辑 解析 定义 代理模式调用示意图 代理模式的特点 本质 ​编辑何时选用 场景 我有一个订单类&#xff0c;包含订单数、用户名和商品名&#xff0c;有一个订单接口包含了对订单类的getter和setter 现在有一个需求&#xff0c;a创建的订单只…

css元素定位:通过元素的标签或者元素的id、class属性定位,还不明白的伙计,看这个就行了!

前言 大部分人在使用selenium定位元素时&#xff0c;用的是xpath元素定位方式&#xff0c;因为xpath元素定位方式基本能解决定位的需求。xpath元素定位方式更直观&#xff0c;更好理解一些。 css元素定位方式往往被忽略掉了&#xff0c;其实css元素定位方式也有它的价值&…