计算机视觉项目中的文件批量操作与文件批量预处理
计算机视觉项目中的文件批量操作与文件批量预处理
目录
- 数据集制作
- 文件批量重命名
- 文件批量移动
- 将文件批量按照一定格式进行重命名
- 修改xml文件内容的方法
引言
在计算机视觉项目中,文件批量操作和文件批量预处理是必不可少的步骤。它们涉及处理大量的图像文件,包括读取、处理、保存和预处理。本文将介绍一些常见的技术和方法,以帮助您在计算机视觉项目中有效地进行文件批量操作和文件批量预处理。
1. 数据集制作
人工智能就是有多少人工产生多少智能,在计算机视觉项目中,数据集至关重要,下面是如何使用视频抽帧技术实现数据集的制作。以下代码中_videoPlay函数是实时显示导入的视频,CutVideo函数实现手动抽帧,在显示时按c键则抽取当前帧,按Esc键关闭视频。ExtractAll函数是自动抽帧功能,frameGap参数是隔多少帧进行自动抽取。
class ExtractImg(object):def __init__(self, videopath: Path, savepath: Path, delay: int = 1) -> None:self.spath = savepathself.vpath = videopathself.delay = delaycv2.namedWindow("cv", cv2.WINDOW_NORMAL)cv2.resizeWindow("cv", 640, 480)self.cap = cv2.VideoCapture(str(self.vpath))self._timeflag = 0if not savepath.exists():os.mkdir(Path(savepath))def _videoPlay(self, size: list) -> None:self.cap.set(3, size[0])self.cap.set(4, size[1])while self.cap.isOpened():ret, frame = self.cap.read()# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)cv2.imshow("cv", frame)if cv2.waitKey(self.delay) & 0xFF == ord('c'):cv2.imwrite(str(PurePath.joinpath(self.spath,"{}.jpg".format(str(time.time())))), frame)print("保存成功")time.sleep(1)elif cv2.waitKey(self.delay) & 0xFF == 27:breakdef ExtractAll(self, frameGap: int = 3) -> None:"""这是将视频流中的帧全部抽出:frame: 跳帧:return:"""while self.cap.isOpened():self._timeflag += 1ret, frame = self.cap.read()if ret:cv2.imshow("cv", frame)if self._timeflag % frameGap == 0:cv2.imwrite(str(PurePath.joinpath(self.spath,"{}.jpg".format(str(time.time())))), frame)print("保存成功")if (cv2.waitKey(self.delay) & 0xFF == 27) or not ret:breakcv2.destroyAllWindows()self.cap.release()self._timeflag = 0def CutVideo(self) -> None:"""这是手动抽帧:return:"""ifm = input("文件中已经存在{}张图片,是否有继续添加""(Y or N):".format(len(os.listdir(self.spath))))if self.spath.exists() and ifm == 'Y':self._videoPlay(size=[640, 480])elif self.spath.exists() and ifm == 'N':return Noneelse:print("\n请输入Y(yes)或者N(no)")cv2.destroyAllWindows()self.cap.release()
2. 文件批量重命名
将文件夹下的图片进行升序的重命名。
def statistics(path: Union[str, Path], dstpath: Union[Path, str], count: int = 0, random: bool = False) -> None:"""这是存放图片的文件夹安升序重命名:param path:需要重命名的文件文件:param count:观察图片总数添加使用"""assert isinstance(path, (Path, str)), print("请输入的路径")l = os.listdir(str(path))if not Path.exists(dstpath):Path.mkdir(dstpath)# l = sorted(l)print(l)# print(l)print("存在文件{}张!".format(len(l)))if random:np.random.shuffle(l)# print(l)# 将保存图片文件中的图片按照升序的方法重命名suffix = Path(l[0]).suffixfor file in tqdm(l):src = PurePath.joinpath(path, file)dst = PurePath.joinpath(dstpath, Path(str(count + int(Path(file).stem))).with_suffix(suffix))os.rename(src, dst)
3. 文件批量移动
下面的是将文件批量按照一定规则挑选出来放到目标文件夹下。
def choosen(src: Union[str, Path] , folder: Union[Path,str] ,dst: Union[str, Path] , suffix: str) -> None:"""1.将xml/jpg文件夹中的文件名字拿出来并且在jpg/xml对应的文件夹中将名称相同的文件挑出来2.将文件夹中的文件随取出:param xmlsrc:目标xml文件:param imgsrc:frameImg文件:param dst:根据xml挑选出的img文件:return: None"""# l = os.listdir(str(xmlsrc))if not isinstance(folder,Path):pa = Path(folder)if not isinstance(src,Path):l = Path(src)# parent = src.parentfor i in l.iterdir():file = Path(i.name).with_suffix(suffix)(pa / file).rename(Path(dst) / file)
4. 将文件批量按照一定格式进行重命名
下面将文件按照5位数字的格式进行重命名 1.jpg->00001.jpg。
def batchrenames(src: Union[str, Path], dst: Union[str, Path], sorted: bool = False) -> None:"""进行特定格式的重命名:param src:原文件:param dst: 存储文件:param sorted: 是否已经有顺序,若有学按照1.jpg ->00001.jpg:return: None"""d = {1: "0000", # 这是命名格式的字典2: "000",3: "00",4: "0",5: ""}l = os.listdir(src)suffix = Path(l[0]).suffixl.sort(key=lambda x: int(x.split('.')[0]))if sorted:for obj in tqdm(l):old = PurePath.joinpath(src, obj)new = PurePath.joinpath(dst, d[len(obj.split('.')[0])] + obj.split('.')[0] + suffix)os.rename(old, new)else:# for c, i in tqdm(enumerate(l)):pass
5. 修改xml文件内容的方法
这是修改xml文件内容的代码。
def revampXml(xml_path: Union[Path, str], update_content: str) -> None:"""这是一个修改xml文件内容的方法,将xml文件爱中的类别改称另一个类别:param xml_path: 存放xml文件的路径:param xml_dw: xpath关键字:param update_content: 更新的内容:return:None"""# 打开xml文档if not isinstance(xml_path, Path):xml_path = Path(xml_path)for i in tqdm(xml_path.iterdir()):xmlfile = xml_path / f"{i}"doc = ET.parse(xmlfile)root = doc.getroot()# 查找修改路劲for obj in root.iter("object"):sub1 = obj.find("name")if sub1.text == "motorboat":# 修改标签内容sub1.text = update_content# 保存修改doc.write(xmlfile)
总结
本文介绍了计算机视觉项目中的文件批量操作与文件批量预处理技术。 掌握这些技术将使您能够高效地处理大规模的图像数据,并为计算机视觉项目的成功实施提供强大的支持。
希望本文对您在计算机视觉项目中的文件批量操作与文件批量预处理有所启发!让我们一起推动计算机视觉的发展,创造更多可能性!
以下是完整代码
# -*- coding: utf-8 -*-
# @Author : cvYouTian
# @Software: PyCharmfrom pathlib import Path, PurePath
import xml.etree.ElementTree as ET
from typing import Union
import numpy as np
# import torch
from tqdm import tqdm
import time
import cv2
import osclass ExtractImg(object):def __init__(self, videopath: Path, savepath: Path, delay: int = 1) -> None:self.spath = savepathself.vpath = videopathself.delay = delaycv2.namedWindow("cv", cv2.WINDOW_NORMAL)cv2.resizeWindow("cv", 640, 480)self.cap = cv2.VideoCapture(str(self.vpath))self._timeflag = 0if not savepath.exists():os.mkdir(Path(savepath))def _videoPlay(self, size: list) -> None:self.cap.set(3, size[0])self.cap.set(4, size[1])while self.cap.isOpened():ret, frame = self.cap.read()# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)cv2.imshow("cv", frame)if cv2.waitKey(self.delay) & 0xFF == ord('c'):cv2.imwrite(str(PurePath.joinpath(self.spath,"{}.jpg".format(str(time.time())))), frame)print("保存成功")time.sleep(1)elif cv2.waitKey(self.delay) & 0xFF == 27:breakdef ExtractAll(self, frameGap: int = 3) -> None:"""这是将视频流中的帧全部抽出:frame: 跳帧:return:"""while self.cap.isOpened():self._timeflag += 1ret, frame = self.cap.read()if ret:cv2.imshow("cv", frame)if self._timeflag % frameGap == 0:cv2.imwrite(str(PurePath.joinpath(self.spath,"{}.jpg".format(str(time.time())))), frame)print("保存成功")if (cv2.waitKey(self.delay) & 0xFF == 27) or not ret:breakcv2.destroyAllWindows()self.cap.release()self._timeflag = 0def CutVideo(self) -> None:"""这是手动抽帧:return:"""ifm = input("文件中已经存在{}张图片,是否有继续添加""(Y or N):".format(len(os.listdir(self.spath))))if self.spath.exists() and ifm == 'Y':self._videoPlay(size=[640, 480])elif self.spath.exists() and ifm == 'N':return Noneelse:print("\n请输入Y(yes)或者N(no)")cv2.destroyAllWindows()self.cap.release()@staticmethoddef statistics(path: Union[str, Path], dstpath: Union[Path, str], count: int = 5305, random: bool = False) -> None:"""这是存放图片的文件夹安升序重命名:param path:需要重命名的文件文件:param count:观察图片总数添加使用"""assert isinstance(path, (Path, str)), print("请输入的路径")l = os.listdir(str(path))if not Path.exists(dstpath):Path.mkdir(dstpath)# l = sorted(l)print(l)# print(l)print("存在文件{}张!".format(len(l)))if random:np.random.shuffle(l)# print(l)# 将保存图片文件中的图片按照升序的方法重命名suffix = Path(l[0]).suffixfor file in tqdm(l):src = PurePath.joinpath(path, file)dst = PurePath.joinpath(dstpath, Path(str(count + int(Path(file).stem))).with_suffix(suffix))os.rename(src, dst)@staticmethoddef choosen(src: Union[str, Path]="/home/you/Desktop/2023海上高速目标检测/val", folder: Union[Path,str]="/home/you/Desktop/2023海上高速目标检测/annotations",dst: Union[str, Path]="/home/you/Desktop/2023海上高速目标检测/train", suffix: str=".xml") -> None:"""1.将xml/jpg文件夹中的文件名字拿出来并且在jpg/xml对应的文件夹中将名称相同的文件挑出来2.将文件夹中的文件随取出:param xmlsrc:目标xml文件:param imgsrc:frameImg文件:param dst:根据xml挑选出的img文件:return: None"""# l = os.listdir(str(xmlsrc))if not isinstance(folder,Path):pa = Path(folder)if not isinstance(src,Path):l = Path(src)# parent = src.parentfor i in l.iterdir():file = Path(i.name).with_suffix(suffix)(pa / file).rename(Path(dst) / file)@staticmethoddef batchrenames(src: Union[str, Path], dst: Union[str, Path], sorted: bool = False) -> None:"""进行特定格式的重命名:param src:原文件:param dst: 存储文件:param sorted: 是否已经有顺序,若有学按照1.jpg ->00001.jpg:return: None"""d = {1: "0000", # 这是命名格式的字典2: "000",3: "00",4: "0",5: ""}l = os.listdir(src)suffix = Path(l[0]).suffixl.sort(key=lambda x: int(x.split('.')[0]))if sorted:for obj in tqdm(l):old = PurePath.joinpath(src, obj)new = PurePath.joinpath(dst, d[len(obj.split('.')[0])] + obj.split('.')[0] + suffix)os.rename(old, new)else:# for c, i in tqdm(enumerate(l)):pass@staticmethoddef text(file: Union[Path, str]):l = []f = open(file)for i in f.readlines():i = i.strip()stem = Path(i).stemsuffix = Path(i).suffixn1, n2 = int(stem) - 1, int(stem) + 1l.append(str(n1) + ".xml")l.append(str(n2) + ".xml")print(l)@staticmethoddef revampXml(xml_path: Union[Path, str], update_content: str) -> None:"""这是一个修改xml文件内容的方法,将xml文件爱中的类别改称另一个类别:param xml_path: 存放xml文件的路径:param xml_dw: xpath关键字:param update_content: 更新的内容:return:None"""# 打开xml文档if not isinstance(xml_path, Path):xml_path = Path(xml_path)for i in tqdm(xml_path.iterdir()):xmlfile = xml_path / f"{i}"doc = ET.parse(xmlfile)root = doc.getroot()# 查找修改路劲for obj in root.iter("object"):sub1 = obj.find("name")if sub1.text == "motorboat":# 修改标签内容sub1.text = update_content# 保存修改doc.write(xmlfile)@staticmethoddef movefile(folder_path: Union[Path, str], dst: Union[Path, str], suffix: str) -> None:"""批量移动剪切文件:param folder_path: 原文件夹路径:param dst: 目标文件夹路径:param suffix: 移动的文件格式/后缀:return:"""if not isinstance(folder_path, Path):folder_path = Path(folder_path)# for i in folder_path.iterdir():# if i.is_dir():# ExtractImg.movefile(folder_path / i, suffix, res)# else:# if i.suffix == suffix:# res.append(str(i))# # return res if suffix is None or suffix == "" else list(filter(lambda x: str(x).endswith(suffix),res))# return resfor i in tqdm(folder_path.rglob(f"*{suffix}")):i.rename(dst / i.name)@staticmethoddef convert_box(size, box):dw, dh = 1. / size[0], 1. / size[1]x, y, w, h = (box[0] + box[1]) / 2.0 - 1, (box[2] + box[3]) / 2.0 - 1, box[1] - box[0], box[3] - box[2]return x * dw, y * dh, w * dw, h * dhif __name__ == "__main__":# 目标视频文件videopath = Path("videoSet/seabird6.mp4")# 图片保存文件savepath = Path("./dataset/imgs")# xin = Path("./VOC6detect/imgss")# savepath = Path("frameSave")# 目标xml文件# xmlpath = Path("./VOC6detect/annotations")# old = Path("/home/you/Desktop/dateset/20(pass)/seabird5")# new = Path("/home/you/Desktop/dateset/11(pass)/temp")# pa = Path("./labels/")# xin = Path()# renamepath = Path("/home/you/Desktop/dateset/4(pass)/a-1")# 实例化a = ExtractImg(videopath=videopath, savepath=savepath)a.choosen()# VOC2YOLO# a.convert_label()# 将帧全部抽出# a.ExtractAll(frameGap=8)# 手动抽帧# a.CutVideo()# 根据xml文选出对应的文件# a.choosen(xmlsrc=xmlpath, imgsrc=savepath, dst=xin)# 将数字命名的图片按照加上一个数字的方式命名# a.statistics(path=Path("./DATA/xml"), dstpath=Path("./DATA/t"), count=5305)# 对已经有顺序或者没顺序的文件进行特定格式的重命名78.jpg -> 00078.jpg# a.batchrenames(src=new, dst=old, sorted=True)# a.text("./data1.txt")# 对xml文件进行修改# a.revampXml(xml_path= "/home/you/Desktop/tools/dataset/annotations", update_content="speedboat")# 批量拿到文件夹中的某格式的文件# a.movefile(folder_path="/home/you/Desktop/网上快艇", dst=pa, suffix=".jpg")相关文章:
计算机视觉项目中的文件批量操作与文件批量预处理
计算机视觉项目中的文件批量操作与文件批量预处理 目录 数据集制作文件批量重命名文件批量移动将文件批量按照一定格式进行重命名修改xml文件内容的方法 引言 在计算机视觉项目中,文件批量操作和文件批量预处理是必不可少的步骤。它们涉及处理大量的图像文件&am…...
PHP数组转对象和对象转数组
PHP数组转对象和对象转数组 <?php function array_to_object($arr){$obj new stdClass();foreach ($arr as $key > $val) {if (is_array($val) || is_object($val)) {$obj->$key array_to_object($val);} else {$obj->$key $val;}}return $obj; } function o…...
前后端分离开发中的传参
1.post请求,后台代码使用RequestBody注解接收前端传过来的参数 PostMapping("/saveHosp") public Result SaveHosp(RequestBody HospitalSet hospitalSet){//此处省略中间代码......} 此时前端传过来的参数须为JSON格式,前端VUE传参数为&…...
mount: wrong fs type, bad option, bad superblock报错 ubuntu
问题描述 mount: wrong fs type, bad option, bad superblock Ubuntu无法挂载磁盘。 原因 很大概率是你的硬盘是NTFS。 解决 sudo apt install ntfs-3g即可。...
【图像分类】CNN+Transformer结合系列.3
介绍两篇图像分类的论文:ResMLP(arXiv2305),MetaFormer(CVPR2022),两者都与Transformer有关系,前者基于transformer结构的特点设计ResMLP,后者认为宏观架构才是Transform…...
IDA分析实例android_crackme/EasyJNI/Transformers/pingan2
文章目录 第一个实例android_crackme将32位的android_server放到手机目录下给android_server赋予root更改root用户组运行android_serverpc端端口转发安装apk,并运行app打开32位IDA并attach到进程先使用jadx看java层逻辑定位要分析的方法IDA 给两个方法打断点 第二个…...
拿捏--->求一元二次方程的根
文章目录 题目描述算法思路代码示例 题目描述 从键盘输入a, b, c的值,编程计算并输出一元二次方程ax2 bx c 0的根,当a 0时,输出“Not quadratic equation”,当a ≠ 0时,根据△ b2 - 4ac的三种情况计算并输出方程…...
深入浅出之Docker Compose详解
目录 1.Docker Compose概述 1.1 Docker Compose 定义 1.2 Docker Compose产生背景 1.3 Docker Compose 核心概念 1.4 Docker Compose 使用步骤 1.5 Docker Compose 常用命令 2. Docker Compose 实战 2.1 Docker Compose下载和卸载 2.2 Docker Compose 项目概述 2.3 Do…...
spring5源码篇(12)——spring-mvc请求流程
spring-framework 版本:v5.3.19 文章目录 一、请求流程1、处理器映射器1.1、 RequestMappingHandlerMapping1.2、获取对应的映射方法1.3、添加拦截器 2、获取合适的处理器适配器3、通过处理器适配器执行处理器方法3.1、拦截器的前置后置3.2、处理器的执行3.2.1 参数…...
风辞远的科技茶屋:来自未来的信号枪
很久之前,有位朋友问我,现在科技资讯这么发达了,你们还写啊写做什么呢? 我是这么看的。最终能够凝结为资讯的那个新闻点,其实是一系列事情最终得出的结果,而这个结果又会带来更多新的结果。其中这些“得出”…...
MongoDB教程-8
ObjectId 在之前的所有章节中,我们一直在使用MongoDB的Object Id。在本章中,我们将了解ObjectId的结构。 ObjectId是一个12字节的BSON类型,具有以下结构-- 1. 前4个字节代表自unix epoch以来的秒数 接下来的3个字节是机器标识符 接下来的2…...
Redis 理论部分
前面写了很多redis项目,今天在通过redis的理论加深redis的了解,顺便做个总结 Redis 理论部分 1.redis 速度快的原因 纯内存操作单线程操作,避免频繁的上下文切换以及资源争用的问题,多线程需要占用更多的cpu资源采用非阻塞I/O多…...
Android—Monkey用法
文章目录 Monkey知识 Monkey知识 介绍 Monkey是Android中的一个命令行工具,可以运行在模拟器里或实际设备中。它向系统发送伪随机的用户事件流(如按键输入、触摸屏输入、手势输入等),实现对正在开发的应用程序进行压力测试。Monkey测试是一种为了测试软…...
几个影响 cpu cache 性能因素及 cache 测试工具介绍
》内核新视界文章汇总《 文章目录 1 cache 性能及影响因素1.1 内存访问和性能比较1.2 cache line 对性能的影响1.3 L1 和 L2 缓存大小1.4 指令集并行性对 cache 性能的影响1.5 缓存关联性对 cache 的影响1.6 错误的 cacheline 共享 (缓存一致性)1.7 硬件设计 2 cpu cache benc…...
Java从入门到精通(二)· 基本语法
Java从入门到精通(二) 基本语法 一 变量 1.字面量 计算机是用来处理数据的,字面量就是告诉程序员:数据在程序中的书写格式。 特殊的字符: \n 表示换行, \t 表示一个制表符,即一个tab 2.变量…...
云安全攻防(三)之 面向云原生环境的安全体系
面向云原生环境的安全体系 根据云原生环境的构成,面向云原生环境的安全体系可包含三个层面的安全体制,它们分别是容器安全、编排系统安全和云原生应用安全,下面,我们逐步来讲解这三点: 容器安全 容器环境࿰…...
BGP汇总和破解水平分割
一,BGP的宣告问题 在BGP协议中每台运行BGP的设备上,宣告本地直连路由在BGP协议中运行BGP协议的设备来宣告,通过IGP学习到的,未运行BGP协议设备产生的路由; 在BGP协议中宣告本地路由表中路由条目时,将携带本…...
BUG:pm2启动verdaccio报错:Invalid or unexpected toke
输入命令: pm2 state verdaccio 问题描述: pm2 logs verdaccio报错翻译:数据格式错误 导致我呢提原因,没有找到运行文件, 发现问题:因为命令默认查找verdaccio是去系统盘查找。 解决方式 1:…...
Zookeeper笔记
为什么要使用Zookeeper dubbo需要一个注册中心,而Zookeeper是我们在使用Dubbo是官方推荐的注册中心 Zookeeper介绍 Zookeeper的集群机制 Zookeeper是为了其他分布式程序提供服务的,所以不能随便就挂了。Zookeeper的集群机制采取的是半数存活机制。也…...
【视觉SLAM入门】5.1. 特征提取和匹配--FAST,ORB(关键点描述子),2D-2D对极几何,本质矩阵,单应矩阵,三角测量,三角化矛盾
"不言而善应" 0. 基础知识1. 特征提取和匹配1.1 FAST关键点1.2 ORB的关键点--改进FAST1.3 ORB的描述子--BRIEF1.4 总结 2. 对极几何,对极约束2.1 本质矩阵(对极约束)2.1.1 求解本质矩阵2.1.2 恢复相机运动 R , t R,t R,…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
