OpenCV中的透视变换方法详解

news2025/4/23 11:06:49

文章目录

  • 引言
  • 1. 什么是透视变换
  • 2. 透视变换的数学原理
  • 3. OpenCV中的透视变换代码实现
    • 3.1 首先定义四个函数
      •  3.1.1 cv_show() 函数
      •  3.1.2 def resize() 函数
      •  3.1.3 order_points() 函数
      •  3.1.4 four_point_transform() 函数
    • 3.2 读取图片并做预处理
    • 3.3 轮廓检测
    • 3.4 获取最大轮廓
    • 3.5 透视变换矫正
    • 3.6 保存矫正后的图片并显示窗口属性
  • 4. 结语

引言

透视变换是计算机视觉中一项重要的图像处理技术,它能够将图像从一个视角投影到另一个视角,广泛应用于文档校正、增强现实、视角转换等场景。本文将深入探讨OpenCV中透视变换的原理与实现方法。

1. 什么是透视变换

透视变换(Perspective Transformation)是一种将图像从一个平面投影到另一个平面的变换方式。与仿射变换不同,透视变换能够处理"近大远小"的透视效果,更真实地模拟人眼观察世界的视角变化。

2. 透视变换的数学原理

透视变换可以用一个3×3的变换矩阵表示:

[a11 a12 a13]
[a21 a22 a23]
[a31 a32 a33]

对于原始图像中的点(x,y),变换后的坐标(x’,y’)计算方式为:

x' = (a11*x + a12*y + a13) / (a31*x + a32*y + a33)
y' = (a21*x + a22*y + a23) / (a31*x + a32*y + a33)

3. OpenCV中的透视变换代码实现

3.1 首先定义四个函数

 3.1.1 cv_show() 函数

def cv_show(name,img):
    cv2.imshow(name, img)
    cv2.waitKey(0)

这段代码是用来输入图像并将其显示出来


 3.1.2 def resize() 函数

def resize(image,width=None,height=None,inter=cv2.INTER_AREA):
    dim = None
    (h,w)=image.shape[:2]
    if width is None and height is None:
        return image
    if width is None:
        r = height/float(h)
        dim = (int(w*r),height)
    else:
        r = width/float(w)
        dim = (width,int(h*r))
    resized = cv2.resize(image,dim,interpolation=inter)
    return resized

这段代码定义了一个名为 resize 的函数,用于调整图像的大小(缩放),同时保持图像的宽高比例(aspect ratio)。它使用 OpenCV(cv2)来实现图像缩放。


函数参数说明:

  • image: 输入的图像(NumPy 数组格式,OpenCV 默认读取的图像)。
  • width (可选): 目标宽度(如果提供,则按宽度缩放)。
  • height (可选): 目标高度(如果提供,则按高度缩放)。
  • inter (可选): 插值方法(默认 cv2.INTER_AREA,适用于缩小图像)。

函数逻辑解析

 1.获取原始尺寸:

(h, w) = image.shape[:2]  # 获取图像的高度和宽度

 2.检查是否传入 width 或 height:

  • 如果两者都未提供(width is None and height is None),直接返回原图。
  • 如果只提供 height(width is None),则按高度缩放,宽度按比例计算:
r = height / float(h)  # 计算缩放比例
dim = (int(w * r), height)  # 新尺寸:(宽度按比例缩放, 目标高度)
  • 如果提供 width(不管 height 是否提供),则按宽度缩放,高度按比例计算:
r = width / float(w)  # 计算缩放比例
dim = (width, int(h * r))  # 新尺寸:(目标宽度, 高度按比例缩放)

 3.执行缩放:

resized = cv2.resize(image, dim, interpolation=inter)  # 使用 OpenCV 进行缩放

 4.返回缩放后的图像:

return resized

总结

  • 用途:保持宽高比的情况下缩放图像,避免直接 cv2.resize 可能导致的变形。

适用场景:

  • 需要固定宽度或高度,但保持比例不变。
  • 适用于图像预处理(如深度学习输入尺寸调整)。

注意:

  • 如果同时传入 width 和 height,此函数仍然只会按其中一个参数缩放(优先 width)。
  • 如果要强制指定宽高(可能变形),直接用 cv2.resize(image, (width, height))

 3.1.3 order_points() 函数

def order_points(pts):
    # 一共4个坐标点
    rect = np.zeros((4,2),dtype="float32") 
    # 用来存储排序之后的坐标位置
    # 按顺序找到对应坐标0123分别是 左上、右上、右下、左下
    s = pts.sum(axis=1) #对pts矩阵的每一行进行求和操作,(x+y)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]
    diff = np.diff(pts,axis=1) #对pts矩阵的每一行进行求差操作,(y-x)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]
    return rect

这段代码定义了一个名为 order_points 的函数,用于对给定的 4个二维坐标点 进行排序,使其按照 左上、右上、右下、左下 的顺序排列。


逐步解析

输入
pts:一个形状为 (4, 2) 的 NumPy 数组,表示 4 个点的 (x, y) 坐标。例如:

pts = np.array([[x1, y1], [x2, y2], [x3, y3], [x4, y4]])

输出
rect:排序后的 (4, 2) 数组,顺序为 左上、右上、右下、左下。


排序逻辑

1.初始化存储数组

rect = np.zeros((4, 2), dtype="float32")  # 存储排序后的坐标

2.计算 x + y 并找到左上和右下点

  • 左上点(rect[0]):x + y 最小的点(因为左上角的 x 和 y 都较小)。
  • 右下点(rect[2]):x + y 最大的点(因为右下角的 x 和 y 都较大)。
s = pts.sum(axis=1)  # 计算每个点的 x + y
rect[0] = pts[np.argmin(s)]  # 左上点
rect[2] = pts[np.argmax(s)]  # 右下点

3.计算 y - x 并找到右上和左下点

  • 右上点(rect[1]):y - x 最小的点(因为右上角的 y 较小,x 较大)。
  • 左下点(rect[3]):y - x 最大的点(因为左下角的 y 较大,x 较小)。
diff = np.diff(pts, axis=1)  # 计算 y - x
rect[1] = pts[np.argmin(diff)]  # 右上点
rect[3] = pts[np.argmax(diff)]  # 左下点

4.返回排序后的坐标

return rect

注意事项

  • 输入必须是 4 个点,否则会报错。
  • 适用于凸四边形,如果点排列异常(如交叉),可能排序错误。
  • 如果 4 个点本身是 旋转的矩形(如 45° 倾斜),该方法仍然有效。

 3.1.4 four_point_transform() 函数

def four_point_transform(image,pts):
    # 获取输入坐标点
    rect = order_points(pts)
    (tl,tr,br,bl) = rect
    # 计算输入的w和h值
    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA),int(widthB))
    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA),int(heightB))
    # 变换后对应坐标位置
    dst = np.array([[0,0],[maxWidth - 1,0],
                    [maxWidth - 1,maxHeight - 1],[0,maxHeight - 1]],dtype="float32")

    M = cv2.getPerspectiveTransform(rect,dst)
    warped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))
    # 返回变换后的结果
    return warped

这段代码定义了一个名为 four_point_transform 的函数,用于对图像进行 透视变换(Perspective Transformation),将图像中的任意四边形区域 矫正为一个 矩形。


代码解析

输入参数

  • image:输入的图像(OpenCV 格式,即 NumPy 数组)。
  • pts:一个形状为 (4, 2) 的 NumPy 数组,表示待矫正的 4 个角点坐标(顺序任意)。

输出

  • warped:矫正后的图像(矩形视角)。

执行步骤

1.对 4 个点进行排序(使用 order_points 确保顺序为 左上、右上、右下、左下):

rect = order_points(pts)  # 排序后的 4 个点:[tl, tr, br, bl]
(tl, tr, br, bl) = rect   # tl: 左上, tr: 右上, br: 右下, bl: 左下

2.计算矫正后的目标宽度 maxWidth:

计算底边宽度(br 到 bl 的距离):

widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))

计算顶边宽度(tr 到 tl 的距离):

widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))

取最大值作为最终宽度:

maxWidth = max(int(widthA), int(widthB))

3.计算矫正后的目标高度 maxHeight:

计算右侧高度(tr 到 br 的距离):

heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))

计算左侧高度(tl 到 bl 的距离):

heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))

取最大值作为最终高度:

maxHeight = max(int(heightA), int(heightB))

4.定义目标矩形坐标 dst:

dst = np.array([
    [0, 0],                     # 左上
    [maxWidth - 1, 0],          # 右上
    [maxWidth - 1, maxHeight - 1],  # 右下
    [0, maxHeight - 1]          # 左下
], dtype="float32")

5.计算透视变换矩阵 M:

M = cv2.getPerspectiveTransform(rect, dst)
  • rect:原始 4 个点(四边形)。
  • dst:目标 4 个点(矩形)。

6.执行透视变换:

warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
  • image:输入图像。
  • M:透视变换矩阵。
  • (maxWidth, maxHeight):输出图像的尺寸。

7.返回矫正后的图像:

return warped

总结

four_point_transform 的作用是:

  • 1.对 4 个点进行排序(order_points)。
  • 2.计算目标矩形的尺寸(maxWidth 和 maxHeight)
  • 3.计算透视变换矩阵(cv2.getPerspectiveTransform)
  • 4.执行透视变换(cv2.warpPerspective),将任意四边形矫正为矩形

这样,我们就可以将 倾斜拍摄的物体 转换为 正面视角,便于后续处理。


3.2 读取图片并做预处理

#读取输入
image = cv2.imread('fapiao.jpg')
cv_show('image',image)

#图片过大,进行缩小处理
ratio = image.shape[0] / 500.0 #计算最小比率
orig = image.copy()
image = resize(orig,height=500)
cv_show('1',image)

读取一张发票图片,并进行缩小处理。
图片如下:

在这里插入图片描述


3.3 轮廓检测

gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)

edged = cv2.threshold(gray,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cnts= cv2.findContours(edged.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[-2]
image_contours = cv2.drawContours(image.copy(),cnts,-1,(0,0,255),1)
cv_show('image_contours',image_contours)

这段代码的作用是 对输入图像进行边缘检测并绘制轮廓,以下是逐步解析:

1. 转换为灰度图

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
  • 功能:将彩色图像(BGR格式)转换为灰度图(单通道)。
  • 为什么需要:简化后续处理,减少计算量。

2.二值化处理(阈值分割

edged = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

功能:使用 Otsu算法 自动计算最佳阈值,将灰度图转换为黑白二值图。

  • cv2.THRESH_BINARY:二值化(大于阈值的设为255,否则为0)。
  • cv2.THRESH_OTSU:自动确定最佳阈值(适合双峰直方图的图像)。

输出:edged 是一个二值图像,白色(255)代表目标,黑色(0)代表背景。

3. 查找轮廓

cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[-2]

功能:在二值图像中查找所有轮廓。

  • edged.copy():确保不修改原始二值图像。
  • cv2.RETR_LIST:检测所有轮廓,不建立层级关系。
  • cv2.CHAIN_APPROX_SIMPLE:压缩轮廓点(例如直线只保留端点)。

返回值:cnts 是一个列表,每个元素是一个轮廓(点的集合)。

4. 绘制轮廓

image_contours = cv2.drawContours(image.copy(), cnts, -1, (0, 0, 255), 1)

功能:在原图的副本上绘制所有轮廓。

  • image.copy():避免修改原图。
  • -1:绘制所有轮廓(若为 0 则只绘制第一个轮廓)。
  • (0, 0, 255):轮廓颜色(红色,BGR格式)。
  • 1:轮廓线宽(像素)。

5. 显示结果

cv_show('image_contours', image_contours)

功能:显示带轮廓的图像(假设 cv_show 是自定义的显示函数,等同于 cv2.imshow + cv2.waitKey)。
在这里插入图片描述


3.4 获取最大轮廓

screenCnt = sorted(cnts,key=cv2.contourArea,reverse=True)[0]

peri = cv2.arcLength(screenCnt,True) #计算轮廓周长
screenCnt = cv2.approxPolyDP(screenCnt,0.05 * peri,True) #轮廓近似
print(screenCnt.shape)
image_contours = cv2.drawContours(image.copy(),[screenCnt],-1,(0,255,0),2)

cv2.imshow("image_contours",image_contours)
cv2.waitKey(0)

这段代码的作用是从所有检测到的轮廓中筛选出面积最大的轮廓,并进行多边形近似,最终绘制出这个近似后的轮廓。以下是逐步解析:


1. 筛选面积最大的轮廓

screenCnt = sorted(cnts, key=cv2.contourArea, reverse=True)[0]

功能:对轮廓列表 cnts 按面积从大到小排序,并选择面积最大的一个。

  • cv2.contourArea:计算轮廓的面积。
  • reverse=True:降序排列。
  • [0]:取第一个(即最大轮廓)。

用途:假设图像中只有一个主要目标,直接取最大轮廓可排除噪声。

2. 计算轮廓周长

peri = cv2.arcLength(screenCnt, True)

功能:计算轮廓的周长。

  • screenCnt:输入的轮廓点集。
  • True:表示轮廓是闭合的(首尾相连)。

3.多边形近似(轮廓简化)

screenCnt = cv2.approxPolyDP(screenCnt, 0.05 * peri, True)

功能:用更少的点近似轮廓,减少冗余点(如将弯曲边缘近似为直线)。

  • screenCnt:原始轮廓点集。
  • 0.05 * peri:近似精度(周长的5%作为阈值,值越小越接近原始形状)。
  • True:轮廓闭合。

输出:近似后的轮廓点集(如果是矩形,会返回4个角点)。

示例:

  • 输入:复杂轮廓(如几十个点组成的弯曲边缘)。
  • 输出:简化后的多边形(如4个点组成的矩形)。

4.检查近似结果

print(screenCnt.shape)

用途:打印近似后轮廓的形状(如 (4, 1, 2) 表示4个点,每个点是 (x, y) 坐标)。

典型输出

  • 矩形:(4, 1, 2)
  • 三角形:(3, 1, 2)
  • 若点数过多(如 (10, 1, 2)),可能需要调整 0.05 参数。

5.绘制近似后的轮廓

image_contours = cv2.drawContours(image.copy(), [screenCnt], -1, (0, 255, 0), 2)

功能:在原图的副本上绘制绿色(BGR格式 (0,255,0))的近似轮廓。

  • [screenCnt]:将轮廓包装为列表(因 drawContours 接受列表输入)。
  • -1:绘制所有轮廓(此处只有一个)。
  • 2:线宽(像素)。

6.显示结果

cv2.imshow("image_contours", image_contours)
cv2.waitKey(0)

效果:显示带绿色轮廓的图像,标识出检测到的主要目标。

在这里插入图片描述


完整流程总结

  1. 输入:所有检测到的轮廓 cnts(来自 cv2.findContours)。
  2. 筛选:选择面积最大的轮廓。
  3. 简化:用多边形近似轮廓(如提取矩形角点)。
  4. 绘制:在原图上标出简化后的轮廓。
  5. 输出:可视化结果。

3.5 透视变换矫正

warped = four_point_transform(orig,screenCnt.reshape(4,2) * ratio)

功能:将原始图像 orig 中的四边形区域(screenCnt)矫正为正面视角的矩形。

  • screenCnt.reshape(4, 2):将轮廓点从 (4, 1, 2) 转换为 (4, 2) 格式(4个点的x,y坐标)。
  • ratio:如果预处理时图像被缩小过(如为了加速轮廓检测),需通过比例 ratio 将坐标映射回原始图像尺寸。
  • four_point_transform:自定义函数(基于 cv2.getPerspectiveTransform 和
    cv2.warpPerspective)。

3.6 保存矫正后的图片并显示窗口属性

cv2.imwrite('invoice_new.jpg', warped)
cv2.namedWindow('xx',cv2.WINDOW_NORMAL)
cv2.resizeWindow('xx',800,600)

cv_show('xx',warped)
cv2.waitKey(0)
cv2.destroyAllWindows()

代码解析:

cv2.imwrite('invoice_new.jpg', warped)

功能:将矫正后的图像 warped 保存为 invoice_new.jpg。
用途:持久化处理结果,便于后续使用(如OCR识别、打印等)。

在这里插入图片描述


cv2.namedWindow('xx', cv2.WINDOW_NORMAL)
cv2.resizeWindow('xx', 800, 600)

功能:创建一个可调整大小的窗口,并设置初始尺寸为 800x600 像素。

  • cv2.WINDOW_NORMAL:允许手动调整窗口大小(默认是固定大小的 cv2.WINDOW_AUTOSIZE)。

适用场景:当图像较大时,可通过缩放窗口查看完整内容。


cv2.imshow('xx', warped)
cv2.waitKey(0)

功能:在窗口 ‘xx’ 中显示矫正后的图像 warped,并等待用户按键关闭窗口。
注意:cv_show 可能是封装好的函数,若未定义需替换为 cv2.imshow + cv2.waitKey。
在这里插入图片描述

4. 结语

本篇博客到这里就结束啦,希望能帮助大家更好的理解和和使用OpenCV中的透视变换方法。其实方法并不难,只要我们能沉下心来慢慢学习,一定会有收获的。我一直坚信,努力会让我们收获一个更好的自己,加油!!!

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

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

相关文章

并发设计模式实战系列(3):工作队列

🌟 ​大家好,我是摘星!​ 🌟 今天为大家带来的是并发设计模式实战系列,第三章工作队列(Work Queue)​​,废话不多说直接开始~ 目录 一、核心原理深度拆解 1. 生产者-消费者架构 …

如何理解抽象且不易理解的华为云 API?

API的概念在华为云的使用中非常抽象,且不容易理解,用通俗的语言 形象的比喻来讲清楚——什么是华为云 API,怎么用,背后原理,以及主要元素有哪些,尽量让新手也能明白。 🧠 一句话先理解&#xf…

深度学习-全连接神经网络(过拟合,欠拟合。批量标准化)

七、过拟合与欠拟合 在训练深层神经网络时,由于模型参数较多,在数据量不足时很容易过拟合。而正则化技术主要就是用于防止过拟合,提升模型的泛化能力(对新数据表现良好)和鲁棒性(对异常数据表现良好)。 1. 概念认知 …

系统架构设计师:流水线技术相关知识点、记忆卡片、多同类型练习题、答案与解析

流水线记忆要点‌ ‌公式 总时间 (n k - 1)Δt 吞吐率 TP n / 总时间 → 1/Δt(max) 加速比 S nk / (n k - 1) | 效率 E n / (n k - 1) 关键概念 周期:最长段Δt 冲突‌: ‌数据冲突(RAW) → 旁路/…

复刻低成本机械臂 SO-ARM100 3D 打印篇

视频讲解: 复刻低成本机械臂 SO-ARM100 3D 打印篇 清理了下许久不用的3D打印机,挤出机也裂了,更换了喷嘴和挤出机夹具,终于恢复了正常工作的状态,接下来还是要用起来,不然吃灰生锈了,于是乎想起…

Flutter IOS 真机 Widget 错误。Widget 安装后系统中没有

错误信息: SendProcessControlEvent:toPid: encountered an error: Error Domaincom.apple.dt.deviceprocesscontrolservice Code8 "Failed to show Widget com.xxx.xxx.ServerStatus error: Error DomainFBSOpenApplicationServiceErrorDomain Code1 "T…

Spring之我见 - Spring MVC重要组件和基本流程

核心组件详解 前端控制器 - DispatcherServlet 作用:所有请求的入口,负责请求分发和协调组件。 public class DispatcherServlet extends HttpServlet {// 核心服务方法protected void doService(HttpServletRequest request, HttpServletResponse re…

使用 Axios 进行 API 请求与接口封装:打造高效稳定的前端数据交互

引言 在现代前端开发中,与后端 API 进行数据交互是一项核心任务。Axios 作为一个基于 Promise 的 HTTP 客户端,以其简洁易用、功能强大的特点,成为了前端开发者处理 API 请求的首选工具。本文将深入探讨如何使用 Axios 进行 API 请求&#x…

理解字符设备、设备模型与子系统:以 i.MX8MP 平台为例

视频教程请关注 B 站:“嵌入式 Jerry” Linux 内核驱动开发中,很多人在接触字符设备(char device)、设备模型(device model)和各种子系统(subsystem)时,往往会感到概念混…

鸿蒙Flutter仓库停止更新?

停止更新 熟悉 Flutter 鸿蒙开发的小伙伴应该知道,Flutter 3.7.12 鸿蒙化 SDK 已经在开源鸿蒙社区发布快一年了, Flutter 3.22.x 的鸿蒙化适配一直由鸿蒙突击队仓库提供,最近有小伙伴反馈已经 2 个多月没有停止更新了,不少人以为停…

网络基础概念(下)

网络基础概念(上)https://blog.csdn.net/Small_entreprene/article/details/147261091?sharetypeblogdetail&sharerId147261091&sharereferPC&sharesourceSmall_entreprene&sharefrommp_from_link 网络传输的基本流程 局域网网络传输流…

一个关于相对速度的假想的故事-4

回到公式, 正写速度叠加和倒写速度叠加的倒写相等,这就是这个表达式所要表达的意思。但倒写叠加用的是减法,而正写叠加用的是加法。当然是这样,因为正写叠加要的是单位时间上完成更远的距离,而倒写叠加说的是单位距离需…

Idea创建项目的搭建方式

目录 一、普通Java项目 二、普通JavaWeb项目 三、maven的JavaWeb项目 四、maven的Java项目 一、普通Java项目 1. 点击 Create New Project 2. 选择Java项目,选择JDK,点击Next 3. 输入项目名称(驼峰式命名法),可选…

【DeepSeek 学习推理】Llumnix: Dynamic Scheduling for Large Language Model Serving实验部分

6.1 实验设置 测试平台。我们使用阿里云上的16-GPU集群(包含4个GPU虚拟机,类型为ecs.gn7i-c32g1.32xlarge)。每台虚拟机配备4个NVIDIA A10(24 GB)GPU(通过PCI-e 4.0连接)、128个vCPU、752 GB内…

Kubernetes相关的名词解释kubeadm(19)

kubeadm是什么? kubeadm 是 Kubernetes 官方提供的一个用于快速部署和管理 Kubernetes 集群的命令行工具。它简化了集群的初始化、节点加入和升级过程,特别适合在生产环境或学习环境中快速搭建符合最佳实践的 Kubernetes 集群。 kubeadm 的定位 不是完整…

什么是负载均衡?NGINX是如何实现负载均衡的?

大家好,我是锋哥。今天分享关于【什么是负载均衡?NGINX是如何实现负载均衡的?】面试题。希望对大家有帮助; 什么是负载均衡?NGINX是如何实现负载均衡的? 1000道 互联网大厂Java工程师 精选面试题-Java资源…

基于Python(Django)+SQLite实现(Web)校园助手

校园助手 本校园助手采用 B/S 架构。并已将其部署到服务器上。在网址上输入 db.uplei.com 即可访问。 使用说明 可使用如下账号体验: 学生界面: 账号1:123 密码1:123 账户2:201805301348 密码2:1 # --------------…

从零开始搭建Django博客②--Django的服务器内容搭建

本文主要在Ubuntu环境上搭建,为便于研究理解,采用SSH连接在虚拟机里的ubuntu-24.04.2-desktop系统搭建,当涉及一些文件操作部分便于通过桌面化进行理解,通过Nginx代理绑定域名,对外发布。 此为从零开始搭建Django博客…

【读论文】HM-RAG:分层多智能体多模态检索增强生成

如何在多模态信息检索和生成中,通过协作式多智能体系统来处理复杂的多模态查询。传统的单代理RAG系统在处理需要跨异构数据生态系统进行协调推理的复杂查询时存在根本性限制:处理多种查询类型、数据格式异质性和检索任务目标的多样性;在视觉内容和文本内…

文件操作和IO(上)

绝对路径和相对路径 文件按照层级结构进行组织(类似于数据结构中的树型结构),将专门用来存放管理信息的特殊文件称为文件夹或目录。对于文件系统中文件的定位有两种方式,一种是绝对路径,另一种是相对路径。 绝对路径…