OpenCV练习(2)图像校正

  • 1、傅里叶变换 + 霍夫变换+ 直线 + 角度 + 旋转
  • 2、边缘检测 + 霍夫变换 + 直线+角度 + 旋转
  • 3、四点透视 + 角度 + 旋转
  • 4、检测矩形轮廓 + 角度 + 旋转

1.目的

实现类似全能扫面王的图像校正功能

2. 基于轮廓提取和透射变换

基于轮廓提取和透射变换的矫正算法更适用于车牌身份证人民币书本、 发票一类矩形形状而且边界明显的物体矫正。所校正的内容必须要有清晰的完整轮廓。

基本步骤:

  1. 图片灰度化
  2. 二值化/canny边缘检测等操作
  3. 检测轮廓,并筛选出目标轮廓(通过横纵比或面积去除干扰轮廓)
  4. 获取图像顶点
  5. 透视变换

代码:

import cv2
import numpy as np

def contour_to_rect(contour):
    pts = contour.reshape(4, 2)
    print(pts)
    rect = np.zeros((4, 2), dtype = "float32")
    # top-left point has the smallest sum
    # bottom-right has the largest sum
    s = pts.sum(axis = 1)
#     print(s)
    rect[0] = pts[np.argmin(s)]
#     print(pts[np.argmin(s)])
    rect[2] = pts[np.argmax(s)]
    # compute the difference between the points:
    # the top-right will have the minumum difference
    # the bottom-left will have the maximum difference
    diff = np.diff(pts, axis = 1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]
    return rect

# approximate the contour by a more primitive polygon shape
def approximate_contour(contour):
    peri = cv2.arcLength(contour, True)
    return cv2.approxPolyDP(contour, 0.032 * peri, True)

# 获取顶点坐标
def get_receipt_contour(contours):
    # loop over the contours
    for c in contours:
        approx = approximate_contour(c)
        # if our approximated contour has four points, we can assume it is receipt's rectangle
        if len(approx) == 4:
            return approx


def wrap_perspective(img, rect):
    # unpack rectangle points: top left, top right, bottom right, bottom left
    (tl, tr, br, bl) = rect
    # compute the width of the new image
    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))
    # compute the height of the new image
    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))
    # take the maximum of the width and height values to reach
    # our final dimensions
    maxWidth = max(int(widthA), int(widthB))
    maxHeight = max(int(heightA), int(heightB))
    # destination points which will be used to map the screen to a "scanned" view
    dst = np.array([
        [0, 0],
        [maxWidth - 1, 0],
        [maxWidth - 1, maxHeight - 1],
        [0, maxHeight - 1]], dtype = "float32")
    # calculate the perspective transform matrix
    M = cv2.getPerspectiveTransform(rect, dst)
    # warp the perspective to grab the screen
    return cv2.warpPerspective(img, M, (maxWidth, maxHeight))


if __name__ == "__main__":
    img = cv2.imread("./images/1.png")
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 用高斯滤波处理原图像降噪
    blur = cv2.GaussianBlur(gray, (5, 5), 0)
	#canny边缘检测(仅针对这次的输入图片)
    edged = cv2.Canny(blur, 50, 150)
    contours, h = cv2.findContours(edged.copy(), mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)
    # img_contours = cv2.drawContours(img.copy(), contours, -1, (0, 0, 255), 3)

    largest_contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5]
    # img_largest_contours = cv2.drawContours( img.copy(), largest_contours, -1, (0, 0, 255), 3)

    receipt_contour = get_receipt_contour(largest_contours)
    # img_receipt_contour = cv2.drawContours(img.copy(), [receipt_contour], -1, (0, 0, 255), 3)

    ori_img = img.copy()
    for coor in contour_to_rect(receipt_contour):
        cv2.circle(ori_img, (int(coor[0]), int(coor[1])), 1, (0, 0, 255), 4)
	# 进行透视变换
    scanned = wrap_perspective(img.copy(), contour_to_rect(receipt_contour))
2.1 图像灰度化 

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

blur = cv2.GaussianBlur(gray, (5, 5), 0)(如果有噪声可以加个高斯滤波)

2.2 canny边缘检测

edged = cv2.Canny(blur, 50, 150) (三个参数分别输入图像,最小阈值,最大阈值)

下图中左为输出,右为边缘检测结果。看起来效果还行

左为所有轮廓绘制,右为面积前五的绘制。可以看到,书中长方形的框并不在检测中,这是因为在进行边缘检测时色差不大导致没检测出来,后续使用闭运算处理将连断出填充完。

 

 但是对于,图像边框,或者说前景与背景色差不大时,很难检测出来,如下图放在白色桌面的安装指南

 

2.3 检测轮廓,并筛选出目标轮廓(通过横纵比或面积去除干扰轮廓) 

 contours, h = cv2.findContours(edged.copy(), mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)
  img_contours = cv2.drawContours(img.copy(), contours, -1, (0, 0, 255), 3)

检测图像轮廓并绘制所有的轮廓

# approximate the contour by a more primitive polygon shape
def approximate_contour(contour):
    peri = cv2.arcLength(contour, True)
    return cv2.approxPolyDP(contour, 0.032 * peri, True)

# 获取顶点坐标
def get_receipt_contour(contours):
    # loop over the contours
    for c in contours:
        approx = approximate_contour(c)
        # if our approximated contour has four points, we can assume it is receipt's rectangle
        if len(approx) == 4:
            return approx

在这个实现中,approximate_contour函数使用cv2.approxPolyDP来近似轮廓。cv2.approxPolyDP的第二个参数是精度参数,它是一个距离值,表示近似轮廓与原始轮廓之间的最大距离。这个值是原始轮廓周长的百分比,通常设置为一个很小的值(例如0.02)。第三个参数True表示近似出的多边形是闭合的。

get_receipt_contour函数然后使用这个近似函数来寻找一个四边形的轮廓,假设这个四边形是收据的形状。如果找到了一个有四个顶点的轮廓,它就返回这个轮廓。如果没有找到,函数将返回None

请注意,这个函数假设收据是一个四边形,并且是轮廓列表中面积最大的四边形。在实际应用中,可能需要根据收据的具体形状和图像中的其他元素来调整这个函数。

 2.4 获取图像顶点            

    receipt_contour = get_receipt_contour(largest_contours)
    img_receipt_contour = cv2.drawContours(img.copy(), [receipt_contour], -1, (0, 0, 255), 3)

def contour_to_rect(contour):
    pts = contour.reshape(4, 2)
    print(pts)
    rect = np.zeros((4, 2), dtype = "float32")
    # top-left point has the smallest sum
    # bottom-right has the largest sum
    s = pts.sum(axis = 1)
#     print(s)
    rect[0] = pts[np.argmin(s)]
#     print(pts[np.argmin(s)])
    rect[2] = pts[np.argmax(s)]
    # compute the difference between the points:
    # the top-right will have the minumum difference
    # the bottom-left will have the maximum difference
    diff = np.diff(pts, axis = 1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]
    return rect

在您提供的contour_to_rect函数中,目标是将以四个顶点形式给出的轮廓转换为一个矩形,这个矩形以特定的顺序排列顶点,通常是顺时针或逆时针方向,从左上角开始。然而,在您提供的代码中存在一些问题,包括一个拼写错误和一个可能的逻辑错误。我将为您纠正这些问题,并提供一个完整的函数,该函数将返回一个按正确顺序排列顶点的矩形。

ori_img = img.copy() for coor in contour_to_rect(receipt_contour): cv2.circle(ori_img, (int(coor[0]), int(coor[1])), 1, (0, 0, 255), 4) 

将筛选的四个点在图像中标注

 

2.5 透视变换

scanned = wrap_perspective(img.copy(), contour_to_rect(receipt_contour))
def wrap_perspective(img, rect):
    # unpack rectangle points: top left, top right, bottom right, bottom left
    (tl, tr, br, bl) = rect
    # compute the width of the new image
    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))
    # compute the height of the new image
    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))
    # take the maximum of the width and height values to reach
    # our final dimensions
    maxWidth = max(int(widthA), int(widthB))
    maxHeight = max(int(heightA), int(heightB))
    # destination points which will be used to map the screen to a "scanned" view
    dst = np.array([
        [0, 0],
        [maxWidth - 1, 0],
        [maxWidth - 1, maxHeight - 1],
        [0, maxHeight - 1]], dtype = "float32")
    # calculate the perspective transform matrix
    M = cv2.getPerspectiveTransform(rect, dst)
    # warp the perspective to grab the screen
    return cv2.warpPerspective(img, M, (maxWidth, maxHeight))

 

您提供的wrap_perspective函数是用于执行透视变换的,这种变换通常用于将图像中的四边形区域(如收据、文档等)转换为俯视图,即“扫描”视图。这个函数接受两个参数:要变换的图像img和一个表示四边形顶点的numpy数组rect

以下是函数中每一步的解释:

  1. 解包四边形顶点:将四边形的顶点解包为tl(左上角)、tr(右上角)、br(右下角)和bl(左下角)。

  2. 计算新图像的宽度:通过计算对角线brbltrtl的距离来得到新图像的潜在宽度。使用欧几里得距离公式sqrt((x2 - x1)^2 + (y2 - y1)^2)

  3. 计算新图像的高度:同样,通过计算对角线trbrtlbl的距离来得到新图像的潜在高度。

  4. 确定最终尺寸:通过取宽度 和高度的最大值来确保四边形在变换后不会被裁剪。

  5. 定义目标点:创建一个目标点数组dst,这些点定义了变换后的四边形的顶点位置。这些点将图像映射到一个矩形的“扫描”视图。

  6. 计算透视变换矩阵:使用cv2.getPerspectiveTransform函数计算从原始四边形到目标矩形的透视变换矩阵M

  7. 执行透视变换:使用cv2.warpPerspective函数应用透视变换到图像img上,得到变换后的图像。

 

3. 基于霍夫直线探测和仿射变换

基于霍夫直线探测的矫正算法更适用于文本类等无明显边界图像的矫正。图像画面必须要干净

算法步骤

  1. 霍夫线变换探测出图像中的所有直线。
  2. 计算出每条直线的倾斜角,求他们的平均值。
  3. 根据倾斜角旋转矫正。
  4. 最后根据文本尺寸裁剪图片。

代码:

import cv2
import numpy as np


img = cv2.imread("./images/4.jpg")
src = img.copy()
gray = cv2.cvtColor(src, cv2.COLOR_RGB2GRAY);
edged = cv2.Canny(src, 50, 200, apertureSize=3);

# 第5个参数就是阈值,阈值越大,检测精度越高
plines = cv2.HoughLines(edged, 1, np.pi / 180, 200);
if plines is not None:
    plines = plines.reshape(-1, 2)
    print(len(plines), "lines detected")

    sum_theta = 0.0
    for rho, theta in plines:
        # rho, theta = line[0]
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a * rho
        y0 = b * rho
        x1 = int(x0 + 1000*(-b))
        y1 = int(y0 + 1000*(a))
        x2 = int(x0 - 1000*(-b))
        y2 = int(y0 - 1000*(a))
        sum_theta += theta
        # 绘制直线
        cv2.line(src, (x1, y1), (x2, y2), (0, 0, 255), 2)

# 计算平均角度
average_theta = sum_theta / len(plines)
angle = np.degrees(average_theta) - 90

# 计算旋转中心
center = (float(img.shape[1] / 2.0), float(img.shape[0] / 2.0))
# 计算对角线长度作为旋转后图像的尺寸
length = int(np.sqrt(img.shape[0]**2 + img.shape[1]**2))
# 计算旋转矩阵
M = cv2.getRotationMatrix2D(center, angle, 1)
# 进行仿射变换并填充背景色为白色
src_rotate = cv2.warpAffine(img, M, (length, length), borderValue=(255, 255, 255))
3.1 图像预处理

在此不赘述,根据实际情况来

3.2 霍夫线变换

# 第5个参数就是阈值,阈值越大,检测精度越高
plines = cv2.HoughLines(edged, 1, np.pi / 180, 200);

在OpenCV中,cv2.HoughLines函数用于检测图像中的直线。它使用霍夫变换(Hough Transform)来找到图像中直线的参数,然后将这些参数转换为图像中的直线。

您提供的代码片段plines = cv2.HoughLines(edged, 1, np.pi / 180, 200);是使用霍夫变换在经过Canny边缘检测的图像edged中检测直线的例子。

这里的参数解释如下:

  • edged:这是已经通过Canny边缘检测得到的图像。
  • 1:这是霍夫变换中使用的rho(极径)参数。它定义了检测直线时的搜索范围。较大的值会使检测到的直线更加稀疏,而较小的值则可能导致检测到的直线更加密集。
  • np.pi / 180:这是霍夫变换中使用的theta(角度)参数。它定义了检测直线时的角度范围。这里使用的是单位圆上的角度,即0到π弧度。
  • 200:这是霍夫变换中使用的阈值参数。它定义了直线检测的灵敏度。较大的值会使检测到的直线更加稀疏,而较小的值则可能导致检测到的直线更加密集。


if plines is not None:
    plines = plines.reshape(-1, 2)
    print(len(plines), "lines detected")

在OpenCV中,cv2.HoughLines函数返回一个包含检测到的直线的数组,如果没有任何直线被检测到,这个数组将为None。这种情况可能发生在以下几种情况下:

  1. 图像噪声:如果图像中的噪声非常强烈,可能会导致无法检测到任何直线。

  2. 参数设置cv2.HoughLines函数的参数设置(如阈值、角度范围等)可能不适合当前图像。如果参数设置不当,可能无法检测到任何直线。

  3. 图像内容:如果图像中没有明显的直线结构,例如在复杂的自然场景中,可能会检测不到直线。

  4. 图像大小:如果图像非常小,可能没有足够的空间来检测直线。

  5. 图像分辨率:如果图像的分辨率非常低,可能会导致检测到的直线不够精确。

  6. 图像方向:如果图像的方向与霍夫变换的搜索方向不匹配,可能会检测不到直线。

在实际应用中,您可能需要根据图像的具体内容和噪声水平来调整cv2.HoughLines函数的参数,以提高检测直线的效果。

    sum_theta = 0.0
    for rho, theta in plines:
        # rho, theta = line[0]
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a * rho
        y0 = b * rho
        x1 = int(x0 + 1000*(-b))
        y1 = int(y0 + 1000*(a))
        x2 = int(x0 - 1000*(-b))
        y2 = int(y0 - 1000*(a))
        sum_theta += theta
        # 绘制直线
        cv2.line(src, (x1, y1), (x2, y2), (0, 0, 255), 2)

在您提供的代码片段中,您正在遍历通过霍夫变换检测到的直线,并将这些直线绘制在原始图像src上。每个直线由两个点定义,这两个点是根据极径rho和角度theta计算出来的。

代码中的关键步骤如下:

  1. 遍历plines数组,其中每个元素是一个包含两个值rhotheta的元组。rho是直线与原点之间的距离,而theta是从x轴正向到直线方向的角度(以弧度为单位)。

  2. 对于每个元素,您首先提取rhotheta值。

  3. 使用np.cos(theta)np.sin(theta)计算直线斜率ab

  4. 使用a * rhob * rho计算直线上的两个点的x和y坐标。这些坐标是通过将rho值乘以直线的方向向量(由ab定义)得到的。

  5. 使用int(x0 + 1000*(-b))int(y0 + 1000*(a))计算第一个点的坐标,其中1000是一个常数,用于确保坐标在图像范围内。

  6. 使用int(x0 - 1000*(-b))int(y0 - 1000*(a))计算第二个点的坐标。

  7. 使用cv2.line函数在原始图像src上绘制从第一个点到第二个点的直线,颜色为红色,线宽为2。

  8. 将角度theta累加到变量sum_theta中。

 

经过自己的多次实验,发现霍夫线变换的弧度转化为角度时,是以Y轴正半轴为0°,顺时针计算角度。并且在仿射变换进行旋转时,如果给的是正数,就会逆时针旋转,如果给的是负数,就会顺时针旋转 

 

3.3 旋转角度和旋转中心

# 计算平均角度
average_theta = sum_theta / len(plines)
angle = np.degrees(average_theta) - 90

在OpenCV中,cv2.HoughLines函数返回的角度参数是以弧度为单位的。这个函数使用霍夫变换来检测图像中的直线,而霍夫变换中使用的角度参数是弧度制的。

霍夫变换是一种在图像中检测直线的方法,它将图像中的每个点转换为一个参数空间中的一个点,其中参数空间是一个笛卡尔坐标系,x轴和y轴分别对应于图像中的直线的斜率和截距。在霍夫变换中,θ是直线的倾斜角,以弧度为单位。

因此,当您从cv2.HoughLines函数中获取θ值时,这些值是以弧度为单位的。如果您需要将它们转换为角度,可以使用NumPy的np.degrees函数来进行转换。

请注意,弧度与度的转换关系是:

1 弧度=180​ /Π 度

# 计算旋转中心
center = (float(img.shape[1] / 2.0), float(img.shape[0] / 2.0))
# 计算对角线长度作为旋转后图像的尺寸
length = int(np.sqrt(img.shape[0]**2 + img.shape[1]**2))

3.4 旋转矩阵

# 计算旋转矩阵

M = cv2.getRotationMatrix2D(center, angle, 1)

3.5 仿射变换

# 进行仿射变换并填充背景色为白色

src_rotate = cv2.warpAffine(img, M, (length, length), borderValue=(255, 255, 255))

 

4. 基于最小外接矩形和仿射变换

有明显的单一外接矩形的图像,最好是文本。

算法步骤:

  1. 灰度化以及预处理
  2. 二值化/边缘检测
  3. 最小外接矩形
  4. 旋转中心、旋转角度以及旋转矩阵
  5. 仿射变换

代码:

import numpy as np
import cv2 as cv


def binary(img):
    gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    ret,binary = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV|cv.THRESH_OTSU)
    return binary

def  Transformation(img,src):
    coords=np.column_stack(np.where(img>0))
    print(coords)
    print(coords.shape)
    angle =cv.minAreaRect(coords)[-1]    #最小外接矩形

    print(angle)
    if angle<-45:
        angle = -(90+angle)
    else:
        angle=-angle
    center = (w//2,h//2)
    M= cv.getRotationMatrix2D(center,angle,1.0) #传入中心和角度 得到旋转矩形
    rotated = cv.warpAffine(src,M,(w,h),flags=cv.INTER_CUBIC,borderMode=cv.BORDER_REPLICATE)
    cv.putText(rotated,'Angle:{:.2f} degrees'.format(angle),(10,30),cv.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2)#绘制文字
    cv.imshow('Rotated',rotated)


src= cv.imread('imageTextR.png')
(h,w)=src.shape[:2]
gray=cv.cvtColor(src,cv.COLOR_BGR2GRAY)
binary=binary(src)
Transformation(binary,src)
cv.imshow('src',src)
cv.imshow('binary',binary)
cv.waitKey(0)



4.1 灰度化以及预处理

不多赘述

4.2 二值化
ret,binary = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV|cv.THRESH_OTSU)

在OpenCV中,cv2.threshold函数用于将图像转换为二值图像。您提供的代码片段ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)是使用cv2.threshold函数对灰度图像gray进行阈值处理,以创建一个二值图像binary

这里的参数解释如下:

  • gray:这是您要进行阈值处理的灰度图像。
  • 0:这是阈值处理的下限值。所有小于这个值的像素将被设置为0(通常是黑色)。
  • 255:这是阈值处理的上限值。所有大于这个值的像素将被设置为255(通常是白色)。
  • cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU:这是阈值处理的方法。cv2.THRESH_BINARY_INV表示使用二值反转阈值,即小于阈值的像素将被设置为255(白色),大于阈值的像素将被设置为0(黑色)。cv2.THRESH_OTSU表示使用Otsu’s方法来自动选择阈值。

Otsu’s方法是一种自动选择阈值的方法,它通过最大化类间方差来选择最佳阈值。这种方法通常用于图像分割,以区分前景和背景。

 

 

4.3 最小外接矩形 

首先获取所有非零坐标元素

coords=np.column_stack(np.where(binary>0))

在您提供的代码片段中,coords = np.column_stack(np.where(binary > 0)) 是一个用于获取二值图像中所有非零像素位置的坐标的方法。具体步骤如下:

  1. 创建二值图像:首先,您使用cv2.threshold函数创建了一个二值图像binary。在这个二值图像中,值为0的像素代表背景,而值为255的像素代表前景。

  2. 使用np.where获取非零像素位置np.where(binary > 0) 是一个NumPy函数,用于获取二值图像中所有非零像素的位置。这个函数返回一个包含所有非零像素坐标的元组,其中第一个元素是x坐标,第二个元素是y坐标。

  3. 将坐标堆叠为列向量:使用np.column_stack函数将坐标堆叠为列向量。这个操作将创建一个二维数组,其中每一行代表一个像素点的坐标。

然后寻找最小外接矩形 

 angle =cv.minAreaRect(coords)[-1]

在OpenCV中,cv2.minAreaRect函数用于找到图像中最小的外接矩形,并返回该矩形的旋转角度。这个函数通常用于对象检测和图像识别任务中。

cv2.minAreaRect函数的参数是一个包含像素点的坐标数组,这些点定义了一个多边形。函数返回的参数有三个:1.矩形的中心点 2.矩形的长和宽 3.矩形的旋转角度

在您提供的代码片段中,angle = cv2.minAreaRect(coords)[-1]的目的是获取最小外接矩形的旋转角度。coords是一个包含像素点的坐标数组,这些点定义了一个多边形,即您之前通过np.column_stack(np.where(binary > 0))获取的非零像素位置。

angle_p =cv.minAreaRect(coords)
box_points = cv.boxPoints(angle_p)
box_points = np.int0(box_points)
cv.circle(src, [146,284], 1, (0, 0, 255), 4)
srca = cv.drawContours(src, [box_points], -1, (0, 0, 255), 2)
》》》15.60267448425293

可以看到画出的外接矩形不能将文字全部包裹住,其角度为15.6,同样是以y轴正半轴为0°,顺时针为度数。

 

4.4 中心,角度,矩阵

cv.getRotationMatrix2D(center, angle, scale) → M

参数说明
center表示旋转中心坐标,二元元组 (x0, y0)。
angle表示旋转角度,单位为角度,逆时针为正数,顺时针为负数
scale表示缩放因子。

4.5 仿射变换

需要说明的是,不管是霍夫线变换还是最小外接矩形,都是为了获取图像的一个倾斜角度。按上面画出的外接矩形来进行仿射变换来本不能完成校正,但是因为文字与x轴正半轴夹角为15.5,然后最小外接矩形的角度刚好是15.6,经过angle=-angle变换后,输入到旋转矩阵的角度就为-15.5,然后仿射变换的旋转就是,正数就是逆时针旋转,负数就是顺时针旋转,所以将原图顺时针旋转后,就刚好校正了。

参考:

OpenCV实战之三 | 基于OpenCV实现图像校正 

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

局域网共享文件夹怎么加密?方法很简单

局域网共享文件夹是企业内部信息、数据传递沟通的重要工具&#xff0c;而为了保护共享文件夹数据安全&#xff0c;我们需要使用专业的加密软件加密保护局域网共享文件夹。下面我们就来了解一下局域网共享文件夹加密方法。 局域网共享文件夹加密 在加密共享文件夹时&#xff0c…

LeetCode 338.比特位计数

各位朋友们&#xff0c;大家好啊&#xff0c;今天此题我用的方法比较好理解&#xff0c;但时间复杂度比较高如果大家觉得可以的话&#xff0c;不妨给个免费的赞吧&#xff0c;谢谢了^ _ ^ 1.题目要求如图所示: 2.做题步骤: 1.先计算总共多少个数: int count 0;int number 0;…

树(数据结构篇)

数据结构之树 树 本篇讲的树也就是多叉树 普通树(多叉树) 概念&#xff1a; 树就是由根节点(父亲)分出多个分支节点(儿子)&#xff0c;然后分支又分出多个分支&#xff0c;我们将这种结构称为树,树也可以这么定义&#xff1a;一棵树由称作根的节点r以及0个或多个非空的(子…

【C++题解】1670 - 象棋大赛

问题&#xff1a;1670 - 象棋大赛 类型&#xff1a;分支问题 题目描述&#xff1a; 市里要组织象棋大赛&#xff0c;年龄在 8∼30 周岁之间的选手可以报名参赛。为了公平起见&#xff0c;大赛组委会将选手们分了青年组、少年组和儿童组&#xff0c;大赛组委会规定&#xff1a…

渗透测试基础(五) 获取WiFi密码

1. 前提条件 需要无线网卡&#xff0c;kali无法识别电脑自带的网卡。 2. 实验步骤&#xff1a; 2.1 查看网卡 命令&#xff1a;airmon-ng 2.2 启动网卡监听模式 命令airmon-ng start wlan0 检查下是否处于监听模型&#xff1a;ifconfig查看一下&#xff0c;如果网卡名加…

1. zabbix监控服务器部署

zabbix监控服务器部署 一、监控的作用1、监控的方式2、zabbix监控获取数据的方式 二、zabbix server部署1、确保时间同步2、添加epel源3、添加zabbix仓库4、安装zabbix服务端软件5、在数据库创建zabbix需要的表、授权用户6、编辑zabbix server配置文件&#xff0c;指定数据库连…

Nginx自定义错误页面配置

Nginx错误页面包括404 403 500 502 503 504等页面&#xff0c;只需要在server中进行如下配置即可&#xff1a; error_page 404 500 502 503 504 /50x.html;location /50x.html {root /usr/share/nginx/html;}注意&#xff1a; /usr/local/nginx/html/ 路径下必须有50x.ht…

变压器电机绕组阻值测试

产品概述 武汉凯迪正大变压器直流电阻的测量是变压器制造中半成品、成品出厂试验、安装、交接试验及电力部门预防性试验项目&#xff0c;能有效发现变压器线圈的选材、焊接、连接部位松动、缺股、断线等制造缺陷和运行后存在的隐患。KDZRS-20A直流电阻测试仪是测量变压器、互感…

OceanBase 列存中多列过滤性能解析

今天有同事问我&#xff0c;列存大宽表场景下&#xff0c;如果在多个列上有等值过滤条件&#xff0c;OceanBase 的性能是不是无法满足要求&#xff1f; Hi 晓楚&#xff0c;帮评估个OTS替换场景 大概1亿大宽表&#xff0c;查询姿势就是任意字段的组合&#xff0c;进行等值查询g…

【扫雷游戏】C语言教程

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

办公人必备宝藏网站,提升工作效率!

对于每个办公人来说&#xff0c;如何在繁杂的工作中保持高效&#xff0c;是每位职场人士追求的目标。其中&#xff0c;高效的工具和资源可以极大地提升我们的工作效率。下面小编就来和大家分享一些办公人必备的宝藏网站&#xff0c;提升大家的工作效率。 1. 办公人导航 办公人…

突破客户关系,客服的新战略与实践

互联网和信息技术的快速发展&#xff0c;让客服服务的内涵和外延也在不断变化&#xff0c;以客户为中心的服务理念&#xff0c;已经成为行业共识。有研究显示&#xff0c;目前有70%-80%的企业在发展战略中都提到了客户关系。 客户关系更多是一种动态的过程&#xff0c;是企业…

更换域名流程记录

华为云的服务器&#xff0c;阿里云购买的域名。 1.购买域名 2.在域名服务商绑定服务器ip&#xff08;以阿里云为例&#xff09; 控制台->域名控制台->域名列表->点击域名->域名解析->添加记录 记录类型填A , 主机记录“”或“www”&#xff0c;记录值填服务器i…

Python+appium 自动化测试-Android 端环境配置

一、安装配置 JDK 一、安装环境 1、本机系统&#xff1a;Windows 10&#xff08;64 位&#xff09; 2、JDK 版本&#xff1a;1.8&#xff08;64 位&#xff09; 二、下载安装 1、JDK 和 JRE 简介 Java 环境分 JDK 和 JRE &#xff0c;JDK 就是 Java Development Kit。简单…

Day6—热点搜索词统计

一、要求 根据用户上网的搜索记录对每天的热点搜索词进行统计&#xff0c;以了解用户所关心的热点话题。 要求完成&#xff1a;统计每天搜索数量前3名的搜索词&#xff08;同一天中同一用户多次搜索同一个搜索词视为1次&#xff09;。 二、数据 三、配置scala环境 1.下载sca…

微信小程序端在线客服源码系统 聊天记录实时保存 带完整的安装代码包以及搭建教程

系统概述 在当今数字化时代&#xff0c;客户服务的质量和效率成为企业竞争的关键因素之一。微信小程序作为一种便捷的应用形式&#xff0c;为在线客服提供了广阔的平台。而具备聊天记录实时保存功能的微信小程序端在线客服源码系统&#xff0c;则能够更好地满足企业与客户之间…

你用AI作画工具生成过哪些惊艳、令人拍案叫绝的作品?

在水墨的基础上追加了一些水彩润色&#xff0c;大家多提提建议&#xff0c;喜欢的话我就定期追加各种全新融合的水墨风格。 应评论区要求&#xff0c;更新了一些横屏的供大家作壁纸用&#xff0c;同时更换了一组新合成的更适合这个风格的模型。 目前为止&#xff0c;Stable D…

idea插件开发之在项目右键添加菜单

写在前面 本文看下如何在右键列表中增加菜单。 正戏 首先创建一个Action&#xff0c;要显示的menu选择ProjectViewPopupMenu&#xff0c;如下&#xff1a; action public class CAction extends AnAction {Overridepublic void actionPerformed(AnActionEvent e) { // …

一分钟生成论文全文,这款AI论文神器你不会还不知道吧?

毕业季写论文就选范文喵AI论文助手。范文喵V2.0主要包括了论文范文、选题分析、开题报告、任务书的写作、以及论文答辩PPT、论文解读等功能。此外&#xff0c;我们也会在近期进一步优化范文喵论文助手&#xff0c;写作效果更好的V3.0版本预计将于今年7月份和大家见面&#xff0…

Python——Flask开发框架基础使用介绍

目录 Flask简介 安装 Flask 创建一个简单的 Flask 应用 运行你的Flask应用 添加模板和静态文件 使用静态文件 处理表单和数据 使用 Flask 扩展 结论 Flask简介 Flask 是一个轻量级的 Python Web 框架&#xff0c;它以其简洁和灵活的特点广受欢迎。Flask 让开发者能够快…