行业前10的网站建设公司/武汉seo关键词优化
✅ 大三下时弄的
文章目录
- 最终效果图
- 摘要
- 1 研究背景及意义
- 2 基本原理描述
- 3 实验数据来源
- 3.1 原始图像的来源
- 3.2 天空背景图像的来源
- 4 实验步骤及相应处理结果
- 4.1 原始图像的预处理
- 4.2 区域生长法分割图像
- 4.3 形态学处理填充孔洞
- 4.4 边缘检测+根据二值图像构造RGB图像
- 4.5 图像拼接
- 4.6 图像整体融合
- 4.7 边缘处理
- 5 实验结果分析
- 6 总结与心得体会
- 7 完整代码
- 8 补充说明及下载链接
最终效果图
● 注: 鼠标点击,选完 “点” 后,需要再按一下 “回车”。
摘要
● 本次从日常生活的实际需要出发,主要研究了图像的分割转换及重组部分与原图像的融合,基于区域生长法和形态学处理的方法可以将图像的背景转换为其他想要替换的图片,并通过边缘融合等方法将转换后的背景与原图像进行调整,减轻违和感以达到更好的显示效果。相比于人工手动抠图的方法,这种处理方法更加方便简洁,极大地简化了修图的繁琐步骤,学习基础低,容易上手使用,并且大大缩短了同类型修图的时间,处理速度较快,故而可以广泛应用于日常的照片处理。
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 == 0figure(1),imshow(originalImageRGB),title('原始图像');
elsesubplot(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 == 0figure(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_numtotal_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 flagflag = false;for i = i_sta:i_endx = seeds(i,1);y = seeds(i,2);for u = -r:r % 判断周围 r^2-1 个点是否符合生长规则for v = -r:rif x+u>0 && x+u<Height && y+v>0 && y+v<Widthif Mark_1(x+u,y+v)==0 && abs(grayImage(x+u,y+v)-seed_mean_gray)<=thresholdMark_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;endendendend
end
i_sta = i_end+1;
i_end = seeds_num;
end
if draw_flag == 0figure(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 == 0figure(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:Heightfor j = 1:Widthif Mark_1(i,j) == 0for u = -1:1for v = -1:1if i+u>0 && i+u<Height && j+v>0 && j+v<Widthif 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) == 0Edge(i,j) = 0;Edge(i+u, j+v) = 0; endendendendendif Mark_1(i,j) == 0Mark_2(i,j,1) = 1; % 白色是1Mark_2(i,j,2) = 1; % 白色是1Mark_2(i,j,3) = 1; % 白色是1Mark_3(i,j,1) = 0; % 黑色是0Mark_3(i,j,2) = 0; % 黑色是0Mark_3(i,j,3) = 0; % 黑色是0elseMark_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; endend
end
if draw_flag == 0figure(5),imshow(Edge),title('边缘检测图');
elsesubplot(3,4,5),imshow(Edge),title('边缘检测图');
endif draw_flag == 0figure(6),imshow(Mark_2),title('准备放原图的前景底片(白色部分)');
elsesubplot(3,4,6),imshow(Mark_2),title('准备放原图的前景底片(白色部分)');
end
if draw_flag == 0figure(7),imshow(Mark_3),title('准备放背景的后景底片(白色部分)');
elsesubplot(3,4,7),imshow(Mark_3),title('准备放背景的后景底片(白色部分)');
end
originalImageRGB_1 = originalImageRGB .* Mark_2;
if draw_flag == 0figure(8),imshow(originalImageRGB_1),title('去除背景后并在前后景底片区域的图像');
elsesubplot(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 == 0figure(9),imshow(backgroungImage_1),title('在底片区域的背景图像');
elsesubplot(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 == 0figure(10),imshow(jointImage),title('拼接后的图像');
elsesubplot(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 == 0for i = 1:Heightfor j = 1:Widthif Mark_2(i,j) == 0tmp1 = jointImage_HSV(i,j,3) .* mean_o_v ./ mean_b_v;if 0.95 < tmp1jointImage_HSV(i,j,3) = tmp1 .* 0.95;elsejointImage_HSV(i,j,3) = tmp1;endendendend
elsefor i = 1:Heightfor j = 1:Widthif Mark_2(i,j) == 1tmp1 = jointImage_HSV(i,j,3) ./ mean_o_v .* mean_b_v;if 0.95 < tmp1jointImage_HSV(i,j,3) = tmp1 .* 0.95;elsejointImage_HSV(i,j,3) = tmp1;endendendend
end
jointImage = hsv2rgb(jointImage_HSV); % HSV → RGB 得到最终图片
if draw_flag == 0figure(11),imshow(jointImage),title('图像整体融合后的图像');
elsesubplot(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_sumx = edge_points(i,1);y = edge_points(i,2);R = 0;G = 0;B = 0;for u = -mix_r:mix_rfor v = -mix_r:mix_rif x+u>0 && x+u<Height && y+v>0 && y+v<WidthR = R + jointImage(x+u,y+v,1);G = G + jointImage(x+u,y+v,2);B = B + jointImage(x+u,y+v,3);endendjointImage(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 == 0figure(12),imshow(jointImage),title('边缘融合后的图像');
elsesubplot(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 == 0figure(1),imshow(originalImageRGB),title('原始图像');
elsesubplot(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 == 0figure(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_numtotal_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 flagflag = false;for i = i_sta:i_endx = seeds(i,1);y = seeds(i,2);for u = -r:r % 判断周围 r^2-1 个点是否符合生长规则for v = -r:rif x+u>0 && x+u<=Height && y+v>0 && y+v<=Widthif Mark_1(x+u,y+v)==0 && abs(grayImage(x+u,y+v)-seed_mean_gray)<=thresholdMark_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;endendendendendi_sta = i_end+1;i_end = seeds_num;
end
if draw_flag == 0figure(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 == 0figure(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:Heightfor j = 1:Widthif Mark_1(i,j) == 0for u = -1:1for v = -1:1if i+u>0 && i+u<=Height && j+v>0 && j+v<=Widthif Mark_1(i,j) == 0 && Mark_1(i+u, j+v) == 1 % 把边缘找出来(1代表白色)if Edge(i,j) == 0edge_points_sum = edge_points_sum + 1;edge_points(edge_points_sum, 1) = i;edge_points(edge_points_sum, 2) = j;endedge_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) == 0edge_points_sum = edge_points_sum + 1;edge_points(edge_points_sum, 1) = i;edge_points(edge_points_sum, 2) = j;endedge_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;endendendendendif Mark_1(i,j) == 0Mark_2(i,j,1) = 1; % 白色是1Mark_2(i,j,2) = 1; % 白色是1Mark_2(i,j,3) = 1; % 白色是1Mark_3(i,j,1) = 0; % 黑色是0Mark_3(i,j,2) = 0; % 黑色是0Mark_3(i,j,3) = 0; % 黑色是0elseMark_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; endend
end
if draw_flag == 0figure(5),imshow(Edge),title('边缘检测图');
elsesubplot(3,4,5),imshow(Edge),title('边缘检测图');
endif draw_flag == 0figure(6),imshow(Mark_2),title('准备放原图的底片(白色部分)');
elsesubplot(3,4,6),imshow(Mark_2),title('准备放原图的底片(白色部分)');
endif draw_flag == 0figure(7),imshow(Mark_3),title('准备放背景的底片(白色部分)');
elsesubplot(3,4,7),imshow(Mark_3),title('准备放背景的底片(白色部分)');
endoriginalImageRGB_1 = originalImageRGB .* Mark_2;
if draw_flag == 0figure(8),imshow(originalImageRGB_1),title('去除背景后并在底片区域的图像');
elsesubplot(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 == 0figure(9),imshow(backgroungImage_1),title('在底片区域的背景图像');
elsesubplot(3,4,9),imshow(backgroungImage_1),title('在底片区域的背景图像');
end
%---------------背景底片剪裁+抠取(end)-------------------------%---------------图片拼接(start)---------------------
jointImage = originalImageRGB_1 + backgroungImage_1;
if draw_flag == 0figure(10),imshow(jointImage),title('拼接后的图像');
elsesubplot(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 == 0for i = 1:Heightfor j = 1:Widthif 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 < tmp1jointImage_HSV(i,j,3) = tmp1 .* 0.95;elsejointImage_HSV(i,j,3) = tmp1;endendendend
elsefor i = 1:Heightfor j = 1:Widthif Mark_2(i,j) == 1tmp1 = jointImage_HSV(i,j,3) ./ mean_o_v .* mean_b_v;if 0.95 < tmp1jointImage_HSV(i,j,3) = tmp1 .* 0.95;elsejointImage_HSV(i,j,3) = tmp1;endendendend
end
jointImage = hsv2rgb(jointImage_HSV); % HSV → RGB 得到最终图片
if draw_flag == 0figure(11),imshow(jointImage),title('图像整体融合后的图像');
elsesubplot(3,4,11),imshow(jointImage),title('图像整体融合后的图像');
end
%---------------图像整体融合(end)---------------------%---------------边缘处理(stert)---------------------
mix_r = mix_radius; % 融合边缘的半径选取
for i = 1:edge_points_sumx = edge_points(i,1);y = edge_points(i,2);R = 0;G = 0;B = 0;for u = -mix_r:mix_rfor v = -mix_r:mix_rif x+u>0 && x+u<=Height && y+v>0 && y+v<=WidthR = R + jointImage(x+u,y+v,1);G = G + jointImage(x+u,y+v,2);B = B + jointImage(x+u,y+v,3);endendjointImage(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 == 0figure(12),imshow(jointImage),title('边缘处理后的图像');
elsesubplot(3,4,12),imshow(jointImage),title('边缘处理后的图像');
end
%---------------边缘处理(end)-----------------------
8 补充说明及下载链接
● 完整文档、所有测试图片、代码使用说明的链接:https://download.csdn.net/download/Wang_Dou_Dou_/87535824
● 若有写得不对的地方,或有疑问,欢迎评论交流。
⭐️ ⭐️
相关文章:

基于区域生长和形态学处理的图像融合方法——Matlab图像处理
✅ 大三下时弄的 文章目录最终效果图摘要1 研究背景及意义2 基本原理描述3 实验数据来源3.1 原始图像的来源3.2 天空背景图像的来源4 实验步骤及相应处理结果4.1 原始图像的预处理4.2 区域生长法分割图像4.3 形态学处理填充孔洞4.4 边缘检测根据二值图像构造RGB图像4.5 图像拼接…...

三个案例场景带你掌握Cisco交换机VLAN互通
VLAN间路由的方式现在主流的组网主要是依靠三层交换机通过配置SVI接口【有的厂商叫VLANIF接口】,当然也有比较小型的网络,它就一个出口路由器可管理的二层交换机,还有一种更加差的,就是出口路由一个可管理的二层交换机,…...

小白入门之持久连接与非持久连接的差别
对比 HTTP 0.9 已过时 HTTP1.0:非持续连接,每个连接只处理一个请求响应事务,有些服务器端甚至还在用此,可以在一定时间内复用连接,具体复用时间的长短可以由服务器控制,一般在15s左右。 HTTP 1.1 默认使用持…...

TypeScript篇.01-简介,类,接口,基础类型
1.简介(1)安装及编译安装: npm install -g typescript创建 .ts 后缀名的文件编译: tsc 文件名.ts 编译后会生成同名 .js 的文件查看: 在html文件中script引入js文件,运行查看控制台即可(2)类型注解TypeScript里的类型注解是一种轻量级的为函数或变量添加约束的方式 变量或函数声…...

分享几种WordPress怎么实现相关文章功能
一淘模板(56admin.com)给大家介绍一下WordPress代码实现相关文章的几种方法,希望对大家有所帮助! WordPress很多插件可以实现相关文章的功能,插件的优点是配置简单,但是可能会对网站的速度造成一些小的影响…...

PANGO的IOB的电平能力那些事
LVCMOS33 如果要使用33电平,VCCIO则必须供电3V3. 在此制式下,VILMAX为0.8V,VIHMIN为2.0V,即,电平处于0.8V到2.0V之间时,处于浮游态。 VOLMAX是0.4V,VOHMIN是VCCIO-0.4V,折算下来&am…...

scrpy学习-02
新浪微博[Scrapy 教程] 3. 利用 scrapy 爬取网站中的详细信息 - YouTubedef parse(self,response):soup BeautifulSoup(response.body,html.parser)tags soup.find_all(a,hrefre.compile(r"sina.*\d{4}-\d{2}-\d{2}.*shtmls"))#匹配日期for tag in tags:url tag.get(…...

MySQL运维篇之Mycat分片规则
3.5.3、Mycat分片规则 3.5.3.1、范围分片 根据指定的字段及其配置的范围与数据节点的对应情况,来决定该数据属于哪一个分片。 示例: 可以通过修改autopartition-long.txt自定义分片范围。 注意: 范围分片针对于数字类型的字段,…...

vue router elementui template CDN模式实现多个页面跳转
文章目录前言一、elementui Tabs标签页和NavMenu 导航菜单是什么?二、使用方式1.代码如下2.页面效果总结前言 写上一篇bloghttps://blog.csdn.net/jianyuwuyi/article/details/128959803的时候因为整个前端都写在一个index.html页面里,为了写更少的代码…...

ElasticSearch - ElasticSearch基本概念及集群内部原理
文章目录1. ElasticSearch的应用场景01. Elasticsearch 是什么?02. 为何使用 Elasticsearch?03. Elasticsearch 的用途是什么?04. Elasticsearch 的工作原理是什么?05. Elasticsearch 索引是什么?06. Logstash 的用途是…...

【反射中,Class.forName和ClassLoader区别】
在Java中,可以使用反射机制来获取类的信息并动态地创建对象。其中,Class是Java反射机制中的重要类,表示一个类的信息。 Class.forName()和ClassLoader都可以用于获取类的Class对象,但它们之间存在一些差别: 1、是否会…...

2023了为什么还有人在问:女生适合做跨境电商吗?
女生适合做跨境电商吗?这是东哥最近咨询里面问最多的,今天东哥就给大家解答一下你们内心的疑惑,虽然代表的是东哥我自己的观点,但我觉得还是很值得深思的。 女生适合做跨境电商吗? 性别并不是决定一个人是否适合从事跨…...

磁盘分区和挂载
磁盘分区和挂载一、linux分区1.原理介绍2.分区和文件关系示意图:3.硬盘说明二、linux分区1.查看所有设备挂载情况三、挂载案例1.使用lsblk命令查看2. 虚拟机硬盘分区3.虚拟机硬盘分区格式化4.mount挂载 重启挂载失效4.1挂载名词解释4.2注意事项4.3挂载4.4挂载非空目…...

电子技术——晶体管尺寸
电子技术——晶体管尺寸 在本节我们介绍关于IC设计的一个重要的参数晶体管尺寸(例如长度和长宽比)。我们首先考虑MOS反相器。 反相器尺寸 为了说明 (W/L)(W/L)(W/L) 的尺寸大小以及 (W/L)p(W/L)_p(W/L)p 和 (W/L)n(W/L)_n(W/L)n 的比例问题对于MO…...

Tuxera NTFS2023MacOS读写软件功能介绍使用
当我们遇到磁盘不能正常使用的情况时本能的会以为是磁盘损坏了,但某些情况下却并非如此。对于mac操作系统来说,软件无法使用设备无法正常读写似乎是很常见的事,毕竟现在的mac电脑对PC机上的产品无法完全适应使用,经常会存在兼容方…...

2022年数维杯国际大学生数学建模挑战赛A题自动地震地平线跟踪解题全过程论文及程序
2022年数维杯国际大学生数学建模挑战赛 A题 自动地震地平线跟踪 原题再现: 随着我国经济社会发展,地质工作的重要性也日益提高。地震资料解释是地震勘探工程的一个重要阶段,可以明确油气勘探的地下构造特征,为油气勘探提供良好和…...

推荐系统[八]:推荐系统常遇到问题和解决方案[物品冷启动问题、多目标平衡问题、数据实时性问题等]
相关文章推荐: 推荐系统[一]:超详细知识介绍,一份完整的入门指南,解答推荐系统相关算法流程、衡量指标和应用,以及如何使用jieba分词库进行相似推荐,业界广告推荐技术最新进展 推荐系统[二]:召回算法超详细讲解[召回模型演化过程、召回模型主流常见算法(DeepMF/TDM/Ai…...

shutil.copyfile PermissionError: [Errno 13] Permission denied
File "G:/od15/调试/翻译文件更换/更新翻译po文件.py", line 42, in <module> shutil.copyfile(gxpath,dir_file_path) File "E:\odsoft\python\lib\shutil.py", line 120, in copyfile with open(src, rb) as fsrc: PermissionError: [Er…...

07react+echart,大屏代码开发
react框架引入第三方插件原链接gitHub:GitHub - hustcc/echarts-for-react: ⛳ Apache ECharts components for React wrapper. 一个简单的 Apache echarts 的 React 封装。import ReactECharts from echarts-for-react;import * as echarts from echarts;一、软件简介echarts-…...

【数据库原理复习】ch2 SQL语句(主要基于sql server)
这里写目录标题基本知识常用基本数据类型字符型数据类型二进制数据类型日期类型数字类型约束条件表SQL语句创建语句修改基本表 & 删除基本表数据查询基本知识 常用基本数据类型 字符型数据类型 名称大小说明char(n)占n个字节只能显示英文字符nchar(n)2n字节2字节额外开销…...

Cadence Allegro 导出Component Pin Report详解
⏪《上一篇》 🏡《上级目录》 ⏩《下一篇》 目录 1,概述2,Component Pin Report作用3,Component Pin Report示例4,Component Pin Report导出方法4.1,方法14.2,方法2B站关注“硬小二”浏览更多演示视频 1,概述...

PAT甲级 1110 Complete Binary Tree
题目链接 PAT甲级 1110 Complete Binary Tree 思路 第一次的写法不是很好。 对于这种完全二叉树的层序遍历,比较烦人的就是空孩子使得处理很麻烦。 思来想去还是把空位置也入队比较好。 这样的话,访问到空指针的时机被推迟了一个level 而完全二叉树的…...

【JavaSE】逻辑控制语句
文章目录一. 顺序结构二. 分支结构1. if 语句2. switch 语句3、循环结构3.1 while 循环3.2 do while 循环3.3 for 循环3.4 break 和 continue三. 输入输出1. 输出到控制台2. 从键盘输入一. 顺序结构 顺序结构比较简单,即程序按照代码书写的顺序一行一行执行下去。 …...

Motionbuilder系统文件说明
安装路径 Motionbuilder 默认的安装路径在 C:\Program Files\Autodesk\MotionBuilder\ 用户数据(user data) 位于安装路径下的 bin\config 非管理员用户的配置文件路径 Motionbuilder会将配置文件备份到 \Users[user]\AppData\Local\Autodesk[MotionBuilder] 当用户第一次打开…...

【我的Android开发】AMS中Activity栈管理
概述 Activity栈管理是AMS的另一个重要功能,栈管理又和Activity的启动模式和startActivity时所设置的Flag息息相关,Activity栈管理的主要处理逻辑是在ActivityStarter#startActivityUnchecked方法中,本文也会围绕着这个方法进进出出…...

C++源程序的构成————学习笔记
以下内容为,在学校上课时的课堂总结,偶尔我也会扩展一些内容内容仅供参考,欢迎大佬的指正简单的C程序#include <iostream> using namespace std;int main() {int x0;int y 0;cout << "请输入x,y的值"<<endl;cin…...

Spark Catalyst
Spark Catalyst逻辑计划逻辑计划解析逻辑计划优化Catalyst 规则优化过程物理计划Spark PlanJoinSelection生成 Physical PlanEnsureRequirementsSpark SQL 端到端的优化流程: Catalyst 优化器 : 包含逻辑优化/物理优化Tungsten : Spark SQL的优化过程 : 逻辑计划 …...

element 远程搜索下拉加载
created() { this.getList(); this.getGroupList(); }, directives: { /** 下拉框懒加载 */ “el-select-loadmore”: { bind(el, binding) { const SELECTWRAP_DOM el.querySelector( “.el-select-dropdown .el-select-dropdown__wrap” ); SELECTWRAP_DOM.addEventListener…...

空间复杂度与顺序表的具体实现操作(1)
最近更新的少,主要是因为参加了ACM竞赛空间复杂度空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度 。空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量…...

【springmvc】Rest ful风格
RESTful 1、RESTful简介 REST:Representational State Transfer,表现层资源状态转移。 a>资源 资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一…...