OpenCV基础(18)使用 OpenCV 和 Python 进行自动色彩校正

上周我们实现了如何进行直方图匹配。使用直方图匹配,我们可以获取一幅图像的颜色分布并将其与另一幅图像匹配。

色彩匹配的一个实际应用是通过色彩恒常性来执行基本色彩校正。颜色恒定性的目标是正确感知物体的颜色,而不管光源、照明等的差异(正如您想象的那样,说起来容易做起来难)。

摄影师和计算机视觉从业者可以通过使用颜色校正卡来帮助获得颜色稳定性,比如下面这张:
在这里插入图片描述
使用色彩校正/色彩恒常卡,我们可以:

  • 检测输入图像中的色彩校正卡
  • 计算卡片的直方图,其中包含不同颜色、色调、阴影、黑色、白色和灰色的渐变颜色
  • 将色卡的直方图匹配应用到另一幅图像,从而尝试实现颜色恒常性

在本指南的最后,您将了解如何使用颜色校正卡与直方图匹配来构建基本的颜色校正器的基础知识,而不管图像是在什么样的光照条件下捕获的。

1.使用 OpenCV 和 Python 进行自动色彩校正

在本教程的第一部分,我们将讨论什么是色彩校正和色彩恒定性,包括 OpenCV 如何促进自动色彩校正。 然后我们将为这个项目配置我们的开发环境并查看我们的项目目录结构。 然后,我们将实现一个利用 OpenCV 执行色彩校正的 Python 脚本。

2.什么是自动色彩校正?

人类视觉系统受到光照和光源的显着影响。颜色恒常性是指研究人类如何感知颜色。

例如,请查看维基百科关于颜色恒常性的文章中的以下图片:
在这里插入图片描述
看着这张卡片,似乎粉红色的阴影(左起第二个)比底部的粉红色要强得多——但事实证明,它们是相同的颜色!

两张卡具有相同的 RGB 值。然而,我们人类的色彩感知系统会受到照片其余部分的偏色(即在其上应用暖红色滤镜)的影响。

如果我们寻求标准化我们的图像处理环境,这会产生一些问题:为受控条件下捕获的图像编写代码要比在没有保证的动态条件下编写代码容易得多。

如果我们可以尽可能多地控制我们的图像捕获环境,那么编写代码来分析和处理这些从受控环境捕获的图像就会更容易。

假设我们可以控制环境的照明条件。在这种情况下,我们可以放弃昂贵的计算机视觉/深度学习算法,这有助于我们在非理想条件下获得理想的结果。相反,我们利用基本的图像处理程序,允许我们对参数进行硬编码,包括高斯模糊大小、Canny 边缘检测阈值等。

基本上,在受控环境中,我们可以使用基本的图像处理算法,这些算法更容易实现。问题是,我们需要对照明条件进行安全假设。色彩校正和白色平衡帮助我们实现这一点。

有一种方法可以帮助我们控制环境,即使光照条件改变了一点,那就是应用色彩校正。

色卡是摄影师最喜欢的工具:
在这里插入图片描述
摄影师将这些卡片放入拍摄的场景中。然后他们拍照,调整他们的灯光(同时仍然保持卡在相机的视野),然后继续拍摄,直到他们完成。

拍摄完成后,他们回到自己的电脑,将照片转移到他们的系统中,并使用Adobe Lightroom等工具在整个拍摄过程中实现颜色的一致性(如果你感兴趣,这里有一个关于如何做这个过程的教程)。

当然,作为计算机视觉从业者,我们没有使用 Adob​​e Lightroom 的奢侈,也不想通过手动调整色彩平衡来启动/停止我们的管道——这违背了使用软件来自动化现实世界流程的全部目的。

相反,利用这些相同的色彩校正卡,再加上一些直方图匹配,我们可以构建一个能够执行色彩校正的系统。

在本文的其余部分,您将使用直方图匹配和颜色校正卡(来自Pantone)来执行基本的颜色校正。

3.Pantone颜色校正卡

在这里插入图片描述
这张卡类似于摄影师使用的色彩校正卡,但Pantone 却用它来帮助消费者将场景中感知到的颜色与Pantone 销售的一种色调(最相似的颜色)相匹配。

其大意是:

  • 1.您将颜色校正卡放在要匹配的阴影上
  • 2.在手机上打开Pantone 的智能手机应用程序
  • 3.你拍一张卡片的照片
  • 4.该应用程序自动检测卡片,进行颜色匹配,然后返回Pantone 销售的最相似的色调

出于我们的目的,我们将严格使用该卡进行色彩校正(但您可以根据需要轻松扩展它)。

4.项目结构

虽然颜色匹配和颜色校正可能看起来是一个复杂的过程,但我们会发现,我们将能够用不到 100 行代码(包括注释)完成整个项目。
但是在开始写代码之前,让我们先回顾一下我们的项目目录结构。
在这里插入图片描述
我们今天要查看一个 Python 脚本,color_correction.py。该脚本将:

  • 1.加载我们的 reference.png 图像(其中包含我们的 Pantone 色彩校正卡)
  • 2.加载示例目录中的图像之一(我们将对其进行颜色校正以匹配 reference.png 的颜色)
  • 3.通过ArUco标记检测参考图像和输入图像中的颜色匹配卡
  • 4.应用直方图匹配来完善颜色校正过程

5.使用 OpenCV 实现自动色彩校正

5.1代码

链接:https://pan.baidu.com/s/1ja5RZUiV5Hyu-Z65JEJWzg 
提取码:123a
# color_correction.py
#  用法
# python color_correction.py --reference reference.jpg --input examples/03.jpg
# 导入相关库
from imutils.perspective import four_point_transform
from skimage import exposure
import numpy as np
import argparse
import imutils
import cv2
import sys

def find_color_card(image):
    # 加载ArUCo字典,获取ArUCo参数并检测输入图像中的标记
    arucoDict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_ARUCO_ORIGINAL)
    arucoParams = cv2.aruco.DetectorParameters_create()
    (corners, ids, rejected) = cv2.aruco.detectMarkers(image, arucoDict, parameters=arucoParams)
    # 尝试提取颜色校正卡的坐标
    try:
        # 否则,这意味着四个ArUCo标记已经被发现,因此继续扁平化ArUCo id列表
        ids = ids.flatten()
        # 提取左上角的标记
        i = np.squeeze(np.where(ids == 923))
        topLeft = np.squeeze(corners[i])[0]
        # 提取右上角的标记
        i = np.squeeze(np.where(ids == 1001))
        topRight = np.squeeze(corners[i])[1]
        # 提取右下角的标记
        i = np.squeeze(np.where(ids == 241))
        bottomRight = np.squeeze(corners[i])[2]
        # 提取左下角的标记
        i = np.squeeze(np.where(ids == 1007))
        bottomLeft = np.squeeze(corners[i])[3]
    # 如果校色卡找不到了
    except:
        return None
    # 构建参考点列表并应用透视变换以获得配色卡的自上而下的鸟瞰图
    cardCoords = np.array([topLeft, topRight, bottomRight, bottomLeft])
    card = four_point_transform(image, cardCoords)
    # 将颜色匹配卡返回给调用函数
    return card


# 构造参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-r", "--reference", required=True, help="Path to the input reference image")
ap.add_argument("-i", "--input", required=True, help="Path to the input image to apply color correction to")
args = vars(ap.parse_args())

# 从磁盘加载参考图像和输入图像
print("[INFO] Loading images...")
ref = cv2.imread(args["reference"])
image = cv2.imread(args["input"])

# 调整参考和输入图像的大小
ref = imutils.resize(ref, width=600)
image = imutils.resize(image, width=600)

# 在屏幕上显示参考和输入图像
cv2.imshow("Reference", ref)
cv2.imshow("Input", image)

# 找出每张图片中的配色卡
print("[INFO] Finding color matching cards...")
refCard = find_color_card(ref)
imageCard = find_color_card(image)

# 如果在参考或输入图像中都没有找到配色卡,请退出程序
if refCard is None or imageCard is None:
    print("[INFO] Could not find color matching cards in both images! Exiting...")
    sys.exit(0)

# 分别在参考图像和输入图像中显示颜色匹配卡
cv2.imshow("Reference Color Card", refCard)
cv2.imshow("Input Color Card", imageCard)

# 将参考图像中的颜色匹配卡的直方图匹配应用到输入图像中的颜色匹配卡
print("[INFO] Matching images...")
imageCard = exposure.match_histograms(imageCard, refCard, multichannel=True)

# 直方图匹配后显示输入的颜色匹配卡
cv2.imshow("Input Color Card After Matching", imageCard)
cv2.waitKey(0)

5.2代码解析

打开项目目录结构中的 color_correction.py 文件,让我们开始工作:

# 导入库
from imutils.perspective import four_point_transform
from skimage import exposure
import numpy as np
import argparse
import imutils
import cv2
import sys

导入我们需要的 Python 包。值得注意的包括:

  • four_point_transform:应用透视变换以获得输入配色卡的自上而下的鸟瞰图。
  • exposure:包含来自 scikit-image 的直方图匹配函数。
  • imutils:使用 OpenCV 执行图像处理的一组便利函数。
  • cv2:OpenCV Python库。

导入后,我们可以继续定义 find_color_card 函数,该方法负责在输入图像中定位 Pantone 颜色匹配卡:

def find_color_card(image):
	# 加载 ArUCo 字典,获取 ArUCo 参数,并检测输入图像中的标记
	arucoDict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_ARUCO_ORIGINAL)
	arucoParams = cv2.aruco.DetectorParameters_create()
	(corners, ids, rejected) = cv2.aruco.detectMarkers(image,
		arucoDict, parameters=arucoParams)

我们的 find_color_card 函数只需要一个参数 image,它是包含我们的颜色匹配卡的图像。执行 ArUco 标记检测以找到配色卡上的四个 ArUco 标记。
接下来,让我们按左上角、右上角、右下角和左下角的顺序(应用自上而下的透视变换所需的顺序)对四个 ArUco 标记进行排序:

# 尝试提取颜色校正卡的坐标
	try:
		# 否则,我们已经找到了四个 ArUco 标记,因此我们可以通过展平 ArUco ID 列表来继续
		ids = ids.flatten()
		# 提取左上角标记
		i = np.squeeze(np.where(ids == 923))
		topLeft = np.squeeze(corners[i])[0]
		# 提取右上角的标记
		i = np.squeeze(np.where(ids == 1001))
		topRight = np.squeeze(corners[i])[1]
		# 提取右下角标记
		i = np.squeeze(np.where(ids == 241))
		bottomRight = np.squeeze(corners[i])[2]
		# 提取左下角标记
		i = np.squeeze(np.where(ids == 1007))
		bottomLeft = np.squeeze(corners[i])[3]
	# 找不到校色卡,返回
	except:
		return None

首先,我们将整个代码块包装在一个 try/except 块中。我们这样做是为了防止使用 np.where 调用无法检测到所有四个标记。如果只有一个 np.where 调用失败,Python 将抛出错误。

我们的 try/except 块将捕获错误并返回 None,这意味着无法找到色彩校正卡。

否则,按左上角、右上角、右下角和左下角的顺序提取每个单独的 ArUco 标记。

注:您可能想知道我是如何知道每个标记的id将是923、1001、241和1007的?在下一篇关于ArUco标记检测的教程中将解决这个问题。

如果我们找到了所有四个 ArUco 标记,我们现在可以应用透视变换:

# 建立我们的参考点列表并应用透视变换以获得配色卡的自上而下的鸟瞰图
	cardCoords = np.array([topLeft, topRight,
		bottomRight, bottomLeft])
	card = four_point_transform(image, cardCoords)
	# 将配色卡返回给调用函数
	return card

从我们的 ArUco 标记坐标构建一个 NumPy 数组,然后应用four_point_transform 函数以获得颜色校正卡的自上而下的鸟瞰图。

卡片的自顶向下视图返回给调用函数。

实现了 find_color_card 函数后,让我们继续解析命令行参数:

# 构造参数解析器并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-r", "--reference", required=True,
	help="path to the input reference image")
ap.add_argument("-i", "--input", required=True,
	help="path to the input image to apply color correction to")
args = vars(ap.parse_args())

为了进行颜色匹配,我们需要两张图片:

  • reference 图像的路径包含我们想要校正任何输入图像的“理想”条件下的输入场景。
  • input 图像的路径,我们假设它具有不同的颜色分布,大概是由于光照条件的变化。

我们的目标是获取 --input 图像并执行颜色匹配,使其分布与 --reference 图像的分布相匹配。

但在此之前,我们需要从磁盘加载参考图像和源图像:

# 从磁盘加载参考图像和输入图像
print("[INFO] loading images...")
ref = cv2.imread(args["reference"])
image = cv2.imread(args["input"])
# 调整参考和输入图像的大小
ref = imutils.resize(ref, width=600)
image = imutils.resize(image, width=600)
# 在我们的屏幕上显示参考和输入图像
cv2.imshow("Reference", ref)
cv2.imshow("Input", image)

从磁盘加载我们的输入图像,将它们调整为 600 像素的宽度(以更快地处理图像)来预处理它们。

然后将原始参考和图像显示到我们的屏幕上。

加载图像后,现在让我们将 find_color_card 函数应用于两个图像:

# 在每个图像中找到颜色匹配卡
print("[INFO] finding color matching cards...")
refCard = find_color_card(ref)
imageCard = find_color_card(image)
# 如果在参考图像或输入图像中都没有找到配色卡,请退出
if refCard is None or imageCard is None:
	print("[INFO] could not find color matching card in both images")
	sys.exit(0)

尝试在参考和图像中找到颜色匹配卡。

如果我们在任一图像中都找不到颜色匹配卡,我们退出脚本。

否则,我们可以放心地假设我们找到了颜色匹配卡,所以让我们应用颜色校正:

# 分别在参考图像和输入图像中显示颜色匹配卡
cv2.imshow("Reference Color Card", refCard)
cv2.imshow("Input Color Card", imageCard)
# 将参考图像中配色卡的直方图匹配应用于输入图像中的配色卡
print("[INFO] matching images...")
imageCard = exposure.match_histograms(imageCard, refCard,
	multichannel=True)
# 在直方图匹配后显示我们输入的颜色匹配卡
cv2.imshow("Input Color Card After Matching", imageCard)
cv2.waitKey(0)

将我们的 refCardimageCard 显示到我们的屏幕上。

然后我们应用 match_histograms 函数将颜色分布从 refCard 传输到 imageCard

最后,经过直方图匹配后的输出 imageCard 显示在我们的屏幕上。这个新的 imageCard 现在包含原始 imageCard 的颜色校正版本。

6.自动色彩校正结果

在这里插入图片描述
左 : 输 入 图 像 。 右 : 参 考 图 像 。 左:输入图像。右:参考图像。

在这里插入图片描述
左 : 检 测 参 考 图 像 中 的 配 色 卡 。 中 : 从 输 入 图 像 中 提 取 色 卡 。 右 : 应 用 颜 色 匹 配 后 的 输 出 。 左:检测参考图像中的配色卡。中:从输入图像中提取色卡。右:应用颜色匹配后的输出。

参考目录

https://www.pyimagesearch.com/2021/02/15/automatic-color-correction-with-opencv-and-python/

  • 13
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
立体校正(Stereo Rectification)是将双目图像中的像素点转换成同一平面上的像素点,以便进行后续的立体匹配。使用Python-OpenCV可以很方便地进行立体校正。下面是一个简单的示例代码: ```python import cv2 # 读取左右两张图像 img_left = cv2.imread('left.png') img_right = cv2.imread('right.png') # 读取相机参数 K_left = np.loadtxt('K_left.txt') d_left = np.loadtxt('dist_left.txt') K_right = np.loadtxt('K_right.txt') d_right = np.loadtxt('dist_right.txt') R = np.loadtxt('R.txt') T = np.loadtxt('T.txt') # 计算校正映射 size = img_left.shape[:2][::-1] R1, R2, P1, P2, Q, validPixROI1, validPixROI2 = cv2.stereoRectify(K_left, d_left, K_right, d_right, size, R, T) # 计算校正映射表 map1_left, map2_left = cv2.initUndistortRectifyMap(K_left, d_left, R1, P1, size, cv2.CV_16SC2) map1_right, map2_right = cv2.initUndistortRectifyMap(K_right, d_right, R2, P2, size, cv2.CV_16SC2) # 应用校正映射表,进行立体校正 img_left_rect = cv2.remap(img_left, map1_left, map2_left, cv2.INTER_LINEAR) img_right_rect = cv2.remap(img_right, map1_right, map2_right, cv2.INTER_LINEAR) ``` 在上述代码中,我们首先读取了左右两张图像和相机参数,然后使用`cv2.stereoRectify()`函数计算校正映射。该函数的参数包括左右相机的内参矩阵、畸变系数、旋转矩阵和平移向量,以及图像大小。校正映射包括左右两张图像的旋转矩阵、投影矩阵和视差变换矩阵等信息,可以通过该函数的返回值获取。 接着,我们使用`cv2.initUndistortRectifyMap()`函数计算校正映射表。该函数的参数包括相机的内参矩阵、畸变系数、旋转矩阵、投影矩阵、图像大小和插值方法等。该函数的返回值是两个映射表,可以通过这两个映射表对图像进行校正。 最后,我们使用`cv2.remap()`函数对左右两张图像进行校正。该函数的参数包括原图像、映射表和插值方法等。校正后得到的图像可以用于后续的立体匹配。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值