数字图像处理课设——魔方拼图(把所有图片变成可以用魔方拼出来的图片)python和matlab均可以实现


前言

在一次赠送礼物的过程中,我采用了一款软件,它能够通过魔方拼图的方式来展示图片。不过,这款软件有一个限制,即最多只能处理100个魔方。为了突破这一限制,我决定自行开发一个程序,它不仅支持自定义魔方的数量,还能设定分辨率,从而实现将任意图片转换成由魔方组成的拼图。该程序的核心机制是将图片进行像素化处理,并根据颜色进行分割,以确定所需魔方的数量和每个魔方的具体拼接方法。(但是我在开发过程中的计算刚好实现了连魔方阶数也可以自定义)

魔方拼图是什么?

魔方拼图通常是指利用魔方的结构和原理开展的一种拼图游戏。这种拼图可能涉及把魔方的各个部分组合在一起,形成更复杂的图案或形状,而不仅仅是复原为单一颜色的魔方。
魔域的软件
上面这个是魔域开发的小程序,无法自定义大小,而且被限制住了大小。
在这里插入图片描述
GitHub上开源的魔方像素化软件
在这里插入图片描述
用开源的软件渲染出来的相片
在这里插入图片描述
魔域软件导出的像素化照片

总的来说,我感觉出来的效果不是很好,只有像素化的照片,没有严格的魔方分层,却没有我所需要的每个魔方的拼图效果,学习了数字图像处理的课程之后,我决定自己开发一个更好用的程序,并可以对魔方数量,像素,每个魔方拼图效果展示出来并可以自行设置的软件程序。

设计要求

应用图像滤波以使图片更平滑,从而便于进行颜色分割
压缩图片以便于设置魔方数量
进行颜色分割,按照魔方的六种颜色进行像素化处理
扩展图片尺寸
在每个色块之间添加分割线
在每个魔方中添加分割线
计算魔方的数量以确保符合要求
导出处理后的图片

设计过程

图像滤波

高斯滤波(Gaussian filter) 包含许多种,包括低通、带通和高通等,我们通常图像上说的高斯滤波,指的是高斯模糊(Gaussian Blur) ,是一种高斯低通滤波 ,其过滤调图像高频成分(图像细节部分),保留图像低频成分(图像平滑区域),所以对图像进行‘高斯模糊’后,图像会变得模糊。
二维高斯函数具有旋转对称性,即滤波器在各个方向上的平滑程度是相同的.一般来说,一幅图像的边缘方向是事先不知道的,因此,在滤波前是无法确定一个方向上比另一方向上需要更多的平滑.旋转对称性意味着高斯平滑滤波器在后续边缘检测中不会偏向任一方向。所以在对图像的进行分割时候更加方便,后面进行颜色分割的时候不会有很明显的边界,而且因为是只有六个颜色,所以更好的保留了图像更明显的颜色特征,而不是高分辨率的细节特征。
高斯模糊原理
高斯模糊本质上就是利用 高斯函数 生成的 高斯核(高斯模板) 对图像进行卷积操作。
在这里插入图片描述
二维的高斯分布函数
高斯模糊通过将高斯滤波器(卷积核)与图像进行卷积,计算每个像素的新值。卷积核的中心对应图像中要模糊的像素周围的像素,根据距离中心的远近,赋予不同的权重。通过计算每个像素及其邻域像素的加权平均值,最终得到模糊的图像。这样可以有效地消除图像中的细节,产生柔和的效果。这里,(x,y) 是距离模糊中心的坐标,σ是标准差,控制模糊程度。标准差越大,模糊效果越明显。
在这里插入图片描述
高斯核加权计算的原理
将这9个值加起来,就是中心点25进行高斯滤波后的值。对所有点重复这个过程,就得到了高斯模糊后的图像。
高斯模板在对图像边缘像素进行卷积时,会有一部分权重没有对应像素,因此我们需要在图像的边缘补0。这种方法称作Zero Padding。并且权值g(卷积核)要进行归一化操作。

**`img = imread('1.png');  
if size(img, 3) == 3  
    img = rgb2gray(img);  
end  
sigma = 2; % 标准差  
kernelSize = 5; 
blurredImg = imgaussfilt(img, sigma, 'FilterSize', kernelSize);  
figure;  
subplot(1, 2, 1);  
imshow(img);  
title('原始图像');  
subplot(1, 2, 2);  
imshow(blurredImg);  
title('高斯模糊后的图像');`**

效果图
在这里插入图片描述

              **原始图像                 高斯模糊后的图像**

压缩图片

图像缩放(image scaling)是指对数字图像的大小进行调整的过程。图像缩放是一种非平凡的过程,需要在处理效率以及结果的平滑度(smoothness)和清晰度(sharpness)上做一个权衡。当一个图像的大小增加之后,组成图像的像素的可见度将会变得更高,从而使得图像表现得“软”。相反地,缩小一个图像将会增强它的平滑度和清晰度。
图像的放大和缩小的基本原理就是一种映射,即缩小后或者放大后的图像像素位置向原图的一个映射。简单理解就是,将放大或者缩小后的图像将其的坐标(长宽)拉伸或者压缩到和原图一样大时,其像素坐标点对应在原图上的位置就是其映射位置。
欠采样是信号处理中比较常见的用法,只要采样频率高于信号最高频率的两倍,就可以从采样信号中恢复出原始信号,采样频率低于信号最高频率的两倍,这种采样被称为欠采样。欠采样通常表现为图像的分辨率降低,细节模糊。例如,较低的像素数目在一定尺寸的图像中可能导致显著的信息丢失。
在这里插入图片描述
欠采样在压缩过程中,可能会丢弃一些图像的高频信息以降低文件大小,这种技术如果处理不当,也可能引入欠采样导致的画质降低。
就比如我这一份报告里面的图,我是一个很高清的图片,分辨率很高,具有很丰富的色彩细节,当我使用一台较低分辨率的打印机打印的时候,就会出现欠采样的情况,从而导致图像失真。在这次实验中我使用压缩图片大小可以控制使用的魔方数量,当9个像素是一魔方时,我只需要控制压缩图像大小即可控制魔方所需的多少。
需要一百个魔方时候的图像

需要一百个魔方时候的图像

在这里插入图片描述
需要40000个魔方时候生成的魔方图
需要的魔方数量越多,采样频率越高,图像越加还原,对于数字信号来说,采样率越高也是信号还原也是越清晰

进行颜色分割

这一步我们需要按照魔方的六种颜色进行像素化处理,在一幅RGB图像中分割某个指定的颜色区域(bbox)的物体。给定一个感兴趣的有代表性色彩的彩色样点集,可得到我们希望分割的颜色的“平均”估计。平均彩色用RGB向量α \alphaα表示,分割的目的是将图像中每个RGB像素分类,即在指定的区域内是否有一种颜色。为了执行这一比较,需要有相似性度量:欧氏距离和曼哈顿距离。
欧氏距离
在这里插入图片描述
其中,z 和 α 分别代表两个像素点的RGB颜色向量,R,G,B表示红色、绿色和蓝色分量。在图像分割过程中,首先确定一个或多个有代表性的颜色样本点,计算出这些点的平均颜色向量。然后,对于图像中的每个像素点,计算其与平均颜色向量之间的欧式距离。根据这个距离与预设的阈值比较,决定该像素点是否属于目标颜色区域。
在实际的代码实现中,可以通过遍历图像中的每个像素点,获取其邻域像素点,并计算它们之间的欧式距离。根据距离的远近,将像素点分为不同的类别,并用不同的颜色或标记来表示,最终得到分割后的图像。
代码

clc;  
clear all;  
A = imread('123.jpg');   
A = im2double(A);  
sigma = 3;   
A = imgaussfilt(A, sigma);   
A = imresize(A, [30, 30]);   
[m, n, p] = size(A);   
colors = [   
    1, 0, 0;   
    1, 1, 0;    
    0, 0, 1;   
    0, 1, 0;    
    1, 1, 1;     
    1, 0.5, 0; ];   
outputImage = zeros(m, n, 3);   
for i = 1:m   
    for j = 1:n   
        distances = sum((reshape(A(i, j, :), 1, 3) - colors).^2, 2);   
        [~, idx] = min(distances);   
        outputImage(i, j, :) = colors(idx, :);   
    end   
end   
figure  
subplot(1, 2, 1)  
imshow(newOutputImage)  
title('魔方图')  
subplot(1, 2, 2)  
imshow(imread('123.jpg'))  
title('原图像')

遍历每个像素 (i, j):reshape(A(i, j, 😃, 1, 3) 将当前像素颜色重塑为 1x3 的行向量(RGB格式)。计算当前像素与定义的多种颜色之间的欧式距离(平方和),返回的是每种颜色的离。min(distances) 找到最小距离的索引 idx,从而确定当前像素应该映射到哪个目标颜色。将对应的目标颜色赋值给 outputImage 中对应的位置。通过这种方法,原图的颜色信息被简化为预定义的几个颜色。
效果图
在这里插入图片描述
原图的颜色信息被简化为魔方的六个颜色

扩展图片尺寸

与插值法的不同
在日常对图像进行操作的过程中会涉及到图像的放大与缩小,这一系列的操作都是通过插值法来实现的;matlab中imresize()函数的实现原理就是通过插值算法,如果不对应用某种算法进行设置,则默认采用双线性插值算法。简单来说,插值指利用已知的点来“猜”未知的点,图像领域插值常用在修改图像尺寸的过程,由旧的图像矩阵中的点计算新图像矩阵中的点并插入,不同的计算过程就是不同的插值算法
插值法顾名思义就是从原图像矩阵中找到与它(目标图像的像素点)距离最近的点,然后将最近点的像素值,赋给它就行了。
在这里插入图片描述
上面已计算出目标图像的第一行像素点坐标对应于原图像的坐标. 其余的坐标对应关系也可上述方法依次计算.最近邻插值缺点:最邻近算法计算量较小,但可能会造成插值生成的灰度上的不连续,在灰度变化的位置处,可能会产生明显的锯齿现象。
不过在这里我不使用插值法,而是使用复制法,就是通过在每个原始像素周围复制其值来实现的,这种方法简单但可能会导致放大后的图像边缘出现锯齿状,在像素化过程中就是需要锯齿状,以方便识别魔方的色块。
在这里插入图片描述
通过等效复制的方法把图像复制出来。从而扩大像素的大小,也实现了分辨率的提高。不过在写代码的时候复杂度也提高了,但是也保证颜色分割后颜色不会被改变。
代码

for i = 1:m  
    for j = 1:n  
        newOutputImage(currentRow:currentRow+scaleFactor-1, currentCol:currentCol+scaleFactor-1, :) = ...  
            repmat(outputImage(i, j, :), scaleFactor, scaleFactor, 1);  
        currentCol = currentCol + scaleFactor;  
        if mod(j, 1) == 0 && j < n  
            newOutputImage(currentRow:currentRow+scaleFactor-1, currentCol, :) = 0.5;  
            currentCol = currentCol + 1;  
        end  
    end  
    currentRow = currentRow + scaleFactor;    
    if mod(i, 1) == 0 && i < m  
        newOutputImage(currentRow, :, :) = 0.5;   
        currentRow = currentRow + 1;  
    end  
    currentCol = 1; 
end  

效果图
在这里插入图片描述
现在还看不出上面作用,不过当图片数量级扩大的时候,为了更加清楚看清色块,需要做这一操作。
**

在每个色块之间添加分割线

**
在拼接魔方的时候,需要知道每一个魔方每一面的色块,所以需要在每个像素之前添加一行一列灰色进行分割,在这次实验中我先添加每一列灰色再进行添加每一行灰色,最后再添加黑色的魔方分界线进行覆盖,这样也更加优化了计算的流程。
在这里插入图片描述
当放大n=2倍时候,灰线应该在第3,6,9……….列,在第3,6,9………行,然后,当放大n=1的时候,灰线应该在2,4,6………列,在第2,4,6……….行,可以得出规律为放大倍率的n*k(第k条)*2,同样的数组也需要扩大,计算为
在这里插入图片描述
m.n分别为原图像的行数和列数,scalefactor为扩大因数,则扩大的数组就是如图计算公式
代码

newM = m * scaleFactor + m ; % 增加灰色行  
newN = n * scaleFactor + n ; % 增加灰色列  
newOutputImage = zeros(newM, newN, p);  
currentRow = 1;  
currentCol = 1; 
for i = 1:m  
    for j = 1:n  
        newOutputImage(currentRow:currentRow+scaleFactor-1, currentCol:currentCol+scaleFactor-1, :) = ...  
            repmat(outputImage(i, j, :), scaleFactor, scaleFactor, 1);  
        currentCol = currentCol + scaleFactor;  
        if mod(j, 1) == 0 && j < n  
            newOutputImage(currentRow:currentRow+scaleFactor-1, currentCol, :) = 0.5;  
            currentCol = currentCol + 1;  
        end  
    end  
    currentRow = currentRow + scaleFactor;    
    if mod(i, 1) == 0 && i < m  
        newOutputImage(currentRow, :, :) = 0.5;   
        currentRow = currentRow + 1;  
    end  
    currentCol = 1; 
end  

每处理完一列后(即 j 的循环),在 newOutputImage 中插入一列灰色(值为 0.5),并调整 currentCol 的位置。每处理完一行后(即 i 的循环)在 newOutputImage 中插入一行灰色(值为 0.5),并调整 currentRow 的位置。
然后是添加黑线,添加黑线就简单了,只需要在第3*n条灰线改为黑色就好

while true  
    rowToChange = (scaleFactor*jieshu+jieshu) * n; 
    x=x+1;
    if rowToChange > newM  
        break;  
    end  
    newOutputImage(rowToChange, :, :) = 0;  
    n = n + 1;  
end  
y=0;
n = 1; 
while true  
    colToChange = (scaleFactor*jieshu+jieshu) * n;  
    y=y+1;
    if colToChange > newN  
        break;   
    end  
    newOutputImage(:, colToChange, :) = 0;  
    n = n + 1; 
end  

效果图
在这里插入图片描述
在每个扩展区域之间插入灰色行和列,形成一种类似于魔方的视觉效果
这个是先添加灰色线再添加黑色线,如果先添加黑色线再添加灰色线,就会很大程度增加计算复杂度和时间。
在这里插入图片描述
先插入灰色再插入黑色的运行的时间
在这里插入图片描述
先插入黑色再插入灰色的运行时间
在这里插入图片描述
先插入灰色再插入黑色的数组大小
在这里插入图片描述
先插入黑色再插入灰色的数组
这是因为我在写代码的时候做了很多判断,复杂度提升了非常之多,而且先插入黑色再插入灰色的时候,要重新计算数组大小,又多了很大一部的判断,插入灰色的时候还要判断有没有黑色,产生了极大的数据量。而且也产生了冗余计算。
在这里插入图片描述
使用该方法生成的图片

与视频处理组合成的最终代码

clc;  
clear all;  
tic
outputVideoFile = 'zhiyu2.mp4';
vidReader = VideoReader("123456.mp4");  
vidWriter = VideoWriter(outputVideoFile);  
open(vidWriter);  
while hasFrame(vidReader)  
    A = readFrame(vidReader);  
    A = im2double(A);  
    sigma = 3;   
    A = imgaussfilt(A, sigma);   
    A = imresize(A, [90, 120]);   
    [m, n, p] = size(A);   
    colors = [   
        1, 0, 0;   
        1, 1, 0;    
        0, 0, 1;   
        0, 1, 0;    
        1, 1, 1;     
        1, 0.5, 0;   
    ];   
    outputImage = zeros(m, n, 3);   
    for i = 1:m   
        for j = 1:n   
            distances = sum((reshape(A(i, j, :), 1, 3) - colors).^2, 2);   
            [~, idx] = min(distances);   
            outputImage(i, j, :) = colors(idx, :);   
        end   
    end   
    scaleFactor = 2;   
    jieshu = 3; 
    newM = m * scaleFactor + m; % 增加灰色行  
    newN = n * scaleFactor + n; % 增加灰色列  
    newOutputImage = zeros(newM, newN, p);  
    currentRow = 1;  
    currentCol = 1;   
    for i = 1:m  
        for j = 1:n  
            newOutputImage(currentRow:currentRow+scaleFactor-1, currentCol:currentCol+scaleFactor-1, :) = ...  
                repmat(outputImage(i, j, :), scaleFactor, scaleFactor, 1);  
            currentCol = currentCol + scaleFactor;  
            if mod(j, 1) == 0 && j < n  
                newOutputImage(currentRow:currentRow+scaleFactor-1, currentCol, :) = 0.5;  
                currentCol = currentCol + 1;  
            end  
        end  
        currentRow = currentRow + scaleFactor;    

        if mod(i, 1) == 0 && i < m  
            newOutputImage(currentRow, :, :) = 0.5;   
            currentRow = currentRow + 1;  
        end  
        currentCol = 1;   
    end  
    n = 1;   
    while true  
        rowToChange = (scaleFactor * jieshu + jieshu) * n;   
        if rowToChange > newM  
            break;  
        end  
        newOutputImage(rowToChange, :, :) = 0;  
        n = n + 1;  
    end  
    n = 1;   
    while true  
        colToChange = (scaleFactor * jieshu + jieshu) * n;  
        if colToChange > newN  
            break;   
        end  
        newOutputImage(:, colToChange, :) = 0;  
        n = n + 1;   
    end  
    writeVideo(vidWriter, newOutputImage);  
end  
close(vidWriter);  
disp('视频处理完成,已导出为 outputVideo.avi');
toc

效果展示

outputVideo

可改变的魔方个数
在这里插入图片描述
100个魔方(三阶)
在这里插入图片描述
900个魔方
在这里插入图片描述
90000个魔方(三阶)
在这里插入图片描述
细节展示,非常的清楚也可以清楚的知道每个地方的每个魔方的细节
可改变的魔方阶数
在这里插入图片描述
2025个二阶魔方
在这里插入图片描述
细节效果图片
在这里插入图片描述
100个四阶
在这里插入图片描述
100个五阶魔方
分辨率可调
当图片很大时,如果分辨率太低就会出现导出图片看不清每个魔方的面,所以需要增大分辨率。
在这里插入图片描述
放大一倍分辨率
在这里插入图片描述
放大一倍分辨率的内存大小
当分辨率太小时就会出现这样的情况
在这里插入图片描述
这是放大了15倍的分辨率
在这里插入图片描述
放大了15倍的分辨率细节还原
在这里插入图片描述
放大了15倍的分辨率的内存占用
细节还原十分好,已经可以支持扫描了,依然可以看的清楚每一个的面
在这里插入图片描述
放大了2倍的分辨率
在这里插入图片描述

放大了2倍的分辨率的内存占用
支持魔方数量计算
在这里插入图片描述
10000个三阶魔方
在这里插入图片描述
400个二阶魔方
在这里插入图片描述
36个六阶魔方

实际上使用效果图

在这里插入图片描述
这是送的礼物
在这里插入图片描述
这是用魔方拼出来的图

总结

通过这个项目,我学习了图像滤波、颜色分割、图像缩放等关键技术。我了解到高斯滤波在平滑图像、准备颜色分割中的重要性。在项目开发过程中,我遇到的最大挑战是如何在不使用插值法的情况下,通过复制法实现图像的放大,同时保持颜色的一致性。通过不断尝试和调试,我最终实现了一个既简单又有效的解决方案。项目完成后,我成功地将多张图片转换成了魔方拼图,并且能够自定义魔方的数量和分辨率。
这个礼物也是送出去了,这个课程我拿到了A+,这个课程报告也拿到了接近满分的答卷。可是该分开的人还是分开了,人总是要和留不住的东西说再见的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值