在图像处理的世界里,颜色操作就像是一个魔术师的基本功。今天,让我们一起来解锁这些有趣又实用的"魔法"吧!
在计算机视觉中,我们经常会遇到RGB和BGR两种颜色格式。它们就像是"外国人"和"中国人"的称呼顺序,一个是姓在后,一个是姓在前。
对于一个彩色图像 I I I,其RGB通道可以表示为:
I R G B = [ R G B ] I_{RGB} = \begin{bmatrix} R & G & B \end{bmatrix} IRGB=[RGB]
通道替换操作可以用矩阵变换表示:
I B G R = I R G B [ 0 0 1 0 1 0 1 0 0 ] I_{BGR} = I_{RGB} \begin{bmatrix} 0 & 0 & 1 \\ 0 & 1 & 0 \\ 1 & 0 & 0 \end{bmatrix} IBGR=IRGB 001010100
// C++实现
vector<Mat> channels;
split(src, channels);
vector<Mat> new_channels = {
channels[2], // R
channels[1], // G
channels[0] // B
};
# Python实现
b, g, r = cv2.split(img)
result = cv2.merge([r, g, b])
将彩色图像转换为灰度图像,就像是把一幅油画变成素描。我们使用加权平均的方法,因为人眼对不同颜色的敏感度不同。
标准RGB到灰度的转换公式:
Y = 0.2126 R + 0.7152 G + 0.0722 B Y = 0.2126R + 0.7152G + 0.0722B Y=0.2126R+0.7152G+0.0722B
这个公式来自于ITU-R BT.709标准,考虑了人眼对不同波长光的敏感度。更一般的形式是:
Y = ∑ i ∈ { R , G , B } w i ⋅ C i Y = \sum_{i \in \{R,G,B\}} w_i \cdot C_i Y=i∈{R,G,B}∑wi⋅Ci
其中 w i w_i wi 是权重系数, C i C_i Ci 是对应的颜色通道值。
// C++实现
result.at<uchar>(y, x) = static_cast<uchar>(
0.2126 * r + 0.7152 * g + 0.0722 * b
);
二值化就像是给图像下"最后通牒":要么是黑色,要么是白色,没有中间地带!
数学表达式:
g ( x , y ) = { 255 , if f ( x , y ) > T 0 , if f ( x , y ) ≤ T g(x,y) = \begin{cases} 255, & \text{if } f(x,y) > T \\ 0, & \text{if } f(x,y) \leq T \end{cases} g(x,y)={255,0,if f(x,y)>Tif f(x,y)≤T
其中:
// C++实现
result.at<uchar>(y, x) = (gray.at<uchar>(y, x) > threshold) ? 255 : 0;
大津算法就像是一个"智能裁判",能自动找到最佳的分割阈值。它通过最大化类间方差来实现这一目标。
类间方差的计算公式:
σ B 2 ( t ) = ω 0 ( t ) ω 1 ( t ) [ μ 0 ( t ) − μ 1 ( t ) ] 2 \sigma^2_B(t) = \omega_0(t)\omega_1(t)[\mu_0(t) - \mu_1(t)]^2 σB2(t)=ω0(t)ω1(t)[μ0(t)−μ1(t)]2
其中:
最优阈值的选择:
t ∗ = arg max t { σ B 2 ( t ) } t^* = \arg\max_{t} \{\sigma^2_B(t)\} t∗=argtmax{σB2(t)}
// 计算类间方差
double variance = wBack * wFore * pow(meanBack - meanFore, 2);
HSV色彩空间更符合人类对颜色的感知方式,就像是把RGB这个"理工男"变成了更感性的"艺术家"。
RGB到HSV的转换公式:
V = max ( R , G , B ) V = \max(R,G,B) V=max(R,G,B)
S = { V − min ( R , G , B ) V , if V ≠ 0 0 , if V = 0 S = \begin{cases} \frac{V-\min(R,G,B)}{V}, & \text{if } V \neq 0 \\ 0, & \text{if } V = 0 \end{cases} S={VV−min(R,G,B),0,if V=0if V=0
H = { 60 ( G − B ) / Δ , if V = R 120 + 60 ( B − R ) / Δ , if V = G 240 + 60 ( R − G ) / Δ , if V = B H = \begin{cases} 60(G-B)/\Delta, & \text{if } V = R \\ 120 + 60(B-R)/\Delta, & \text{if } V = G \\ 240 + 60(R-G)/\Delta, & \text{if } V = B \end{cases} H=⎩ ⎨ ⎧60(G−B)/Δ,120+60(B−R)/Δ,240+60(R−G)/Δ,if V=Rif V=Gif V=B
其中 Δ = V − min ( R , G , B ) \Delta = V - \min(R,G,B) Δ=V−min(R,G,B)
// 手动实现RGB到HSV的转换
for (int y = 0; y < src.rows; y++) {
for (int x = 0; x < src.cols; x++) {
Vec3b pixel = src.at<Vec3b>(y, x);
float b = pixel[0] / 255.0f;
float g = pixel[1] / 255.0f;
float r = pixel[2] / 255.0f;
float maxVal = max(max(r, g), b);
float minVal = min(min(r, g), b);
float diff = maxVal - minVal;
// 计算H
float h = 0;
if (diff != 0) {
if (maxVal == r) {
h = 60 * (fmod(((g - b) / diff), 6));
} else if (maxVal == g) {
h = 60 * ((b - r) / diff + 2);
} else {
h = 60 * ((r - g) / diff + 4);
}
if (h < 0) h += 360;
}
// 计算S
float s = (maxVal == 0) ? 0 : diff / maxVal;
// 计算V
float v = maxVal;
// 转换到OpenCV的HSV范围
result.at<Vec3b>(y, x) = Vec3b(
static_cast<uchar>(h / 2), // H: [0, 180]
static_cast<uchar>(s * 255), // S: [0, 255]
static_cast<uchar>(v * 255) // V: [0, 255]
);
}
}
记住:颜色操作是图像处理的基础,掌握好这些操作,就像掌握了调色盘的魔法!