✅ 大三下时弄的
文章目录
- 最终效果图
- 摘要
- 1 研究背景及意义
- 2 基本原理描述
- 3 实验数据来源
-
- 3.1 原始图像的来源
- 3.2 天空背景图像的来源
- 4 实验步骤及相应处理结果
- 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.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 区域生长法分割图像
● 完成图像的背景转换需要把原始图像的背景与目标预留部分进行分离,在比较多种分割方法后,选择采用区域生长法来分割图像背景。区域生长法主要考虑像素及其空间邻域像素之间的关系,开始时确定一个或多个像素点作为种子,然后按某种相似性准则增长区域,将相邻的具有相似性质的像素或区域归并,从而逐步增长区域,直到没有可以归并的点或其他小区域为止。区域内像素的相似性度量可以是平均灰度值、纹理、颜色等信息,本次选择灰度值为区域内像素的相似性度量进行区域生长。
● 首先用 size
函数获取灰度图像的尺寸,然后用 getpts
函数(需要鼠标自行点击选取)选择种子点,用 round
将种子点的坐标取整,存放种子点的灰度值,根据原灰度图像的尺寸大小构造相同大小的全零矩阵以提高运算的效率,设置存储符合区域生长条件的点数初值为在屏幕中选取的种子点数 seeds_num
,然后设置一个阈值 threshold
,再根据嵌套的 for
循环和 if条件语句判断目标点以及其八零域的点是否满足生长规则,最后即可得到区域生长的二值图像结果,用 imshow
将其显示出来。
● 种子点和阈值的选择并不是固定不变的,在区域生长分割图像的过程中,可以根据区域生长的结果不断地对种子点和阈值 threshold
进行调整。同时对于生长半径r,也要根据图像的特张来选取。如果有“树枝类”遮挡物分割了背景,那么生长半径r应该适度地调大,直到出现满意的结果为止。相关程序代码如下。
%---------------区域生长算法(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.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.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)-----------------------
● 对于新背景,我们做同样的处理,这里需要剪裁一下,使得新背景的大小和原图的一样。用剪裁后的新背景和后景底片相乘即得新的背景图像。
%---------------后景底片剪裁+抠取(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.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.6 图像整体融合
● 在对新获得的图像进行进一步处理的时候,先通过颜色空间转换将 RGB
转化为 HSV
,然后选择提取原图片或者新背景图片的平均亮度作为主亮度值,并依据获得的主亮度值将拼接后得到的图像中所有像素的亮度重新赋值,再通过颜色空间转换重新变为 RGB
,得到整体融合后的图像。相关程序代码如下。
%---------------图像整体融合(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.7 边缘处理
● 最后对融合后的图像做细节处理,将每一个边缘点周围 8
个点的像素值(3
个通道)取平均值,作为该边缘点新的像素值。
%---------------边缘处理(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
● 若有写得不对的地方,或有疑问,欢迎评论交流。
⭐️ ⭐️
查看全文
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dgrt.cn/a/1629474.html
如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!
相关文章:
基于区域生长和形态学处理的图像融合方法——Matlab图像处理
✅ 大三下时弄的 文章目录最终效果图摘要1 研究背景及意义2 基本原理描述3 实验数据来源3.1 原始图像的来源3.2 天空背景图像的来源4 实验步骤及相应处理结果4.1 原始图像的预处理4.2 区域生长法分割图像4.3 形态学处理填充孔洞4.4 边缘检测根据二值图像构造RGB图像4.5 图像拼接……
oneblog_justauth_三方登录配置【Gitee】
文章目录oneblog添加第三方平台gitee中创建三方应用完善信息oneblog添加第三方平台
1.oneblog管理端,点击左侧菜单 网站管理——>社会化登录配置管理 ,添加一个社会化登录 2.编辑信息如下,选择gitee平台后复制redirectUri,然后去gitee获取clientId和……
CG-CTF web题(部分)
南邮CTF练习题(http://ctf.nuptzj.cn/) 记录新手努力的每一时刻! 在学习CTF之前可以先了解一下它: CTF入门简介:https://blog.csdn.net/Fly_hps/article/details/79783253, 然后从这个网站了解一下CTF web方……
《网络信息安全》作业题和考试复习题
开卷必备!
网络信息安全需求包含哪六个基本方面?分别简单说明它们的含义。
机密性:防止未授权用户非法获得保密信息。 完整性:在未经许可的情况下,保证数据不会被他人删除或修改(至少能发现被修改过&……
pip包下载极速源
经常遗忘的pip清华下载源 emm…pip包的安装经常用到——临时使用。
-i https://pypi.tuna.tsinghua.edu.cn/simple用法:
pip install (需要下载的包) 上述临时下载源…
配置远程登录
1.查找虚拟机IP地址。
ifconfig我这里ip地址为192.168.118.130。 2.vim或者gedit修改ssh_config文件。 第34行PermitRootLogin注释取消,改为:PermitRootLogin yes。
gedit /etc/ssh/sshd_config3.获取网络信息并配置端口
lsof -i :22 4.重启sshd服务 ……
kali网络永久配置
emmm,吃了很多坑… 1.网络永久配置:
gedit /etc/network/interfaces 进行网络配置,根据自己的IP、网关等设置,详细在winr输入ipconfig进行查看。 这是配置完自己的固定IP,保存退出。 接下来需要重启:
/e……
DNS服务器搭建及配置
以Windows 2008 server为例安装DNS服务打开DNS服务关于DNS主服务器的相关配置新建主机设置相关IP客户机访问设置清空DNS缓存命令查看DNS缓存命令解析服务器IP地址安装DNS服务
具体的安装方式是:
控制面板——打开或关闭Windows功能——角色——添加角色——勾选DN……
dlib安装之python3.10对应的.whl文件
关于python3.10版本安装dlib各种报错,不一一列出网上资料很多。
解决方案
直接采用.whl文件安装,免除各种问题。 GitHub文件地址:
https://github.com/jloh02/dlib/releases/download/v19.22/dlib-19.22.99-cp310-cp310-win_amd64.whl在我……
基于intel x86+fpga智能驾驶舱和高级驾驶辅助系统硬件设计(二)
系统功能架构及各模块功能介绍 智能驾驶舱和高级驾驶辅助系统是一个车载智能终端嵌入式平台,系统是一个能够运行 虚拟化操作系统的软件和硬件的综合体。本文的车载主机包括硬件主控处理器、电源管理芯 片、存储设备、输入输出控制器、数字仪表系统系统、后座娱乐系统……
通过JavaScript实现漂浮
<html>
<head><meta http-equiv"Content-Type" content"text/html"; charset"gb2312" /><title>漂浮广告</title><style type"text/css">div{position:absolute;}</style>
</head>
&……
序列动画和图片内存问题
一、帧动画问题 /*** 帧动画总结:* 1、如果精灵进行新建时,加载了纹理,那么setRestoreOriginalFrame可以设置为false或者true* 2、如果精灵新建时,没有加载纹理的话,那么setRestoreOriginalFrame需要设置为false&#……
冒泡排序的两种写法
//第一种写法: #include <iostream> using namespace std; void bubbleSort(int arr[], int n) { for (int i 0; i < n-1; i) { int temp 0; for (int j i1; j <n; j) { if (arr[i] ……
c++中this详解
http://blog.csdn.net/ugg/article/details/606396…
编程是可以从事一生的职业吗
暂且不论这个话题,留待以后再添加见解。
目前而言,编程符合自己的性格和兴趣,且可以解决自我的生活,一举两得,总算是做了自己喜欢的事情。2016-7-24
在此立文以自勉!
2016-8-30: 1、编程就是一个不断和自我的惰性做……
STL中常用容器详解
常用的容器
一、顺序容器 1、vector 向量 :随机访问(按照下标)任何一个元素,在尾部增删元素,相当于是一个动态的数组。 vector容器,在头部增加、删除元素,其时间消耗和元素数目成正比ÿ……
c++中的继承的讲解
cpp中的继承的总结: 1、继承方式对于之列继承自父类的成的访问权限的影响 对于父类本身没有影响,对于子类扩展成员也没有影响。 2、子类中从父类继承的成员,其访问权限不能高于继承声明时使用的访问权限。 私有继承: 子类中所有继……
c++虚函数的讲解
废话少说,直接上测试代码 #include<iostream> #include<stdlib.h>usingnamespace std;//有虚函数的类 class A{public:A();~A();virtual void f(){cout<<"this is A f()"<<endl;}virtual void g(){cout<<"this is A g……
ListView TableView ScrollView三者之Tableview
注意点:
1、其中的listView和Tableview都是继承自Scrollview
2、本文主要讲解的是tableview
3、代码部分引用的是在公司中做的产品
4、以上代码可以修改的地方:将cell改为继承自TableviewCell,这样可以直接创建cell。
5、代码中使用了函数的绑定和函……
c++中的std::shared_ptr和std::weak_ptr
std::share类型在c11提出,其意义:使用shared_ptr解决的主要问题是知道删除一个被多个对象共享的资源的正确时机,在本项目中所有的数据类均是使用智能指针来存储,就是为了解决这个问题。
一、std::shared_ptr采用的是引用计数来管……
编程日记2023/4/16 15:01:17