人脸修复祛马赛克算法CodeFormer——C++与Python模型部署

news2024/12/26 10:43:57

一、人脸修复算法

1.算法简介

CodeFormer是一种基于AI技术深度学习的人脸复原模型,由南洋理工大学和商汤科技联合研究中心联合开发,它能够接收模糊或马赛克图像作为输入,并生成更清晰的原始图像。算法源码地址:https://github.com/sczhou/CodeFormer
这种技术在图像修复、图像增强和隐私保护等领域可能会有广泛的应用。算法是由南洋理工大学和商汤科技联合研究中心联合开发的,结合了VQGAN和Transformer。
VQGAN是一个生成模型,通常用于图像生成任务。它使用了向量量化技术,将图像编码成一系列离散的向量,然后通过解码器将这些向量转化为图像。这种方法通常能够生成高质量的图像,尤其在与Transformer等神经网络结合使用时。
Transformer是一种广泛用于自然语言处理和计算机视觉等领域的神经网络架构。它在序列数据处理中表现出色,也可以用于图像生成和处理任务。
在这里插入图片描述

在监控、安全和隐私保护领域,人脸图像通常会受到多种因素的影响,其中包括光照、像素限制、聚焦问题和人体运动等。这些因素可能导致图像模糊、变形或者包含大量的噪声。在这种情况下,尝试恢复清晰的原始人脸图像是一个极具挑战性的任务。
盲人脸复原是一个不适定问题(ill-posed problem),这意味着存在多个可能的解决方案,而且从有限的观察数据中无法唯一确定真实的原始图像。因此,在这个领域中,通常需要依赖先进的计算机视觉和图像处理技术,以及深度学习模型,来尝试改善模糊或受损图像的质量。
一些方法和技术可以用于处理盲人脸复原问题,包括但不限于:
深度学习模型: 使用卷积神经网络(CNN)和生成对抗网络(GAN)等深度学习模型,可以尝试从模糊或变形的人脸图像中恢复原始细节。
超分辨率技术: 超分辨率方法旨在从低分辨率图像中重建高分辨率图像,这也可以用于人脸图像复原。
先验知识: 利用先验知识,如人脸结构、光照模型等,可以帮助提高复原的准确性。
多模态融合: 结合不同传感器和信息源的数据,可以提高复原的鲁棒性。
然而,即使使用这些技术,由于问题的不适定性,完全恢复清晰的原始人脸图像仍然可能是一项极具挑战性的任务,特别是在极端条件下。在实际应用中,可能需要权衡图像质量和可用的信息,以达到最佳的结果。

2.算法效果

在官方公布修复的人脸效果里面,可以看到算法在各种输入的修复效果:
老照片修复
在这里插入图片描述
人脸修复
在这里插入图片描述
黑白人脸图像增强修复
在这里插入图片描述
人脸恢复
在这里插入图片描述

二、模型部署

如果想用C++进行模型推理部署,首先要把模型转换成onnx,转成onnx就可以使用onnxruntime c++库进行部署,或者使用OpenCV的DNN也可以,转成onnx后,还可以再转成ncnn模型使用ncnn进行模型部署。原模型可以从官方开源界面可以下载。
模型推理这块有两种做法,一是不用判断有没有人脸,直接对全图进行超分,但这种方法好像对本来是清晰的图像会出现bug,就是生成一些无法理解的处理。

1. C++使用onnxruntime部署模型

#include "CodeFormer.h"

CodeFormer::CodeFormer(std::string model_path)
{
	//OrtStatus* status = OrtSessionOptionsAppendExecutionProvider_CUDA(sessionOptions, 0);  ///nvidia-cuda加速
	sessionOptions.SetGraphOptimizationLevel(ORT_ENABLE_BASIC);
	std::wstring widestr = std::wstring(model_path.begin(), model_path.end());   ///如果在windows系统就这么写
	ort_session = new Ort::Session(env, widestr.c_str(), sessionOptions);   ///如果在windows系统就这么写
	///ort_session = new Session(env, model_path.c_str(), sessionOptions);  ///如果在linux系统,就这么写

	size_t numInputNodes = ort_session->GetInputCount();
	size_t numOutputNodes = ort_session->GetOutputCount();
	Ort::AllocatorWithDefaultOptions allocator;
	for (int i = 0; i < numInputNodes; i++)
	{
		input_names.push_back(ort_session->GetInputName(i, allocator));
		Ort::TypeInfo input_type_info = ort_session->GetInputTypeInfo(i);
		auto input_tensor_info = input_type_info.GetTensorTypeAndShapeInfo();
		auto input_dims = input_tensor_info.GetShape();
		input_node_dims.push_back(input_dims);
	}
	for (int i = 0; i < numOutputNodes; i++)
	{
		output_names.push_back(ort_session->GetOutputName(i, allocator));
		Ort::TypeInfo output_type_info = ort_session->GetOutputTypeInfo(i);
		auto output_tensor_info = output_type_info.GetTensorTypeAndShapeInfo();
		auto output_dims = output_tensor_info.GetShape();
		output_node_dims.push_back(output_dims);
	}

	this->inpHeight = input_node_dims[0][2];
	this->inpWidth = input_node_dims[0][3];
	this->outHeight = output_node_dims[0][2];
	this->outWidth = output_node_dims[0][3];
	input2_tensor.push_back(0.5);
}

void CodeFormer::preprocess(cv::Mat &srcimg)
{
	cv::Mat dstimg;
	cv::cvtColor(srcimg, dstimg, cv::COLOR_BGR2RGB);
	resize(dstimg, dstimg, cv::Size(this->inpWidth, this->inpHeight), cv::INTER_LINEAR);
	this->input_image_.resize(this->inpWidth * this->inpHeight * dstimg.channels());
	int k = 0;
	for (int c = 0; c < 3; c++)
	{
		for (int i = 0; i < this->inpHeight; i++)
		{
			for (int j = 0; j < this->inpWidth; j++)
			{
				float pix = dstimg.ptr<uchar>(i)[j * 3 + c];
				this->input_image_[k] = (pix / 255.0 - 0.5) / 0.5;
				k++;
			}
		}
	}
}

cv::Mat CodeFormer::detect(cv::Mat &srcimg)
{
	int im_h = srcimg.rows;
	int im_w = srcimg.cols;
	this->preprocess(srcimg);
	std::array<int64_t, 4> input_shape_{ 1, 3, this->inpHeight, this->inpWidth };
	std::vector<int64_t> input2_shape_ = { 1 };

	auto allocator_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
	std::vector<Ort::Value> ort_inputs;
	ort_inputs.push_back(Ort::Value::CreateTensor<float>(allocator_info, input_image_.data(), input_image_.size(), input_shape_.data(), input_shape_.size()));
	ort_inputs.push_back(Ort::Value::CreateTensor<double>(allocator_info, input2_tensor.data(), input2_tensor.size(), input2_shape_.data(), input2_shape_.size()));
	std::vector<Ort::Value> ort_outputs = ort_session->Run(Ort::RunOptions{ nullptr }, input_names.data(), ort_inputs.data(), ort_inputs.size(), output_names.data(), output_names.size());

	post_process
	float* pred = ort_outputs[0].GetTensorMutableData<float>();
	//cv::Mat mask(outHeight, outWidth, CV_32FC3, pred); /经过试验,直接这样赋值,是不行的
	const unsigned int channel_step = outHeight * outWidth;
	std::vector<cv::Mat> channel_mats;
	cv::Mat rmat(outHeight, outWidth, CV_32FC1, pred); // R
	cv::Mat gmat(outHeight, outWidth, CV_32FC1, pred + channel_step); // G
	cv::Mat bmat(outHeight, outWidth, CV_32FC1, pred + 2 * channel_step); // B
	channel_mats.push_back(rmat);
	channel_mats.push_back(gmat);
	channel_mats.push_back(bmat);
	cv::Mat mask;
	merge(channel_mats, mask); // CV_32FC3 allocated

	///不用for循环遍历cv::Mat里的每个像素值,实现numpy.clip函数
	mask.setTo(this->min_max[0], mask < this->min_max[0]);
	mask.setTo(this->min_max[1], mask > this->min_max[1]);   也可以用threshold函数,阈值类型THRESH_TOZERO_INV

	mask = (mask - this->min_max[0]) / (this->min_max[1] - this->min_max[0]);
	mask *= 255.0;
	mask.convertTo(mask, CV_8UC3);
	//cvtColor(mask, mask, cv::COLOR_BGR2RGB);
	return mask;
}


void CodeFormer::detect_video(const std::string& video_path,const std::string& output_path, unsigned int writer_fps)
{
	cv::VideoCapture video_capture(video_path);

	if (!video_capture.isOpened())
	{
		std::cout << "Can not open video: " << video_path << "\n";
		return;
	}

	cv::Size S = cv::Size((int)video_capture.get(cv::CAP_PROP_FRAME_WIDTH),
		(int)video_capture.get(cv::CAP_PROP_FRAME_HEIGHT));

	cv::VideoWriter output_video(output_path, cv::VideoWriter::fourcc('m', 'p', '4', 'v'),
		30.0, S);
	
	if (!output_video.isOpened())
	{
		std::cout << "Can not open writer: " << output_path << "\n";
		return;
	}

	cv::Mat cv_mat;
	while (video_capture.read(cv_mat))
	{
		cv::Mat cv_dst = detect(cv_mat);

		output_video << cv_dst;
	}
	video_capture.release();
	output_video.release();
}

先试试官方给的样本的效果:
在这里插入图片描述
薄马赛克的超分效果:
在这里插入图片描述
在这里插入图片描述
厚马赛克的超分效果不是很好,就是有点贴脸的感觉:
在这里插入图片描述
如果是已经是清晰的图像,超分之后不是很理想,基本上是不能用的,onnx这个效果只能优化人脸:

在这里插入图片描述
在这里插入图片描述

2.onnx模型python推理

import os
import cv2
import argparse
import glob
import torch
import torch.onnx
from torchvision.transforms.functional import normalize
from basicsr.utils import imwrite, img2tensor, tensor2img
from basicsr.utils.download_util import load_file_from_url
from basicsr.utils.misc import gpu_is_available, get_device
from facelib.utils.face_restoration_helper import FaceRestoreHelper
from facelib.utils.misc import is_gray
import onnxruntime as ort

from basicsr.utils.registry import ARCH_REGISTRY

pretrain_model_url = {
    'restoration': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth',
}

if __name__ == '__main__':
    # device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    device = get_device()
    parser = argparse.ArgumentParser()

    parser.add_argument('-i', '--input_path', type=str, default='./inputs/whole_imgs', 
            help='Input image, video or folder. Default: inputs/whole_imgs')
    parser.add_argument('-o', '--output_path', type=str, default=None, 
            help='Output folder. Default: results/<input_name>_<w>')
    parser.add_argument('-w', '--fidelity_weight', type=float, default=0.5, 
            help='Balance the quality and fidelity. Default: 0.5')
    parser.add_argument('-s', '--upscale', type=int, default=2, 
            help='The final upsampling scale of the image. Default: 2')
    parser.add_argument('--has_aligned', action='store_true', help='Input are cropped and aligned faces. Default: False')
    parser.add_argument('--only_center_face', action='store_true', help='Only restore the center face. Default: False')
    parser.add_argument('--draw_box', action='store_true', help='Draw the bounding box for the detected faces. Default: False')
    # large det_model: 'YOLOv5l', 'retinaface_resnet50'
    # small det_model: 'YOLOv5n', 'retinaface_mobile0.25'
    parser.add_argument('--detection_model', type=str, default='retinaface_resnet50', 
            help='Face detector. Optional: retinaface_resnet50, retinaface_mobile0.25, YOLOv5l, YOLOv5n, dlib. \
                Default: retinaface_resnet50')
    parser.add_argument('--bg_upsampler', type=str, default='None', help='Background upsampler. Optional: realesrgan')
    parser.add_argument('--face_upsample', action='store_true', help='Face upsampler after enhancement. Default: False')
    parser.add_argument('--bg_tile', type=int, default=400, help='Tile size for background sampler. Default: 400')
    parser.add_argument('--suffix', type=str, default=None, help='Suffix of the restored faces. Default: None')
    parser.add_argument('--save_video_fps', type=float, default=None, help='Frame rate for saving video. Default: None')

    args = parser.parse_args()

    # ------------------------ input & output ------------------------
    w = args.fidelity_weight
    input_video = False
    if args.input_path.endswith(('jpg', 'jpeg', 'png', 'JPG', 'JPEG', 'PNG')): # input single img path
        input_img_list = [args.input_path]
        result_root = f'results/test_img_{w}'
    # elif args.input_path.endswith(('mp4', 'mov', 'avi', 'MP4', 'MOV', 'AVI')): # input video path
    #     from basicsr.utils.video_util import VideoReader, VideoWriter
    #     input_img_list = []
    #     vidreader = VideoReader(args.input_path)
    #     image = vidreader.get_frame()
    #     while image is not None:
    #         input_img_list.append(image)
    #         image = vidreader.get_frame()
    #     audio = vidreader.get_audio()
    #     fps = vidreader.get_fps() if args.save_video_fps is None else args.save_video_fps   
    #     video_name = os.path.basename(args.input_path)[:-4]
    #     result_root = f'results/{video_name}_{w}'
    #     input_video = True
    #     vidreader.close()
    # else: # input img folder
    #     if args.input_path.endswith('/'):  # solve when path ends with /
    #         args.input_path = args.input_path[:-1]
    #     # scan all the jpg and png images
    #     input_img_list = sorted(glob.glob(os.path.join(args.input_path, '*.[jpJP][pnPN]*[gG]')))
    #     result_root = f'results/{os.path.basename(args.input_path)}_{w}'
    else:
        raise ValueError("wtf???")

    if not args.output_path is None: # set output path
        result_root = args.output_path

    test_img_num = len(input_img_list)
    if test_img_num == 0:
        raise FileNotFoundError('No input image/video is found...\n' 
            '\tNote that --input_path for video should end with .mp4|.mov|.avi')

    # # ------------------ set up background upsampler ------------------
    # if args.bg_upsampler == 'realesrgan':
    #     bg_upsampler = set_realesrgan()
    # else:
    #     bg_upsampler = None

    # # ------------------ set up face upsampler ------------------
    # if args.face_upsample:
    #     if bg_upsampler is not None:
    #         face_upsampler = bg_upsampler
    #     else:
    #         face_upsampler = set_realesrgan()
    # else:
    #     face_upsampler = None

    # ------------------ set up CodeFormer restorer -------------------
    net = ARCH_REGISTRY.get('CodeFormer')(dim_embd=512, codebook_size=1024, n_head=8, n_layers=9, 
                                            connect_list=['32', '64', '128', '256']).to(device)
    
    # ckpt_path = 'weights/CodeFormer/codeformer.pth'
    ckpt_path = load_file_from_url(url=pretrain_model_url['restoration'], 
                                    model_dir='weights/CodeFormer', progress=True, file_name=None)
    checkpoint = torch.load(ckpt_path)['params_ema']
    net.load_state_dict(checkpoint)
    net.eval()

    # # ------------------ set up FaceRestoreHelper -------------------
    # # large det_model: 'YOLOv5l', 'retinaface_resnet50'
    # # small det_model: 'YOLOv5n', 'retinaface_mobile0.25'
    # if not args.has_aligned: 
    #     print(f'Face detection model: {args.detection_model}')
    # # if bg_upsampler is not None: 
    # #     print(f'Background upsampling: True, Face upsampling: {args.face_upsample}')
    # # else:
    # #     print(f'Background upsampling: False, Face upsampling: {args.face_upsample}')
    # else:
    #     raise ValueError("wtf???")

    face_helper = FaceRestoreHelper(
        args.upscale,
        face_size=512,
        crop_ratio=(1, 1),
        # det_model = args.detection_model,
        # save_ext='png',
        # use_parse=True,
        # device=device
    )

    # -------------------- start to processing ---------------------
    for i, img_path in enumerate(input_img_list):
        # # clean all the intermediate results to process the next image
        # face_helper.clean_all()
        
        if isinstance(img_path, str):
            img_name = os.path.basename(img_path)
            basename, ext = os.path.splitext(img_name)
            print(f'[{i+1}/{test_img_num}] Processing: {img_name}')
            img = cv2.imread(img_path, cv2.IMREAD_COLOR)
        # else: # for video processing
        #     basename = str(i).zfill(6)
        #     img_name = f'{video_name}_{basename}' if input_video else basename
        #     print(f'[{i+1}/{test_img_num}] Processing: {img_name}')
        #     img = img_path

        if args.has_aligned: 
            # the input faces are already cropped and aligned
            img = cv2.resize(img, (512, 512), interpolation=cv2.INTER_LINEAR)
            # face_helper.is_gray = is_gray(img, threshold=10)
            # if face_helper.is_gray:
            #     print('Grayscale input: True')
            face_helper.cropped_faces = [img]
        # else:
        #     face_helper.read_image(img)
        #     # get face landmarks for each face
        #     num_det_faces = face_helper.get_face_landmarks_5(
        #         only_center_face=args.only_center_face, resize=640, eye_dist_threshold=5)
        #     print(f'\tdetect {num_det_faces} faces')
        #     # align and warp each face
        #     face_helper.align_warp_face()
        else:
            raise ValueError("wtf???")

        # face restoration for each cropped face
        for idx, cropped_face in enumerate(face_helper.cropped_faces):
            # prepare data
            cropped_face_t = img2tensor(cropped_face / 255., bgr2rgb=True, float32=True)
            normalize(cropped_face_t, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True)
            cropped_face_t = cropped_face_t.unsqueeze(0).to(device)

            try:
                with torch.no_grad():
                    # output = net(cropped_face_t, w=w, adain=True)[0]
                    # output = net(cropped_face_t)[0]
                    output = net(cropped_face_t, w)[0]
                    restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1))
                del output
                # torch.cuda.empty_cache()
            except Exception as error:
                print(f'\tFailed inference for CodeFormer: {error}')
                restored_face = tensor2img(cropped_face_t, rgb2bgr=True, min_max=(-1, 1))
            
            # now, export the "net" codeformer to onnx
            print("Exporting CodeFormer to ONNX...")
            torch.onnx.export(net,
                # (cropped_face_t,),
                (cropped_face_t,w),
                "codeformer.onnx", 
                # verbose=True,
                export_params=True,
                opset_version=11,
                do_constant_folding=True,
                input_names = ['x','w'],
                output_names = ['y'],
            )

            # now, try to load the onnx model and run it
            print("Loading CodeFormer ONNX...")
            ort_session = ort.InferenceSession("codeformer.onnx", providers=['CPUExecutionProvider'])
            print("Running CodeFormer ONNX...")
            ort_inputs = {
                ort_session.get_inputs()[0].name: cropped_face_t.cpu().numpy(),
                ort_session.get_inputs()[1].name: torch.tensor(w).double().cpu().numpy(),
            }
            ort_outs = ort_session.run(None, ort_inputs)
            restored_face_onnx = tensor2img(torch.from_numpy(ort_outs[0]), rgb2bgr=True, min_max=(-1, 1))
            restored_face_onnx = restored_face_onnx.astype('uint8')

            restored_face = restored_face.astype('uint8')

            print("Comparing CodeFormer outputs...")
            # see how similar the outputs are: flatten and then compute all the differences
            diff = (restored_face_onnx.astype('float32') - restored_face.astype('float32')).flatten()
            # calculate min, max, mean, and std
            min_diff = diff.min()
            max_diff = diff.max()
            mean_diff = diff.mean()
            std_diff = diff.std()
            print(f"Min diff: {min_diff}, Max diff: {max_diff}, Mean diff: {mean_diff}, Std diff: {std_diff}")

            # face_helper.add_restored_face(restored_face, cropped_face)
            face_helper.add_restored_face(restored_face_onnx, cropped_face)

        # # paste_back
        # if not args.has_aligned:
        #     # upsample the background
        #     if bg_upsampler is not None:
        #         # Now only support RealESRGAN for upsampling background
        #         bg_img = bg_upsampler.enhance(img, outscale=args.upscale)[0]
        #     else:
        #         bg_img = None
        #     face_helper.get_inverse_affine(None)
        #     # paste each restored face to the input image
        #     if args.face_upsample and face_upsampler is not None: 
        #         restored_img = face_helper.paste_faces_to_input_image(upsample_img=bg_img, draw_box=args.draw_box, face_upsampler=face_upsampler)
        #     else:
        #         restored_img = face_helper.paste_faces_to_input_image(upsample_img=bg_img, draw_box=args.draw_box)

        # save faces
        for idx, (cropped_face, restored_face) in enumerate(zip(face_helper.cropped_faces, face_helper.restored_faces)):
            # save cropped face
            if not args.has_aligned: 
                save_crop_path = os.path.join(result_root, 'cropped_faces', f'{basename}_{idx:02d}.png')
                imwrite(cropped_face, save_crop_path)
            # save restored face
            if args.has_aligned:
                save_face_name = f'{basename}.png'
            else:
                save_face_name = f'{basename}_{idx:02d}.png'
            if args.suffix is not None:
                save_face_name = f'{save_face_name[:-4]}_{args.suffix}.png'
            save_restore_path = os.path.join(result_root, 'restored_faces', save_face_name)
            imwrite(restored_face, save_restore_path)

        # # save restored img
        # if not args.has_aligned and restored_img is not None:
        #     if args.suffix is not None:
        #         basename = f'{basename}_{args.suffix}'
        #     save_restore_path = os.path.join(result_root, 'final_results', f'{basename}.png')
        #     imwrite(restored_img, save_restore_path)

    # # save enhanced video
    # if input_video:
    #     print('Video Saving...')
    #     # load images
    #     video_frames = []
    #     img_list = sorted(glob.glob(os.path.join(result_root, 'final_results', '*.[jp][pn]g')))
    #     for img_path in img_list:
    #         img = cv2.imread(img_path)
    #         video_frames.append(img)
    #     # write images to video
    #     height, width = video_frames[0].shape[:2]
    #     if args.suffix is not None:
    #         video_name = f'{video_name}_{args.suffix}.png'
    #     save_restore_path = os.path.join(result_root, f'{video_name}.mp4')
    #     vidwriter = VideoWriter(save_restore_path, height, width, fps, audio)
         
    #     for f in video_frames:
    #         vidwriter.write_frame(f)
    #     vidwriter.close()

    print(f'\nAll results are saved in {result_root}')

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

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

相关文章

同步 -- 互斥锁

本篇文章基于Linux-6.5源码 建议&#xff1a;搭配Linux源码观看更佳 struct mutex {atomic_long_t owner;spinlock_t wait_lock; //自旋锁&#xff0c;该自旋锁的作用是保护后面的等待队列的原子性struct list_head wait_list; //等待队列 }; API接口 mutex_init 互斥锁初…

C语言入门Day_22 初识指针

目录 前言&#xff1a; 1.内存地址 2.指针的定义 3.指针的使用 4.易错点 5.思维导图 前言&#xff1a; 之前我们学过变量可以用来存储数据&#xff0c;就像一个盒子里面可以放不同的球一样。 这是一个方便大家理解专业概念的比喻。 在计算机世界里面&#xff0c;数据实…

海外ASO优化之提高应用曝光度的技巧1

作为应用程序的开发者&#xff0c;如何使我们的应用在众多竞争对手中脱颖而出&#xff1f;如何应用在App Store搜索中排名更高&#xff0c;以确保它符合App Store搜索中的搜索意图&#xff1f;这些都是我们要重点考虑的。 1、做好应用程序描述。 在填写应用描述之前&#xff0…

直线模组的常用语

在工业生产中&#xff0c;直线模组的叫法有很多种&#xff0c;对于新手小白来说&#xff0c;很容易就会被绕晕&#xff0c;今天我们就来简单说一下直线模组的常用称呼吧&#xff01; 1、直线模组&#xff1a;与直线滑台同义&#xff0c;基本可以相互互换。直线模组一般是指可以…

编译原理.龙书学习1

第一章&#xff1a; 编译器&#xff1a;将程序翻译成一种能够被计算机执行的形式 解释器&#xff1a;解释器直接利用用户提供的输入执行源程序中指定的操作 一个编译器的结构 编译器将源程序映射为语义上等价的目标程序&#xff0c;这个映射过程由两部分组成&#xff1a;分析…

ApiFox衔接前后端开发人员,提升沟通效率实践

1. 为什么不用Postman而要使用ApiFox 1.1. Postman的问题(不便) 多系统数据不互通 API设计者、前端开发、后端开发、测试人员大量重复工作。 效率低 可视化程度低、操作不友好。 无法团队协作 单机离线使用为主&#xff0c;成员之间无法实时同步数据&#xff0c;无法协作。 …

Unity实现简易太阳系

开发环境&#xff1a;Unity 2022.3.5f1c1 Visual Studio 2022 太阳系相关星体&#xff1a;太阳、八大行星、月球 模拟星系&#xff1a;太阳系、地月系 功能&#xff1a;支持行星以太阳为中心&#xff0c;任意轴进行公转&#xff0c;此处演示同一平面。 a1-a8为公转轴&#xff…

需要每日温习的java八股文基础

一、java &#xff08;1&#xff09;集合 1.list&#xff1a;LinkedList、ArrayList和Vector ArrayList、LinkedList和Vector的区别&#xff0c;场景和扩容机制源码 LinkedList 接口实现类&#xff0c; 链表&#xff0c; 插入删除&#xff0c; 没有同步&#xff0c; 线程不…

网站降权的康复办法(详解百度SEO数据分析)

随着搜索引擎算法的不断升级&#xff0c;很多网站在SEO优化过程中遭遇到降权的情况。如果您的网站也遭遇到了类似的问题&#xff0c;不必惊慌失措。本文将为您详细介绍网站降权恢复的方法&#xff0c;包括百度SEO数据分析、网站收录少的5个原因、网站被降权的6个因素以及百度SE…

什么是顶点颜色

在3D模型的渲染中&#xff0c;除了贴图属性外&#xff0c;顶点颜色也是一种常见的材质属性。顶点颜色是指通过给模型的顶点分配颜色值来定义模型表面的颜色。 顶点颜色通常以顶点数据的形式储存&#xff0c;并且与每个顶点的位置信息相对应。在渲染过程中&#xff0c;顶点颜色会…

【性能测试】JMeter:集合点,同步定时器的应用实例!

一、集合点的定义 在性能测试过程中&#xff0c;为了真实模拟多个用户同时进行操作以度量服务器的处理能力&#xff0c;可以考虑同步虚拟用户以便恰好在同一时刻执行操作或发送请求。 通过插入集合点可以较真实模拟多个用户并发操作。 (注意&#xff1a;虽然通过加入集合点可…

pdf在线加密怎么做?几个好用的方法分享

PDF在线加密怎么做&#xff1f;在进行PDF在线加密时&#xff0c;您可以使用以下几种方法来确保文档的安全性&#xff0c;并注意以下一些加密的注意事项。 方法一&#xff1a;使用PDF转换加密工具 首先我们可以在电脑上打开迅捷PDF转换器工具&#xff0c;进入该转换工具后先在顶…

Vue系列(二)之 基础语法【上篇】

目录 一. 插值 1.1 文本 1.2 原始HTML 1.3 属性 1.4 表达式 二. 指令 2.1 v-if/v-else-if/v-else指令 2.2 v-show指令 2.3 v-for指令 2.4 下拉框/复选框 2.5 动态参数 三. 过滤器 3.1 局部过滤器基本应用 3.2 局部过滤器串行使用 3.3 局部过滤器传参 3.4 全局过…

无涯教程-JavaScript - LCM函数

描述 LCM函数返回整数的最小公倍数。最小公倍数是最小的正整数,它是所有整数参数number1,number2等的倍数。使用LCM添加具有不同分母的分数。 语法 LCM (number1, [number2] ...)争论 Argument描述Required/OptionalNumber1, number2... 您想要最小公倍数的1到255个值。 如…

【JDK 8-函数式编程】4.2 BiFunction

一、BiFunction 二、改造上节课&#xff1a;四则运算 一、BiFunction Function 只能接收一个参数&#xff0c;要传递两个参数,则用 BiFunction 两个参数&#xff1a;可以是两种不同数据类型 调用方法: R apply(T t, U u); import java.util.Objects;/*** param <T>…

无涯教程-JavaScript - GCD函数

描述 GCD函数返回两个或多个整数的最大公约数。最大公约数是将number1和number2均除而无余数的最大整数。 语法 GCD (number1, [number2] ...)争论 Argument描述Required/OptionalNumber11 to 255 values. If any value is not an integer, it is truncated.Requirednumber…

颠覆传统!老子云支持70+三维格式转换,在线即可一键处理!

老子云自研AMRT展示框架及三维格式具有广泛兼容性&#xff0c;同时还会用户提供了3D格式在线转换工具&#xff0c;支持实现70三维格式模型的快速处理和转换。 你是不是也遇到过这种情况&#xff1a;做了半天的3D模型图&#xff0c;好不容易弄好了&#xff0c;到最后插入的时候居…

win11 腾讯会议提示未检测到可用麦克风

平时都用的耳机&#xff0c;今天直接用笔记本的音频进行会议&#xff0c;发现提示&#xff1a;显示未检测到可用麦克风&#xff0c;请插入设备重试&#xff1b;很奇怪的设置了腾讯会议的音频和电脑的&#xff0c;发现电脑的显示无可用的音频输入设备&#xff0c;折腾一番&#…

PYTHON第一次

1. 输入三个整数&#xff0c;按降序输出 a int(input("请输入第一个整数&#xff1a;")) b int(input("请输入第二个整数&#xff1a;")) c int(input("请输入第三个整数&#xff1a;"))nums [a, b, c] nums.sort(reverseTrue)print("…

mysql某批量更新导致死锁

#查询当前数据库全部线程show full processlist #查询当前运行的全部事务select * from information_schema.innodb_trx#查询锁情况select * from information_schema.innodb_locks#查询锁等待情况select * from information_schema.innodb_lock_waits #查看mysql设置等待锁时长…