第二章 图像基本运算及变换

news2024/12/25 3:02:01

文章目录

  • 前言
  • 一、图像运算
    • 1. 加减乘除
    • 2. 位运算
  • 二、翻转与旋转
    • 1.翻转
    • 2.旋转
  • 三、仿射变换
    • 1. 介绍
    • 2. 变换的数学表达
      • 2.1 平移
      • 2.2 缩放
      • 2.3 旋转
    • 3. 变换矩阵
    • 4. 变换叠加
    • 5. 变换矩阵的逆推
    • 6.OpenCV实现以及手动实现
      • 6.1 手动实现
      • 6.2OpenCV实现
    • 7.向量空间----补充
  • 四、透视变换
    • 1. 齐次坐标
    • 2. 透视
    • 3. 透视变换
    • 4. 透视变换的逆推
    • 5. OpenCV代码
  • 五、图像边界填充
  • 总结


前言

本章主要讲解图像的一些基本运算及仿射变换以及透视变换。


一、图像运算

1. 加减乘除

  • 图像相加

    #图像直接相加
    imgC = imgA +  imgB
    
    #opencv方式
    cv2.add(imgA, imgB)
    cv2.addWeighted(imgA, alpha, imgB, beta, gamma)
    
    • imgA + imgB:当其和大于一个字节时, 大于一个字节的位数将被丢失,类似于取模。
      ( A + B ) % 256 (A + B) \% 256 (A+B)%256
    • cv2.add(imgA, imgB):当数值超过255时,取值为255
      m i n ( A + B , 255 ) min(A + B, 255) min(A+B,255)
    • cv2.addWeighted(imgA, alpha, imgB, beta, gamma)
      m i n ( r o u n d ( A ∗ α + B ∗ β + γ ) , 255 ) min(round(A * \alpha + B * \beta + \gamma), 255) min(round(Aα+Bβ+γ),255)

    图像相加的应用:针对降噪的带噪图像相加(平均)可参看 数字图像处理(第三版)—冈萨雷斯

  • 图像相减
    与相加类似

    #图像直接相减
    imgC = imgA - imgB
    
    #opencv方式
    sub = cv2.subtract(img2, img1)
    
    • imgA - imgB:通常图像的类型为8位无符号整型,当涉及到一个小数减一个大数时,会运用到补码的相关运算,并最终转换成一个无符号八位整型

    • cv2.subtract(img2, img1):当数值小于0时,取值为0
      m a x ( A − B , 0 ) max(A - B, 0) max(AB,0)

    图像相减的应用:增强差别的图像相减 可参看 数字图像处理(第三版)—冈萨雷斯

  • 图像相乘
    图像相乘有点乘与叉乘
    在计算机视觉中,图像的点乘和叉乘是两种不同的运算方式。

    1. 图像的点乘(也称为哈达玛积或按位乘)

      点乘是一种逐像素地乘以两张图像的相应像素所得到的结果,并表示为 Dst(I)=Src1(I)⋅Src2(I),其中Dst表示输出图像,Src1Src2表示输入图像,I表示图像中的一个像素位置。

      在OpenCV中,可以使用 cv2.multiply() 函数来实现图像的点乘运算。下面是一段代码示例:

      import cv2
      
      # 加载两个图像
      img1 = cv2.imread('image1.jpg')
      img2 = cv2.imread('image2.jpg')
      
      # 点乘运算
      res = cv2.multiply(img1, img2)
      
      # 显示结果
      cv2.imshow('Result', res)
      cv2.waitKey(0)
      cv2.destroyAllWindows()
      

      逐元素乘积常常用于图像的调整和处理。例如,可以将两张图像的对应像素值进行逐元素乘积,得到两张图像的混合效果。另外,逐元素乘积也可以用于进行图像的掩模操作,即使用一个掩模矩阵对图像矩阵进行加权处理,从而达到一些特定的效果

    2. 矩阵的叉乘(也称为向量积或“交叉乘积”)
      假设有两个矩阵 A A A B B B,它们的维度分别为 m × n m \times n m×n n × p n \times p n×p,则它们的乘积 C = A B C = AB C=AB 的维度为 m × p m \times p m×p。矩阵乘法的计算公式为: C i j = ∑ k = 1 n A i k B k j C_{ij} = \sum_{k=1}^n A_{ik} B_{kj} Cij=k=1nAikBkj其中 C i j C_{ij} Cij 是矩阵 C C C 的第 i i i 行、第 j j j 列的元素, A i k A_{ik} Aik 是矩阵 A A A 的第 i i i 行、第 k k k 列的元素, B k j B_{kj} Bkj 是矩阵 B B B 的第 k k k 行、第 j j j 列的元素。

      在图像处理中,矩阵乘法常常被用于进行变换操作,例如将图像进行平移、缩放、旋转等操作。为了进行这些操作,我们可以构建变换矩阵,然后将这个矩阵乘以图像矩阵,得到变换后的图像矩阵。

总的来说,矩阵乘法和逐元素乘积都在图像处理中有着重要的作用。但是,在对图像进行几何变换方面,我们通常使用矩阵乘法,而在进行像素-level的调整和处理方面,我们通常使用逐元素乘积。

  • 图像除法
    图像相除也是一种常见的图像处理操作,它通常用于图像增强或去除伪影。

    图像相除的基本原理是将一张图像的像素值除以另一张图像的像素值,从而得到一个新的图像矩阵。具体来说,假设有两张相同大小的图像 A A A B B B,它们的对应像素值分别为 A i j A_{ij} Aij B i j B_{ij} Bij,则它们的像素相除得到的新图像的像素值为: C i j = A i j B i j C_{ij} = \frac{A_{ij}}{B_{ij}} Cij=BijAij

    需要注意的是,当除数的像素值为0时,相应位置的结果将可能会出现不确定或非数值的情况。因此,在进行图像相除时,需要注意避免除数为0的情况。

    使用图像相除可以有效地去除图像中的光照变化或噪声等影响,从而得到更加清晰、准确的图像信息。例如,在医学图像处理中,可以通过将两张X光片的像素相除得到更加清晰的影像,进而提高对病情的诊断准确度。

2. 位运算

# 与运算
bitwise_and(src1:image, src2:image[, dst[, mask]]) -> dst
# 或运算
bitwise_or(src1:image, src2:image[, dst[, mask]]) -> dst
# 异或运算
bitwise_xor(src1:image, src2:image[, dst[, mask]]) -> dst
# 非
bitwise_not(src1:image[, dst[, mask]]) -> dst
  • 与、或、异或: 实质就是两个图像数组,相同位置的数据直接进行与、或、异或运算
    • 与: 图片整体会变暗,与运算会将值变小,不超过255
    • 或: 图片亮度会整体变量,或运算会将值变大,不超过255
  • 非: 与程序中按位取反不一样, OpenCV中实现的是对颜色的反转
    d s t = 255 − s r c dst = 255 - src dst=255src

在这里插入图片描述

二、翻转与旋转

1.翻转

# 翻转
# flip(src, flipCode[, dst]) -> dst
# flipCode = 0:垂直翻转
# flipCode < 0:垂直 + 水平翻转
# flipCode > 0:水平翻转
imgVertical = cv2.flip(img, 0)
imgVAndH = cv2.flip(img, -1)
imgHorizontal = cv2.flip(img, 1)
res = np.hstack((img, imgVertical, imgHorizontal, imgVAndH))

在这里插入图片描述

2.旋转

作用: 以图片为中心, 对图片进行旋转,角度只能为180, 顺时针90,逆时针90。

# 旋转
# rotate(src, rotateCode[, dst]) -> dst
# roteCode:cv2.ROTATE_
imgClockWise90 = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
imgCounterClockWise90 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
imgRotate180 = cv2.rotate(img, cv2.ROTATE_180)

在这里插入图片描述

注:翻转不会改变原来图片的np.ndarray.shape,旋转会改变。

三、仿射变换

1. 介绍

仿射变换是一种二维坐标变换方法,它能够将平面上的点进行线性变换平移,从而得到一个新的图形。常见的仿射变换包括缩放、旋转、平移、翻转等变换。

在仿射变换中,原图形的每个点都被映射到新的图形中,并按照一定的规则进行变换。这些规则由仿射矩阵来描述,仿射矩阵包括平移、旋转、缩放和剪裁等操作。

在计算机视觉和图像处理领域中,仿射变换被广泛应用于图像的变形、畸变校正、目标跟踪和图像配准等方面。

仿射变换中的一些性质保持不变:

  • 共线性: 若几个点在变换前在一条直线上,则变换后仍在一条直线上。
  • 平行性: 若某两条直线在变换前是平行的,则变换后仍是平行的。
  • 共线比例不变性: 变换前在一条线上的两条线段的比例在变换前后不变。

2. 变换的数学表达

2.1 平移

将原图的每一个像素坐标进行移动,其数学表达就为:
{ x = x c + t x y = y c + t y \left\{\begin{aligned} x = x_c + t_x \\y = y_c + t_y\end{aligned}\right. {x=xc+txy=yc+ty
( x c , y c ) (x_c, y_c) (xc,yc)为原图像素的坐标, ( x , y ) (x, y) (x,y)为变换后的像素坐标, t x , t y t_x, t_y tx,ty表示坐标x与坐标y移动的距离。将上面公式改写成矩阵形式:
[ x y ] = [ 1 0 0 1 ] [ x c y c ] + [ t x t y ] \begin{bmatrix} x\\ y\end{bmatrix}=\begin{bmatrix} 1 & 0\\ 0&1\end{bmatrix}\begin{bmatrix} x_c\\ y_c\end{bmatrix}+\begin{bmatrix} t_x\\ t_y\end{bmatrix} [xy]=[1001][xcyc]+[txty]

2.2 缩放

将原图的像素坐标进行缩放,其数学表达式就是:
{ x = x c ⋅ α x y = y c ⋅ α y \left\{\begin{aligned} x = x_c \cdot \alpha_x \\y = y_c \cdot \alpha_y\end{aligned}\right. {x=xcαxy=ycαy
其用矩阵可表示为:
[ x y ] = [ α x 0 0 α y ] [ x c y c ] \begin{bmatrix} x\\ y\end{bmatrix}=\begin{bmatrix} \alpha_x & 0\\ 0&\alpha_y\end{bmatrix}\begin{bmatrix} x_c\\ y_c\end{bmatrix} [xy]=[αx00αy][xcyc]

2.3 旋转

在这里插入图片描述
由几何关系可得方程组:
{ x = c o s ( α + θ ) ⋅ r y = s i n ( α + θ ) ⋅ r x c = c o s α ⋅ r y c = s i n α ⋅ r \left\{\begin{aligned} &x = cos(\alpha + \theta) \cdot r& \\ &y = sin(\alpha + \theta) \cdot r& \\ &x_c = cos\alpha \cdot r&\\ &y_c = sin\alpha \cdot r& \end{aligned}\right. x=cos(α+θ)ry=sin(α+θ)rxc=cosαryc=sinαr
化简解得:
{ x = x c ⋅ c o s θ − y c ⋅ s i n θ y = x c ⋅ s i n θ + y c ⋅ c o s θ \left\{\begin{aligned} &x = x_c \cdot cos\theta - y_c \cdot sin\theta& \\ &y = x_c \cdot sin\theta + y_c \cdot cos\theta\\\end{aligned}\right. {x=xccosθycsinθy=xcsinθ+yccosθ
矩阵形式:
[ x y ] = [ c o s θ − s i n θ s i n θ c o n θ ] [ x c y c ] \begin{bmatrix} x\\ y\end{bmatrix} = \begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & con\theta \end{bmatrix}\begin{bmatrix} x_c \\ y_c\end{bmatrix} [xy]=[cosθsinθsinθconθ][xcyc]

3. 变换矩阵

上一节中我们推导了平移、缩放以及旋转的数学表达公式并写了矩阵形式:
[ x y ] = [ 1 0 0 1 ] [ x c y c ] + [ t x t y ] \begin{bmatrix} x\\ y\end{bmatrix}=\begin{bmatrix} 1 & 0\\ 0&1\end{bmatrix}\begin{bmatrix} x_c\\ y_c\end{bmatrix}+\begin{bmatrix} t_x\\ t_y\end{bmatrix} [xy]=[1001][xcyc]+[txty]
[ x y ] = [ α x 0 0 α y ] [ x c y c ] \begin{bmatrix} x\\ y\end{bmatrix}=\begin{bmatrix} \alpha_x & 0\\ 0&\alpha_y\end{bmatrix}\begin{bmatrix} x_c\\ y_c\end{bmatrix} [xy]=[αx00αy][xcyc]
[ x y ] = [ c o s θ − s i n θ s i n θ c o n θ ] [ x c y c ] \begin{bmatrix} x\\ y\end{bmatrix} = \begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & con\theta \end{bmatrix}\begin{bmatrix} x_c \\ y_c\end{bmatrix} [xy]=[cosθsinθsinθconθ][xcyc]
从中可以看出缩放和旋转都能通过一个2×2的矩阵进行变换,而平移带有一个偏移项,为了能够统一变换矩阵形式,就可以将平移改写成为:
[ x y 1 ] = [ 1 0 t X 0 1 t X 0 0 1 ] [ x c y c 1 ] \begin{bmatrix} x \\ y \\ 1\end{bmatrix} = \begin{bmatrix} 1 & 0 & t_X \\ 0 & 1 & t_X \\0 & 0 & 1 \\\end{bmatrix}\begin{bmatrix} x_c \\ y_c \\ 1\end{bmatrix} xy1 = 100010tXtX1 xcyc1
同理,也可将旋转和缩放改写为3×3矩阵形式:
[ x y 1 ] = [ α x 0 0 0 α y 0 0 0 1 ] [ x c y c 1 ] \begin{bmatrix} x\\ y\\ 1\end{bmatrix}=\begin{bmatrix} \alpha_x & 0 & 0\\ 0&\alpha_y & 0 \\ 0 & 0 & 1\end{bmatrix}\begin{bmatrix} x_c\\ y_c\\ 1\end{bmatrix} xy1 = αx000αy0001 xcyc1
[ x y 1 ] = [ c o s θ − s i n θ 0 s i n θ c o s θ 0 0 0 1 ] [ x c y c 1 ] \begin{bmatrix} x\\ y \\ 1\end{bmatrix} = \begin{bmatrix} cos\theta & -sin\theta & 0 \\ sin\theta & cos\theta & 0\\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} x_c \\ y_c \\ 1\end{bmatrix} xy1 = cosθsinθ0sinθcosθ0001 xcyc1
综上,旋转、平移、缩放都可以通过一个3×3的变换矩阵实现坐标变换。该变换矩阵还能实现切变、对称以及翻转操作。

4. 变换叠加

上午所述变换都是实现图像的单步变换,通过变换矩阵就可以实现更便捷的多步变换。假设旋转的变换矩阵为R,平移的变换矩阵为M,缩放的变换矩阵为S,那么旋转、平移以及缩放的组合变换就能写为:
[ x y 1 ] = M k R j S i ⋅ ⋅ ⋅ M 1 R 1 S 1 [ x c y c 1 ] \begin{bmatrix} x\\ y \\ 1\end{bmatrix} = M_kR_jS_i \cdot \cdot \cdot M_1R_1S_1 \begin{bmatrix} x_c \\ y_c \\ 1\end{bmatrix} xy1 =MkRjSiM1R1S1 xcyc1
组合变换的变换矩阵顺序为从右往左, 这样就能首先计算出整体的变换矩阵,然后才进行像素的坐标变换。

5. 变换矩阵的逆推

逆推两张相同图片之间的仿射变换矩阵,最少需要3组对应像素的坐标点。

变换矩阵的形式可以表示为:
[ x y 1 ] = [ a 11 a 12 a 13 a 21 a 22 a 23 0 0 1 ] [ x c y c 1 ] \begin{bmatrix} x\\ y\\ 1\end{bmatrix}=\begin{bmatrix} a_{11} & a_{12} & a_{13}\\ a_{21}&a_{22} & a_{23} \\ 0 & 0 & 1\end{bmatrix}\begin{bmatrix} x_c\\ y_c\\ 1\end{bmatrix} xy1 = a11a210a12a220a13a231 xcyc1

总共6个未知数,至少需要6个方程才能解出,然而1对坐标点对应2个方程,因此最少需要3组对应坐标点才能求出变换矩阵。

6.OpenCV实现以及手动实现

6.1 手动实现

  • 平移实现:

    import cv2
    import numpy as np
    
    canvas = np.zeros(shape=(400, 400, 3), dtype=np.uint8)
    
    img = cv2.imread("F:\MyOpenCV\hello.jpg")
    img = cv2.resize(img, dsize=(200, 200))
    offsetH = (canvas.shape[0] - img.shape[0]) // 2
    offsetW = (canvas.shape[1] - img.shape[1]) // 2
    canvas[offsetH : offsetH + img.shape[0], offsetW : offsetW + img.shape[1]] = img
    
    
    def move(tx, ty, img):
        canvasNew = np.zeros(shape=(400, 400, 3), dtype=np.uint8)  #定义一个新的画布
        C = np.array([[1, 0, tx], [0, 1, ty], [0, 0, 1]])  #定义变换矩阵
        for xc in range(img.shape[0]): #遍历原画布每一个像素
            for yc in range(img.shape[1]):
                a = np.array([xc, yc, 1]) #构造向量
                (x, y, t) = C @ a #叉积
                if x >= 400 or y >= 400: #移除出的丢弃
                    continue
                canvasNew[x, y] = img[xc, yc]  #更新画布
        return canvasNew
    
    
    imgNew = move(200, 0, canvas)
    cv2.imshow("img", canvas)
    cv2.imshow("imgNew", imgNew)
    cv2.waitKey(0)
    
    

    执行结果:

    这里暂时不太清楚 为什么平移的方向与预想的相反。

    在这里插入图片描述

  • 旋转实现:

    这里仅实现饶左上角的逆时针旋转,即(0,0)点为旋转点。

    import cv2
    import numpy as np
    import math
    
    canvas = np.zeros(shape=(400, 400, 3), dtype=np.uint8)
    
    img = cv2.imread("F:\MyOpenCV\hello.jpg")
    img = cv2.resize(img, dsize=(200, 200))
    offsetH = (canvas.shape[0] - img.shape[0]) // 2
    offsetW = (canvas.shape[1] - img.shape[1]) // 2
    canvas[offsetH : offsetH + img.shape[0], offsetW : offsetW + img.shape[1]] = img
    
    
    def rotate(de, img):
        canvasNew = np.zeros(shape=(400, 400, 3), dtype=np.uint8)  # 定义一个新的画布
        C = np.array(
            [[math.cos(de), -math.sin(de), 0], [math.sin(de), math.cos(de), 0], [0, 0, 1]]
        )  # 定义变换矩阵
        for xc in range(img.shape[0]):  # 遍历原画布每一个像素
            for yc in range(img.shape[1]):
                a = np.array([xc, yc, 1])  # 构造向量
                (x, y, t) = C @ a  # 叉积
                if x >= 400 or y >= 400:  # 移除出的丢弃
                    continue
                canvasNew[int(x), int(y)] = img[xc, yc]  # 更新画布
        return canvasNew
    
    
    imgNew = rotate(math.pi / 6, canvas)
    cv2.imshow("img", canvas)
    cv2.imshow("imgNew", imgNew)
    cv2.waitKey(0)
    
    

    在这里插入图片描述

    疑惑:不太明白为啥会有黑点。。

  • 缩放实现:

    import cv2
    import numpy as np
    import math
    
    canvas = np.zeros(shape=(400, 400, 3), dtype=np.uint8)
    
    img = cv2.imread("F:\MyOpenCV\hello.jpg")
    img = cv2.resize(img, dsize=(200, 200))
    offsetH = (canvas.shape[0] - img.shape[0]) // 2
    offsetW = (canvas.shape[1] - img.shape[1]) // 2
    canvas[offsetH : offsetH + img.shape[0], offsetW : offsetW + img.shape[1]] = img
    
    
    def zoom(fx, fy, img):
        canvasNew = np.zeros(shape=(400, 400, 3), dtype=np.uint8)  # 定义一个新的画布
        C = np.array([[fx, 0, 0], [0, fy, 0], [0, 0, 1]])  # 定义变换矩阵
        for xc in range(img.shape[0]):  # 遍历原画布每一个像素
            for yc in range(img.shape[1]):
                a = np.array([xc, yc, 1])  # 构造向量
                (x, y, t) = C @ a  # 叉积
                if x >= 400 or y >= 400:  # 移除出的丢弃
                    continue
                canvasNew[int(x), int(y)] = img[xc, yc]  # 更新画布
        return canvasNew
    
    
    imgNew = rotate(1.5, 1.5, canvas)
    cv2.imshow("img", canvas)
    cv2.imshow("imgNew", imgNew)
    cv2.waitKey(0)
    
    

在这里插入图片描述

6.2OpenCV实现

import cv2
import numpy as np
import math

# 逆时针旋转转变换矩阵  用于产生旋转矩阵
# center,旋转中心
# angle,逆时针旋转角度
# scale,图片缩放值
# cv2.getRotationMatrix2D: (center: tuple, angle, scale) -> dst

# M :仿射变换矩阵
# dsize :输出图片的大小
# flags:图片的插值算法,默认算法就不错
# borderMode:查看 图像边界扩展 小节
# cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) -> dst

# 仿射变换矩阵逆推
# src:3x2 的 numpy.ndarray 矩阵,数据类型为 np.float
# dst:3x2 的 numpy.ndarray 矩阵,数据类型为 np.float
# cv2.getAffineTransform(src, dst) -> M

canvas = np.zeros(shape=(400, 400, 3), dtype=np.uint8)
img = cv2.imread("F:\MyOpenCV\hello.jpg")
img = cv2.resize(img, dsize=(200, 200))
offsetH = (canvas.shape[0] - img.shape[0]) // 2
offsetW = (canvas.shape[1] - img.shape[1]) // 2
canvas[offsetH : offsetH + img.shape[0], offsetW : offsetW + img.shape[1]] = img

dsize = (400, 400)


def move():
    C = np.array([[1, 0, 200], [0, 1, 0]], dtype=np.float32)  # 旋转矩阵  旋转矩阵为2乘3
    img = cv2.warpAffine(canvas, C, dsize)
    cv2.imshow("imgMove", img)


def rotate():
    de = 30
    C = cv2.getRotationMatrix2D((0, 0), de, scale=1)
    img = cv2.warpAffine(canvas, C, dsize)
    cv2.imshow("imgRotate", img)


def zoom():
    C = np.array([[1.5, 0, 0], [0, 1.5, 0]])  # 旋转矩阵
    img = cv2.warpAffine(canvas, C, dsize)
    cv2.imshow("imgZoom", img)


move()
rotate()
zoom()
cv2.waitKey(0)

在这里插入图片描述

7.向量空间----补充

考研线代学过向量空间 与这个有点相关在此总结一下。

名词解释及相关定理:

  • 向量空间:全体n维向量连同向量的加法和数乘运算合成为n维向量空间。
  • 如果向量空间V中的m个向量 α 1 , α 2 , ⋅ ⋅ ⋅ , α m \alpha_1,\alpha_2,\cdot\cdot\cdot,\alpha_m α1,α2,,αm满足
    1. α 1 , α 2 , ⋅ ⋅ ⋅ , α m \alpha_1,\alpha_2,\cdot\cdot\cdot,\alpha_m α1,α2,,αm线性无关;
    2. V中任意向量 β \beta β均可由向量组 α 1 , α 2 , ⋅ ⋅ ⋅ , α m \alpha_1,\alpha_2,\cdot\cdot\cdot,\alpha_m α1,α2,,αm线性表出,即 x 1 α 1 + x 2 α 2 + … + x m α m = β x_1\alpha_1 + x_2\alpha_2 + …+x_m\alpha_m = \beta x1α1+x2α2++xmαm=β则称 α 1 , α 2 , ⋅ ⋅ ⋅ , α m \alpha_1,\alpha_2,\cdot\cdot\cdot,\alpha_m α1,α2,,αm为向量空间V的一个基底(或称).基中所含向量的个数m称为向量空间V的维数.向量 β \beta β的表示系数 x 1 , x 2 , … , x m x_1,x_2,…,x_m x1,x2,,xm称为向量 β \beta β在基底 α 1 , α 2 , ⋅ ⋅ ⋅ , α m \alpha_1,\alpha_2,\cdot\cdot\cdot,\alpha_m α1,α2,,αm下的坐标。
  • 规范正交基:基向量中每两个向量之间相互正交,如直角坐标系的坐标轴可以取出规范正交基。
  • 如果一个基底能由另一个基底乘以一个矩阵表出,则称该矩阵为过渡矩阵
    [ β 1 , β 2 , … , β n ] = [ α 1 , α 2 , … , α n ] C [\beta_1,\beta_2,…,\beta_n] = [\alpha_1,\alpha_2,…,\alpha_n]C [β1,β2,,βn]=[α1,α2,,αn]C
    其中, C = [ c 11 c 12 … c 1 n c 21 c 22 … c 2 n ⋮ ⋮ ⋮ c n 1 c n 1 … c n n ] C = \begin{bmatrix} c_{11} & c_{12} & \dots & c_{1n}\\ c_{21} & c_{22} & \dots & c_{2n}\\ \vdots & \vdots & & \vdots \\ c_{n1} & c_{n1} & \dots &c_{nn}\end{bmatrix}_{} C= c11c21cn1c12c22cn1c1nc2ncnn

    注:过渡矩阵是对基向量做相应的变换,因此一定是一组基向量右乘过渡矩阵,且过渡矩阵一定可逆。

  • 如果向量 γ \gamma γ在基底 α 1 , α 2 , … , α n \alpha_1,\alpha_2,…,\alpha_n α1,α2,,αn的坐标为 x 1 , x 2 , … , x n x_1,x_2,\dots,x_n x1x2,,xn,向量 γ \gamma γ在基底 β ! , β 2 , … , β n \beta_!,\beta_2,\dots,\beta_n β!,β2,,βn的坐标为 y 1 , y 2 , … , y n y_1,y_2,\dots,y_n y1y2,,yn,则坐标变换公式为
    [ x 1 x 2 ⋮ x n ] = C [ y 1 y 2 ⋮ y n ] 或 [ y 1 y 2 ⋮ y n ] = C − 1 [ x 1 x 2 ⋮ x n ] 或 x = C y \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n\end{bmatrix}=C \begin{bmatrix}y_1 \\ y_2 \\ \vdots \\ y_n\end{bmatrix} 或\begin{bmatrix}y_1 \\ y_2 \\ \vdots \\ y_n\end{bmatrix}=C^{-1} \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n\end{bmatrix}或\mathbf{x} = C\mathbf{y} x1x2xn =C y1y2yn y1y2yn =C1 x1x2xn x=Cy

    注:向量(坐标)的变换一定是左乘矩阵,具体是乘过渡矩阵还是过渡矩阵的逆,需要看是哪个基向量到哪个基向量的过渡矩阵。

四、透视变换

1. 齐次坐标

[ x y 1 ] = [ a 11 a 12 a 13 a 21 a 22 a 23 0 0 1 ] [ x c y c 1 ] \begin{bmatrix} x\\ y\\ 1\end{bmatrix}=\begin{bmatrix} a_{11} & a_{12} & a_{13}\\ a_{21}&a_{22} & a_{23} \\ 0 & 0 & 1\end{bmatrix}\begin{bmatrix} x_c\\ y_c\\ 1\end{bmatrix} xy1 = a11a210a12a220a13a231 xcyc1
仿射变换中,用来表示二维像素位置的坐标为:
[ x y 1 ] \begin{bmatrix} x\\ y\\ 1\end{bmatrix} xy1
从形式上来说,这就是用了三维坐标来表示二维坐标。这种用n维坐标表达n-1维坐标就称为齐次坐标

2. 透视

透视本是一种画法技巧,通过在平面上画出三维物体来创造视觉上的深度感和立体感。透视的基本原理是,当人们观看物体时,视点和视线两者之间的距离会影响视觉体验。离视点和视线较近的物体看起来会比离得远的物体更大,更接近观察者。

而透视变换能够让一个倾斜的物体,以一个新的视角展现。

对于像素位置(x,y)只能表示像素在平面上的位置关系,此时齐次坐标就能排上用场了。三维的齐次坐标虽然表示的是二维的平面,但是其本质还是一个三维空间的坐标值,这样就能将图片像素由二维空间扩展到三维空间进行处理,齐次坐标的w分量也就有了新的含义:三维空间的深度
在这里插入图片描述
在仿射变换中,像素的齐次坐标为 [ x , y , 1 ] T [x, y, 1]^T [x,y,1]T,可以解释为图像位于三维空间的w=1平面上,即w=1的平面是我们在三维空间中的视线平面(三维空间中所有东西都被投影到w=1平面,然后我们才能看见,因为图片本身是个二维平面,每个像素点由行列坐标所定)。透视就规定了所有物体如何投影到视线平面上。数学描述就是根据像素三维空间中的坐标点 P ( x , y , w ) P(x, y, w) P(x,y,w)得出像素在视线平面上的坐标 P e ( x e , y e , 1 ) P_e(x_e, y_e, 1) Pe(xe,ye,1), 两个关系如图所示。由于 P 与 P e P与P_e PPe在同一条直线上根据直线公式:
x 2 − x 1 l = y 2 − y 1 m = z 1 − z 2 n = λ ,其中,( l , m , n )为直线的方向向量 \frac{x_2-x_1}{l} = \frac{y_2-y_1}{m} = \frac{z_1-z_2}{n} = \lambda,其中,(l, m, n)为直线的方向向量 lx2x1=my2y1=nz1z2=λ,其中,(l,m,n)为直线的方向向量可得:
{ x − 0 l = y − 0 m = w − 0 n = λ 1 x e − 0 l = y e − 0 m = 1 − 0 n = λ 2 \left\{\begin{aligned} &\frac{x-0}{l} = \frac{y-0}{m} = \frac{w-0}{n} = \lambda_1& \\&\frac{x_e-0}{l} = \frac{y_e-0}{m} = \frac{1-0}{n} =\lambda_2&\end{aligned}\right. lx0=my0=nw0=λ1lxe0=mye0=n10=λ2
化简得:
{ x e = x w y e = y w 1 = w w \left\{ \begin{aligned} x_e = \frac{x}{w}\\ y_e = \frac{y}{w}\\ 1 = \frac{w}{w} \end{aligned} \right. xe=wxye=wy1=ww
上述公式就实现了三维空间像素坐标向实现平面的透视投影。

3. 透视变换

[ x y w ] = [ a 11 a 12 a 13 a 21 a 22 a 23 a 31 a 32 a 33 ] [ x c y c 1 ] \begin{bmatrix} x\\ y\\ w\end{bmatrix}=\begin{bmatrix} a_{11} & a_{12} & a_{13}\\ a_{21}&a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33}\end{bmatrix}\begin{bmatrix} x_c\\ y_c\\ 1\end{bmatrix} xyw = a11a21a31a12a22a32a13a23a33 xcyc1
根据仿射变换可知, 上述矩阵就能实现图片像素坐标 [ x e , y e , 1 ] T [x_e, y_e, 1]^T [xe,ye,1]T在三维空间中的旋转,缩放、切变的变换操作(不能实现三维空间的平移操作,还记得仿射变换中平移操作至少是一个2乘3的矩阵吗,平移的变换矩阵比旋转、缩放这些多一个维度),得到像素位置变换后的三维坐标就为 [ x , y , w ] T [x, y, w]^T [x,y,w]T。再将信的像素坐标进行透视处理,将坐标映射到w=1平面,得到的像素位置就是最终透视变换的结果。

因此透视变换的变换矩阵就能改写为:
[ x ′ y ′ 1 ] = 1 w [ a 11 a 12 a 13 a 21 a 22 a 23 a 31 a 32 a 33 ] [ x c y c 1 ] \begin{bmatrix} {x}' \\ {y}'\\ 1\end{bmatrix}=\frac{1}{w}\begin{bmatrix} a_{11} & a_{12} & a_{13}\\ a_{21}&a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33}\end{bmatrix}\begin{bmatrix} x_c\\ y_c\\ 1\end{bmatrix} xy1 =w1 a11a21a31a12a22a32a13a23a33 xcyc1
由于w是一个常量,也可以放入变换矩阵:
[ x ′ y ′ 1 ] = [ b 11 b 12 b 13 b 21 b 22 b 23 b 31 b 32 b 33 ] [ x c y c 1 ] \begin{bmatrix} {x}' \\ {y}'\\ 1\end{bmatrix}=\begin{bmatrix} b_{11} & b_{12} & b_{13}\\ b_{21}&b_{22} & b_{23} \\ b_{31} & b_{32} & b_{33}\end{bmatrix}\begin{bmatrix} x_c\\ y_c\\ 1\end{bmatrix} xy1 = b11b21b31b12b22b32b13b23b33 xcyc1
写乘方程组形式:
{ x ′ = b 11 x c + b 12 y c + b 13 y ′ = b 21 x c + b 22 y c + b 23 1 = b 31 x c + b 32 y c + b 33 ⇒ { x ′ = b 11 x c + b 12 y c + b 13 b 31 x c + b 32 y c + b 33 y ′ = b 21 x c + b 22 y c + b 23 b 31 x c + b 32 y c + b 33 ⇒ 同乘一个非零常数 α { x ′ = α ( b 11 x c + b 12 y c + b 13 ) α ( b 31 x c + b 32 y c + b 33 ) y ′ = α ( b 21 x c + b 22 y c + b 23 ) α ( b 31 x c + b 32 y c + b 33 ) \left\{\begin{aligned}{x}' = b_{11}x_c + b_{12}y_c + b_{13} \\{y}' = b_{21}x_c + b_{22}y_c + b_{23} \\1 = b_{31}x_c + b_{32}y_c + b_{33} \\\end{aligned}\right.\Rightarrow\left\{\begin{aligned}{x}' = \frac{b_{11}x_c + b_{12}y_c + b_{13}}{b_{31}x_c + b_{32}y_c + b_{33}} \\{y}' = \frac{b_{21}x_c + b_{22}y_c + b_{23}}{b_{31}x_c + b_{32}y_c + b_{33}} \\\end{aligned}\right.\overset{\text{同乘一个非零常数}\alpha}{\Rightarrow}\left\{\begin{aligned}{x}' = \frac{\alpha(b_{11}x_c + b_{12}y_c + b_{13})}{\alpha(b_{31}x_c + b_{32}y_c + b_{33})} \\{y}' = \frac{\alpha(b_{21}x_c + b_{22}y_c + b_{23})}{\alpha(b_{31}x_c + b_{32}y_c + b_{33})} \\\end{aligned}\right. x=b11xc+b12yc+b13y=b21xc+b22yc+b231=b31xc+b32yc+b33 x=b31xc+b32yc+b33b11xc+b12yc+b13y=b31xc+b32yc+b33b21xc+b22yc+b23同乘一个非零常数α x=α(b31xc+b32yc+b33)α(b11xc+b12yc+b13)y=α(b31xc+b32yc+b33)α(b21xc+b22yc+b23)
再写成矩阵形式:
[ x ′ y ′ 1 ] = α [ b 11 b 12 b 13 b 21 b 22 b 23 b 31 b 32 b 33 ] [ x c y c 1 ] \begin{bmatrix} {x}' \\ {y}'\\ 1\end{bmatrix}=\alpha\begin{bmatrix} b_{11} & b_{12} & b_{13}\\ b_{21}&b_{22} & b_{23} \\ b_{31} & b_{32} & b_{33}\end{bmatrix}\begin{bmatrix} x_c\\ y_c\\ 1\end{bmatrix} xy1 =α b11b21b31b12b22b32b13b23b33 xcyc1
可以看出变换矩阵乘以一个非零常数,对结果毫无影响,不妨令 α = 1 b 33 \alpha = \frac{1}{b_{33}} α=b331其结果就为:
[ x ′ y ′ 1 ] = [ b 11 b 12 b 13 b 21 b 22 b 23 b 31 b 32 1 ] [ x c y c 1 ] \begin{bmatrix} {x}' \\ {y}'\\ 1\end{bmatrix}=\begin{bmatrix} b_{11} & b_{12} & b_{13}\\ b_{21}&b_{22} & b_{23} \\ b_{31} & b_{32} &1\end{bmatrix}\begin{bmatrix} x_c\\ y_c\\ 1\end{bmatrix} xy1 = b11b21b31b12b22b32b13b231 xcyc1

可以看出两张图片之间的透视变换,只涉及8个自由度,因此至少需要3对变换前后的坐标点,才能求出透视变换矩阵。

从最后公式也可看出,仿射变换是透视变换的一种特例。

4. 透视变换的逆推

虽然上述矩阵中每一对像素能产生3个方程,但是它们之间并不是相互独立的,第三个方程可以带入前两个中,因此有效的只有两个(具体为啥我还不太清楚)。
{ x ′ = b 11 x c + b 12 y c + b 13 b 31 x c + b 32 y c + b 33 y ′ = b 21 x c + b 22 y c + b 23 b 31 x c + b 32 y c + b 33 \left\{\begin{aligned}{x}' = \frac{b_{11}x_c + b_{12}y_c + b_{13}}{b_{31}x_c + b_{32}y_c + b_{33}} \\{y}' = \frac{b_{21}x_c + b_{22}y_c + b_{23}}{b_{31}x_c + b_{32}y_c + b_{33}} \\\end{aligned}\right. x=b31xc+b32yc+b33b11xc+b12yc+b13y=b31xc+b32yc+b33b21xc+b22yc+b23
综上,逆向求解变换矩阵需要4对像素坐标。

5. OpenCV代码

# 逆向计算透视变换矩阵
# srcPoints : 原像素点坐标
# dstPoints : 转换后像素点坐标
# solveMethod :是选择计算矩阵的方法
cv2.getPerspectiveTransform(srcPoints:np.ndarray, dstPoints:np.ndarray[, solveMethod]) -> retval

# 透视变换
# src :图片
# M :透视变换矩阵 3x3
# dsize : 要显示的图片大小
cv2.warpPerspective(src:image, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) -> dst:image

注意,需要保证这四个点满足以下条件:

  • 任意三个点不共线(non-collinear);
  • 包含四个点的直线不相交;

示例:

import cv2
import numpy as np
import math

img = cv2.imread("./image/book.jpg")
img = cv2.resize(img, (0, 0), fx=0.2, fy=0.2, interpolation=cv2.INTER_AREA)

# 这个我手动获取了书的4个角的坐标 (155,142) (321,143) (421, 320) (228,340)
# 顺序是 左上  右上  右下  左下
srcPoints = np.array([[155, 142], [321, 143], [421, 320], [228, 340]], dtype=np.float32)
w = max(
    math.sqrt((321 - 155) ** 2 + (143 - 142) ** 2),
    math.sqrt((228 - 421) ** 2 + (340 - 320) ** 2),
)  # 取变换后的宽
h = max(
    math.sqrt((155 - 228) ** 2 + (142 - 340) ** 2),
    math.sqrt((321 - 421) ** 2 + (143 - 320) ** 2),
)  # 取变换后的高
dstPoints = np.array([[0, 0], [w - 1, 0], [w - 1, h - 1], [0, h - 1]], dtype=np.float32)

M = cv2.getPerspectiveTransform(srcPoints, dstPoints)

res = cv2.warpPerspective(img, M, (int(w), int(h)))

cv2.imshow("img", img)
cv2.imshow("res", res)
cv2.waitKey(0)

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

五、图像边界填充

作用: 当图像需要变大,但是不想直接进行缩放时,则可以选择不同的方法将图像外围进行扩展,使得原图变大。

dst = cv2.copyMakeBorder(src, top, bottom, left, right, borderType[, dst[, value]])

其中:

  • src是输入的原始图像,可以是灰度图像或彩色图像。
  • topbottomleftright是指定要添加的边框的上、下、左、右边界的像素个数。
  • borderType是指定要添加的边框类型的标志。常用的有:
    • cv2.BORDER_CONSTANT:添加一个常数值的边框。
    • cv2.BORDER_REPLICATE:复制最边缘的像素值,直到边框大小达到指定的大小。
    • cv2.BORDER_REFLECT:对边缘像素进行反射,两边反射。dcba|abcd|dcba
      • cv2.BORDER_REFLECT101:对边缘像素进行反射,以边缘为轴。abc|cba
    • cv2.BORDER_WRAP:外包装法,abcd|abcd|abcd
  • dst(可选)是输出的图像,如果不为None,则需要确保大小和类型正确,否则会抛出错误。
  • value(可选)是当borderTypecv2.BORDER_CONSTANT时给定的常数值。

总结

本章主要讲述了图像的基本运算、图像的翻转与旋转、仿射变换、透视变换以及边界扩充,不仅描述了其原理,也进行了形象的表达,加深了自己对图像的一些操作。

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

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

相关文章

人脉变现小程序裂变定制开发

人脉变现小程序裂变定制开发可以提供以下服务&#xff1a; 需求分析&#xff1a;了解客户的业务需求和目标&#xff0c;并为其量身定制开发方案&#xff0c;提供专业的建议和意见。 UI/UX设计&#xff1a;根据客户的品牌形象和用户体验要求&#xff0c;设计出符合客户需…

简单实现基于UDP与TCP的回显服务器

目录 前言UDP 版的回显服务器需要用到的 api服务端客户端UDP 版本的字典客户端和字典服务器 TCP 版的回显服务器需要用到的 api服务器客户端对服务器进行改进(使用线程池)TCP 版本的字典客户端和字典服务器 前言 我们写网络程序, 主要编写的是应用层代码. 真正要发送这个数据,…

浅谈联网汽车安全漏洞

​“智能网联汽车存在内生共性问题&#xff0c;即软硬件的漏洞后门&#xff0c;基于此进行的网络攻击可以直接带来勒索、盗窃、大规模车辆恶意操控风险&#xff0c;还有数据泄露等网络安全事件。如果内生的漏洞后门问题不解决&#xff0c;系统自身难保&#xff0c;很难谈系统安…

浙大数据结构第四周之二叉搜索树与平衡二叉搜索树(AVL)

题目详情&#xff1a;04-树4 是否同一棵二叉搜索树 给定一个插入序列就可以唯一确定一棵二叉搜索树。然而&#xff0c;一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{2, 1, 3}和{2, 3, 1}插入初始为空的二叉搜索树&#xff0c;都得到一样的结果。于是…

【Python系列】一个简单的抽奖小程序

序言 很开心你能在万千博文中打开这一篇&#xff0c;希望能给你带来一定的帮助&#xff01;&#x1f44d;&#x1f3fb; 如果有什么问题&#xff0c;都可以添加下方我的联系方式&#xff0c;联系我噢~&#x1f601; ⭐️⭐️⭐️⭐️⭐️沟通交流&#xff0c;一起成为技术达人&…

SpringMVC底层原理源码解析

SpringMVC的作用毋庸置疑&#xff0c;虽然我们现在都是用SpringBoot&#xff0c;但是SpringBoot中仍然是在使用SpringMVC来处理请求。 我们在使用SpringMVC时&#xff0c;传统的方式是通过定义web.xml&#xff0c;比如&#xff1a; <web-app><servlet><servle…

jenkins配置笔记

文章目录 1.装Gogs插件2.配置截图及说明3.其他1.装Gogs插件 Gogs 插件是一个专门为集成 Gogs(一种类似于 Git 的开源版本控制系统)与 Jenkins 进行交互的插件。它允许 Jenkins 与 Gogs 之间进行双向通信,提高了 Jenkins 对于代码管理和构建的支持。 2.配置截图及说明 目前…

【操作系统】第二章进程与线程、处理机调度与调度算法

文章目录 &#x1f337; 一、进程的定义、组成、组织方式及特征⭐️ 1、 进程的定义⭐️ 2、 进程实体的组成⭐️ 3、 进程的组织⭐️ 4、 进程的特征 &#x1f337; 二、进程的状态与转换⭐️ 1、 状态⭐️ 2、 进程状态间的转换 &#x1f337; 三、进程控制⭐️ 1、 定义⭐️…

SwiftUI 新 Alert 弹出窗口圆你文本输入之梦

概览 小伙伴们都知道&#xff0c;弹出 Alert 不能包含文本输入框是 SwiftUI 的阿喀琉斯之踵(Achilles’ Heel) 。当然&#xff0c;这说的有些夸张了。&#x1f609; 不过&#xff0c;Alert 不能包含 TextField 在某些情况下着实不方便。于是乎&#xff0c;从 SwiftUI 3.0&…

JAVA模拟堆

堆的性质 堆是一种特殊的树。 只要满足以下两点&#xff0c;它就是一个堆&#xff1a; 堆是一个完全二叉树。堆中每一个节点的值都必须大于等于&#xff08;或小于等于&#xff09;其子树中每个节点的值。 第一点&#xff0c;堆必须是一个完全二叉树。完全二叉树要求&#…

O2OA (翱途) 平台 V8.0 发布新增数据台账能力

亲爱的小伙伴们&#xff0c;O2OA (翱途) 平台开发团队经过几个月的持续努力&#xff0c;实现功能的新增、优化以及问题的修复。2023 年度 V8.0 版本已正式发布。欢迎大家到 O2OA 的官网上下载进行体验&#xff0c;也希望大家在藕粉社区里多提宝贵建议。本篇我们先为大家介绍应用…

Android 输入系统

概述 Android 输入系统的工作原理概括来说&#xff0c;内核将原始事件写入到设备节点中&#xff0c;InputReader 不断地通过 EventHub 将原始事件取出来并翻译加工成 Android 输入事件&#xff0c;然后交给 InputDispatcher。 InputDispatcher 根据 WMS 提供的窗口信息将事件…

24.Stream流

Stream流 一、什么是Stream流 Stream流操作是Java 8提供一个重要新特性&#xff0c;它允许开发人员以声明性方式处理集合&#xff0c;其核心类库主要改进了对集合类的 API和新增Stream操作。Stream类中每一个方法都对应集合上的一种操作。将真正的函数式编程引入到Java中&…

2023全栈开发人员职业路线图

0. 全栈开发人员职业路线图 全栈开发人员是IT行业中薪资最高的职业之一。 如果您想成为一名全栈开发人员&#xff0c;以下是2023年全栈开发人员路线图上的十一个步骤&#xff1a; 掌握敏捷开发和Scrum学习浏览器技术&#xff0c;如HTML和CSS熟练掌握JavaScript或TypeScript了…

单月涨粉303.72w,反差感才是主流吗?

据新抖「直播带货风向」数据显示&#xff0c;抖音4月的直播商品数量达到1021.32w&#xff0c;较上月的695.91w环比增长50.18%&#xff0c;直播销量环比增加16.81%。从这几个数值就可以看出4月的抖音电商依旧如火如荼...... 那么&#xff0c;4月&#xff0c;抖音又出现哪些新的看…

“世界中医药之都” 亳州市医保局领导一行莅临万民健康交流指导

为进一步推进智慧医疗、智慧服务、智慧管理“三位一体”为主旨的“智慧中医、健康社区”项目建设。2023 年 5 月 3 日&#xff0c;“世界中医药之都” 亳州市医保局 局长 吴旭春 、 医保中心主任秦克靖 、 办公室主任徐伟 等一行 5 人莅临 万民健康交流 指导工作 &#xff0c…

day27_mysql

今日内容 零、 复习昨日 一、单表查询 二、多表联查 零、 复习昨日 1 DDL,DML,DQL是啥 DDL 数据定义语言&#xff08;库&#xff0c;表&#xff0c;列&#xff09;DML 数据操作语言&#xff08;表内数据的操作增删改&#xff09;DQL 数据查询语言&#xff08;表内数据的查询&am…

酷游浅谈网站Javas cript型别

最近整理了一下&#xff0c;【酷游娜娜手机&#x1d54d;找看看nay3989提供】就决定跟大家讨论一下最近对于Javascripet的型别认识。 弱型别&#xff36;&#xff33; 强型别 Javascripet是一种「弱型别」的语言&#xff0c;所以会产生很多你意想不到恶心的事情 至于什么是弱…

软件测试、测试和开发、测试和调试【详细介绍】

目录 一、什么是软件测试 1.软件测试的定义 2.软件测试的目的 3.软件测试的不可穷性 二、开发和测试的区别 三、测试和调试的区别 一、什么是软件测试 在日常生活中&#xff0c;测试是无处不在的。比如新买的手机是否好用、新买的衣服穿着是否合身等等场景&#xff0c;均…

点成案例丨细胞培养芯片用于构建肠模型实例分享

器官芯片是一种利用微芯片制造技术制造的微流体细胞培养设备。该设备包含多个连续灌注腔室&#xff0c;具有多细胞层结构、组织界面、物理化学微环境以及人体血管循环等特征&#xff0c;可以模拟和重构人体器官的生理功能&#xff0c;为相关研究提供了可靠的平台。器官芯片技术…