深度學習項目實例(一)—— 實時AI換臉項目

一、前言

人工智能(AI)技術的快速發展為各個領域帶來了革命性的變化,其中之一就是人臉識別與圖像處理技術。在這之中,AI換臉技術尤其引人注目。這種技術不僅在娛樂行業中得到廣泛應用,如電影制作、視頻特效等,還在社交媒體上掀起了一股風潮。AI換臉技術不僅可以實現實時的面部替換,還能夠在圖像和視頻中生成高度逼真的換臉效果。

AI換臉技術的核心在于多種機器學習和深度學習模型的結合。它通常涉及幾個關鍵步驟:人臉檢測、人臉特征點檢測、人臉對齊、換臉處理以及圖像增強。每個步驟都依賴于不同的深度學習模型,以確保最終的換臉效果逼真且自然。

本項目實現了一個完整的AI換臉系統,集成了多個深度學習模型,包括YOLO人臉檢測模型、68關鍵點檢測模型、ArcFace人臉識別模型、InSwapper換臉模型以及GFPGAN人臉增強模型。通過這些模型的協同工作,我們能夠從源圖像中提取人臉特征,并將其無縫地替換到目標圖像或視頻中,生成自然的換臉效果。

接下來,我們將詳細介紹這個AI換臉系統的實現細節和工作原理。通過這些介紹,讀者可以深入了解AI換臉技術的實際應用和技術實現過程。

二、系統架構與工作流程

2.1 系統整體架構

在這里插入圖片描述

2.1 主要模塊與功能介紹(附代碼)

該項目主要由5個主要模塊組成,他們分別是人臉檢測,人臉關鍵點檢測,人臉對齊,換臉處理和圖像增強。

2.1.1 人臉檢測

首先我們需要檢測源圖像和目標圖像中的人臉相關數據,獲取圖像中包含的人臉坐標,即由左上和右下坐標決定的矩陣框,對應的面部關鍵點和置信度分數。在該部分中所采用的檢測模型是YOLOv8,它是最新一代的 YOLO(You Only Look Once)系列模型之一,專為實時目標檢測任務而設計。它在精度和速度方面相比之前的模型均有顯著提升,非常適用于需要快速響應的應用場景,如視頻監控、自動駕駛和增強現實等。所以在實時換臉項目中,YOLOv8顯然非常適合用于人臉檢測。以下是具體步驟:

  1. 模型初始化
    首先設定模型的參數置信度閾值和iou閾值,之后加載YOLOv8的ONNX 模型,并設置推理會話的選項。需要在初始化中獲取模型的輸入名稱和形狀,以便后續進行圖像預處理。

    def __init__(self, modelpath, conf_thres=0.5, iou_thresh=0.4):self.conf_threshold = conf_thresself.iou_threshold = iou_threshsession_option = onnxruntime.SessionOptions()session_option.log_severity_level = 3self.session = onnxruntime.InferenceSession(modelpath, sess_options=session_option)model_inputs = self.session.get_inputs()self.input_names = [model_inputs[i].name for i in range(len(model_inputs))]self.input_shape = model_inputs[0].shapeself.input_height = int(self.input_shape[2])self.input_width = int(self.input_shape[3])
    
  2. 圖像預處理
    在使用YOLOv8進行推理之前需要先調整輸入圖像大小并進行邊界填充,還需要將圖像像素值歸一化到 [-1, 1] 的范圍,并調整通道順序,使其符合模型的輸入要求。

    def preprocess(self, srcimg):height, width = srcimg.shape[:2]temp_image = srcimg.copy()if height > self.input_height or width > self.input_width:scale = min(self.input_height / height, self.input_width / width)new_width = int(width * scale)new_height = int(height * scale)temp_image = cv2.resize(srcimg, (new_width, new_height))self.ratio_height = height / temp_image.shape[0]self.ratio_width = width / temp_image.shape[1]input_img = cv2.copyMakeBorder(temp_image, 0, self.input_height - temp_image.shape[0], 0, self.input_width - temp_image.shape[1], cv2.BORDER_CONSTANT, value=0)input_img = (input_img.astype(np.float32) - 127.5) / 128.0input_img = input_img.transpose(2, 0, 1)input_img = input_img[np.newaxis, :, :, :]return input_img
    
  3. 進行推理
    在推理過程中,首先要調用 preprocess 方法對輸入圖像進行預處理獲得符合模型要求的輸入。再使用 ONNX Runtime 進行推理,得到檢測結果。之后調用 postprocess 方法(下面提到)處理輸出結果。

    def detect(self, srcimg):input_tensor = self.preprocess(srcimg)outputs = self.session.run(None, {self.input_names[0]: input_tensor})[0]boxes, kpts, scores = self.postprocess(outputs)return boxes, kpts, scores
    
  4. 后處理
    在執行推理后調用后處理函數解析模型輸出,獲取邊界框、關鍵點和得分。同時使用非極大值抑制(NMS)去除冗余的檢測框。再根據縮放比例調整邊界框和關鍵點的坐標。

    def postprocess(self, outputs):bounding_box_list, face_landmark5_list, score_list = [], [], []outputs = np.squeeze(outputs, axis=0).Tbounding_box_raw, score_raw, face_landmark_5_raw = np.split(outputs, [4, 5], axis=1)keep_indices = np.where(score_raw > self.conf_threshold)[0]if keep_indices.any():bounding_box_raw, face_landmark_5_raw, score_raw = bounding_box_raw[keep_indices], face_landmark_5_raw[keep_indices], score_raw[keep_indices]bboxes_wh = bounding_box_raw.copy()bboxes_wh[:, :2] = bounding_box_raw[:, :2] - 0.5 * bounding_box_raw[:, 2:]bboxes_wh *= np.array([[self.ratio_width, self.ratio_height, self.ratio_width, self.ratio_height]])face_landmark_5_raw *= np.tile(np.array([self.ratio_width, self.ratio_height, 1]), 5).reshape((1, 15))score_raw = score_raw.flatten()indices = cv2.dnn.NMSBoxes(bboxes_wh.tolist(), score_raw.tolist(), self.conf_threshold, self.iou_threshold)if isinstance(indices, np.ndarray):indices = indices.flatten()if len(indices) > 0:bounding_box_list = list(map(lambda x: np.array([x[0], x[1], x[0] + x[2], x[1] + x[3]], dtype=np.float64), bboxes_wh[indices]))score_list = list(score_raw[indices])face_landmark5_list = list(face_landmark_5_raw[indices])return bounding_box_list, face_landmark5_list, score_list
  5. 繪制檢測結果
    最后將得到的邊界框,關鍵點以及對應的置信度繪制在輸入圖像上,這里為了方便換臉后前后對比,把輸入圖像復制了一份,在該副本上進行繪制。得到的結果如下:
    在這里插入圖片描述

2.1.2人臉關鍵點檢測

這里我們來介紹一個可以識別人臉圖像關鍵點的模型,2DFAN4 模型。該模型可以檢測人臉上的68個關鍵點,這些關鍵點包括眼睛、眉毛、鼻子、嘴巴和面部輪廓等。

  1. 模型初始化:
    和上一步類似,初始化 ONNX 模型會話,設置模型路徑并獲取模型輸入信息。
  2. 圖像預處理
    計算縮放比例和平移量,使邊界框居中到 256x256 的圖像中。使用 warp_face_by_translation 方法進行仿射變換,返回裁剪后的圖像和仿射矩陣。轉置圖像通道順序,并進行歸一化處理。
    def preprocess(self, srcimg, bounding_box):'''bounding_box里的數據格式是[xmin. ymin, xmax, ymax]'''scale = 195 / np.subtract(bounding_box[2:], bounding_box[:2]).max()natranslation = (256 - np.add(bounding_box[2:], bounding_box[:2]) * scale) * 0.5crop_img, affine_matrix = warp_face_by_translation(srcimg, translation, scale, (256, 256))crop_img = crop_img.transpose(2, 0, 1).astype(np.float32) / 255.0crop_img = crop_img[np.newaxis, :, :, :]return crop_img, affine_matrix
    
  3. 人臉關鍵點檢測
    調用 preprocess 方法,得到輸入張量和仿射矩陣,再使用 ONNX 模型進行推理,得到人臉的 68 個關鍵點。對關鍵點進行歸一化處理,并應用逆仿射變換,將關鍵點坐標轉換回原圖像坐標系中。將 68 個關鍵點轉換為 5 個關鍵點(這里其實和上面的YOLOv8實現的功能類似)。
        def detect(self, srcimg, bounding_box):'''如果直接crop+resize,最后返回的人臉關鍵點有偏差'''input_tensor, affine_matrix = self.preprocess(srcimg, bounding_box)face_landmark_68 = self.session.run(None, {self.input_names[0]: input_tensor})[0]face_landmark_68 = face_landmark_68[:, :, :2][0] / 64face_landmark_68 = face_landmark_68.reshape(1, -1, 2) * 256face_landmark_68 = cv2.transform(face_landmark_68, cv2.invertAffineTransform(affine_matrix))face_landmark_68 = face_landmark_68.reshape(-1, 2)face_landmark_5of68 = convert_face_landmark_68_to_5(face_landmark_68)return face_landmark_68, face_landmark_5of68
    
  4. 繪制檢測結果
    最后將得到的68個人臉面部關鍵點繪制在輸入圖像上。得到的結果如下:
    在這里插入圖片描述

2.1.3 人臉對齊

  1. 模型初始化
    同上一步,所有onnx模型初始化的步驟都是一樣的。
  2. 圖像預處理
    使用 warp_face_by_face_landmark_5 函數按人臉特征點進行裁剪和對齊。將圖像像素值從原始范圍 [0, 255] 轉換到范圍 [-1, 1]。轉置圖像通道順序,使其符合模型的輸入格式。
       def preprocess(self, srcimg, face_landmark_5):crop_img, _ = warp_face_by_face_landmark_5(srcimg, face_landmark_5, 'arcface_112_v2', (112, 112))crop_img = crop_img / 127.5 - 1crop_img = crop_img[:, :, ::-1].transpose(2, 0, 1).astype(np.float32)crop_img = np.expand_dims(crop_img, axis = 0)return crop_img
    
  3. 特征向量提取
    首先調用 preprocess 方法對輸入圖像進行預處理。使用 ONNX Runtime 進行推理,提取人臉特征向量(embedding)。對特征向量進行歸一化處理,得到歸一化后的特征向量(normed_embedding)。
        def detect(self, srcimg, face_landmark_5):input_tensor = self.preprocess(srcimg, face_landmark_5)# Perform inference on the imageembedding = self.session.run(None, {self.input_names[0]: input_tensor})[0]embedding = embedding.ravel()normed_embedding = embedding / np.linalg.norm(embedding)return embedding, normed_embedding
    

該模型的主要功能是通過人臉對齊來提取人臉特征向量。人臉對齊是人臉識別任務中的關鍵步驟,它有助于將輸入的人臉圖像標準化,使其在不同的拍攝角度、光照和表情變化下具有一致的表示。

2.1.4換臉處理

前面做了那么多處理,終于我們來到了關鍵步驟:換臉處理!此處用到的模型是inswapper_128,該模型通過將源圖像中的人臉特征嵌入到目標圖像中的人臉區域,實現自然逼真的換臉效果。

  1. 模型初始化
    繼續同樣地加載 ONNX 模型,并創建 ONNX Runtime 會話,并獲取模型的輸入名稱和輸入形狀。和之前不同的是這一步需要加載模型矩陣,用于對源人臉特征向量進行變換。
    def __init__(self, modelpath):# Initialize modelsession_option = onnxruntime.SessionOptions()session_option.log_severity_level = 3self.session = onnxruntime.InferenceSession(modelpath, sess_options=session_option)model_inputs = self.session.get_inputs()self.input_names = [model_inputs[i].name for i in range(len(model_inputs))]self.input_shape = model_inputs[0].shapeself.input_height = int(self.input_shape[2])self.input_width = int(self.input_shape[3])self.model_matrix = np.load('model_matrix.npy')
    
  2. 圖像處理和換臉
    • 圖像預處理
      • 人臉對齊:使用 warp_face_by_face_landmark_5 函數將目標圖像按人臉特征點進行裁剪和對齊。
      • 創建遮罩:使用 create_static_box_mask 創建靜態盒子遮罩,方便后續將換臉結果融合回原圖像。
      • 歸一化處理:將圖像像素值從原始范圍 [0, 255] 轉換到 [0, 1],并進行標準化處理,使其符合模型的輸入要求。
    • 特征向量變換
      • 源人臉特征變換:將源人臉特征向量進行變換,并歸一化處理,以符合模型的輸入要求。
    • 模型推理
      • 換臉推理:使用 ONNX Runtime 對預處理后的圖像和源人臉特征向量進行推理,得到換臉結果。
      • 結果處理:將換臉結果圖像轉換回原始圖像格式。
    • 融合換臉結果
      • 融合處理:將換臉結果圖像融合回原圖像中,確保換臉區域自然逼真。
      def process(self, target_img, source_face_embedding, target_landmark_5):###preprocesscrop_img, affine_matrix = warp_face_by_face_landmark_5(target_img, target_landmark_5, 'arcface_128_v2', (128, 128))crop_mask_list = []box_mask = create_static_box_mask((crop_img.shape[1],crop_img.shape[0]), FACE_MASK_BLUR, FACE_MASK_PADDING)crop_mask_list.append(box_mask)crop_img = crop_img[:, :, ::-1].astype(np.float32) / 255.0crop_img = (crop_img - INSWAPPER_128_MODEL_MEAN) / INSWAPPER_128_MODEL_STDcrop_img = np.expand_dims(crop_img.transpose(2, 0, 1), axis = 0).astype(np.float32)source_embedding = source_face_embedding.reshape((1, -1))source_embedding = np.dot(source_embedding, self.model_matrix) / np.linalg.norm(source_embedding)###Perform inference on the imageresult = self.session.run(None, {'target':crop_img, 'source':source_embedding})[0][0]###normalize_crop_frameresult = result.transpose(1, 2, 0)result = (result * 255.0).round()result = result[:, :, ::-1]crop_mask = np.minimum.reduce(crop_mask_list).clip(0, 1)dstimg = paste_back(target_img, result, crop_mask, affine_matrix)return dstimg

2.1.5圖像增強

此處采用的模型是gfpgan_1.4,用于人臉圖像增強,旨在提高圖像的清晰度和質量,使得換臉效果更為自然逼真。

  1. 模型初始化
    同上上一步一致。
  2. 圖像處理和增強
  • 圖像預處理
    • 人臉對齊:使用 warp_face_by_face_landmark_5 函數將目標圖像按人臉特征點進行裁剪和對齊。
    • 創建遮罩:使用 create_static_box_mask 創建靜態盒子遮罩,方便后續將增強結果融合回原圖像。
    • 歸一化處理:將圖像像素值從原始范圍 [0, 255] 轉換到 [-1, 1],這有助于提高模型的性能。
  • 模型推理
    • 圖像增強推理:使用 ONNX Runtime 對預處理后的圖像進行推理,得到增強后的圖像。
    • 結果處理:將增強后的圖像從 [-1, 1] 轉換回 [0, 255] 的范圍,并轉換為 uint8 類型。(這一步是不是量化?)
  • 融合增強結果
    • 融合處理:將增強后的圖像融合回原圖像中,確保增強區域自然逼真。
    def process(self, target_img, target_landmark_5):###preprocesscrop_img, affine_matrix = warp_face_by_face_landmark_5(target_img, target_landmark_5, 'ffhq_512', (512, 512))box_mask = create_static_box_mask((crop_img.shape[1],crop_img.shape[0]), FACE_MASK_BLUR, FACE_MASK_PADDING)crop_mask_list = [box_mask]crop_img = crop_img[:, :, ::-1].astype(np.float32) / 255.0crop_img = (crop_img - 0.5) / 0.5crop_img = np.expand_dims(crop_img.transpose(2, 0, 1), axis = 0).astype(np.float32)###Perform inference on the imageresult = self.session.run(None, {'input':crop_img})[0][0]###normalize_crop_frameresult = np.clip(result, -1, 1)result = (result + 1) / 2result = result.transpose(1, 2, 0)result = (result * 255.0).round()result = result.astype(np.uint8)[:, :, ::-1]crop_mask = np.minimum.reduce(crop_mask_list).clip(0, 1)paste_frame = paste_back(target_img, result, crop_mask, affine_matrix)dstimg = blend_frame(target_img, paste_frame)return dstimg
    

最終結果展示

  • 源圖片
    在這里插入圖片描述
  • 目標圖片
    在這里插入圖片描述
  • 最終結果
    在這里插入圖片描述

四、結論

本項目通過使用多個先進的深度學習模型,實現了高效且逼真的AI換臉功能。首先,利用YOLOface_8n模型進行人臉檢測,并通過face_68_landmarks模型獲取面部68個關鍵點,確保了檢測結果的精確性和一致性。接著,arcface_w600k_r50.onnx模型提取源人臉的高維特征向量,通過對齊和歸一化處理,確保特征向量的穩定性和準確性。然后,inswapper_128.onnx模型負責將源人臉特征嵌入到目標人臉圖像中,實現自然逼真的人臉替換。最后,使用gfpgan_1.4.onnx模型對換臉結果進行圖像增強和修復,進一步提高圖像的清晰度和細節,使最終結果更加自然逼真。本項目展示了AI換臉技術的強大潛力和廣泛應用前景,為影視制作、社交媒體和隱私保護等領域提供了有力的技術支持

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/35542.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/35542.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/35542.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

static在C/C++中的作用

C語言中 static 的作用: 文件作用域的全局變量: 當static修飾一個全局變量時,這個變量只在定義它的文件內部可見,不會被其他文件訪問。 靜態局部變量: 在函數內部,static關鍵字確保局部變量的生命周期貫穿整…

目前公認最好用充電寶!四款高性價比充電寶推薦,一文看懂!

當我們在旅行途中,手機和相機等設備必不可少。長時間使用這些設備,電量很容易耗盡。此時,充電寶就能派上用場,讓我們在欣賞美景、記錄美好時光的同時,不再擔心電量不足。特別在假期出游的時候在我們玩的特別盡興的時候…

力扣2815.數組中的最大數對和

力扣2815.數組中的最大數對和 遍歷每個元素 并求其中最大的數字 將每個數字對應的最大元素存在數組中每遍歷到一個新的元素 &#xff0c;將其存在對應的數組位置中 class Solution {public:int maxSum(vector<int>& nums) {vector<int> cnt(10,INT_MIN);int…

【C++】關于虛函數的理解

深入探索C虛函數&#xff1a;原理、應用與實例分析 一、虛函數的原理二、虛函數的應用三、代碼實例分析四、總結 在C面向對象編程的世界里&#xff0c;虛函數&#xff08;Virtual Function&#xff09;扮演著至關重要的角色。它不僅實現了多態性這一核心特性&#xff0c;還使得…

查看linux服務器cpu,硬盤,內存

lscpu 查看cpu 釋義 Architecture: x86_64 // 指定系統架構&#xff0c;這里是 x86_64&#xff0c;表示一個64位系統。 CPU op-mode(s): 32-bit, 64-bit // 指示支持的 CPU 操作模式&#xff0c;顯示了32位和64位兩種模式。 Byte Order: Little…

紅酒與珠寶:璀璨與醇香的奢華交響,雙重誘惑難擋

在璀璨的燈光下&#xff0c;紅酒與珠寶各自閃耀著迷人的光芒&#xff0c;它們如同夜空中的繁星&#xff0c;交相輝映&#xff0c;共同演繹著奢華的雙重誘惑。今天&#xff0c;就讓我們一起走進這個充滿魅力的世界&#xff0c;感受紅酒與珠寶帶來的無盡魅力。 首先&#xff0c;讓…

java中自定義線程池最佳實踐

java中自定義線程池最佳實踐 在現代應用程序中&#xff0c;線程池是一種常用的技術&#xff0c;可以有效管理和復用線程資源&#xff0c;從而提升系統的并發性能和穩定性。本文將詳細介紹自定義線程池的最佳實踐&#xff0c;涵蓋從線程池大小配置、隊列選擇到拒絕策略、任務設…

基于STM32+ESP8266打造智能家居溫濕度監控系統(附源碼接線圖)

摘要: 本文將介紹如何使用STM32單片機、ESP8266 Wi-Fi模塊和Python Flask框架構建一個完整的物聯網系統&#xff0c;實現傳感器數據采集、無線傳輸、云端存儲及Web可視化展示。 關鍵詞: STM32, ESP8266, 傳感器, Flask, 物聯網, 云平臺, 數據可視化 1. 系統概述 本系統以STM…

Spring底層原理之proxyBeanMenthod實例 動態代理 反射 Bean的攔截

proxyBeanMenthod 假設我們要進行一個系統的二次開發 然后第一次開發我們實用的是XML聲明bean 二次開發的時候要用注解 我們如何把bean都加載上來呢 我們首先創建一個全新的配置類 package com.bigdata1421.config;public class SpringConfig32 { } 我們創建一個APP 加載…

Perl語言入門學習讀物

1. PERL 是什么? Perl 最初的設計者為Larry Wall&#xff0c;Perl借取了C、sed、awk、shell scripting以及很多其他程序語言的特性。Perl一般被稱為“實用報表提取語言”(PracticalExtraction andReportLanguage)&#xff0c;有時也被稱做“病態折中垃圾列表器”(Pathologica…

springboot+vue3無感知刷新token實戰

目錄 一、java后端 1、token構造實現類 ①驗證碼方式實現類 ②刷新token方式實現類 2、token相關操作&#xff1a;setCookie ①createToken ②refreshToken 二、前端&#xff08;vue3axios&#xff09; web網站中&#xff0c;前后端交互時&#xff0c;通常使用token機制…

全球最快的 JSON 文件查詢工具

本文字數&#xff1a;1684&#xff1b;估計閱讀時間&#xff1a;5分鐘 審校&#xff1a;莊曉東&#xff08;魏莊&#xff09; 本文在公眾號【ClickHouseInc】首發 介紹 在 ClickHouse&#xff0c;我們熱衷于基準測試和性能優化。所以當我第一次看到 Hacker News 上那篇“查詢大…

代碼隨想錄算法訓練營day31|134.加油站、135. 分發糖果、406.根據身高重建隊列

134.加油站 如下圖所示&#xff1a; 當索引一道2的時候&#xff0c;剩余油量的總量13-6 < 0&#xff0c;這個時候說明以索引0為起點不合適&#xff0c;將起點更新為索引3. 兩點證明&#xff1a; 1.如果我們從藍色段中間選一個點開始&#xff0c;是不是最后sumGas就不小于0…

從靈感到成品:使用AI生成博客文章的完整指南

在信息爆炸的時代&#xff0c;每個人都有講述自己故事的權利和需求。博客作為一種表達方式&#xff0c;不僅能記錄個人經歷&#xff0c;還能分享知識和觀點。然而&#xff0c;許多人在寫博客文章時&#xff0c;常常會遇到靈感枯竭、時間不夠用或者不知道如何開始等問題。幸運的…

光伏儲能系統/安科瑞DTSD1352-CF雙向計量表-安科瑞 蔣靜

1 長期以來&#xff0c;我國施行居民用電低價政策&#xff0c;居民電價大幅低于供電成本&#xff0c;雖然實施了全天分三時段的階梯電價政策&#xff0c;但過去近10年中高峰節電的效果卻不夠明顯。從分時用電運作機制來看&#xff0c;居民用電價格的波動幅度不大&#xff0c;但…

華為云鯤鵬架構docker部署2048小游戲

華為云鯤鵬架構docker部署2048小游戲 1. 鯤鵬架構ESC2. 配置docker3. 上傳2048鏡像4. 刪除容器,鏡像 1. 鯤鵬架構ESC 2. 配置docker 安裝dockeryum -y install docker開機啟動 systemctl enable docker啟動docker服務 systemctl start docker查詢docker的運行版本 docker -v3…

時序分析基本概念介紹——min pulse width 最小脈沖寬度

文章目錄 前言一、什么是 min pulse width&#xff1f;二、為什么檢查 min pulse width&#xff1f;三、如何設置 min pulse width約束&#xff1f;1. 在sdc里面定義2. library里面定義 四、如何檢查 min pulse width&#xff1f;五、如何修復 min pulse width&#xff1f;總結…

docker啟動ws-scrcpy和redroid記錄

git克隆最新的ws-scrcpy代碼 git clone gitgithub.com:NetrisTV/ws-scrcpy.git進入ws-scrcpy目錄新建Dockerfile文件&#xff0c;內容如下 FROM node:16-alpine WORKDIR /appRUN npm config set registry http://mirrors.cloud.tencent.com/npm/ RUN npm install -g node-gyp…

攻防世界-Web題目1

目錄 cookie 1、題目 2、知識點 3、思路 get_post 1、題目 2、知識點 3、思路 disabled_button 1、題目 2、知識點 3、思路 backup 1、題目 2、知識點 3、思路 cookie 1、題目 2、知識點 cookie&#xff0c;數據包 3、思路 題目提示我們cookie&#xff0c;抓…

Markdown中如何插入空行和空格

Markdown 是一種輕量級的標記語言&#xff0c;它的主要目標是以易讀易寫為優先&#xff0c;并兼容 HTML。雖然 Markdown 本身對于排版的要求比較寬松&#xff0c;但在某些情況下&#xff0c;我們可能需要在文檔中插入空行或空格來達到特定的排版效果。 插入空行 在Markdown中…