工作小计- RGB相关算子实现

项目中的模型一直都是直接操作NV12的yuv格式数据,这次的模型只支持RGB格式的输入,正好来自己实现对应的算子。
这里记录一下对应算子的实现过程,主要涉及到NV12到RGB的变换,RGB的crop/resize操作,对于数据的Norm/ToFloat操作,调整Layout等等。

cu文件是要nvcc来进行编译的,但是其头文件可以供外部的cpp文件调用,另外这里的核函数并没有涉及到stream的考虑,因为这个涉及到之后的性能优化环节,要有先来后到。实际stream也就是在核函数调用前的<<<>>>中传入stream而已,然后之后要跟着同步stream的操作。与函数实现逻辑无关。

cuda_transformation.cu

在这里实现真正的核函数,

NV12toRGB

在这里插入图片描述

这里的坑点在于 BT.601/709 FULL/非FULL的yuv格式,如果出了差错会导致图像看起来色度不对,遇到过的问题就是红色很不明显,原因就是转换公式写的有问题。

__global__ void NV12toRGB(uint8_t *yuv, uint8_t *rgb, int width,int height) {const int nv_start = width * height;int i, j, nv_index = 0;uint8_t y, u, v;int r, g, b;j = blockIdx.x * blockDim.x + threadIdx.x;i = blockIdx.y * blockDim.y + threadIdx.y;if (i >= height || j >= width)return;nv_index = i / 2 * width + j - j % 2;int rgb_index = i * width + j;y = yuv[rgb_index];u = yuv[nv_start + nv_index];v = yuv[nv_start + nv_index + 1];r = y + (140 * (v - 128)) / 100;                         // rg = y - (34 * (u - 128)) / 100 - (71 * (v - 128)) / 100; // gb = y + (177 * (u - 128)) / 100;                         // bif (r > 255)r = 255;if (g > 255)g = 255;if (b > 255)b = 255;if (r < 0)r = 0;if (g < 0)g = 0;if (b < 0)b = 0;rgb[rgb_index * 3 + 0] = b;rgb[rgb_index * 3 + 1] = g;rgb[rgb_index * 3 + 2] = r;
}int cudaNV12toRGB(uint8_t *input, uint8_t *output, size_t width,size_t height) {if (!input || !output)return cudaErrorInvalidDevicePointer;const dim3 blockDim(32, 32, 1);const dim3 gridDim((width + blockDim.x - 1) / blockDim.x,(height + blockDim.y - 1) / blockDim.y, 1);NV12toRGB<<<gridDim, blockDim>>>(input, output, width, height);return cudaDeviceSynchronize();
}

RGBBilinearResize

__global__ void RGBBilinearResize(uint8_t *input, uint8_t *output,int inputWidth, int inputHeight,int outputWidth, int outputHeight) {// 计算线程的全局索引int x = blockIdx.x * blockDim.x + threadIdx.x;int y = blockIdx.y * blockDim.y + threadIdx.y;if (x >= outputWidth || y >= outputHeight)return;// gx,gy是相对于resize后的图中的点,这里计算对应的原图中的浮点位置,确定要从哪里采样float gx = ((float)x) / outputWidth * (inputWidth - 1);float gy = ((float)y) / outputHeight * (inputHeight - 1);// 对应的整数位置及其偏移量int gxi = (int)gx;int gyi = (int)gy;float dx = gx - gxi;float dy = gy - gyi;// 读取四个最近的像素值uint8_t topLeft[3] = {input[(gyi * inputWidth + gxi) * 3 + 0],input[(gyi * inputWidth + gxi) * 3 + 1],input[(gyi * inputWidth + gxi) * 3 + 2]};uint8_t topRight[3] = {input[(gyi * inputWidth + gxi + 1) * 3 + 0],input[(gyi * inputWidth + gxi + 1) * 3 + 1],input[(gyi * inputWidth + gxi + 1) * 3 + 2]};uint8_t bottomLeft[3] = {input[((gyi + 1) * inputWidth + gxi) * 3 + 0],input[((gyi + 1) * inputWidth + gxi) * 3 + 1],input[((gyi + 1) * inputWidth + gxi) * 3 + 2]};uint8_t bottomRight[3] = {input[((gyi + 1) * inputWidth + gxi + 1) * 3 + 0],input[((gyi + 1) * inputWidth + gxi + 1) * 3 + 1],input[((gyi + 1) * inputWidth + gxi + 1) * 3 + 2]};// 对每个通道进行双线性插值for (int i = 0; i < 3; i++) {float top = topLeft[i] * (1 - dx) + topRight[i] * dx;float bottom = bottomLeft[i] * (1 - dx) + bottomRight[i] * dx;output[(y * outputWidth + x) * 3 + i] = top * (1 - dy) + bottom * dy;}
}int cudaRGBBilinearResize(uint8_t *input, uint8_t *output, size_t width,size_t height, size_t resize_width,size_t resize_height) {if (!input || !output)return cudaErrorInvalidDevicePointer;const dim3 blockDim(32, 32, 1);const dim3 gridDim((width + blockDim.x - 1) / blockDim.x,(height + blockDim.y - 1) / blockDim.y, 1);RGBBilinearResize<<<gridDim, blockDim>>>(input, output, width, height,resize_width, resize_height);return cudaDeviceSynchronize();
}

RGBToFloat

这里的实现要额外记录下,因为涉及到debug中的opencv-dump所以在传入模型前的数据都是BGR格式的,在转浮点这里重新调整成模型需要的RGB格式。

__global__ void RGBToFloat(uint8_t *input, float *output, int width, int height) {int x = blockIdx.x * blockDim.x + threadIdx.x;int y = blockIdx.y * blockDim.y + threadIdx.y;if (x >= width || y >= height) return;int idx = y * width + x;output[idx * 3 + 0] = input[idx * 3 + 2] / 255.0f; // Routput[idx * 3 + 1] = input[idx * 3 + 1] / 255.0f; // Goutput[idx * 3 + 2] = input[idx * 3 + 0] / 255.0f; // B
}int cudaRGBToFloat(uint8_t *input, float *output, int width, int height) {dim3 blockDim(16, 16);dim3 gridDim((width + blockDim.x - 1) / blockDim.x, (height + blockDim.y - 1) / blockDim.y);RGBToFloat<<<gridDim, blockDim>>>(input, output, width, height);return cudaDeviceSynchronize();
}

RGBNormalize

__global__ void RGBNormalize(float *image, int width, int height, float mean[], float std[]) {int x = blockIdx.x * blockDim.x + threadIdx.x;int y = blockIdx.y * blockDim.y + threadIdx.y;if (x >= width || y >= height) {return;}int idx = y * width + x;if (std[0] < 1e-6 || std[1] < 1e-6 || std[2] < 1e-6) {printf("Error: std values are too small for safe division.\n");return;}image[idx * 3 + 0] = (image[idx * 3 + 0] - mean[0]) / std[0]; // Bimage[idx * 3 + 1] = (image[idx * 3 + 1] - mean[1]) / std[1]; // Gimage[idx * 3 + 2] = (image[idx * 3 + 2] - mean[2]) / std[2]; // R
}int cudaRGBNormalize(float *d_image, int width, int height, float mean[], float std[]) {dim3 blockDim(16, 16);dim3 gridDim((width + blockDim.x - 1) / blockDim.x, (height + blockDim.y - 1) / blockDim.y);RGBNormalize<<<gridDim, blockDim>>>(d_image, width, height, mean, std);cudaError_t cudaStatus = cudaDeviceSynchronize();if (cudaStatus != cudaSuccess) {fprintf(stderr, "CUDA error: %s\n", cudaGetErrorString(cudaStatus));return -1;}return 0;
}

HWC2CHW

template <typename T>
__global__ void HWC2CHW(const T* input, T* output, int height, int width) {int x = blockIdx.x * blockDim.x + threadIdx.x;int y = blockIdx.y * blockDim.y + threadIdx.y;if (x >= width || y >= height) return;int channelSize = width * height;int hwcIndex = y * width + x;int chwIndex;for (int c = 0; c < 3; ++c) {chwIndex = c * channelSize + y * width + x;output[chwIndex] = input[hwcIndex * 3 + c];}
}template <typename T>
int cudaHWC2CHW(const T* input, T* output, int height, int width) {dim3 blockDim(16, 16);dim3 gridDim((width + blockDim.x - 1) / blockDim.x, (height + blockDim.y - 1) / blockDim.y);HWC2CHW<<<gridDim, blockDim>>>(input, output, height, width);return cudaDeviceSynchronize();
}
template int cudaHWC2CHW<float>(const float* input, float* output, int height, int width);

cuda_transformation.h

void convertNV12toYUV444withActions_cuda(uint8_t *src_img, uint8_t *src_imgcuda,uint8_t *tmpImagecuda,ImageTransParam &trans_param,uint8_t *dst_imgcuda, uint8_t *dst_img,cudaStream_t stream);
void convertNV12toYUV444withActions_cuda1(uint8_t *src_imgcuda,ImageTransParam &trans_param,uint8_t *dst_imgcuda);int cudaNV12toRGB(uint8_t* input, uint8_t* output, size_t width, size_t height);int cudaRGBBilinearResize(uint8_t *input, uint8_t *output, size_t width,size_t height, size_t resize_width,size_t resize_height);int cudaRGBToFloat(uint8_t *input, float *output, int width, int height);int cudaRGBNormalize(float *d_image, int width, int height, float mean[], float std[]);template <typename T>
int cudaHWC2CHW(const T* input, T* output, int height, int width);

image_transformation.h

这里也是对该变换进行封装,虽然项目是面向对象的抽象出了类似Transformer这个类,但是出于逻辑清晰和方便调试,我这里提供的都是面向过程的代码,另外附上了cpu中算子的实现。实际上一个图像处理算子的实现,一般过程是先生成cpu的,基于NCHW的循环版本,再对其改装成gpu上的算子,毕竟gpu的算子调试相较cpu不是很方便。虽然有cuda-gdb这种东西。可以看到cpu和gpu的版本基本上只在循环方式上有差别,因此核函数也是可以称为 for_each_pixel_func

  void TransformNV12toRGB(uint8_t *input, uint8_t *output,int width, int height) {int ret = cudaNV12toRGB(input, output, width, height);if (ret != 0){HSLOG_E << "cudaNV12toRGB FAILED";}}void CpuTransformNV12toRGB(uint8_t *yuv, uint8_t *rgb,int width, int height) {const int nv_start = width * height;uint32_t i, j, index = 0, rgb_index = 0;uint8_t y, u, v;int r, g, b, nv_index = 0;for (i = 0; i < height; i++) {for (j = 0; j < width; j++) {// nv_index = (rgb_index / 2 - width / 2 * ((i + 1) / 2)) * 2;nv_index = i / 2 * width + j - j % 2;y = yuv[rgb_index];u = yuv[nv_start + nv_index];v = yuv[nv_start + nv_index + 1];r = y + (140 * (v - 128)) / 100;                         // rg = y - (34 * (u - 128)) / 100 - (71 * (v - 128)) / 100; // gb = y + (177 * (u - 128)) / 100;                         // bif (r > 255)r = 255;if (g > 255)g = 255;if (b > 255)b = 255;if (r < 0)r = 0;if (g < 0)g = 0;if (b < 0)b = 0;// index = rgb_index % width + (height - i - 1) * width;index = rgb_index % width + i * width;rgb[index * 3 + 0] = b;rgb[index * 3 + 1] = g;rgb[index * 3 + 2] = r;rgb_index++;}}}void TransformRGBResize(uint8_t *input, uint8_t *output, size_t width,size_t height, size_t resize_width,size_t resize_height) {int ret = cudaRGBBilinearResize(input, output, width, height, resize_width, resize_height);if (ret != 0){HSLOG_E << "cudaRGBBilinearResize FAILED: " << ret;}}void CPURGBBilinearResize(uint8_t *input, uint8_t *output,int inputWidth, int inputHeight, int outputWidth,int outputHeight) {for (int y = 0; y < outputHeight; y++) {for (int x = 0; x < outputWidth; x++) {// 计算对应的原图中的浮点位置float gx = ((float)x) / outputWidth * (inputWidth - 1);float gy = ((float)y) / outputHeight * (inputHeight - 1);// 对应的整数位置及其偏移量int gxi = (int)gx;int gyi = (int)gy;float dx = gx - gxi;float dy = gy - gyi;// 读取四个最近的像素值uint8_t topLeft[3] = {input[(gyi * inputWidth + gxi) * 3 + 0],input[(gyi * inputWidth + gxi) * 3 + 1],input[(gyi * inputWidth + gxi) * 3 + 2]};uint8_t topRight[3] = {input[(gyi * inputWidth + gxi + 1) * 3 + 0],input[(gyi * inputWidth + gxi + 1) * 3 + 1],input[(gyi * inputWidth + gxi + 1) * 3 + 2]};uint8_t bottomLeft[3] = {input[((gyi + 1) * inputWidth + gxi) * 3 + 0],input[((gyi + 1) * inputWidth + gxi) * 3 + 1],input[((gyi + 1) * inputWidth + gxi) * 3 + 2]};uint8_t bottomRight[3] = {input[((gyi + 1) * inputWidth + gxi + 1) * 3 + 0],input[((gyi + 1) * inputWidth + gxi + 1) * 3 + 1],input[((gyi + 1) * inputWidth + gxi + 1) * 3 + 2]};// 对每个通道进行双线性插值for (int i = 0; i < 3; i++) {float top = topLeft[i] * (1 - dx) + topRight[i] * dx;float bottom = bottomLeft[i] * (1 - dx) + bottomRight[i] * dx;output[(y * outputWidth + x) * 3 + i] =static_cast<uint8_t>(top * (1 - dy) + bottom * dy);}}}}void TransfromConvertRGBToFloat(uint8_t *input, float *output, int width, int height){int ret = cudaRGBToFloat(input, output, width, height);if (ret != 0){HSLOG_E << "cudaRGBToFloat FAILED: " << ret;}}void TransfromRGBNormalize(float *input, int width, int height, float* mean, float* std){int ret = cudaRGBNormalize(input, width, height, mean, std);if (ret != 0){HSLOG_E << "cudaRGBNormalize FAILED: " << ret;}}template <typename T>int TransfromHWC2CHW(const T* input, T* output, int height, int width){int ret = cudaHWC2CHW<T>(input, output, height, width);if (ret != 0){HSLOG_E << "cudaHWC2CHW FAILED: " << ret;}}

pre_process_module.cpp

这里额外加入一些dump的操作,以及debuggpu前N个字节的操作,方便调试

void PreProcessModule::Transform21dImage(hobot::dataflow::spMsgResourceProc proc,const hobot::dataflow::MessageLists &msgs){UNUSED(proc);auto &input_img_batch_msgs = msgs[0];std::shared_ptr<ImageBatchMsg<GPUImageMsg>> batch_image_msg =std::static_pointer_cast<ImageBatchMsg<GPUImageMsg>>(input_img_batch_msgs->at(0));for (int i = 0; i < batch_image_msg->batch_size_; ++i) {auto image_msg = batch_image_msg->batch_img_msg_[i];int height = image_msg->img_trans_param_.src_height;int width = image_msg->img_trans_param_.src_width;image_transformation_[i].TransformNV12toRGB(image_msg->cuda_nv12_, image_transformation_[i].cuda_image_out_, width, height);static int cnt = 0;if (true){std::string input_file_path= "/home/yuxuan03.zhang/utils_code/lcc/query/" + std::to_string(cnt) + ".jpg";cv::Mat bgrImage = cv::imread(input_file_path);if (bgrImage.empty()) {std::cerr << "Error: Image cannot be loaded!" << std::endl;}size_t size = bgrImage.total() * bgrImage.elemSize(); // 计算需要复制的内存大小HSLOG_E << "height: " << height << "width: " << width << "size: " << size << "file" << input_file_path;// 将数据从 cv::Mat 复制到 GPU 内存cudaMemcpy(image_transformation_[i].cuda_image_out_, bgrImage.ptr(), size, cudaMemcpyHostToDevice);image_msg->SetDoneTimestamp(cnt);cnt++;}// int size = width * height * 3 / 2;// uint8_t* cpu_nv12 = new uint8_t[size];// cudaMemcpy(cpu_nv12, image_msg->cuda_nv12_, size, cudaMemcpyDeviceToHost);// cv::Mat nv12Img(height + height / 2, width, CV_8UC1, cpu_nv12);// cv::Mat bgrImg;// cv::cvtColor(nv12Img, bgrImg, cv::COLOR_YUV2BGR_NV12);// std::string file = std::to_string(image_msg->GetGenTimestamp()) + "_nv12.png";// cv::imwrite(file, bgrImg);// delete[] cpu_nv12;// int dataSize = width * height * 3;// uint8_t* cpu_rgb = new uint8_t[dataSize];// cudaMemcpy(cpu_rgb, image_transformation_[i].cuda_image_out_, dataSize, cudaMemcpyDeviceToHost);// cv::Mat rgb_img(height, width, CV_8UC3, cpu_rgb);// std::string file1 = std::to_string(image_msg->GetGenTimestamp()) + "_rgb.png";// cv::imwrite(file1, rgb_img);// delete[] cpu_rgb;image_transformation_[i].TransformRGBResize(image_transformation_[i].cuda_image_out_, image_transformation_[i].cuda_image_trans_buffer_, width, height, 910, 512);HSLOG_E <<"Resize: " << PrintFirstNUint8Bytes((uint8_t*)image_transformation_[i].cuda_image_trans_buffer_);// uint8_t* cpu_rgb_resize = new uint8_t[910*512*3];// cudaMemcpy(cpu_rgb_resize, image_transformation_[i].cuda_image_trans_buffer_, 910*512*3, cudaMemcpyDeviceToHost);// cv::Mat rgb_resize_img(512, 910, CV_8UC3, cpu_rgb_resize);// std::string file2 = std::to_string(image_msg->GetGenTimestamp()) + "_rgb_resize.png";// cv::imwrite(file2, rgb_resize_img);// delete[] cpu_rgb_resize;image_transformation_[i].TransfromConvertRGBToFloat(image_transformation_[i].cuda_image_trans_buffer_, (float*)image_transformation_[i].cuda_image_out_, 910, 512);HSLOG_E <<"BRGToRGBFloat: " << PrintFirstNFloatBytes((float*)image_transformation_[i].cuda_image_out_);std::vector<float> mean = {0.485, 0.456, 0.406};std::vector<float> std  = {0.229, 0.224, 0.225};float* mean_gpu = (float*)image_transformation_[i].cuda_image_trans_buffer_;float* std_gpu = mean_gpu+3;cudaMemcpy(mean_gpu, mean.data(), 3 * sizeof(float), cudaMemcpyHostToDevice);cudaMemcpy(std_gpu, std.data(), 3 * sizeof(float), cudaMemcpyHostToDevice);image_transformation_[i].TransfromRGBNormalize((float*)image_transformation_[i].cuda_image_out_, 910, 512, mean_gpu, std_gpu);HSLOG_E <<"Norm: " << PrintFirstNFloatBytes((float*)image_transformation_[i].cuda_image_out_);image_transformation_[i].TransfromHWC2CHW((float*)image_transformation_[i].cuda_image_out_, (float*)image_msg->cuda_yuv444_, 512, 910);HSLOG_E <<"HWC2CHW: " << PrintFirstNFloatBytes((float*)image_msg->cuda_yuv444_);if (true) {float *cuda_image_out_ = (float*)image_msg->cuda_yuv444_;size_t dataSize = 3 * 512 * 910 * sizeof(float);float *hostData = new float[dataSize / sizeof(float)];cudaMemcpy(hostData, cuda_image_out_, dataSize, cudaMemcpyDeviceToHost);std::string input_file_path= "./dump_bin/" + std::to_string(cnt) + ".bin";std::ofstream outFile(input_file_path, std::ios::out | std::ios::binary);outFile.write(reinterpret_cast<char *>(hostData), dataSize);outFile.close();delete[] hostData;}}SEND_DATA(SLOT_OUT_BATCH_TRANS_IMAGE, batch_image_msg);
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P1068 [NOIP2009 普及组] 分数线划定————C++、Python

目录 [NOIP2009 普及组] 分数线划定题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 解题思路C CodePython Code运行结果 [NOIP2009 普及组] 分数线划定 题目描述 世博会志愿者的选拔工作正在 A 市如火如荼的进行。为了选拔最合适的人才&#xff0c;A 市对所有报…

常用芯片学习——HC573芯片

HC573 三态输出八路透明 D 类锁存器 使用说明 锁存器是一种对脉冲电平敏感的存储单元电路&#xff0c;它们可以在特定输入脉冲电平作用下改变状态。锁存&#xff0c;就是把信号暂存以维持某种电平状态。锁存器的最主要作用是缓存&#xff0c;其次完成高速的控制器与慢速的外设…

Android studio 之 适配器

ListView仅作为容器&#xff08;列表&#xff09;&#xff0c;用于装载 & 显示数据&#xff08;即 列表项Item&#xff09;而容器内的具体数据&#xff08;列表项Item&#xff09;则是由 适配器&#xff08;Adapter&#xff09;提供 适配器&#xff08;Adapter&#xff09…

《WebKit 技术内幕》学习之八(3):硬件加速机制

3 其他硬件加速模块 3.1 2D图形的硬件加速机制 其实网页中有很多绘图操作是针对2D图形的&#xff0c;这些操作包括通常的网页绘制&#xff0c;例如绘制边框、文字、图片、填充等&#xff0c;它们都是典型的2D绘图操作。在HTML5中&#xff0c;规范又引入了2D绘图的画布功能&a…

YARN节点故障的容错方案

YARN节点故障的容错方案 1. RM高可用1.1 选主和HA切换逻辑 2. NM高可用2.1 感知NM节点异常2.2 异常NM上的任务处理 4. 疑问和思考4,1 RM感知NM异常需要10min&#xff0c;对于app来说是否太长了&#xff1f; 5. 参考文档 本文主要探讨yarn集群的高可用容错方案和容错能力的探讨。…

【复现】D-Tale SSRF漏洞(CVE-2024-21642)_26

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 D-Tale数据可视化插件是后端框架Flask与前端框架React组合产生的一款开源的数据可视化分析插件。 目前支持DataFrame、Series、Mu…

深入理解C语言(2):字符、字符串与内存函数

文章主题&#xff1a;字符、字符串与内存函数&#x1f30f;所属专栏&#xff1a;深入理解C语言&#x1f4d4;作者简介&#xff1a;更新有关深入理解C语言知识的博主一枚&#xff0c;记录分享自己对C语言的深入解读。&#x1f606;个人主页&#xff1a;[₽]的个人主页&#x1f3…

简单屏蔽WPforms垃圾留言和无效询盘的方法

简单屏蔽WPforms垃圾留言和无效询盘的方法 发表评论 因为WPforms的可视化操作非常简单&#xff0c;不少外贸网站都使用WPforms来制作询盘表单&#xff0c;而只要网站可以提交留言&#xff0c;就非常容易被垃圾留言骚扰。本文奶爸将给大家介绍两种屏蔽WPforms表单垃圾留言的方…

单片机中MCU跑RTOS相比裸机的优势

经常有读者问关于RTOS的问题&#xff0c;比如&#xff1a;我现在要不要学习RTOS&#xff1f; 学习RTOS有什么好处&#xff1f; 我的项目要不要跑RTOS&#xff1f; 问这些问题&#xff0c;其实归根结底还是对RTOS理解的不够&#xff0c;项目开发的经验还不足等。针对这部分朋友…

实战项目(一)内容管理系统

一、实现技术 前端技术&#xff1a;html、javascript(jquery、ajax、json)、css 后端技术&#xff1a;java、mysql、servlet 开发工具&#xff1a;eclipse、vscode 二、项目描述 首页仿写某大学网页&#xff0c;上面有各种栏目及栏目内容&#xff0c;管理员能登录进去对首…

FPGA之分布式RAM(2)

1) 128 X1 Single Port Distributed RAM 下图中可以看出来,通过2个LUT的组合使用可以串联实现更大深度的分布式RAM.下图中出现了F7BMUX的加入, F7BMUX可以用于LUT输出的选通. 原语调用&#xff1a; RAM128XIS#(INIT(128h00000000000000000000000000000000) // Initial conten…

2.服务拆分和远程调用

2.服务拆分和远程调用 任何分布式架构都离不开服务的拆分&#xff0c;微服务也是一样。 2.1.服务拆分原则 这里我总结了微服务拆分时的几个原则&#xff1a; 不同微服务&#xff0c;不要重复开发相同业务微服务数据独立&#xff0c;不要访问其它微服务的数据库微服务可以将…

接口测试详解,看完就会。。。

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、什么是接口测试&#xff1f; 所谓接口&#xff0c;是指同一个系统中模块与模块间的数据传递…

力扣移掉k位数字402

Problem: 402. 移掉 K 位数字 给你一个以字符串表示的非负整数 num 和一个整数 k &#xff0c;移除这个数中的 k 位数字&#xff0c;使得剩下的数字最小。请你以字符串形式返回这个最小的数字。 示例 1 &#xff1a; 给你一个以字符串表示的非负整数 num 和一个整数 k &…

Aria2 WebUI控制台 任意文件读取漏洞复现(CVE-2023-39141)

0x01 产品简介 Aria2 WebUI控制台是用于下载文件的实用程序。它支持 HTTP(S)/FTP/SFTP/BitTorrent 和 Metalink 协议。aria2可以从多个来源/协议下载文件,并尝试利用您的最大下载带宽。它支持同时从HTTP(S)/FTP/SFTP和BitTorrent下载文件,而从HTTP(S)/FTP/SFTP下载的数据上…

怎么抹掉 Macbook系统 并将它还原为出厂设置

抹掉 Mac 并将它还原为出厂设置 借助“抹掉所有内容和设置”这项功能&#xff0c;你可以快速安全地抹掉所有设置、数据和 App&#xff0c;同时保留当前安装的操作系统。 使用“抹掉所有内容和设置” 这项功能要求装有 macOS Monterey 或更高版本&#xff0c;且使用搭载 Apple 芯…

关联系统-智能座舱控制器ICC

智能座舱构成 如上图所示&#xff0c;智能座舱主要是由仪表、中控、HUD、语音、DMS/OMS等多种交互通道组成&#xff0c;其宗旨是提升人的交互体验&#xff0c;使车辆更加智能化&#xff0c;情感化。 智能座舱内部功能 仪表功能 SR场景重构 如上图所示&#xff0c;仪表区域可实…

Redis相关面试题大全

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d7;本文收录于java面试题系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏Rust初阶教程、go语言基…

为啥现在4位单片机依然没有被淘汰?

为啥现在4位单片机依然没有被淘汰&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xf…

网络安全概述

目录 网络安全背景 网络安全常识及术语 网络的脆弱性和常见安全攻击 网络环境的开放性 协议栈(TCP/IP)自身的脆弱性 物理层 -- 物理攻击 链路层 网络层 -- ICMP攻击 传输层 -- TCP SYN Flood攻击 应用层 -- DNS欺骗攻击 ​编辑 操作系统的脆弱性及常见安全攻…