RGB与YCBCR颜色空间转换及python实现

2023-10-22 10:40

本文主要是介绍RGB与YCBCR颜色空间转换及python实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 问题描述
  • 解决方案
  • 原理
    • 0. 图像数据表示
          • 像素的概念
          • 像素的取值及表示
          • 彩色像素
          • 图像坐标系
    • 1. 灰度值与亮度的关系
    • 2. RGB颜色空间与颜色控制
    • 3. YCbCr颜色空间及与RGB的变换关系

问题描述

在处理鱼类行为图像时,遇到这样一个问题,拍摄的鱼类视频数据,经过背景相减、中值滤波的结果如下图所示:
在这里插入图片描述
在这里插入图片描述
可以看到,由于水面光源倒影反光的问题,导致在将鱼类进行分割的时候,总是会把光源算进去,就很烦。

解决方案

在17年浙江大学赵建博士论文《循环水养殖游泳型鱼类精准投喂研究》中,提到了这样一段话:

利用背景去除法对复杂实验环境下的鱼群目标前景分割具有重要意义。图2.2为本章研究中典型的鱼群目标前景分割流程。首先,将待处理图像由RGB色彩模型(图2.2a)转化至YCbCr模型(图2.2b);其次,将Cr分量下的图片信息提取出(图2.2c),因为相对于其他分量下的纹理信息,Cr分量下鱼群前景信息与背景信息纹理差异更明显。
在这里插入图片描述

好了,问题来了,为什么“在将RGB色彩模型转化至TCbCr模型之后,相对于其他分量下的纹理信息,Cr分量下鱼群前景信息与北京信息纹理差异更明显?

原理

0. 图像数据表示

以下内容来自图像数据表示

像素的概念

图像是由很多个小格子组成的,每个小格子都只有一种颜色,这是构成图像的最小单元——像素(pixel)。

像素的取值及表示

不同的像素值代表了不同的颜色,像素值的值域一般在0到255(包括)之间,也就是256个整数,因此可以用完整个unsigned char类型的值域,所以像素值一般都是用unsigned char类型表示。

但0-255并不能映射到像上图所示的彩色,而只是对应黑色到白色之间的灰度值(grayscale),如下图:
在这里插入图片描述

彩色像素

饱和的红绿蓝三种颜色叠加起来就是白色,假如其中一种颜色不那么“饱和”则可以表示其他的颜色,调节三种颜色的比例则可以表示我们常看到的24位色。灰度值的颜色空间在几何上可以用一根直线表示,而RGB彩色空间在几何上则对应了一个立方体,如下图:

在这里插入图片描述
要表示彩色值,我们需要3个维度,也就是3个图像通道,每个像素值用3个数字表示,如(255,255,255)表示白色,(255,0,0)表示红色,(255,255,0)表示黄色。

图像坐标系

在这里插入图片描述
图片中的值用numpy的数据结构表示图像,img这个numpy.array的第一个维度沿着行方向,第二个维度沿着列方向,第三个维度沿着通道

  • img[0, :]可以表示图像的第一行所有像素
  • img[…, 0]可以表示图像的第一个通道所有像素

以下内容来自RGB与YCBCR颜色空间转换及python实现

1. 灰度值与亮度的关系

人类能够从灰度图像中获取理解场景需要的大部分信息,所以看黑白电视机并不会严重影响人对视频中场景的理解。图像的亮度和像素值成正比(越亮的话,像素值就越大),如果需要增加图像的亮度,比如从黑色逐渐过渡到白色,就可以对单通道的灰度图像素值进行增加来实现。保存灰度图像的每个像素值一般采用8个bit,像素值的范围为0-255。

下面的例子展示了灰度图像的像素值增加时亮度的变化过程,假设图像初始像素值为0:
在这里插入图片描述
上面显示了lena图像像素值增加时肤色的变化。代码的实现比较简单,读取图片,然后不断的对图像的每个像素值增加偏移量:

import numpy as np
import matplotlib.pyplot as plt
import imageio
image = imageio.imread("lena.jpg")
# 设置每次循环像素的增加量
shift = 6*np.ones(shape=(64, 64))
plt.figure()for i in range(1, 17):plt.subplot(4, 4, i)plt.imshow(image/255, cmap="gray", vmin=0, vmax=1)plt.axis("off")image = image + shift

2. RGB颜色空间与颜色控制

RGB模型在硬件设备中被广泛的使用,通过R(红色)、G(绿色)、B(蓝色)三者进行叠加可以形成更多的颜色。RGB颜色空间和后面将要进行介绍的YCbCr颜色空间和HSV颜色空间存在线性的变换关系,所以只要拥有RGB图像就能得到其它颜色空间的图像
一幅图像中R、G、B分别作为三个通道,如果某两个通道的值为0,图像的颜色就会被不为零的那个通道控制。比如:
在这里插入图片描述
实现上面的效果需要三个步骤:

(1)创建一幅3通道的空图像

(2)给3通道空图像的R通道添加一幅单通道图像

(3)给3通道图像的R通道像素值不断增加偏移量

# 1:创建一幅3通道的空图像
= np.zeros(shape=(64, 64, 3))
r = imageio.imread("lena.jpg")/2
# 2:给3通道空图像的R通道添加一幅单通道图像
image[:, :, 0] = image[:, :, 0] + r
shift = 4*np.ones(shape=(64, 64))
plt.figure()for i in range(1, 17):plt.subplot(4, 4, i)plt.imshow(image/255, vmin=0, vmax=1)plt.axis("off")# (3)给3通道图像的R通道像素值不断增加偏移量image[:, :, 0] = image[:, :, 0] + shift

但是,由于最终图像呈现出的颜色是三R\G\B三者的叠加,而现实中不仅仅是其中之一的颜色,所以很难控制最终图像的颜色,所以我们需要其它的颜色空间。

3. YCbCr颜色空间及与RGB的变换关系

YCbCr颜色空间中的Y是亮度通道,Cb是蓝色分量,Cr是红色分量。它在电视系统中比较常用,比如早期的黑白电视机使用彩色电视信号线,就可以单独使用亮度值;这种功能RGB颜色空间就做不到,因为我们不能仅仅使用RGB中某个通道作为亮度信号来使用。

由于YCbCr经常和YUV颜色空间比较相似,所以二者容易被认为是从属或者等价关系,按照维基百科的说法:YUV 是模拟信号,而YCbCr是数字信号。

YCbCr和RGB存在线性的变换关系,本文介绍的变换矩阵来自ITU.BT-601,所规定的变换矩阵Trans形式如下:
在这里插入图片描述
实现rgb2ycbcr()函数只需要两个步骤:(1)创建变换矩阵Trans;(2)遍历图像每个像素点,并对三个通道分别进行矩阵计算。下面的代码展示了如何实现从RGB空间到YCBCR变换:

def rgb2ycbcr(rgb_image):"""convert rgb into ycbcr"""if len(rgb_image.shape)!=3 or rgb_image.shape[2]!=3:raise ValueError("input image is not a rgb image")rgb_image = rgb_image.astype(np.float32)# 1:创建变换矩阵,和偏移量transform_matrix = np.array([[0.257, 0.564, 0.098],[-0.148, -0.291, 0.439],[0.439, -0.368, -0.071]])shift_matrix = np.array([16, 128, 128])ycbcr_image = np.zeros(shape=rgb_image.shape)w, h, _ = rgb_image.shape# 2:遍历每个像素点的三个通道进行变换for i in range(w):for j in range(h):ycbcr_image[i, j, :] = np.dot(transform_matrix, rgb_image[i, j, :]) + shift_matrix       return ycbcr_image

如果想要求逆变换,只需要根据矩阵求逆法则进行就可以了,需要注意的是:逆变换时偏移矩阵也需要左乘变换矩阵Trans的逆!逆变换只需要将rgb2ycbcr中的transform_matrix求逆即可,再次强调:shift_matrix也需要乘以transform_matrix的逆,而不是直接减去shift_matrix!

def ycbcr2rgb(ycbcr_image):"""convert ycbcr into rgb"""if len(ycbcr_image.shape)!=3 or ycbcr_image.shape[2]!=3:raise ValueError("input image is not a rgb image")ycbcr_image = ycbcr_image.astype(np.float32)transform_matrix = np.array([[0.257, 0.564, 0.098],[-0.148, -0.291, 0.439],[0.439, -0.368, -0.071]])transform_matrix_inv = np.linalg.inv(transform_matrix)shift_matrix = np.array([16, 128, 128])rgb_image = np.zeros(shape=ycbcr_image.shape)w, h, _ = ycbcr_image.shapefor i in range(w):for j in range(h):rgb_image[i, j, :] = np.dot(transform_matrix_inv, ycbcr_image[i, j, :]) - np.dot(transform_matrix_inv, shift_matrix)return rgb_image.astype(np.uint8)

所需要的包以及绘图代码如下,绘图用到的就是上面定义的两个函数。首先将rgb转为ycbcr,在从ycbcr转为rgb:

import numpy as np
import imageio
import matplotlib.pyplot as plt
rgb_image = imageio.imread("lena.jpg")
ycbcr_image = rgb2ycbcr(rgb_image)
cycle_image = ycbcr2rgb(ycbcr_image)
images = [rgb_image, ycbcr_image, cycle_image]
titles = ["orignal", "ycbcr", "cycle"]
for i in range(1, len(images)+1):plt.subplot(1, 3, i)plt.title(titles[i-1])plt.imshow(images[i-1]/255)

下图中左边是原始的rgb图像,中间是转换得到的ycbcr空间图像,右边是再次转回rgb空间的图像:
在这里插入图片描述
最后,对比了opencv提供的标准库的转换效果:

import cv2
rgb_image = imageio.imread("lena.jpg")
ycrcb_image = cv2.cvtColor(rgb_image, cv2.COLOR_RGB2YCR_CB)
cycle_image = cv2.cvtColor(ycbcr_image, cv2.COLOR_YCR_CB2RGB)
images = [rgb_image, ycrcb_image, cycle_image]
titles = ["orignal", "ycrcb", "cycle"]
for i in range(1, len(images)+1):plt.subplot(1, 3, i)plt.title(titles[i-1])plt.imshow(images[i-1]/255)

在这里插入图片描述
原始rgb效果和cycle(重构)效果很接近,而中间结果不一致是因为opencv采用的是“ycrcb”,而不是“ycbcr”。

这篇关于RGB与YCBCR颜色空间转换及python实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/260893

相关文章

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

Python正则表达式匹配和替换的操作指南

《Python正则表达式匹配和替换的操作指南》正则表达式是处理文本的强大工具,Python通过re模块提供了完整的正则表达式功能,本文将通过代码示例详细介绍Python中的正则匹配和替换操作,需要的朋... 目录基础语法导入re模块基本元字符常用匹配方法1. re.match() - 从字符串开头匹配2.

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

通过Docker容器部署Python环境的全流程

《通过Docker容器部署Python环境的全流程》在现代化开发流程中,Docker因其轻量化、环境隔离和跨平台一致性的特性,已成为部署Python应用的标准工具,本文将详细演示如何通过Docker容... 目录引言一、docker与python的协同优势二、核心步骤详解三、进阶配置技巧四、生产环境最佳实践

Python一次性将指定版本所有包上传PyPI镜像解决方案

《Python一次性将指定版本所有包上传PyPI镜像解决方案》本文主要介绍了一个安全、完整、可离线部署的解决方案,用于一次性准备指定Python版本的所有包,然后导出到内网环境,感兴趣的小伙伴可以跟随... 目录为什么需要这个方案完整解决方案1. 项目目录结构2. 创建智能下载脚本3. 创建包清单生成脚本4

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

Nginx部署HTTP/3的实现步骤

《Nginx部署HTTP/3的实现步骤》本文介绍了在Nginx中部署HTTP/3的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前提条件第一步:安装必要的依赖库第二步:获取并构建 BoringSSL第三步:获取 Nginx

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详