完整代码:https://download.youkuaiyun.com/download/qq_49712456/35325047
一、问题描述
- 对图像建立四叉树
- 对于输入的图像,四叉树能够输出模糊的结果
- 对颜色相近的区域进行模糊
二、实现思路
(一)利用方差
- 构造四叉树
- 求出分叉区域的平均值和方差
- 递归处理,符合模糊条件的将所有值取为平均值
(二)Gauss模糊
- 对于每个值,求其周围9个点的加权平均值,并将该点设为均值
- 重复上述操作
注:高斯模糊只能模糊图像,不能进行压缩
三、 代码实现
(一) 四叉树
1. 建立四叉树结构体
包含左上、左下、右上、右下四部分图像的指针
左、右、上、下四个方向的坐标
struct quadTree
{
quadTree *zs;
quadTree *zx;
quadTree *ys;
quadTree *yx;
int left;
int right;
int up;
int down;
};
2. 处理四叉树
编写除法函数
除以2余1的,全部退位
避免处理图像时两个区域同时处理同一片坐标点
int division(int a, int b)
{
if((a + b) % 2 == 1)
return (a+b-1) / 2;
else
return (a+b) / 2;
}
判断这一部分是否满足模糊图像的要求
bool chuli(quadTree *q)
{
double gave = 0, rave = 0, bave = 0;
bool re = panduan(q, gave, rave, bave);
若需要继续拆分,则将这一部分拆分成四个部分,分别建立节点
quadTree *tmp1 = new quadTree;
tmp1->up = q->up;
tmp1->down = division(q->down , q->up);
tmp1->left = q->left;
tmp1->right = division(q->right , q->left);
tmp1->zs = NULL;
tmp1->zx = NULL;
tmp1->ys = NULL;
tmp1->yx = NULL;
q->zs = tmp1;
对于左上部分结点,左、上坐标不变,右、下坐标为求和后取半
其余三个部分同理
quadTree *tmp2 = new quadTree;
tmp2->up = division(q->up , q->down);
tmp2->down = q->down;
tmp2->left = q->left;
tmp2->right = division(q->right , q->left);
tmp2->zs = NULL;
tmp2->zx = NULL;
tmp2->ys = NULL;
tmp2->yx = NULL;
q->zx = tmp2;
quadTree *tmp3 = new quadTree;
tmp3->up = q->up;
tmp3->down = division(q->down , q->up);
tmp3->left = division(q->left , q->right);
tmp3->right = q->right;
tmp3->zs = NULL;
tmp3->zx = NULL;
tmp3->ys = NULL;
tmp3->yx = NULL;
q->ys = tmp3;
quadTree *tmp4 = new quadTree;
tmp4->up = division(q->up , q->down);
tmp4->down = q->down;
tmp4->left = division(q->left , q->right);
tmp4->right = q->right;
tmp4->zs = NULL;
tmp4->zx = NULL;
tmp4->ys = NULL;
tmp4->yx = NULL;
q->yx = tmp4;
返回false,表示需要继续递归
return false;
若满足条件
则将这一部分所有结点都更改为平均值
并返回true,表示结束递归
else
{
for(int i = q->left; i < q->right; i++)
for(int j = q->down; j < q->up; j++)
{
colors[j][i].b = bave;
colors[j][i].g = gave;
colors[j][i].r = rave;
}
return true;
}
3. 四叉树递归
判断是否需要继续递归
若已经达到模糊条件,则退出递归
否则,分别递归四个部分结点
void digui(quadTree *r)
{
bool re = chuli(r);
if(re == true)
return;
digui(r->yx);
digui(r->zs);
digui(r->ys);
digui(r->zx);
}
创建根结点,并递归根结点
void chaifen(int height)
{
quadTree *root = new quadTree;
root->down = 0;
root->left = 0;
root->right = height;
root->up = height;
root->ys = NULL;
root->zs = NULL;
root->yx = NULL;
root->zx = NULL;
digui(root);
}
(二) 图像处理(方差)
1. 求平均值
将该区域图像内的每一个坐标点的颜色值相加
for(int i=q->left; i<q->right; i++)
for(int j=q->down; j<q->up; j++)
{
rave += double(colors[j][i].r);
gave += double(colors[j][i].g);
bave += double(colors[j][i].b);
}
除以坐标点个数,即面积
int square = (q->up - q->down) * (q->right - q->left);
rave /= square;
gave /= square;
bave /= square;
2. 求方差
方法与求平均值相同
for(int i=q->left; i<q->right; i++)
for(int j=q->down; j<q->up; j++)
{
Dr += (rave - double(colors[j][i].r)) * (rave - double(colors[j][i].r));
Dg += (gave - double(colors[j][i].g)) * (gave - double(colors[j][i].g));
Db += (bave - double(colors[j][i].b)) * (bave - double(colors[j][i].b));
}
Dr /= square;
Dg /= square;
Db /= square;
3. 判断
若方差大于标准值,则继续拆分该区域
否则,将该区域所有的点的颜色设为平均值
bool panduan(quadTree *q, double &gave, double &rave, double &bave)
{
double Dr = 0, Dg = 0, Db = 0;
D(q, Dr, Dg, Db, gave, rave, bave);
if(Dr > standard || Dg > standard || Db > standard)
return true;
return false;
}
(三) 图像处理(Gauss模糊)
1. 单次模糊
高斯模糊的原理:
中间点取周围点的加权平均值,取点个数由取点半径radius决定
周围点的权重由正态分布函数分配,这里取值如下:
double quanzhong[3][3] = {{0.0453542, 0.0566406, 0.0453542}, {0.0566406, 0.0707355, 0.0566406}, {0.0453542, 0.0566406, 0.0453542}};
为了将所有点的权值和为1,每个点都除以0.4787147
遍历整张图象,对每个点都进行处理
for(int i=1; i<width-1; i++)
for(int j=1; j<width-1; j++)
{
double gave = 0, rave = 0, bave = 0;
for(int k=-1; k<=radias; k++)
for(int l=-1; l<=radias; l++)
{
rave += double(colors[j+k][i+l].r) * quanzhong[k][l];
gave += double(colors[j+k][i+l].g) * quanzhong[k][l];
bave += double(colors[j+k][i+l].b) * quanzhong[k][l];
}
colors[j][i].r = rave;
colors[j][i].g = rave;
colors[j][i].b = rave;
}
2. 多次调用
自定义调用次数
void diaoyong(int width)
{
for(int i=1; i<cishu; i++)
{
Guass(width);
}
}
(四) ppm格式转换
为了方便查看结果,用python将ppm格式图片转化为jpg格式
from PIL import Image
img = Image.open("C:\\Users\\15313\\Desktop\\homework\\tree\\b.ppm")
img.save("C:\\Users\\15313\\Desktop\\homework\\tree\\b.jpg")
img.show()
四.实验结果
(一) 利用方差判断
-
标准值取100
-
标准值取500
(二) Gauss模糊
(Gauss模糊只能达到模糊的目的,不能实现压缩)
半径radius = 1,调用次数cishu = 20
五.小结感悟
(一) 失误
- 在chuli函数中,不能用tmp构造4个分叉
因为tmp是指针,传的是地址,会导致4个分叉对应同一个tmp - printImage函数中FILE *f = fopen(fileName, “wb”);
必须要用”wb”,不能使用”w”,否则会有上次的文件残留
(二) 收获
- 对图片压缩的方法有了一个初步认知,可能对以后的学习研究有帮助
了解一些模糊图像的原理和方法 - 运用四叉树后,有助于对树结构的学习
在使用递归函数时经常出现意料之外的bug,解决完这些bug让我对递归的理解更进一步