matlab与opencv基本操作

前言

本文讲解matlab与opencv对图像处理的基础操作,代码会有matlab与python两版,可对比学习。

读写

读入图像

matlab

1
I = imread('cameraman.jpg');

python

1
I = cv2.imread('cameraman.jpg', cv2.IMREAD_COLOR)

cv2.IMREAD_COLOR = 强制读成 3 通道 BGR 彩色图。

存入图像

matlab

1
imwrite(J,'cameramanC.jpg');

python

1
cv2.imwrite('cameramanC.jpg', J)

读图并转 double

matlab

1
Image1 = im2double(imread('lotus.jpg'));
  • imread 把图像读成 0-255 的 uint8
  • im2double 把像素值线性缩放到 0–1 浮点,方便后续计算。

为什么要转成double

如果像素是 0–255,你在代码里写 0.299*r 就永远只用到 0.299×255≈76 灰度级,结果会整体偏暗甚至直接截断。

几级灰度的含义是什么

几级灰度”这句话里的“级”就是“台阶”的意思: 把黑→白这段连续亮度等间隔切成多少份,就有多少个离散台阶,叫多少灰度级

  • 2 级灰度 → 纯黑 + 纯白,一共 2 个台阶(1 bit)
  • 8 级灰度 → 0, 36, 73, 109, 146, 182, 219, 255(3 bit)
  • 256 级灰度 → 0–255,共 256 个台阶(8 bit)

uint8 是“Unsigned Integer, 8 bit”的缩写,含义一句话:

无符号、8 位、整型数字,只能放 0–255 的整数。

图像操作

反色

1
J=255-I;

提取通道

matlab

1
2
3
r = Image1(:,:,1);
g = Image1(:,:,2);
b = Image1(:,:,3);

分别提取rgb三个通道

opencv

1
2
matlab:g = Image1(1,1,2)提取G通道的第一个像素点
opencv:g = image[0,0,1]

opencv是从下标0开始

合并通道

NTSC 标准

1
Y = 0.299*r + 0.587*g + 0.114*b;

0.299、0.587、0.114 是 NTSC 在 1953 年定下的“亮度加权系数”,源于人眼三种视锥细胞对 R、G、B 光谱的灵敏度——绿最亮、红次之、蓝最暗

二值化(阈值 0.3)

1
2
BW = zeros(size(Y));   % 先全黑
BW(Y > 0.3) = 1; % 亮度>0.3 的像素置 1(白)

从RGB颜色空间转换成HSI颜色空间

matlab

1
hsi = rgb2hsv(img);

HSI 颜色空间是把一幅彩色图像的每个像素拆成 3 个独立、且更符合人眼感知习惯的物理量:

  1. H(Hue,色调) 用 0°–360° 的“角度”表示“到底是什么颜色”。 例:0°≈红,120°≈绿,240°≈蓝,绕一圈回到红。
  2. S(Saturation,饱和度) 0–1(或 0–100%)表示“颜色有多鲜艳”。 0 → 灰,1 → 最纯、最艳。
  3. I(Intensity,亮度/强度) 0–1(或 0–255)表示“有多亮”,与颜色本身无关,只反映明暗。
  • RGB 是“机器友好”的立方体坐标,但人眼很难从 (R,G,B) 直接说出“颜色、多艳、多亮”。

matlab基本操作

获取图像行列和通道数

1
[N,M,~]=size(r);

把二维矩阵 r 的“行数”赋给 N,“列数”赋给 M

截取图像

1
newimage=img(H/4:H*3/4,W/4:W*3/4,:);

人脸肤色检测

YCrCb 阈值法

  1. Y “Luma”——亮度(Luminance)。 对应人眼最敏感的黑↔︎白信息,决定了你看到的“明暗”。 计算公式近似:

    1
    Y = 0.299·R + 0.587·G + 0.114·B
  2. Cr “Chroma-red”——红色色度。 表示 红色与亮度的差值Cr = R – Y

  3. Cb “Chroma-blue”——蓝色色度。 表示 蓝色与亮度的差值Cb = B – Y

人眼对 绿色最敏感,对 红、蓝最迟钝。 把“绿色”信息扔掉,只保留 R-YB-Y 两个差值,就能 最小化数据量,同时 还能把颜色还原回来

  • 人类肤色在 YCrCb 颜色空间呈明显的“聚类”特性:无论人种如何,Cb、Cr 两个色度分量都落在狭长带状区域。
  • 选取经验区间(Cr ∈ [133,173],Cb ∈ [77,127])直接做 2D 门限,亮度 Y 不参与判断,因此对光照强度变化不敏感,但对色偏敏感。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def skin_YCrCb(img):
#把一张 BGR 彩色图像从“蓝-绿-红”颜色空间转换到“亮度-红度-蓝度”颜色空间(YCrCb)
ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
min_YCrCb = np.array([0, 133, 77])
max_YCrCb = np.array([255, 173, 127])
'''
把落在 [min_YCrCb, max_YCrCb] 立方体内的像素标为 255(白),其余标为 0(黑),返回一张单通道掩膜图。
执行过程(逐像素):
取 ycrcb 的一个像素 (y, cr, cb)
检查是否同时满足
min_Y ≤ y ≤ max_Y
min_Cr ≤ cr ≤ max_Cr
min_Cb ≤ cb ≤ max_Cb
满足 → 输出 255;任一通道不满足 → 输出 0
'''
skin = cv2.inRange(ycrcb, min_YCrCb, max_YCrCb)
return skin

HSV 阈值法

肤色聚类现象 大量统计表明:

  • 不同人种、不同光照 的肤色在 RGB 空间里沿对角线“灰度轴”散开 → 亮度影响大。
  • 转到 HSV 后,Hue 坐标紧紧挤在 0-20° 之间(红-橙-黄),S 中等偏高V 中等偏亮
1
2
3
4
5
6
def skin_HSV(img):
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
low = np.array([0, 30, 60])
high = np.array([20, 150, 255])
skin = cv2.inRange(hsv, low, high)
return skin

椭圆模型法

原理:将RGB图像转换到YCRCB空间,肤色像素点会聚集到一个椭圆区域。先定义一个椭圆模型,然后将每个RGB像素点转换到YCRCB空间比对是否再椭圆区域,是的话判断为皮肤。

image-20250923091302411
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ---------------- 3. 椭圆模型法 ----------------
def skin_ellipse(img):
ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
# 如果本地没有模型图,现场画一张 256×256 查找表
ellipse_model = cv2.imread('ellipse_skin_model.png', 0)
if ellipse_model is None:
ellipse_model = np.zeros((256, 256), dtype=np.uint8)
cv2.ellipse(ellipse_model, (113, 155), (23, 15),
43, 0, 360, 255, -1)
cr = ycrcb[:, :, 1].astype(np.uint16) # 防止溢出
cb = ycrcb[:, :, 2].astype(np.uint16)
indices = cr * 256 + cb
skin = np.take(ellipse_model, indices)
return skin

将彩色图像 Image1 的 R、B 通道互换

1
img_swap = img(:,:,[3 1 2]);

MATLAB 中,读取彩色图像(如用 imread)默认是 RGB 顺序

  • 第1通道:Red(红)
  • 第2通道:Green(绿)
  • 第3通道:Blue(蓝)

但在 OpenCV(Python/C++) 中,图像默认是 BGR 顺序

  • 第1通道:Blue
  • 第2通道:Green
  • 第3通道:Red

使用或操作,进行图像的嵌入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
clear; clc; close all;  % 清空所有变量
% 读取两张图片
background = imread('图片2.png');
butterfly = imread('图片1.png');

% 设置缩放比例
scale = 0.5;
small_butterfly = imresize(butterfly, scale, 'bilinear');

% 获取尺寸
[ih, iw, ~] = size(small_butterfly);
x = 200; y = 400;

% 提取背景中对应区域
bg_patch = background(x:x+ih-1, y:y+iw-1, :);

is_black = any(small_butterfly~=0,3); % 蝴蝶主体区域

%只把背景中“蝴蝶主体对应位置”设为 0
% 获取尺寸
[ih, iw, ~] = size(bg_patch);

% 双重循环遍历每个像素
for i = 1:ih
for j = 1:iw
if is_black(i, j) % 如果该位置是蝴蝶主体(非黑)
bg_patch(i, j, 1) = 0; % R 通道设为 0
bg_patch(i, j, 2) = 0; % G 通道设为 0
bg_patch(i, j, 3) = 0; % B 通道设为 0
end
end
end

%现在做 bitor:0 | butterfly = butterfly,背景黑区 | 0 = 背景
patch_bitor = bitor(bg_patch, small_butterfly);

% 写回背景
background(x:x+ih-1, y:y+iw-1, :) = patch_bitor;

% 显示结果
figure;
subplot(1,3,1), imshow(small_butterfly), title(['缩小后的蝴蝶 (', num2str(scale*100), '%)']);
subplot(1,3,2), imshow(bg_patch), title('处理后的背景块(蝴蝶位置清黑)');
subplot(1,3,3), imshow(background), title('最终合成图(bitor)');
image-20251016114415946

利用单尺度和多尺度 Retinex 增强方法实现图像增强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
I = imread('gugong1.jpg');

if ndims(I) == 3
I_gray = rgb2gray(I);
else
I_gray = I;
end

I_double = double(I_gray) + eps;

% SSR
sigma = 15;
L_ssr = log(conv2(I_double, fspecial('gaussian', [31, 31], sigma), 'same'));
R_ssr = log(I_double) - L_ssr;
ssr_img = mat2gray(R_ssr);

% MSR
sigmas = [15, 80, 250];
weights = [0.33, 0.34, 0.33];
R_msr = zeros(size(I_double));
for k = 1:length(sigmas)
sigma = sigmas(k);
kernel_size = round(6 * sigma) + 1;
if mod(kernel_size, 2) == 0
kernel_size = kernel_size + 1;
end
L = log(conv2(I_double, fspecial('gaussian', [kernel_size, kernel_size], sigma), 'same'));
R_msr = R_msr + weights(k) * (log(I_double) - L);
end
msr_img = mat2gray(R_msr);

figure;
subplot(1,3,1); imshow(I); title('Original');
subplot(1,3,2); imshow(ssr_img); title('SSR');
subplot(1,3,3); imshow(msr_img); title('MSR');
image-20251104095521796

直方图均衡化计算

image-20251112090645146
image-20251112090723928
image-20251112090714359

5.4代码

opencv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import numpy as np
import matplotlib.pyplot as plt

# --- Step 1: 构造原始图像 ---
gray_levels = np.array([0, 1/7, 2/7, 3/7, 4/7, 5/7, 6/7, 1]) # 原始灰度值 (0~1)
pixel_counts = np.array([560, 920, 1046, 705, 356, 267, 170, 72])
total_pixels = 64 * 64 # 4096

# 将灰度值映射到整数 0~7 用于图像存储
int_gray_levels = np.round(gray_levels * 7).astype(int) # [0,1,2,3,4,5,6,7]

# 创建图像数组
img = np.zeros(total_pixels, dtype=np.uint8)

start_idx = 0
for i, count in enumerate(pixel_counts):
img[start_idx:start_idx + count] = int_gray_levels[i]
start_idx += count

img = img.reshape((64, 64)) # 重塑为 64x64 图像

# --- Step 2: 直方图均衡化 ---
hist, _ = np.histogram(img, bins=8, range=(0, 8))
cdf = np.cumsum(hist) / total_pixels
mapping = np.round(cdf * 7).astype(int)
equalized_img = mapping[img]

# --- Step 3: 可视化对比 ---
plt.figure(figsize=(8, 4))

plt.subplot(1, 2, 1)
plt.imshow(img, cmap='gray', vmin=0, vmax=7)
plt.title('Original')
plt.axis('off')

plt.subplot(1, 2, 2)
plt.imshow(equalized_img, cmap='gray', vmin=0, vmax=7)
plt.title('Equalized')
plt.axis('off')

plt.tight_layout()
plt.show()