手打有点太累了 这篇文章用gpt进行辅助翻译 恳请理解!
本文章对应OpenCV免费公开课第五节 Image Enhancement。课程链节OpenCV官网 配合官网视频教程效果更好。
记得关注专栏啊!最近跟新会很频繁的!
图像处理技术利用数学运算来实现不同的效果。我们通常通过一些基本操作来得到图像的增强版本。在本文中,我们将介绍一些经常在计算机视觉中使用的基本操作。我们将涵盖以下内容:
- 算术运算,如加法和乘法
- 阈值化和掩模
- 位运算,如OR、AND、XOR
准备
下载素材
通过一下命令 理论上可以下载到所需的所有素材。
def download_and_unzip(url, save_path):
print(f"Downloading and extracting assests....", end="")
# Downloading zip file using urllib package.
urlretrieve(url, save_path)
try:
# Extracting zip file using the zipfile package.
with ZipFile(save_path) as z:
# Extract ZIP file contents in the same directory.
z.extractall(os.path.split(save_path)[0])
print("Done")
except Exception as e:
print("\nInvalid file.", e)
URL = r"https://www.dropbox.com/s/0oe92zziik5mwhf/opencv_bootcamp_assets_NB4.zip?dl=1"
asset_zip_path = os.path.join(os.getcwd(), "opencv_bootcamp_assets_NB4.zip")
# Download if assest ZIP does not exists.
if not os.path.exists(asset_zip_path):
download_and_unzip(URL, asset_zip_path)
导入库
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from zipfile import ZipFile
from urllib.request import urlretrieve
from IPython.display import Image //让图像可以直接在jupyter中显示
%matplotlib inline //让图像可以直接在jupyter中显示
原始图片
img_bgr = cv2.imread("New_Zealand_Coast.jpg", cv2.IMREAD_COLOR)
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
# 展示图片
Image(filename="New_Zealand_Coast.jpg")
本节课 我们将使用这张图片作为部分样例的图片。
(使用)加法(修改)亮度
我们讨论的第一个操作是简单的图像加法。这会导致图像亮度的增加或减少,因为我们最终是通过相同的量增加或减少每个像素的强度值。因此,这将导致全局亮度的增加或减少。
matrix = np.ones(img_rgb.shape, dtype="uint8") * 50
img_rgb_brighter = cv2.add(img_rgb, matrix)
img_rgb_darker = cv2.subtract(img_rgb, matrix)
# 展示图片
plt.figure(figsize=[18, 5])
plt.subplot(131); plt.imshow(img_rgb_darker); plt.title("Darker");
plt.subplot(132); plt.imshow(img_rgb); plt.title("Original");
plt.subplot(133); plt.imshow(img_rgb_brighter);plt.title("Brighter");
此处使用np 创建了一个img_rgb大小的数据全部为50的数组。并且使用
cv2.add()
函数将他增加到原始图片上完成了亮度的增加。
(使用)乘法(修改)对比度
就像加法可以导致亮度变化一样,乘法可以用来提高图像的对比度。
对比度是图像中像素强度值的差异。将强度值乘以一个常数可以使差异变大或变小(如果乘数 < 1)。
matrix1 = np.ones(img_rgb.shape) * 0.8
matrix2 = np.ones(img_rgb.shape) * 1.2
img_rgb_darker = np.uint8(cv2.multiply(np.float64(img_rgb), matrix1))
img_rgb_brighter = np.uint8(cv2.multiply(np.float64(img_rgb), matrix2))
# Show the images
plt.figure(figsize=[18,5])
plt.subplot(131); plt.imshow(img_rgb_darker); plt.title("Lower Contrast");
plt.subplot(132); plt.imshow(img_rgb); plt.title("Original");
plt.subplot(133); plt.imshow(img_rgb_brighter);plt.title("Higher Contrast");
同样是创建了一个img_rgb大小的的值为1.2和0.8的np数组,将其乘到原来的数据上就可以将不同像素之间的差值变大 以此来实现对比度增加。
运行代码后,最后一张图像输出为
发生了什么?
你能在图像的某些区域看到乘法后的奇怪颜色吗?
造成这个问题的原因是,在进行乘法后,之前已经很高的值变得大于255。因此,出现了溢出问题。我们如何解决这个问题呢?
通过np.clip解决溢出问题
matrix1 = np.ones(img_rgb.shape) * 0.8
matrix2 = np.ones(img_rgb.shape) * 1.2
img_rgb_lower = np.uint8(cv2.multiply(np.float64(img_rgb), matrix1))
img_rgb_higher = np.uint8(np.clip(cv2.multiply(np.float64(img_rgb), matrix2), 0, 255))
# Show the images
plt.figure(figsize=[18,5])
plt.subplot(131); plt.imshow(img_rgb_lower); plt.title("Lower Contrast");
plt.subplot(132); plt.imshow(img_rgb); plt.title("Original");
plt.subplot(133); plt.imshow(img_rgb_higher);plt.title("Higher Contrast");
clip的作用就是将高于设定的值全部改为最高值
此时图片可以正常显示变化
不过请注意 使用这种方式的变换会丢失高光信息!
图像阈值化
二值图像在图像处理中的用途很多。最常见的用途之一是创建掩模。图像掩模允许我们处理图像的特定部分,同时保持其他部分不变。图像阈值化用于将灰度图像转换为二值图像。您可以使用不同的阈值从同一原始图像创建不同的二值图像。
函数语法
retval, dst = cv2.threshold(src, thresh, maxval, type[, dst])
dst
:输出数组,大小和类型与src
相同,通道数相同。
该函数有4个必需参数:
src
:输入数组(多通道、8位或32位浮点)。thresh
:阈值。maxval
:与THRESH_BINARY和THRESH_BINARY_INV阈值类型一起使用的最大值。type
:阈值类型(见ThresholdTypes)。
函数语法
dst = cv.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst])
dst
:输出图像,与src
大小和类型相同。
该函数有6个必需参数:
src
:输入的8位单通道图像。maxValue
:满足条件的像素赋值的非零值。adaptiveMethod
:自适应阈值算法,见AdaptiveThresholdTypes。BORDER_REPLICATE | BORDER_ISOLATED用于处理边界。thresholdType
:阈值类型,必须是THRESH_BINARY或THRESH_BINARY_INV之一,见ThresholdTypes。blockSize
:用于计算像素阈值值的像素邻域大小:3、5、7,等等。C
:从平均值或加权平均值中减去的常数(见下文详细说明)。通常为正,但也可以为零或负。
OpenCV文档
img_read = cv2.imread("building-windows.jpg", cv2.IMREAD_GRAYSCALE)
retval, img_thresh = cv2.threshold(img_read, 100, 255, cv2.THRESH_BINARY)
# 展示图片
plt.figure(figsize=[18, 5])
plt.subplot(121);plt.imshow(img_read, cmap="gray"); plt.title("Original")
plt.subplot(122);plt.imshow(img_thresh, cmap="gray");plt.title("Thresholded")
print(img_thresh.shape)
运行结果
使用二值化函数可以方便的分辨边界和对一些特征的分别
应用程序:乐谱读取器
假设你想构建一个能够读取(解码)乐谱的应用程序。这类似于文本文档的光学字符识别(OCR),其目标是识别文本字符。在这两种应用中,处理管线的第一步之一是将文档图像中的重要信息分离出来(将其与背景分开)。这一任务可以通过阈值技术来完成。让我们来看一个例子。
# 读取原始图像
img_read = cv2.imread("Piano_Sheet_Music.png", cv2.IMREAD_GRAYSCALE)
# 执行全局阈值化
retval, img_thresh_gbl_1 = cv2.threshold(img_read, 50, 255, cv2.THRESH_BINARY)
# 执行全局阈值化
retval, img_thresh_gbl_2 = cv2.threshold(img_read, 130, 255, cv2.THRESH_BINARY)
# 执行自适应阈值处理
img_thresh_adp = cv2.adaptiveThreshold(img_read, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 7)
# Show the images
plt.figure(figsize=[18,15])
plt.subplot(221); plt.imshow(img_read, cmap="gray"); plt.title("Original");
plt.subplot(222); plt.imshow(img_thresh_gbl_1,cmap="gray"); plt.title("Thresholded (global: 50)");
plt.subplot(223); plt.imshow(img_thresh_gbl_2,cmap="gray"); plt.title("Thresholded (global: 130)");
plt.subplot(224); plt.imshow(img_thresh_adp, cmap="gray"); plt.title("Thresholded (adaptive)");
运行结果
此处可以发现 有的时候由于原始图像存在阴影等干扰项 我们无法使用一个单一的值进行全局阈值化处理 此时我们可以使用自适应阈值处理 通常情况下可以通过这种方式进行文件增强
位运算
函数语法
下方展示的为cv2.bitwise_and()
API 。其他包括:cv2.bitwise_or()
,cv2.bitwise_xor()
,cv2.bitwise_not()
。他们拥有相同的语法
dst = cv2.bitwise_and(src1, src2[, dst[, mask]])
dst
:输出数组,具有与输入数组相同的大小和类型。
该函数有 2 个必需参数:
src1
:第一个输入数组或标量。src2
:第二个输入数组或标量。
一个重要的可选参数是:
mask
:可选操作掩码,8 位单通道数组,指定要更改的输出数组的元素。
OpenCV文档
img_rec = cv2.imread("rectangle.jpg", cv2.IMREAD_GRAYSCALE)
img_cir = cv2.imread("circle.jpg", cv2.IMREAD_GRAYSCALE)
plt.figure(figsize=[20, 5])
plt.subplot(121);plt.imshow(img_rec, cmap="gray")
plt.subplot(122);plt.imshow(img_cir, cmap="gray")
print(img_rec.shape)
and 运算符
result = cv2.bitwise_and(img_rec, img_cir, mask=None)
plt.imshow(result, cmap="gray")
or 运算符
result = cv2.bitwise_or(img_rec, img_cir, mask=None)
plt.imshow(result, cmap="gray")
xor运算符
result = cv2.bitwise_xor(img_rec, img_cir, mask=None)
plt.imshow(result, cmap="gray")
此处就不展示图片了 需要的自己运行代码即可
and or xor等位运算不理解的自行百度即可
应用程序:标志操作
在本节中,我们将向您展示如何使用背景图像填充下面可口可乐标志中的白色字母。
读取前景图片
img_bgr = cv2.imread("coca-cola-logo.png")
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
print(img_rgb.shape)
logo_w = img_rgb.shape[0]
logo_h = img_rgb.shape[1]
读取背景图片
# 读取图片
img_background_bgr = cv2.imread("checkerboard_color.png")
img_background_rgb = cv2.cvtColor(img_background_bgr, cv2.COLOR_BGR2RGB)
# 设置所需宽度 (logo_w) 并保持图像的纵横比
aspect_ratio = logo_w / img_background_rgb.shape[1]
dim = (logo_w, int(img_background_rgb.shape[0] * aspect_ratio))
# 使用resize将图片缩放为前景图片的大小
img_background_rgb = cv2.resize(img_background_rgb, dim, interpolation=cv2.INTER_AREA)
plt.imshow(img_background_rgb)
print(img_background_rgb.shape)
创建原始图片的蒙版
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)
# 使用全局阈值化创建logo的蒙版
retval, img_mask = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
plt.imshow(img_mask, cmap="gray")
print(img_mask.shape)
翻转蒙版
img_mask_inv = cv2.bitwise_not(img_mask)
plt.imshow(img_mask_inv, cmap="gray")
此处也可以重新阈值化原始图片 不过现在使用的这种方法更简便和高效
将背景应用于蒙版上
# 在标志字母“后面”创建彩色背景
img_background = cv2.bitwise_and(img_background_rgb, img_background_rgb, mask=img_mask)
plt.imshow(img_background)
从图像中分离前景
# 使用反向掩码分离前景(从原始图像中的红色)
img_foreground = cv2.bitwise_and(img_rgb, img_rgb, mask=img_mask_inv)
plt.imshow(img_foreground)
结果:合并前景和背景
# 将前两个结果相加即可得到最终结果
result = cv2.add(img_background, img_foreground)
plt.imshow(result)
cv2.imwrite("logo_final.png", result[:, :, ::-1])
note:因为黑色区域为0 所以不会对相加后的结果造成任何影响
课后练习
原文为OpenCV免费公开课Free OpenCV Course - Official Certification by OpenCV
翻译 TallentJoe
未经允许,禁止转载
答案:DBBA