最近想对OpenCV进行系统学习,看到网上这份教程写得不错,于是跟着来学习实践一下。
【youcans@qq.com, youcans 的 OpenCV 例程, https://youcans.blog.csdn.net/article/details/125112487】
程序仓库:https://github.com/zstar1003/OpenCV-Learning

色彩转换

颜色空间转换

常见的色彩空间包括:GRAY 色彩空间(灰度图像)、XYZ 色彩空间、YCrCb 色彩空间、HSV 色彩空间、HLS 色彩空间、CIELab 色彩空间、CIELuv 色彩空间、Bayer 色彩空间等。

色彩空间名词解释:

  • RGB:红色(Red)、绿色(Green)、蓝色(Blue);
  • HSV/HSB:色调(Hue)、饱和度(Saturation)和明度(Value/Brightness);
  • HSl:色调(Hue)、饱和度(Saturation)和灰度(Intensity);
  • HSL:包括色调(Hue)、饱和度(Saturation)和亮度(Luminance/Lightness)

常见色彩空间转换,这里只列举两个常见的。

  • RGB -> GRAY
    注意RGB可以转灰度,灰度不能转RGB
    转换公式:gray = 0.299 x R + 0.587 x G + 0.114 x B

  • RGB -> HSV
    RGB转HSV公式为

OpenCV提供了函数cv.cvtColor()可以将图像从一个颜色空间转换为另一个颜色空间。

cv.cvtColor(src, code [, dst, dstCn]]) → dst

参数说明:

  • src:输入图像,nparray 多维数组,8位无符号/ 16位无符号/单精度浮点数格式
  • code:颜色空间转换代码,详见 ColorConversionCodes
  • dst:输出图像,大小和深度与 src 相同
  • dstCn:输出图像的通道数,0 表示由src和code自动计算

示例程序:

"""
颜色空间转换
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npimgBGR = cv.imread("../img/img.jpg", flags=1)imgRGB = cv.cvtColor(imgBGR, cv.COLOR_BGR2RGB)  # BGR 转换为 RGB, 用于 PyQt5, matplotlib
imgGRAY = cv.cvtColor(imgBGR, cv.COLOR_BGR2GRAY)  # BGR 转换为灰度图像
imgHSV = cv.cvtColor(imgBGR, cv.COLOR_BGR2HSV)  # BGR 转换为 HSV 图像
imgYCrCb = cv.cvtColor(imgBGR, cv.COLOR_BGR2YCrCb)  # BGR转YCrCb
imgHLS = cv.cvtColor(imgBGR, cv.COLOR_BGR2HLS)  # BGR 转 HLS 图像
imgXYZ = cv.cvtColor(imgBGR, cv.COLOR_BGR2XYZ)  # BGR 转 XYZ 图像
imgLAB = cv.cvtColor(imgBGR, cv.COLOR_BGR2LAB)  # BGR 转 LAB 图像
imgYUV = cv.cvtColor(imgBGR, cv.COLOR_BGR2YUV)  # BGR 转 YUV 图像# 调用matplotlib显示处理结果
titles = ['BGR', 'RGB', 'GRAY', 'HSV', 'YCrCb', 'HLS', 'XYZ', 'LAB', 'YUV']
images = [imgBGR, imgRGB, imgGRAY, imgHSV, imgYCrCb,imgHLS, imgXYZ, imgLAB, imgYUV]
plt.figure(figsize=(10, 8))
for i in range(9):plt.subplot(3, 3, i + 1), plt.imshow(images[i], 'gray')plt.title(titles[i])plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()

颜色反转

图像颜色反转也称为反色变换,是像素颜色的逆转,将黑色像素点变白色,白色像素点变黑色,像素位置不变。
RGB图片实现颜色反转非常容易,一种简单的思路就是对每个像素点用255-颜色值。但是这样处理的效率不高。

OpenCV提供了一个查表函数cv.LUT可以快速实现像素值的改变。其本质就是先对每个0-255的像素灰度值建立一个变换字典,这样处理像素值就只需要从字典里去查找对应的数据进行替换,而无需再去运算。

下面的示例程序比较了两种方法的执行效率。

"""
图像颜色反转
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npimg = cv.imread("../img/img.jpg", flags=1)
h, w, ch = img.shape  # 图片的高度, 宽度 和通道数timeBegin = cv.getTickCount()
imgInv = np.empty((w, h, ch), np.uint8)  # 创建空白数组
for i in range(h):for j in range(w):for k in range(ch):imgInv[i][j][k] = 255 - img[i][j][k]
timeEnd = cv.getTickCount()
time = (timeEnd - timeBegin) / cv.getTickFrequency()
print("图像反转(for 循环实现): {} s".format(round(time, 4)))timeBegin = cv.getTickCount()
transTable = np.array([(255 - i) for i in range(256)]).astype("uint8")
invLUT = cv.LUT(img, transTable)
timeEnd = cv.getTickCount()
time = (timeEnd - timeBegin) / cv.getTickFrequency()
print("图像反转(LUT 查表实现): {} s".format(round(time, 4)))plt.figure(figsize=(9, 6))
plt.subplot(131), plt.title("img"), plt.axis('off')
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.subplot(132), plt.title("imgInv"), plt.axis('off')
plt.imshow(cv.cvtColor(imgInv, cv.COLOR_BGR2RGB))
plt.subplot(133), plt.title("invLUT"), plt.axis('off')
plt.imshow(cv.cvtColor(invLUT, cv.COLOR_BGR2RGB))
plt.tight_layout()
plt.show()

输出

图像反转(for 循环实现): 1.9181 s
图像反转(LUT 查表实现): 0.0326 s

由此可见两者速度差异还是比较明显的。

色彩风格滤镜

色彩风格滤镜就是OpenCV提供了一些色彩搭配方案,通过函数cv.applyColorMap可以进行调用。

OpenCV 提供了 22 种色彩风格类型:

ColorMaps[] = { "Autumn", "Bone", "Jet", "Winter", "Rainbow", "Ocean", "Summer", "Spring","Cool", "HSV", "Pink", "Hot", "Parula", "Magma", "Inferno", "Plasma", "Viridis","Cividis", "Twilight", "Twilight Shifted", "Turbo", "Deep Green"};

示例程序:

"""
色彩风格滤镜
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npimg = cv.imread("../img/img.jpg", flags=1)# 伪彩色处理
pseudo1 = cv.applyColorMap(img, colormap=cv.COLORMAP_PINK)
pseudo2 = cv.applyColorMap(img, colormap=cv.COLORMAP_JET)
pseudo3 = cv.applyColorMap(img, colormap=cv.COLORMAP_WINTER)
pseudo4 = cv.applyColorMap(img, colormap=cv.COLORMAP_RAINBOW)
pseudo5 = cv.applyColorMap(img, colormap=cv.COLORMAP_HOT)plt.figure(figsize=(9, 6))
plt.subplot(231), plt.axis('off'), plt.title("Origin")
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.subplot(232), plt.axis('off'), plt.title("cv.COLORMAP_PINK")
plt.imshow(cv.cvtColor(pseudo1, cv.COLOR_BGR2RGB))
plt.subplot(233), plt.axis('off'), plt.title("cv.COLORMAP_JET")
plt.imshow(cv.cvtColor(pseudo2, cv.COLOR_BGR2RGB))
plt.subplot(234), plt.axis('off'), plt.title("cv.COLORMAP_WINTER")
plt.imshow(cv.cvtColor(pseudo3, cv.COLOR_BGR2RGB))
plt.subplot(235), plt.axis('off'), plt.title("cv.COLORMAP_RAINBOW")
plt.imshow(cv.cvtColor(pseudo4, cv.COLOR_BGR2RGB))
plt.subplot(236), plt.axis('off'), plt.title("cv.COLORMAP_HOT")
plt.imshow(cv.cvtColor(pseudo5, cv.COLOR_BGR2RGB))
plt.tight_layout()
plt.show()

调节色彩

通过cv.LUT可以在RGB色彩范围内调节三通道的数值,从而调节色彩。
下面的示例程序将各通道的最大值设置为maxG,将某颜色通道的色阶从 0-255 映射到 0-maxG,就可以使该颜色通道的色彩衰减。

示例程序:

"""
调节色彩
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npimg = cv.imread("../img/img.jpg", flags=1)maxG = 128  # 修改颜色通道最大值,0<=maxG<=255
lutHalf = np.array([int(i * maxG / 255) for i in range(256)]).astype("uint8")
lutEqual = np.array([i for i in range(256)]).astype("uint8")lut3HalfB = np.dstack((lutHalf, lutEqual, lutEqual))  # (1,256,3), B_half/BGR
lut3HalfG = np.dstack((lutEqual, lutHalf, lutEqual))  # (1,256,3), G_half/BGR
lut3HalfR = np.dstack((lutEqual, lutEqual, lutHalf))  # (1,256,3), R_half/BGRblendHalfB = cv.LUT(img, lut3HalfB)  # B 通道衰减 50%
blendHalfG = cv.LUT(img, lut3HalfG)  # G 通道衰减 50%
blendHalfR = cv.LUT(img, lut3HalfR)  # R 通道衰减 50%plt.figure(figsize=(9, 5))
plt.subplot(131), plt.axis('off'), plt.title("B half decayed")
plt.imshow(cv.cvtColor(blendHalfB, cv.COLOR_BGR2RGB))
plt.subplot(132), plt.axis('off'), plt.title("G half decayed")
plt.imshow(cv.cvtColor(blendHalfG, cv.COLOR_BGR2RGB))
plt.subplot(133), plt.axis('off'), plt.title("R half decayed")
plt.imshow(cv.cvtColor(blendHalfR, cv.COLOR_BGR2RGB))
plt.tight_layout()
plt.show()

调节饱和度和明度

将RGB颜色空间转换到HSV空间,可以调整图片的饱和度和明度。

示例程序:

"""
调节饱和度和明度
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npimg = cv.imread("../img/img.jpg", flags=1)
hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)  # 色彩空间转换, BGR->HSV# 调节通道强度
lutWeaken = np.array([int(0.6 * i) for i in range(256)]).astype("uint8")
lutEqual = np.array([i for i in range(256)]).astype("uint8")
lutRaisen = np.array([int(102 + 0.6 * i) for i in range(256)]).astype("uint8")
# 调节饱和度
lutSWeaken = np.dstack((lutEqual, lutWeaken, lutEqual))  # Saturation weaken
lutSRaisen = np.dstack((lutEqual, lutRaisen, lutEqual))  # Saturation raisen
# 调节明度
lutVWeaken = np.dstack((lutEqual, lutEqual, lutWeaken))  # Value weaken
lutVRaisen = np.dstack((lutEqual, lutEqual, lutRaisen))  # Value raisenblendSWeaken = cv.LUT(hsv, lutSWeaken)  # 饱和度降低
blendSRaisen = cv.LUT(hsv, lutSRaisen)  # 饱和度增大
blendVWeaken = cv.LUT(hsv, lutVWeaken)  # 明度降低
blendVRaisen = cv.LUT(hsv, lutVRaisen)  # 明度升高plt.figure(figsize=(9, 6))
plt.subplot(231), plt.axis('off'), plt.title("Saturation weaken")
plt.imshow(cv.cvtColor(blendSWeaken, cv.COLOR_HSV2RGB))
plt.subplot(232), plt.axis('off'), plt.title("Normal saturation")
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.subplot(233), plt.axis('off'), plt.title("Saturation raisen")
plt.imshow(cv.cvtColor(blendSRaisen, cv.COLOR_HSV2RGB))
plt.subplot(234), plt.axis('off'), plt.title("Value weaken")
plt.imshow(cv.cvtColor(blendVWeaken, cv.COLOR_HSV2RGB))
plt.subplot(235), plt.axis('off'), plt.title("Normal value")
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.subplot(236), plt.axis('off'), plt.title("Value raisen")
plt.imshow(cv.cvtColor(blendVRaisen, cv.COLOR_HSV2RGB))
plt.tight_layout()
plt.show()

图像绘制

绘制直线

函数cv.line()绘制图像中点pt1与点pt2之间的线段
函数cv.arrowedLine()绘制图像中点pt1与点pt2之间的带箭头线段

cv.line(img, pt1, pt2, color[, thickness=1, lineType=LINE_8, shift=0]) → img
cv.arrowedLine(img, pt1, pt2, color[, thickness=1, line_type=8, shift=0, tipLength=0.1]) → img

参数说明:

  • img:输入输出图像,允许单通道灰度图像或多通道彩色图像
  • pt1:线段第一个点的坐标,(x1, y1)
  • pt2:线段第二个点的坐标,(x2, y2)
  • tipLength:箭头部分长度与线段长度的比例,默认为 0.1

示例程序:

"""
绘制直线
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npheight, width, channels = 200, 120, 3
img = np.ones((height, width, channels), np.uint8) * 160  # 创建黑色图像 RGB=0# 注意 pt1, pt2 坐标的格式是 (x,y) 而不是 (y,x)
img1 = img.copy()
cv.line(img1, (0, 0), (200, 150), (0, 0, 255), 1)  # 红色 R=255
cv.line(img1, (0, 0), (150, 200), (0, 255, 0), 1)  # 绿色 G=255
cv.line(img1, (0, 50), (200, 50), (128, 0, 0), 2)  # 深蓝色 B = 128
cv.line(img1, (0, 100), (200, 100), 128, 2)  # color=128 等效于 (128,0,0)
cv.line(img1, (0, 150), (200, 150), 255, 2)  # color=255 等效于 (255,0,0)# img2 = img.copy()
# tipLength 指箭头部分长度与整个线段长度的比例
img2 = cv.arrowedLine(img.copy(), (10, 0), (100, 30), (0, 0, 255), tipLength=0.05)  # 从 pt1 指向 pt2
img2 = cv.arrowedLine(img2, (10, 50), (100, 80), (0, 0, 255), tipLength=0.1)
img2 = cv.arrowedLine(img2, (10, 100), (100, 130), (0, 0, 255), tipLength=0.2)  # 双向箭头
img2 = cv.arrowedLine(img2, (100, 130), (10, 100), (0, 0, 255), tipLength=0.2)  # 双向箭头
img2 = cv.arrowedLine(img2, (10, 150), (200, 200), (0, 0, 255), tipLength=0.1)  # 终点越界,箭头不显示# 绘制直线可以用于灰度图像,参数 color 只有第一通道值有效,并被设为灰度值
gray = np.zeros((height, width), np.uint8)  # 创建灰度图像
img3 = cv.line(gray, (0, 10), (200, 10), (0, 255, 255), 2)
img3 = cv.line(gray, (0, 30), (200, 30), (64, 128, 255), 2)
img3 = cv.line(gray, (0, 60), (200, 60), (128, 64, 255), 2)
img3 = cv.line(gray, (0, 100), (200, 100), (255, 0, 255), 2)
img3 = cv.line(gray, (20, 0), (20, 200), 128, 2)
img3 = cv.line(gray, (60, 0), (60, 200), (255, 0, 0), 2)
img3 = cv.line(gray, (100, 0), (100, 200), (255, 255, 255), 2)plt.figure(figsize=(9, 6))
plt.subplot(131), plt.title("img1"), plt.axis('off')
plt.imshow(cv.cvtColor(img1, cv.COLOR_BGR2RGB))
plt.subplot(132), plt.title("img2"), plt.axis('off')
plt.imshow(cv.cvtColor(img2, cv.COLOR_BGR2RGB))
plt.subplot(133), plt.title("img3"), plt.axis('off')
plt.imshow(img3, cmap="gray")
plt.tight_layout()
plt.show()

绘制矩形

函数cv.rectangle()用来在图像上绘制垂直于图像边界的矩形

cv.rectangle(img, pt1, pt2, color[, thickness=1, lineType=LINE_8, shift=0]) → img
cv.rectangle(img, rec, color[, thickness=1, lineType=LINE_8, shift=0]) → img

参数说明:

  • img:输入输出图像,允许单通道灰度图像或多通道彩色图像
  • pt1:矩阵第一个点的坐标,(x1, y1) 格式的元组
  • pt2:与 pt1 成对角的矩阵第二个点的坐标,(x2, y2) 格式的元组
  • color:绘图线条的颜色,(b,g,r) 格式的元组,或者表示灰度值的标量
  • thickness:绘制矩形的线宽,默认值 1px,负数表示矩形内部填充
  • lineType:绘制线段的线性,默认为 LINE_8
  • shift:点坐标的小数位数,默认为 0
"""
绘制矩形
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npheight, width, channels = 400, 300, 3
img = np.ones((height, width, channels), np.uint8) * 160  # 创建黑色图像 RGB=0img1 = img.copy()
cv.rectangle(img1, (0, 20), (100, 200), (255, 255, 255))  # 白色
cv.rectangle(img1, (20, 0), (300, 100), (255, 0, 0), 2)  # 蓝色 B=255
cv.rectangle(img1, (300, 400), (250, 300), (0, 255, 0), -1)  # 绿色,填充
cv.rectangle(img1, (0, 400), (50, 300), 255, -1)  # color=255 蓝色
cv.rectangle(img1, (20, 220), (25, 225), (0, 0, 255), 4)  # 线宽的影响
cv.rectangle(img1, (60, 220), (67, 227), (0, 0, 255), 4)
cv.rectangle(img1, (100, 220), (109, 229), (0, 0, 255), 4)img2 = img.copy()
x, y, w, h = (50, 50, 200, 100)  # 左上角坐标 (x,y), 宽度 w,高度 h
cv.rectangle(img2, (x, y), (x + w, y + h), (0, 0, 255), 2)
text = "({},{}),{}*{}".format(x, y, w, h)
cv.putText(img2, text, (x, y - 5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255))# 绘制直线可以用于灰度图像,参数 color 只有第一通道值有效,并被设为灰度值
gray = np.zeros((height, width), np.uint8)  # 创建灰度图像
img3 = cv.line(gray, (0, 10), (300, 10), 64, 2)
cv.line(img3, (0, 30), (300, 30), (128, 128, 255), 2)
cv.line(img3, (0, 60), (300, 60), (192, 64, 255), 2)
cv.rectangle(img3, (0, 200), (30, 150), 128, -1)  # Gray=128
cv.rectangle(img3, (60, 200), (90, 150), (128, 0, 0), -1)  # Gray=128
cv.rectangle(img3, (120, 200), (150, 150), (128, 255, 255), -1)  # Gray=128
cv.rectangle(img3, (180, 200), (210, 150), 192, -1)  # Gray=192
cv.rectangle(img3, (240, 200), (270, 150), 255, -1)  # Gray=255plt.figure(figsize=(9, 6))
plt.subplot(131), plt.title("img1"), plt.axis('off')
plt.imshow(cv.cvtColor(img1, cv.COLOR_BGR2RGB))
plt.subplot(132), plt.title("img2"), plt.axis('off')
plt.imshow(cv.cvtColor(img2, cv.COLOR_BGR2RGB))
plt.subplot(133), plt.title("img3"), plt.axis('off')
plt.imshow(img3, cmap="gray")
plt.tight_layout()
plt.show()

绘制倾斜矩形

cv.rectangle()只能绘制垂直的矩形,如果需要绘制倾斜矩形,需要绘制多条直线。

示例程序:

"""
绘制倾斜矩形
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npheight, width, channels = 600, 400, 3
img = np.ones((height, width, channels), np.uint8) * 192  # 创建黑色图像 RGB=0# 围绕矩形中心旋转
x, y, w, h = (100, 200, 200, 100)  # 左上角坐标 (x,y), 宽度 w,高度 h
cx, cy = x + w // 2, y + h // 2  # 矩形中心
img1 = img.copy()
cv.circle(img1, (cx, cy), 4, (0, 0, 255), -1)  # 旋转中心
angle = [15, 30, 45, 60, 75, 90]  # 旋转角度,顺时针方向
for i in range(len(angle)):ang = angle[i] * np.pi / 180x1 = int(cx + (w / 2) * np.cos(ang) - (h / 2) * np.sin(ang))y1 = int(cy + (w / 2) * np.sin(ang) + (h / 2) * np.cos(ang))x2 = int(cx + (w / 2) * np.cos(ang) + (h / 2) * np.sin(ang))y2 = int(cy + (w / 2) * np.sin(ang) - (h / 2) * np.cos(ang))x3 = int(cx - (w / 2) * np.cos(ang) + (h / 2) * np.sin(ang))y3 = int(cy - (w / 2) * np.sin(ang) - (h / 2) * np.cos(ang))x4 = int(cx - (w / 2) * np.cos(ang) - (h / 2) * np.sin(ang))y4 = int(cy - (w / 2) * np.sin(ang) + (h / 2) * np.cos(ang))color = (30 * i, 0, 255 - 30 * i)cv.line(img1, (x1, y1), (x2, y2), color)cv.line(img1, (x2, y2), (x3, y3), color)cv.line(img1, (x3, y3), (x4, y4), color)cv.line(img1, (x4, y4), (x1, y1), color)# 围绕矩形左上顶点旋转
x, y, w, h = (200, 200, 200, 100)  # 左上角坐标 (x,y), 宽度 w,高度 h
img2 = img.copy()
cv.circle(img2, (x, y), 4, (0, 0, 255), -1)  # 旋转中心
angle = [15, 30, 45, 60, 75, 90, 120, 150, 180, 225]  # 旋转角度,顺时针方向
for i in range(len(angle)):ang = angle[i] * np.pi / 180x1, y1 = x, yx2 = int(x + w * np.cos(ang))y2 = int(y + w * np.sin(ang))x3 = int(x + w * np.cos(ang) - h * np.sin(ang))y3 = int(y + w * np.sin(ang) + h * np.cos(ang))x4 = int(x - h * np.sin(ang))y4 = int(y + h * np.cos(ang))color = (30 * i, 0, 255 - 30 * i)cv.line(img2, (x1, y1), (x2, y2), color)cv.line(img2, (x2, y2), (x3, y3), color)cv.line(img2, (x3, y3), (x4, y4), color)cv.line(img2, (x4, y4), (x1, y1), color)plt.figure(figsize=(9, 6))
plt.subplot(121), plt.title("img1"), plt.axis('off')
plt.imshow(cv.cvtColor(img1, cv.COLOR_BGR2RGB))
plt.subplot(122), plt.title("img2"), plt.axis('off')
plt.imshow(cv.cvtColor(img2, cv.COLOR_BGR2RGB))
plt.show()

绘制圆形

函数cv.circle()用来在图像上绘制圆形

cv.circle(img, center, radius, color[, thickness=1, lineType=LINE_8, shift=0]) → img

参数说明:

  • img:输入输出图像,允许单通道灰度图像或多通道彩色图像
  • center:圆心点的坐标,(x, y) 格式的元组
  • radius:圆的半径,整数
  • color:绘图线条的颜色,(b,g,r) 格式的元组,或者表示灰度值的标量
  • thickness:绘制矩形的线宽,默认值 1px,负数表示矩形内部填充
  • lineType:绘制线段的线性,默认为 LINE_8
    • cv.LINE_4:4 邻接线型
    • cv.LINE_8:8 邻接线型
    • cv.LINE_AA:抗锯齿线型,图像更平滑
  • shift:点坐标的小数位数,默认为 0

示例程序:

"""
绘制圆形
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npimg = np.ones((400, 600, 3), np.uint8) * 192center = (0, 0)  # 圆心坐标
cx, cy = 300, 200  # 圆心坐标
for r in range(200, 0, -20):color = (r, r, 255 - r)cv.circle(img, (cx, cy), r, color, -1)cv.circle(img, center, r, 255)cv.circle(img, (600, 400), r, color, 5)plt.figure(figsize=(6, 4))
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.axis('off')
plt.show()

绘制椭圆

函数cv.ellipse()用来在图像上绘制椭圆轮廓、填充椭圆、椭圆弧或填充椭圆扇区

cv.ellipse(img, center, axes, angle, startAngle, endAngle, color[, thickness=1, lineType=LINE_8, shift=0]) → img
cv.ellipse(img, box, color[, thickness=1, lineType=LINE_8]) → img

参数说明:

  • img:输入输出图像,允许单通道灰度图像或多通道彩色图像
  • center:椭圆中心点的坐标,(x, y) 格式的元组
  • axes:椭圆半轴长度,(hfirst, hsecond) 格式的元组
  • angle: 椭圆沿 x轴方向的旋转角度(角度制,顺时针方向)
  • startAngle:绘制的起始角度
  • endAngle:绘制的终止角度
  • color:绘图线条的颜色,(b,g,r) 格式的元组,或者表示灰度值的标量
  • thickness:绘制矩形的线宽,默认值 1px,负数表示矩形内部填充
  • lineType:绘制线段的线性,默认为 LINE_8
  • shift:点坐标的小数位数,默认为 0

示例程序:

"""
绘制椭圆
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npimg = np.ones((600, 400, 3), np.uint8) * 224
img1 = img.copy()
img2 = img.copy()# (1) 半轴长度 (haf) 的影响
cx, cy = 200, 150  # 圆心坐标
angle = 30  # 旋转角度
startAng, endAng = 0, 360  # 开始角度,结束角度
haf = [50, 100, 150, 180]  # 第一轴的半轴长度
has = 100  # 第二轴的半轴长度
for i in range(len(haf)):color = (i * 50, i * 50, 255 - i * 50)cv.ellipse(img1, (cx, cy), (haf[i], has), angle, startAng, endAng, color, 2)angPi = angle * np.pi / 180  # 转换为弧度制,便于计算坐标xe = int(cx + haf[i] * np.cos(angPi))ye = int(cy + haf[i] * np.sin(angPi))cv.circle(img1, (xe, ye), 2, color, -1)cv.arrowedLine(img1, (cx, cy), (xe, ye), color)  # 从圆心指向第一轴端点text = "haF={}".format(haf[i])cv.putText(img1, text, (xe + 5, ye), cv.FONT_HERSHEY_SIMPLEX, 0.5, color)
# 绘制第二轴
xe = int(cx + has * np.sin(angPi))  # 计算第二轴端点坐标
ye = int(cy - has * np.cos(angPi))
cv.arrowedLine(img1, (cx, cy), (xe, ye), color)  # 从圆心指向第二轴端点
text = "haS={}".format(has)
cv.putText(img1, text, (xe + 5, ye), cv.FONT_HERSHEY_SIMPLEX, 0.5, color)# (2) 旋转角度 (angle) 的影响
cx, cy = 200, 450  # 圆心坐标
haf, has = 120, 50  # 半轴长度
startAng, endAng = 0, 360  # 开始角度,结束角度
angle = [0, 30, 60, 135]  # 旋转角度
for i in range(len(angle)):color = (i * 50, i * 50, 255 - i * 50)cv.ellipse(img1, (cx, cy), (haf, has), angle[i], startAng, endAng, color, 2)angPi = angle[i] * np.pi / 180  # 转换为弧度制,便于计算坐标xe = int(cx + haf * np.cos(angPi))ye = int(cy + haf * np.sin(angPi))cv.circle(img1, (xe, ye), 2, color, -1)cv.arrowedLine(img1, (cx, cy), (xe, ye), color)  # 从圆心指向第一轴端点text = "rotate {}".format(angle[i])cv.putText(img1, text, (xe + 5, ye), cv.FONT_HERSHEY_SIMPLEX, 0.5, color)# (3) 起始角度 (startAngle) 的影响 I
cx, cy = 50, 80  # 圆心坐标
haf, has = 40, 30  # 半轴长度
angle = 0  # 旋转角度
endAng = 360  # 结束角度
startAng = [0, 45, 90, 180]  # 开始角度
for i in range(len(startAng)):color = (i * 20, i * 20, 255 - i * 20)cxi = cx + i * 100cv.ellipse(img2, (cxi, cy), (haf, has), angle, startAng[i], endAng, color, 2)angPi = angle * np.pi / 180  # 转换为弧度制,便于计算坐标xe = int(cxi + haf * np.cos(angPi))ye = int(cy + haf * np.sin(angPi))cv.arrowedLine(img2, (cxi, cy), (xe, ye), 255)  # 从圆心指向第一轴端点text = "start {}".format(startAng[i])cv.putText(img2, text, (cxi - 40, cy), cv.FONT_HERSHEY_SIMPLEX, 0.5, color)
text = "end={}".format(endAng)
cv.putText(img2, text, (10, cy - 40), cv.FONT_HERSHEY_SIMPLEX, 0.5, 255)# (4) 起始角度 (startAngle) 的影响 II
cx, cy = 50, 200  # 圆心坐标
haf, has = 40, 30  # 半轴长度
angle = 30  # 旋转角度
endAng = 360  # 结束角度
startAng = [0, 45, 90, 180]  # 开始角度
for i in range(len(startAng)):color = (i * 20, i * 20, 255 - i * 20)cxi = cx + i * 100cv.ellipse(img2, (cxi, cy), (haf, has), angle, startAng[i], endAng, color, 2)angPi = angle * np.pi / 180  # 转换为弧度制,便于计算坐标xe = int(cxi + haf * np.cos(angPi))ye = int(cy + haf * np.sin(angPi))cv.arrowedLine(img2, (cxi, cy), (xe, ye), 255)  # 从圆心指向第一轴端点text = "start {}".format(startAng[i])cv.putText(img2, text, (cxi - 40, cy), cv.FONT_HERSHEY_SIMPLEX, 0.5, color)
text = "end={}".format(endAng)
cv.putText(img2, text, (10, cy - 40), cv.FONT_HERSHEY_SIMPLEX, 0.5, 255)# (5) 结束角度 (endAngle) 的影响 I
cx, cy = 50, 320  # 圆心坐标
haf, has = 40, 30  # 半轴长度
angle = 0  # 旋转角度
startAng = 0  # 开始角度
endAng = [45, 90, 180, 360]  # 结束角度
for i in range(len(endAng)):color = (i * 20, i * 20, 255 - i * 20)cxi = cx + i * 100cv.ellipse(img2, (cxi, cy), (haf, has), angle, startAng, endAng[i], color, 2)angPi = angle * np.pi / 180  # 转换为弧度制,便于计算坐标xe = int(cxi + haf * np.cos(angPi))ye = int(cy + haf * np.sin(angPi))cv.arrowedLine(img2, (cxi, cy), (xe, ye), 255)  # 从圆心指向第一轴端点text = "end {}".format(endAng[i])cv.putText(img2, text, (cxi - 40, cy), cv.FONT_HERSHEY_SIMPLEX, 0.5, color)
text = "start={}".format(startAng)
cv.putText(img2, text, (10, cy - 40), cv.FONT_HERSHEY_SIMPLEX, 0.5, 255)# (6) 结束角度 (endAngle) 的影响 II
cx, cy = 50, 420  # 圆心坐标
haf, has = 40, 30  # 半轴长度
angle = 30  # 旋转角度
startAng = 45  # 开始角度
endAng = [30, 90, 180, 360]  # 结束角度
for i in range(len(endAng)):color = (i * 20, i * 20, 255 - i * 20)cxi = cx + i * 100cv.ellipse(img2, (cxi, cy), (haf, has), angle, startAng, endAng[i], color, 2)angPi = angle * np.pi / 180  # 转换为弧度制,便于计算坐标xe = int(cxi + haf * np.cos(angPi))ye = int(cy + haf * np.sin(angPi))cv.arrowedLine(img2, (cxi, cy), (xe, ye), 255)  # 从圆心指向第一轴端点text = "end {}".format(endAng[i])cv.putText(img2, text, (cxi - 40, cy), cv.FONT_HERSHEY_SIMPLEX, 0.5, color)
text = "start={}".format(startAng)
cv.putText(img2, text, (10, cy - 40), cv.FONT_HERSHEY_SIMPLEX, 0.5, 255)# (7) 结束角度 (endAngle) 的影响 II
cx, cy = 50, 550  # 圆心坐标
haf, has = 40, 30  # 半轴长度
angle = 30  # 旋转角度
startAng = [0, 0, 180, 180]  # 开始角度
endAng = [90, 180, 270, 360]  # 结束角度
for i in range(len(endAng)):color = (i * 20, i * 20, 255 - i * 20)cxi = cx + i * 100cv.ellipse(img2, (cxi, cy), (haf, has), angle, startAng[i], endAng[i], color, 2)angPi = angle * np.pi / 180  # 转换为弧度制,便于计算坐标xe = int(cxi + haf * np.cos(angPi))ye = int(cy + haf * np.sin(angPi))cv.arrowedLine(img2, (cxi, cy), (xe, ye), 255)  # 从圆心指向第一轴端点text = "start {}".format(startAng[i])cv.putText(img2, text, (cxi - 40, cy - 20), cv.FONT_HERSHEY_SIMPLEX, 0.5, color)text = "end {}".format(endAng[i])cv.putText(img2, text, (cxi - 40, cy), cv.FONT_HERSHEY_SIMPLEX, 0.5, color)
text = "rotate={}".format(angle)
cv.putText(img2, text, (10, cy - 50), cv.FONT_HERSHEY_SIMPLEX, 0.5, 255)plt.figure(figsize=(9, 6))
plt.subplot(121), plt.title("Ellipse1"), plt.axis('off')
plt.imshow(cv.cvtColor(img1, cv.COLOR_BGR2RGB))
plt.subplot(122), plt.title("Ellipse2"), plt.axis('off')
plt.imshow(cv.cvtColor(img2, cv.COLOR_BGR2RGB))
plt.show()

绘制多段线和多边形

函数cv.polylines()用来绘制多边形曲线或多段线
函数cv.fillPoly()用来绘制一个或多个填充的多边形区域
函数cv.fillConvexPoly()用来绘制一个填充的凸多边形

cv.polylines(img, pts, isClosed, color[, thickness=1, lineType=LINE_8, shift=0]) → img
cv.fillPoly(img, pts, color[, lineType=LINE_8, shift=0, offset=Point()]) → img
cv.fillConvexPoly(img, points, color[, lineType=LINE_8, shift=0]) → img

参数说明:

  • img:输入输出图像,允许单通道灰度图像或多通道彩色图像
  • pts:多边形顶点坐标, 二维 Numpy 数组的列表
  • points:多边形顶点坐标,二维 Numpy 数组
  • isClosed: 闭合标志,True 表示闭合多边形,False 表示多边形不闭合

示例程序:

"""
绘制多段线和多边形
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npimg = np.ones((980, 400, 3), np.uint8) * 224
img1 = img.copy()
img2 = img.copy()
img3 = img.copy()
img4 = img.copy()# 多边形顶点
points1 = np.array([[200, 100], [295, 169], [259, 281], [141, 281], [105, 169]], np.int)
points2 = np.array([[200, 400], [259, 581], [105, 469], [295, 469], [141, 581]])  # (5,2)
points3 = np.array([[200, 700], [222, 769], [295, 769], [236, 812], [259, 881],[200, 838], [141, 881], [164, 812], [105, 769], [178, 769]])# 绘制多边形,闭合曲线
pts1 = [points1]  # pts1 是列表,列表元素是形状为 (m,2) 的 numpy 二维数组
cv.polylines(img1, pts1, True, (0, 0, 255))  # pts1  是列表
cv.polylines(img1, [points2, points3], 1, 255, 2)  # 可以绘制多个多边形# 绘制多段线,曲线不闭合
cv.polylines(img2, [points1], False, (0, 0, 255))
cv.polylines(img2, [points2, points3], 0, 255, 2)  # 可以绘制多个多段线# 绘制填充多边形,注意交叉重叠部分处理
cv.fillPoly(img3, [points1], (0, 0, 255))
cv.fillPoly(img3, [points2, points3], 255)  # 可以绘制多个填充多边形# 绘制一个填充多边形,注意交叉重叠部分
cv.fillConvexPoly(img4, points1, (0, 0, 255))
cv.fillConvexPoly(img4, points2, 255)  # 不能绘制存在自相交的多边形
cv.fillConvexPoly(img4, points3, 255)  # 可以绘制凹多边形,但要慎用plt.figure(figsize=(9, 6))
plt.subplot(141), plt.title("closed polygon"), plt.axis('off')
plt.imshow(cv.cvtColor(img1, cv.COLOR_BGR2RGB))
plt.subplot(142), plt.title("unclosed polygo"), plt.axis('off')
plt.imshow(cv.cvtColor(img2, cv.COLOR_BGR2RGB))
plt.subplot(143), plt.title("fillPoly"), plt.axis('off')
plt.imshow(cv.cvtColor(img3, cv.COLOR_BGR2RGB))
plt.subplot(144), plt.title("fillConvexPoly"), plt.axis('off')
plt.imshow(cv.cvtColor(img4, cv.COLOR_BGR2RGB))
plt.tight_layout()
plt.show()

添加水印

添加水印的思路是先在黑色背景上添加图像或文字制作水印,再使用cv.addWeight函数,通过重叠混合把水印添加到原始图像上。

示例程序:

"""
添加水印
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npimg = cv.imread("../img/lena.jpg", 1)  # 加载原始图片
h, w = img.shape[0], img.shape[1]# 生成水印图案
logo = cv.imread("../img/img.jpg", 0)  # 加载 Logo
logoResize = cv.resize(logo, (200, 200))  # 调整图片尺寸
grayMark = np.zeros(img.shape[:2], np.uint8)  # 水印黑色背景
grayMark[10:210, 10:210] = logoResize  # 生成水印图案# 生成文字水印
mark = np.zeros(img.shape[:2], np.uint8)  # 黑色背景
for i in range(h // 100):cv.putText(mark, "zstar", (50, 70 + 100 * i), cv.FONT_HERSHEY_SIMPLEX, 1.5, 255, 2)
MAR = cv.getRotationMatrix2D((w // 2, h // 2), 45, 1.0)  # 旋转 45 度
grayMark2 = cv.warpAffine(mark, MAR, (w, h))  # 旋转变换,默认为黑色填充# 添加图片水印
markC3 = cv.merge([grayMark, grayMark, grayMark])
imgMark1 = cv.addWeighted(img, 1, markC3, 0.25, 0)  # 加权加法图像融合# 添加文字水印
markC32 = cv.merge([grayMark2, grayMark2, grayMark2])
imgMark2 = cv.addWeighted(img, 1, markC32, 0.25, 0)  # 加权加法图像融合plt.figure(figsize=(9, 6))
plt.subplot(221), plt.title("original"), plt.axis('off')
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.subplot(222), plt.title("watermark"), plt.axis('off')
plt.imshow(cv.cvtColor(markC3, cv.COLOR_BGR2RGB))
plt.subplot(223), plt.title("watermark embedded"), plt.axis('off')
plt.imshow(cv.cvtColor(imgMark1, cv.COLOR_BGR2RGB))
plt.subplot(224), plt.title("watermark embedded"), plt.axis('off')
plt.imshow(cv.cvtColor(imgMark2, cv.COLOR_BGR2RGB))
plt.tight_layout()
plt.show()

添加马赛克

实现马赛克的原理就是将处理区域划分为一个个小方块,每个小方块内所有像素置为相同的或相似的像素值。

示例程序:

"""
添加马赛克
"""
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as npimg = cv.imread("../img/lena.jpg", 1)  # 加载原始图片
roi = cv.selectROI(img, showCrosshair=True, fromCenter=False)
x, y, wRoi, hRoi = roi  # 矩形裁剪区域的位置参数
# x, y, wRoi, hRoi = 208, 176, 155, 215  # 矩形裁剪区域
imgROI = img[y:y + hRoi, x:x + wRoi].copy()  # 切片获得矩形裁剪区域plt.figure(figsize=(9, 6))
plt.subplot(231), plt.title("Original image"), plt.axis('off')
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.subplot(232), plt.title("Region of interest"), plt.axis('off')
plt.imshow(cv.cvtColor(imgROI, cv.COLOR_BGR2RGB))mosaic = np.zeros(imgROI.shape, np.uint8)  # ROI 区域
ksize = [5, 10, 20]  # 马赛克块的宽度
for i in range(3):k = ksize[i]for h in range(0, hRoi, k):for w in range(0, wRoi, k):color = imgROI[h, w]mosaic[h:h + k, w:w + k, :] = color  # 用顶点颜色覆盖马赛克块imgMosaic = img.copy()imgMosaic[y:y + hRoi, x:x + wRoi] = mosaicplt.subplot(2, 3, i + 4), plt.title("Coding image (size={})".format(k)), plt.axis('off')plt.imshow(cv.cvtColor(imgMosaic, cv.COLOR_BGR2RGB))plt.subplot(233), plt.title("Mosaic"), plt.axis('off')
plt.imshow(cv.cvtColor(mosaic, cv.COLOR_BGR2RGB))
plt.show()

趣味应用

下面这个是迷途小书童的Note编写的,通过调整色调和色相,可以将图片变成赛博朋克风格。

完整代码:

"""
Title:赛博朋克特效实现
Author:迷途小书童的Note
Link:https://mp.weixin.qq.com/s/brZSanGvqqi6AHT3wg54Lg
"""import cv2
import numpy as npdef modify_color_temperature(img):# ---------------- 冷色調 ---------------- ## 1.计算三个通道的平均值,并依据平均值调整色调imgB = img[:, :, 0]imgG = img[:, :, 1]imgR = img[:, :, 2]# 调整色调 # 白平衡 -> 三个值变化相同# 冷色调(增加b分量) -> 除了b之外都增加# 暖色调(增加r分量) -> 除了r之外都增加bAve = cv2.mean(imgB)[0]gAve = cv2.mean(imgG)[0] + 10rAve = cv2.mean(imgR)[0] + 10aveGray = (int)(bAve + gAve + rAve) / 3# 2. 计算各通道增益系数,并使用此系数计算結果bCoef = aveGray / bAvegCoef = aveGray / gAverCoef = aveGray / rAveimgB = np.floor((imgB * bCoef))  # 向下取整imgG = np.floor((imgG * gCoef))imgR = np.floor((imgR * rCoef))# 3. 变换后处理imgb = imgBimgb[imgb > 255] = 255imgg = imgGimgg[imgg > 255] = 255imgr = imgRimgr[imgr > 255] = 255cold_rgb = np.dstack((imgb, imgg, imgr)).astype(np.uint8)return cold_rgbdef reverse_hue(image):# 反转色相image_hls = cv2.cvtColor(image, cv2.COLOR_BGR2HLS)image_hls = np.asarray(image_hls, np.float32)hue = image_hls[:, :, 0]hue[hue < 90] = 180 - hue[hue < 90] - 10image_hls[:, :, 0] = hueimage_hls = np.asarray(image_hls, np.uint8)image = cv2.cvtColor(image_hls, cv2.COLOR_HLS2BGR)return imagedef cyberpunk(image):image_lab = cv2.cvtColor(image, cv2.COLOR_BGR2Lab)image_lab = np.asarray(image_lab, np.float32)image_lab[:,:,0] = np.clip(image_lab[:,:,0] * 1.2,0,255)# 提高像素亮度,让亮的地方更亮light_gamma_high = np.power(image_lab[:, :, 0], 0.9)light_gamma_high = np.asarray(light_gamma_high / np.max(light_gamma_high) * 255, np.uint)# 降低像素亮度,让暗的地方更暗light_gamma_low = np.power(image_lab[:, :, 0], 1.1)light_gamma_low = np.asarray(light_gamma_low / np.max(light_gamma_low) * 255, np.uint8)# 调色至偏紫dark_b = image_lab[:, :, 2] * (light_gamma_low / 255) * 0.4dark_a = image_lab[:, :, 2] * (1 - light_gamma_high / 255) * 0.1image_lab[:, :, 2] = np.clip(image_lab[:, :, 2] - dark_b, 0, 255)image_lab[:, :, 1] = np.clip(image_lab[:, :, 1] - dark_a, 0, 255)image_lab = np.asarray(image_lab, np.uint8)return cv2.cvtColor(image_lab, cv2.COLOR_Lab2BGR)if __name__ == "__main__":# 设置窗口可缩放cv2.namedWindow('origin', cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO)cv2.namedWindow('cold_style', cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO)cv2.namedWindow('reverser_hue', cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO)cv2.namedWindow('cyberpunk', cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO)image = cv2.imread("../img/img.jpg")cv2.imshow("origin", image)image = modify_color_temperature(image)cv2.imshow("cold_style", image)image = reverse_hue(image)cv2.imshow("reverser_hue", image)# cv2.waitKey()image = cyberpunk(image)cv2.imshow("cyberpunk", image)cv2.imwrite("result2.jpg", image)cv2.waitKey()

【OpenCV】Chapter10.色彩转换与图像绘制相关推荐

  1. 【OpenCV】OpenCV实战从入门到精通之 -- 基本图像绘制

    目录 1.DrawEllipse()函数的写法 2.DrawFilledCircle()函数的写法 3.DrawPolygon()函数的写法 4.DrawLine()函数的写法 基本绘制图形代码: m ...

  2. Java OpenCV 图像处理04.0 图像形状与文字绘制

    Java OpenCV 图像处理04.0 图像形状与文字绘制 Github 代碼地址 OpenCV 仿射变换.透视变换.旋转.平移.缩放 序號 名稱 方法 1 圖像 添加文字 Imgproc.putT ...

  3. OpenCV学习笔记(九)——图像轮廓(下)

    <OpenCV轻松入门:面向Python>学习笔记(九) 1-3 查找并绘制轮廓.矩特性及Hu矩 4-5 轮廓拟合及凸包 6. 利用形状场景算法比较轮廓 6.1 计算形状场景距离 6.2 ...

  4. 使用Python,OpenCV+OCR检测护照图像中的机器可读区域(MRZ Machine-Readable Zones)

    使用Python,OpenCV+OCR检测护照图像中的机器可读区域(MRZ Machine-Readable Zones) 1. 效果图 2. 原理 3. 源码 参考 这篇博客将介绍如何只使用基本的图 ...

  5. opencv4 c++ 提取图片中的白色区域_修正!【从零学习OpenCV 4】分割图像——分水岭法...

    点击上方"小白学视觉",选择"星标"公众号重磅干货,第一时间送达 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍<OpenCV 4开 ...

  6. task2:opencv的python接口图像储存、色彩空间、

    task2:图像储存.色彩空间.图像的算数运算. 笔记: 使用managers.WindowManager抽象窗口和键盘: main.py: import cv2 from manager impor ...

  7. 使用OpenCV和Python从图像中提取形状

    Welcome to the first post in this series of blogs on extracting features from images using OpenCV an ...

  8. youcans 的 OpenCV 学习课—4.图像的叠加与混合

    youcans 的 OpenCV 学习课-4.图像的叠加与混合 本系列面向 Python 小白,从零开始实战解说 OpenCV 项目实战. 本节介绍图像的加法.叠加与混合,提供完整例程和运行结果:加法 ...

  9. OpenCV中的图像处理 —— 图像阈值+图像平滑+形态转换

    OpenCV中的图像处理 -- 图像阈值+图像平滑+形态转换 目录 OpenCV中的图像处理 -- 图像阈值+图像平滑+形态转换 1. 图像阈值 1.1 简单阈值 1.2 自适应阈值 1.3 Otsu ...

最新文章

  1. php常用20函数,PHP常用函数
  2. 一键摸鱼神器火了!专为Windows系统打造,老板在身后也可以很淡定
  3. 百度吴恩达:图像和音视频搜索是人工智能新方向
  4. 工作中的git实际使用
  5. Spring / Hibernate使用log4jdbc改进了SQL日志记录
  6. Spring Cloud Sleuth进阶实战
  7. [Node.js] 模块化 -- 爬虫
  8. idea搭建springcloud项目_Eureka搭建分布式SpringCloud项目
  9. python logging模块使用详解
  10. 2005开启服务器文件夹,vss2005图文教你如何安装与配置?
  11. 启动虚拟机异常(超完整版)Win10
  12. 洛谷P4158 [SCOI2009]粉刷匠 题解
  13. 2020年开发人员的机器学习基础
  14. 阿波罗java_携程Apollo(阿波罗)安装部署以及java整合实现
  15. java常问面试题总结
  16. 树莓派4B的引脚控制简单demo
  17. 【Meetup讲师】您有一张社区认证讲师证书未领取,点击领取!
  18. 华为云 —— 购买与登录弹性云服务器ECS
  19. nginx阻挡flv视频文件上传解决方案
  20. centos系统安装中文字体几种方法

热门文章

  1. 程序员如何高效提升学习能力?做到这三点,你就会与众不同
  2. php调色板快捷键,ps常用的调色快捷键有哪些?
  3. QString、int、char、QByteArray直接的相互转换
  4. 计算机信息专业考研院校排名,2017年电子信息工程专业考研院校排名
  5. 计算机考研院校所,计算机考研院校全国七大地区考研名校推荐
  6. 我的世界服务器无限漏斗,我的世界漏斗bug 我的世界怎么刷东西
  7. selenium模拟登录12306
  8. 深入理解JVM:java对象的创建过程?
  9. 著名专家托马斯·拉姆什评孙宇晨出任格林纳达常驻WTO代表
  10. 工作流程管理系统,表结构与运行机制