基于区域生长和形态学处理的图像融合方法——Matlab图像处理


✅ 大三下时弄的



最终效果图

在这里插入图片描述
注: 鼠标点击,选完 “点” 后,需要再按一下 “回车”。


摘要

● 本次从日常生活的实际需要出发,主要研究了图像的分割转换及重组部分与原图像的融合,基于区域生长法和形态学处理的方法可以将图像的背景转换为其他想要替换的图片,并通过边缘融合等方法将转换后的背景与原图像进行调整,减轻违和感以达到更好的显示效果。相比于人工手动抠图的方法,这种处理方法更加方便简洁,极大地简化了修图的繁琐步骤,学习基础低,容易上手使用,并且大大缩短了同类型修图的时间,处理速度较快,故而可以广泛应用于日常的照片处理。



1 研究背景及意义

● 在生活中,人们通常使用摄影来记录自己的各种日常,拍照已经成为生活里不可缺少的重要组成部分,融入了每个人的每个生活场景与欢声笑语,每张照片都有其特定的含义,传递着每个人的情感。可是有时候天气不好,拍出来的照片常常会因为背景昏暗、色调阴沉而不够美观,就譬如去景区旅游的时候,如果当天是阴天或者雨天,照片中的天空就是灰蒙蒙的,整张图片也会显得暗淡,这种情况难免会让人觉得可惜。

● 本次研究了一种自动处理图像的方法,可以将拍摄于阴雨天的照片中灰蒙蒙的天空转换成蓝天白云、夕阳西下或者是彩虹以及其他想要的背景,并将整体的色调调整成更明亮的显示效果。这种技术的操作十分简单上手,处理速度较快,可以很成功地对照片进行背景转换,同时优化整合两部分内容,使得处理后的图片看起来更加和谐,视觉效果增强,极大地省略了人工手动修图的繁琐步骤,缩短了同类型图像进行修图的时间,在日常的照片处理方面有重要意义,用数字图像处理的技术解决了生活实际问题。



2 基本原理描述

(1)首先选择一张将要处理的图像,将其从 unit 类型转化为 double 类型,再将其转换为灰度图像。

(2)在灰度图像的背景上选择一个或多个生长点,然后用区域生长法将目标和背景进行分割。应用区域生长算法时,要根据不同的图像特征合理地选取其生长点、生长半径和生长阈值。如果有树枝类遮挡物,那生长半径需要选大一点。如果前景和后景灰度值相差较小,则需使用较大的生长阈值。

(3)得到一个二值图像之后,再用形态学处理填充未分隔成功的孔洞,得到最终的二值图像。

(4)根据此二值图像构造出前景底片和后景底片,分别用于目标和新背景的贴印。另外通过边缘检测算法画出边缘图。

(5)将目标和新背景分别与前景底片和后景底片相乘得到两个“底图”,然后将这两张“底图”相加即可得到拼接后的图像。

(6)然后将新背景的 RGB 转换为 HSV,获取其亮度,再将其亮度融合到目标所在的“底图”,即实现对两张图像的融合。

(7)最后根据第三步得到边缘图的边缘线,来对图像进行边缘处理和图像修复,从而得到最终的融合图像。

基本原理流程图:
在这里插入图片描述



3 实验数据来源

3.1 原始图像的来源

● 本次课程考核大作业一共选取了三幅图像作为原始待处理的图像,图像类型为 RGB,前两幅图像来源于网络,最后一张是用手机拍摄后再用电脑调整分辨率后得到的。其中,“总统府”的分辨率为 960×720 像素,“长江边”的分辨率为 960×720 像素,“操场”的分辨率为 960×720 像素。三幅原始待处理的图像如图 3.1、图 3.2、图 3.3 所示。

图3.1 总统府:
在这里插入图片描述
图3.2 长江边:
在这里插入图片描述

图3.3 操场:
在这里插入图片描述

3.2 天空背景图像的来源

● 本次课程考核大作业一共选取了三幅天空背景图像对原始图像进行转,图像类型为 RGB,两幅图像分别为“晴天”“黄昏”“星空”,图像均来源于网络,其中“晴天”背景图像的分辨率为:960×720 像素,“黄昏”背景图像的分辨率为: 960×720 像素,“星空”背景图像的分辨率为 960×720 像素。三幅天空背景图像如图 3.4、图 3.5、图 3.6 所示。

图3.4 晴天:
在这里插入图片描述
图3.5 黄昏:
在这里插入图片描述
图3.6 星空:
在这里插入图片描述



4 实验步骤及相应处理结果

4.1 原始图像的预处理

● 由于后续的运算中数据类型都是 double 型,所以需要先对图像矩阵中数据的数据类型进行转换,将 unit 数据类型转换为 double 型。用 imread 函数读入原始图像,将 unit 数据类型转换为 double 型,然后用 imshow 函数显示原图像。相关程序代码如下。

%---------------原始图像读取(start)---------------
originalImageRGB = imread(originalImageFilename); % 读入原始图像
originalImageRGB = im2double(originalImageRGB); % 将 unit 类型转换为 double 类型 
if draw_flag == 0
	figure(1),imshow(originalImageRGB),title('原始图像');
else
	subplot(3,4,1),imshow(originalImageRGB),title('原始图像');
end
%---------------原始图像读取(end)---------------

● 读入后显示的图像如图 4.1 所示:
在这里插入图片描述
● 后续操作需要用区域生长法对图像进行分割,而 RGB 图像数据量大,进行区域生长复杂,为了提高程序运行的效率,减少程序的运算量,可以直接对灰度图像进行区域生长,所以将原始的 RGB 图像转换为灰度图像。使用 rgb2gray 函数将原始的 RGB 图像转换为灰度图像,灰度图像的数据类型转换为 double 型,最后用 imshow 函数显示灰度图像。相关程序代码如下。

%---------------原始RGB图像 → 灰度图像(start)---------------
grayImage = rgb2gray(originalImageRGB);
% imshow(grayImage),title('灰色图')
% imshow(originalImageRGB(:,:,1)),title('红色通道');
% imshow(originalImageRGB(:,:,2)),title('绿色通道');
% imshow(originalImageRGB(:,:,3)),title('蓝色通道');
if draw_flag == 0
	figure(2),imshow(grayImage),title('灰度图像');
else 
	subplot(3,4,2),imshow(grayImage),title('灰度图像');
end
%---------------原始RGB图像 → 灰度图像(end)-----------------

图像类型转换后显示的图像如图 4.2 所示:
在这里插入图片描述

4.2 区域生长法分割图像

● 完成图像的背景转换需要把原始图像的背景与目标预留部分进行分离,在比较多种分割方法后,选择采用区域生长法来分割图像背景。区域生长法主要考虑像素及其空间邻域像素之间的关系,开始时确定一个或多个像素点作为种子,然后按某种相似性准则增长区域,将相邻的具有相似性质的像素或区域归并,从而逐步增长区域,直到没有可以归并的点或其他小区域为止。区域内像素的相似性度量可以是平均灰度值、纹理、颜色等信息,本次选择灰度值为区域内像素的相似性度量进行区域生长。

● 首先用 size 函数获取灰度图像的尺寸,然后用 getpts 函数(需要鼠标自行点击选取)选择种子点,用 round 将种子点的坐标取整,存放种子点的灰度值,根据原灰度图像的尺寸大小构造相同大小的全零矩阵以提高运算的效率,设置存储符合区域生长条件的点数初值为在屏幕中选取的种子点数 seeds_num,然后设置一个阈值 threshold,再根据嵌套的 for 循环和 if条件语句判断目标点以及其八零域的点是否满足生长规则,最后即可得到区域生长的二值图像结果,用 imshow 将其显示出来。

● 种子点和阈值的选择并不是固定不变的,在区域生长分割图像的过程中,可以根据区域生长的结果不断地对种子点和阈值 threshold 进行调整。同时对于生长半径r,也要根据图像的特张来选取。如果有“树枝类”遮挡物分割了背景,那么生长半径r应该适度地调大,直到出现满意的结果为止。相关程序代码如下。

图4.3 区域生长原理图:
在这里插入图片描述

%---------------区域生长算法(start)-----------------------
[seed_y,seed_x] = getpts; % 选取种子点(可以选取多个)
seed_x = round(seed_x); % 横纵坐标取整
seed_y = round(seed_y); 

seeds_num = length(seed_x); % 种子个数
seeds = [seed_x,seed_y]; % 各个种子的坐标

[Height,Width] = size(grayImage); % 获取图像的大小
Mark_1 = zeros(Height,Width); % 初始化标记矩阵(一开始均未标记)
total_gray = 0; 
for i=1:seeds_num
	total_gray = total_gray + grayImage(seed_x(i),seed_y(i));
	Mark_1(seed_x(i),seed_y(i)) = 1; % 将种子所在区域的点设为"已标记"
end
seed_mean_gray = total_gray ./ seeds_num; % 种子区域的灰度平均值

r = grow_radius; % 扫描半径(生长半径)
flag = true; % 生长条件的标志(结束时变为 flase)
i_sta = 1;
i_end = seeds_num;
threshold = grow_threshold;
seeds = [seeds;zeros(2000000,2)]; % 预分配, 提升运算时间
while flag
	flag = false;
	for i = i_sta:i_end
		x = seeds(i,1);
		y = seeds(i,2);
		for u = -r:r % 判断周围 r^2-1 个点是否符合生长规则
			for v = -r:r
			if x+u>0 && x+u<Height && y+v>0 && y+v<Width
				if Mark_1(x+u,y+v)==0 && abs(grayImage(x+u,y+v)-seed_mean_gray)<=threshold
					Mark_1(x+u,y+v) = 1;
					flag = true;
					seeds_num = seeds_num + 1;
					seeds(seeds_num,1) = x+u;
					seeds(seeds_num,2) = y+v;
					seed_mean_gray = (seed_mean_gray .* (seeds_num-1) + ...
					grayImage(x+u,y+v)) ./ seeds_num;
				end
			end
		end
	end
end
i_sta = i_end+1;
i_end = seeds_num;
end
if draw_flag == 0
	figure(3),imshow(Mark_1),title('区域生长分割后的二值图像');
else 
	subplot(3,4,3),imshow(Mark_1),title('区域生长分割后的二值图像');
end
%---------------区域生长算法(end)-----------------------

图4.4 区域生长分割后的二值图像:
在这里插入图片描述

4.3 形态学处理填充孔洞

● 在上一个步骤用区域生长法进行图像分割时,由于人为对种子点和阈值进行选择,得到的结果难免出现背景所对应的二值图像部分中存在空洞的现象,这种空洞对后期结果的影响是非常大的,所以需要用形态学处理对背景所对应的二值图像部分中的孔洞进行填充。形态学处理填充空洞选择imfill函数对得到的二值图像进行处理,然后显示填充孔洞之后的二值图像。相关代码如下。

%---------------孔洞填充(start)-----------------------
Mark_1=imfill(Mark_1,'holes');
if draw_flag == 0
	figure(4),imshow(Mark_1),title('填充孔洞后的二值图像');
else 
	subplot(3,4,4),imshow(Mark_1),title('填充孔洞后的二值图像');
end
%---------------孔洞填充(end)-------------------------

图4.5 填充孔洞后的二值图像:
在这里插入图片描述

4.4 边缘检测+根据二值图像构造RGB图像

● 原图像与背景图像均为 RGB 图像,进行区域生长和形态学处理填充孔洞之后得到的图像是一个二值图像,所以要根据此二值图像构造边缘图像和两个 RGB 图像(前景和后景)。之后会与后续步骤中与原图像、背景相乘以得到“底图”。
● 边缘检测原理图:遍历二值图像矩阵,对每一个点进行扫描,如果它周围存在一个与它值相反的像素点,就把它周围 8 个点都标记为边缘点。

图4.6 边缘检测原理图:
在这里插入图片描述
前后景提取原理: 先构造两个与原图像大小相同的全零矩阵,然后用嵌套的 for 循环对图像进行遍历,当一点像素值为 1 时,给第一个全零矩阵中这个位置的像素的 RGB 颜色分量全部赋值 1,给第二个全零矩阵中这个位置的像素的 RGB 颜色分量全部赋值 0;当一点像素值为 0 时,给第一个全零矩阵中这个位置的像素的 RGB 颜色分量全部赋值 0,给第二个全零矩阵中这个位置的像素的 RGB 颜色分量全部赋值 1,完成全部像素的遍历和赋值后,显示这两个 RGB 图像。这样就根据二值图像构造了两个 RGB 图像。相关程序代码如下。

%---------------边缘检测+原图前后景抠取(start)-----------------------
Edge = zeros(Height,Width);
edge_points = zeros(200000,2); % 预分配, 以提升运算时间
edge_points_sum = 0;
Mark_2 = zeros(Height,Width); % 准备放原图的前景
Mark_3 = zeros(Height,Width); % 准备放背景的后景
for i = 1:Height
	for j = 1:Width
		if Mark_1(i,j) == 0
			for u = -1:1
				for v = -1:1
					if i+u>0 && i+u<Height && j+v>0 && j+v<Width
						if Mark_1(i,j) == 0 && Mark_1(i+u, j+v) == 1 % 把边缘找出来(1代表白色)
							edge_points_sum = edge_points_sum + 1;
							edge_points(edge_points_sum, 1) = i;
							edge_points(edge_points_sum, 2) = j;
							edge_points(edge_points_sum, 1) = i+u;
							edge_points(edge_points_sum, 2) = j+v;
							Edge(i,j) = 1; 
							Edge(i+u, j+v) = 1;
						elseif Mark_1(i,j) == 1 && Mark_1(i+u, j+v) == 0
							Edge(i,j) = 0;
							Edge(i+u, j+v) = 0; 
						end
					end
				end
			end
		end
		if Mark_1(i,j) == 0
			Mark_2(i,j,1) = 1; % 白色是1
			Mark_2(i,j,2) = 1; % 白色是1
			Mark_2(i,j,3) = 1; % 白色是1
			Mark_3(i,j,1) = 0; % 黑色是0
			Mark_3(i,j,2) = 0; % 黑色是0
			Mark_3(i,j,3) = 0; % 黑色是0
		else
			Mark_2(i,j,1) = 0; 
			Mark_2(i,j,2) = 0; 
			Mark_2(i,j,3) = 0; 
			Mark_3(i,j,1) = 1; 
			Mark_3(i,j,2) = 1; 
			Mark_3(i,j,3) = 1; 
		end
	end
end
if draw_flag == 0
	figure(5),imshow(Edge),title('边缘检测图');
else
	subplot(3,4,5),imshow(Edge),title('边缘检测图');
end

if draw_flag == 0
	figure(6),imshow(Mark_2),title('准备放原图的前景底片(白色部分)');
else
	subplot(3,4,6),imshow(Mark_2),title('准备放原图的前景底片(白色部分)');
end
if draw_flag == 0
	figure(7),imshow(Mark_3),title('准备放背景的后景底片(白色部分)');
else
	subplot(3,4,7),imshow(Mark_3),title('准备放背景的后景底片(白色部分)');
end
originalImageRGB_1 = originalImageRGB .* Mark_2;
if draw_flag == 0
	figure(8),imshow(originalImageRGB_1),title('去除背景后并在前后景底片区域的图像');
else
	subplot(3,4,8),imshow(originalImageRGB_1),title('去除背景后并在前后景底片区域的图像');
end
%---------------边缘检测+原图前后景底片抠取(end)-----------------------

图4.7 综合图片组:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

● 对于新背景,我们做同样的处理,这里需要剪裁一下,使得新背景的大小和原图的一样。用剪裁后的新背景和后景底片相乘即得新的背景图像。

%---------------后景底片剪裁+抠取(start)-----------------------
backgroungImage = imread(backgroungImageFilename);
backgroungImage = im2double(backgroungImage);

backgroungImage = backgroungImage(1:Height,1:Width,:); % 按比例剪裁图片
backgroungImage_1 = backgroungImage .* Mark_3;
if draw_flag == 0
	figure(9),imshow(backgroungImage_1),title('在底片区域的背景图像');
else
	subplot(3,4,9),imshow(backgroungImage_1),title('在底片区域的背景图像');
end
%---------------后景底片剪裁+抠取(end)-------------------------

图4.8 在后景底片区域的背景图像:
在这里插入图片描述

4.5 图像拼接

● 由于去除后景后的图像中,背景部分的 [R,G,B]=[0,0,0] ,而在去除前景后的图像中,背景区域的 [R,G,B]=[0,0,0],所以将二者直接相加,即可将两张图片进行合成,得到拼接后的图像。相关程序代码如下。

%---------------图片拼接(start)---------------------
jointImage = originalImageRGB_1 + backgroungImage_1;
if draw_flag == 0
	figure(10),imshow(jointImage),title('拼接后的图像');
else
	subplot(3,4,10),imshow(jointImage),title('拼接后的图像');
end
%---------------图片拼接(end)-----------------------

图4.9 拼接后的图像:
在这里插入图片描述

4.6 图像整体融合

● 在对新获得的图像进行进一步处理的时候,先通过颜色空间转换将 RGB 转化为 HSV,然后选择提取原图片或者新背景图片的平均亮度作为主亮度值,并依据获得的主亮度值将拼接后得到的图像中所有像素的亮度重新赋值,再通过颜色空间转换重新变为 RGB,得到整体融合后的图像。相关程序代码如下。

图4.10 亮度融合原理图:
在这里插入图片描述

%---------------图像整体融合(start)---------------------
originalImageRGB_HSV = rgb2hsv(originalImageRGB); % 颜色空间转换: RGB → HSV
o_s = originalImageRGB_HSV(:,:,2); % 饱和度
o_v = originalImageRGB_HSV(:,:,3); % 亮度
[n,m] = size(o_s);
mean_o_s = sum(sum(o_s)) ./ (n * m);
[n,m] = size(o_v);
mean_o_v = sum(sum(o_v)) ./ (n * m);
backgroungImageRGB_HSV = rgb2hsv(backgroungImage);
b_s = backgroungImageRGB_HSV(:,:,2); % 饱和度
b_v = backgroungImageRGB_HSV(:,:,3); % 亮度
[n,m] = size(b_s);
mean_b_s = sum(sum(b_s)) ./ (n * m);
[n,m] = size(b_v);
mean_b_v = sum(sum(b_v)) ./ (n * m);
jointImage_HSV = rgb2hsv(jointImage); 
choose = input('主亮度选择,请输入0或1,0代表原图片,1代表背景图片:');
if choose == 0
	for i = 1:Height
		for j = 1:Width
			if Mark_2(i,j) == 0
				tmp1 = jointImage_HSV(i,j,3) .* mean_o_v ./ mean_b_v;
				if 0.95 < tmp1
					jointImage_HSV(i,j,3) = tmp1 .* 0.95;
				else
					jointImage_HSV(i,j,3) = tmp1;
				end
			end
		end
	end
else
	for i = 1:Height
		for j = 1:Width
			if Mark_2(i,j) == 1
				tmp1 = jointImage_HSV(i,j,3) ./ mean_o_v .* mean_b_v;
				if 0.95 < tmp1
					jointImage_HSV(i,j,3) = tmp1 .* 0.95;
				else
					jointImage_HSV(i,j,3) = tmp1;
				end
			end
		end
	end
end
jointImage = hsv2rgb(jointImage_HSV); % HSV → RGB 得到最终图片
if draw_flag == 0
	figure(11),imshow(jointImage),title('图像整体融合后的图像');
else
	subplot(3,4,11),imshow(jointImage),title('图像整体融合后的图像');
end
%---------------图像整体融合(end)---------------------

图4.11 整体融合后的图像:
在这里插入图片描述

4.7 边缘处理

● 最后对融合后的图像做细节处理,将每一个边缘点周围 8 个点的像素值(3 个通道)取平均值,作为该边缘点新的像素值。

图4.12 边缘处理原理图:
在这里插入图片描述

%---------------边缘处理(stert)---------------------
mix_r = mix_radius; % 融合边缘的半径选取
for i = 1:edge_points_sum
	x = edge_points(i,1);
	y = edge_points(i,2);
	R = 0;
	G = 0;
	B = 0;
	for u = -mix_r:mix_r
		for v = -mix_r:mix_r
			if x+u>0 && x+u<Height && y+v>0 && y+v<Width
				R = R + jointImage(x+u,y+v,1);
				G = G + jointImage(x+u,y+v,2);
				B = B + jointImage(x+u,y+v,3);
			end
		end
		jointImage(x,y,1) = R ./ (4*mix_r^2 + 4*mix_r^2);
		jointImage(x,y,2) = G ./ (4*mix_r^2 + 4*mix_r^2);
		jointImage(x,y,3) = B ./ (4*mix_r^2 + 4*mix_r^2);
	end
end
if draw_flag == 0
	figure(12),imshow(jointImage),title('边缘融合后的图像');
else
	subplot(3,4,12),imshow(jointImage),title('边缘融合后的图像');
end
%---------------边缘处理(end)-----------------------

图4.13 边缘处理后的图像:
在这里插入图片描述
图4.14 细节处理对比图(右边为已处理图):
在这里插入图片描述



5 实验结果分析

● 在对图像进行预处理时,将 unit 数据类型转换成了 double 型,有利于后续对图像的运算处理,再将原 RGB 图像转换成灰度图像并成功地显示了出来。

● 在使用区域生长法分割图像时,种子点和阈值的选择并不是固定不变的,在区域生长分割图像的过程中,可以根据区域生长的结果不断地对种子点和阈值进行调整,直到出现我们满意的结果为止,再将处理此图像的种子点和阈值设置为定值。有的时候二值图像中背景对应的部分会存在孔洞,是由于种子点和阈值的选择不恰当所引起的,但因为后续会对图像进行形态学处理,所以对于最后的实验结果影响并不大。

● 在对图像的形态学处理中,imfill 函数可以很好地对二值图像中背景对应的部分地孔洞进行填充,有效地解决了区域生长分割图像时产生的孔洞。

● 通过边缘检测,可以找出需要更换的背景部分的边缘。

● 在根据二值图像构造 RGB 图像时,成功地得到了基于二值图像的一对 RGB 图像,分别是准备放原图的前景底片(白色部分)和准备放背景的后景底片(白色部分),将准备放原图的前景底片(白色部分)与原 RGB 图像相点乘后,得到了去除背景后并在前景底片区域的图像。将 RGB 背景图像放进程序处理,使得 RGB 背景图像的尺寸与原图像尺寸相同。再将准备放背景的后景底片(白色部分)与背景图像相点乘后,得到了在后景底片区域的背景图像。然后将去除背景后并在前景底片区域的图像与后景底片区域的背景图像相加,成功地得到了拼接后的图像。

● 在图像的整体融合时,先通过颜色空间转换化为 HSV,再选择背景图片的亮度作为主亮度,改变拼接后的图像的亮度,使得原图亮度与背景亮度相近,然后通过颜色空间转换重新转换为 RGB,得到图像整体融合后的图像。

● 最后,通过边缘处理,使得拼接后图像拼接部分更加自然,得到最终图像。各部分程序均起到了作用,成功地实现了图像背景的转换与亮度融合。

6 总结与心得体会

● 通过这段时间的课程学习,深入了解了许多数字图像的相关知识,虽不能完全掌握图像处理技术,但在此次课程大作业中,我们把所学的知识付诸实践,选择做了一种基于区域生长和形态学处理转换图片背景的方法。它能够将照片中的背景替换为其他想要的背景,操作十分简单,处理速度快,可以成功地对照片进行背景转换,并通过边缘融合等技术,使得转换后的图片看起来更加和谐,减轻违和感。这种技术大大缩短了同类型修图的时间,可以广泛应用于日常照片的处理,对于现实生活问题具有实际意义。

● 在进行课程作业实验的过程中,我们选择通过区域生长法对原图像转换而来的灰度图像进行分割,得到了区分背景与目标的二值图像,并依据此二值图像构造一对 RGB 图像,通过这对 RGB 图像分别提取背景与预留的目标图像部分,再拼接完成图像的区域转换;而后在进一步处理获得的新图像的时候,提取想要替换的背景的信息,将所有像素的亮度重新赋值,并进行边缘融合,以使得新背景与预留图像相融合,实现更好的视觉效果。

● 但是在编写代码的过程中,我们也遇到了很多问题,例如对彩色图像处理还不是很熟练,对图像的形态学处理也不熟悉,但是可以快速去回顾理论课上所学的知识,然后对这些知识进一步地进行验证,并通过网络查找有关资料加以学习,以运用到实践中实现想要的特定功能。我们研究出的这个方法仍存在一些不足,还有许多地方可以进一步地优化,以达到更好的使用效果。比如说,对于生长阈值和生长半径需要人为地调整,这需要一定的调试经验后才能调出理想的结果。通过本次课程大作业的合作,我们每个人都受益匪浅。



7 完整代码

Matlab2020:

% 软件:MATLAB2020
clc;
clear;
close all;

%--------------初始值设定(start)-----------------
originalImageFilename = './test_img/river.jpg'; % 原图片(自己设定)
backgroungImageFilename = './test_img/Xing_stars.png'; % 背景图片(自己设定)
draw_flag = 0; % 画图选择(0或者1) 
grow_radius = 3; % 生长半径
grow_threshold = 0.13; % 生长阈值
mix_radius = 1 ; % 融合边缘的半径
%--------------初始值设定(end)-------------------

%---------------原始图像读取(start)---------------
originalImageRGB = imread(originalImageFilename); % 读入原始图像
originalImageRGB = im2double(originalImageRGB); % 将 unit 类型转换为 double 类型 
if draw_flag == 0
	figure(1),imshow(originalImageRGB),title('原始图像');
else
	subplot(3,4,1),imshow(originalImageRGB),title('原始图像');
end
%---------------原始图像读取(end)---------------

%---------------原始RGB图像 → 灰度图像(start)---------------
grayImage = rgb2gray(originalImageRGB);
% imshow(grayImage),title('灰色图')
% imshow(originalImageRGB(:,:,1)),title('红色通道');
% imshow(originalImageRGB(:,:,2)),title('绿色通道');
% imshow(originalImageRGB(:,:,3)),title('蓝色通道');
if draw_flag == 0
	figure(2),imshow(grayImage),title('灰度图像');
else 
	subplot(3,4,2),imshow(grayImage),title('灰度图像');
end
%---------------原始RGB图像 → 灰度图像(end)-----------------


%---------------区域生长算法(start)-----------------------
[seed_y,seed_x] = getpts; % 选取种子点(可以选取多个)
seed_x = round(seed_x); % 横纵坐标取整
seed_y = round(seed_y); 

seeds_num = length(seed_x); % 种子个数
seeds = [seed_x,seed_y]; % 各个种子的坐标

[Height,Width] = size(grayImage); % 获取图像的大小
Mark_1 = zeros(Height,Width); % 初始化标记矩阵(一开始均未标记)

total_gray = 0; 
for i=1:seeds_num
	total_gray = total_gray + grayImage(seed_x(i),seed_y(i));
	Mark_1(seed_x(i),seed_y(i)) = 1; % 将种子所在区域的点设为"已标记"
end
seed_mean_gray = total_gray ./ seeds_num; % 种子区域的灰度平均值

r = grow_radius; % 扫描半径(生长半径)
flag = true; % 生长条件的标志(结束时变为 flase)
i_sta = 1;
i_end = seeds_num;
threshold = grow_threshold;
seeds = [seeds;zeros(2000000,2)]; % 预分配, 提升运算时间
while flag
	flag = false;
	for i = i_sta:i_end
		x = seeds(i,1);
		y = seeds(i,2);
		for u = -r:r % 判断周围 r^2-1 个点是否符合生长规则
			for v = -r:r
				if x+u>0 && x+u<=Height && y+v>0 && y+v<=Width
					if Mark_1(x+u,y+v)==0 && abs(grayImage(x+u,y+v)-seed_mean_gray)<=threshold
						Mark_1(x+u,y+v) = 1;
						flag = true;
						seeds_num = seeds_num + 1;
						seeds(seeds_num,1) = x+u;
						seeds(seeds_num,2) = y+v;
						seed_mean_gray = (seed_mean_gray .* (seeds_num-1) + ...
						grayImage(x+u,y+v)) ./ seeds_num;
					end
				end
			end
		end
	end
	i_sta = i_end+1;
	i_end = seeds_num;
end
if draw_flag == 0
	figure(3),imshow(Mark_1),title('区域生长分割后的二值图像');
else 
	subplot(3,4,3),imshow(Mark_1),title('区域生长分割后的二值图像');
end
%---------------区域生长算法(end)-----------------------

%---------------孔洞填充(start)-----------------------
Mark_1=imfill(Mark_1,'holes');
if draw_flag == 0
	figure(4),imshow(Mark_1),title('填充孔洞后的二值图像');
else 
	subplot(3,4,4),imshow(Mark_1),title('填充孔洞后的二值图像');
end
%---------------孔洞填充(end)-------------------------


%---------------边缘检测+原图底片抠取(start)-----------------------
Edge = zeros(Height,Width);
edge_points = zeros(200000,2); % 预分配, 以提升运算时间
edge_points_sum = 0;
Mark_2 = zeros(Height,Width); % 准备放原图的底片
Mark_3 = zeros(Height,Width); % 准备放背景的底片
for i = 1:Height
	for j = 1:Width
		if Mark_1(i,j) == 0
			for u = -1:1
				for v = -1:1
					if i+u>0 && i+u<=Height && j+v>0 && j+v<=Width
						if Mark_1(i,j) == 0 && Mark_1(i+u, j+v) == 1 % 把边缘找出来(1代表白色)
							if Edge(i,j) == 0
								edge_points_sum = edge_points_sum + 1;
								edge_points(edge_points_sum, 1) = i;
								edge_points(edge_points_sum, 2) = j;
							end
							edge_points_sum = edge_points_sum + 1;
							edge_points(edge_points_sum, 1) = i+u;
							edge_points(edge_points_sum, 2) = j+v;
							Edge(i,j) = 1; 
							Edge(i+u, j+v) = 1;
							elseif Mark_1(i,j) == 1 && Mark_1(i+u, j+v) == 0 
								if Edge(i,j) == 0
									edge_points_sum = edge_points_sum + 1;
									edge_points(edge_points_sum, 1) = i;
									edge_points(edge_points_sum, 2) = j;
								end
								edge_points_sum = edge_points_sum + 1;
								edge_points(edge_points_sum, 1) = i+u;
								edge_points(edge_points_sum, 2) = j+v;
								Edge(i,j) = 1;
								Edge(i+u, j+v) = 1;
							end
						end
					end
				end
			end
			if Mark_1(i,j) == 0
				Mark_2(i,j,1) = 1; % 白色是1
				Mark_2(i,j,2) = 1; % 白色是1
				Mark_2(i,j,3) = 1; % 白色是1
				Mark_3(i,j,1) = 0; % 黑色是0
				Mark_3(i,j,2) = 0; % 黑色是0
				Mark_3(i,j,3) = 0; % 黑色是0
			else
				Mark_2(i,j,1) = 0; 
				Mark_2(i,j,2) = 0; 
				Mark_2(i,j,3) = 0; 
				Mark_3(i,j,1) = 1; 
				Mark_3(i,j,2) = 1; 
				Mark_3(i,j,3) = 1; 
		end
	end
end
if draw_flag == 0
	figure(5),imshow(Edge),title('边缘检测图');
else
	subplot(3,4,5),imshow(Edge),title('边缘检测图');
end

if draw_flag == 0
	figure(6),imshow(Mark_2),title('准备放原图的底片(白色部分)');
else
	subplot(3,4,6),imshow(Mark_2),title('准备放原图的底片(白色部分)');
end

if draw_flag == 0
	figure(7),imshow(Mark_3),title('准备放背景的底片(白色部分)');
else
	subplot(3,4,7),imshow(Mark_3),title('准备放背景的底片(白色部分)');
end

originalImageRGB_1 = originalImageRGB .* Mark_2;
if draw_flag == 0
	figure(8),imshow(originalImageRGB_1),title('去除背景后并在底片区域的图像');
else
	subplot(3,4,8),imshow(originalImageRGB_1),title('去除背景后并在底片区域的图像');
end
%---------------边缘检测+原图底片抠取(end)-----------------------


%---------------背景底片剪裁+抠取(start)-----------------------
backgroungImage = imread(backgroungImageFilename);
backgroungImage = im2double(backgroungImage);

backgroungImage = backgroungImage(1:Height,1:Width,:); % 按比例剪裁图片
backgroungImage_1 = backgroungImage .* Mark_3;
if draw_flag == 0
	figure(9),imshow(backgroungImage_1),title('在底片区域的背景图像');
else
	subplot(3,4,9),imshow(backgroungImage_1),title('在底片区域的背景图像');
end
%---------------背景底片剪裁+抠取(end)-------------------------


%---------------图片拼接(start)---------------------
jointImage = originalImageRGB_1 + backgroungImage_1;
if draw_flag == 0
	figure(10),imshow(jointImage),title('拼接后的图像');
else
	subplot(3,4,10),imshow(jointImage),title('拼接后的图像');
end
%---------------图片拼接(end)-----------------------


%---------------图像整体融合(start)---------------------
originalImageRGB_HSV = rgb2hsv(originalImageRGB); % 颜色空间转换: RGB → HSV
o_s = originalImageRGB_HSV(:,:,2); % 饱和度
o_v = originalImageRGB_HSV(:,:,3); % 亮度
[n,m] = size(o_s);
mean_o_s = sum(sum(o_s)) ./ (n * m);
[n,m] = size(o_v);
mean_o_v = sum(sum(o_v)) ./ (n * m);

backgroungImageRGB_HSV = rgb2hsv(backgroungImage);
b_s = backgroungImageRGB_HSV(:,:,2); % 饱和度
b_v = backgroungImageRGB_HSV(:,:,3); % 亮度
[n,m] = size(b_s);
mean_b_s = sum(sum(b_s)) ./ (n * m);
[n,m] = size(b_v);
mean_b_v = sum(sum(b_v)) ./ (n * m);

jointImage_HSV = rgb2hsv(jointImage); 

choose = input('主亮度选择,请输入0或1,0代表原图片,1代表背景图片:');
if choose == 0
	for i = 1:Height
		for j = 1:Width
			if Mark_2(i,j) == 0
				% jointImage_HSV(i,j,2) = jointImage_HSV(i,j,2) .* mean_o_s ./ mean_b_s;
				tmp1 = jointImage_HSV(i,j,3) .* mean_o_v ./ mean_b_v;
				if 0.95 < tmp1
					jointImage_HSV(i,j,3) = tmp1 .* 0.95;
				else
					jointImage_HSV(i,j,3) = tmp1;
				end
			end
		end
	end
else
	for i = 1:Height
		for j = 1:Width
			if Mark_2(i,j) == 1
				tmp1 = jointImage_HSV(i,j,3) ./ mean_o_v .* mean_b_v;
				if 0.95 < tmp1
					jointImage_HSV(i,j,3) = tmp1 .* 0.95;
				else
					jointImage_HSV(i,j,3) = tmp1;
				end
			end
		end
	end
end
jointImage = hsv2rgb(jointImage_HSV); % HSV → RGB 得到最终图片
if draw_flag == 0
	figure(11),imshow(jointImage),title('图像整体融合后的图像');
else
	subplot(3,4,11),imshow(jointImage),title('图像整体融合后的图像');
end
%---------------图像整体融合(end)---------------------

%---------------边缘处理(stert)---------------------
mix_r = mix_radius; % 融合边缘的半径选取
for i = 1:edge_points_sum
	x = edge_points(i,1);
	y = edge_points(i,2);
	R = 0;
	G = 0;
	B = 0;
	for u = -mix_r:mix_r
		for v = -mix_r:mix_r
			if x+u>0 && x+u<=Height && y+v>0 && y+v<=Width
				R = R + jointImage(x+u,y+v,1);
				G = G + jointImage(x+u,y+v,2);
				B = B + jointImage(x+u,y+v,3);
			end
		end
		jointImage(x,y,1) = R ./ (4*mix_r^2 + 4*mix_r^2);
		jointImage(x,y,2) = G ./ (4*mix_r^2 + 4*mix_r^2);
		jointImage(x,y,3) = B ./ (4*mix_r^2 + 4*mix_r^2);
	end
end
if draw_flag == 0
	figure(12),imshow(jointImage),title('边缘处理后的图像');
else
	subplot(3,4,12),imshow(jointImage),title('边缘处理后的图像');
end
%---------------边缘处理(end)-----------------------


8 补充说明及下载链接

● 完整文档、所有测试图片、代码使用说明的链接:https://download.csdn.net/download/Wang_Dou_Dou_/87535824

● 若有写得不对的地方,或有疑问,欢迎评论交流。


⭐️ ⭐️

猜你喜欢

转载自blog.csdn.net/Wang_Dou_Dou_/article/details/129331781