上机报告
修改第二次作业
3. 使用C/C++编程,对题1得到的灰度图像H0201Gry.bmp进行均值方差规定化,自己设定3组不同的 均值和标准差,保存得到3幅结果图像,请比较它们的不同;在做完均值方差规定化后,再做灰度 范围的线性拉伸,你觉得有意义吗?
(图片部分没有修改)
当图像的方差较小的时候,整体的图像呈现灰蒙蒙的状态,大家的灰度值都比较接近。当方差变大的时候,亮的地方会特别亮,暗的地方会特别暗。均值体现了图像总体的亮度。
(以下是修改的地方)
做完在做完均值方差规定化后,再做灰度范围的线性拉伸,有意义,能加灰度范围拉伸到想要的范围。
调试
- 修复了一些低效率的运算,将RBG转灰度的查表实现中,使用了将浮点数运算转化为整数运算的技巧
- Windows.h中自带的min,max宏定义令人作呕,于是使用 algorithm中的max , min使用方法(max)(a, b)
-
修复了一部分存取效率的问题,增加了bmpConverter的拷贝赋值
- 没有遇到特别的bug,调试耗费时间比较少
- 新增homework.h来保存作业中每个题目的函数
- delete 指针,对于空指针没有效果,所以不需要判断空指针,直接delete
完成第三次作业的C/C++
对于项目的总体结构做出了比较大的改变
结构的改进
- 文件方面:新增类Img来封装指向图像的指针以及一些其他基础的信息。并且作为原来bmpConverter的父类,因为我觉得return一个指向new出来空间的指针过于危险
-
代码方面:总结了一个固定的设计模式。在将老师要求实现的函数设置为私有函数,没有I/O,纯算法。事实上这些函数是静态的。然后设置一个共有函数来调用这些私有函数,并且负责I/O,以及地址空间的开辟,安全性的检查。如果更加进一步地设计,我觉得可以嵌套这些共有函数,让形成一个功能更加强的共有函数,来提供给被人调用。这样的好处是解耦,每一个部分都有明确的目的,之前的实现都是一个函数包办一切,后来修改,调试都更加麻烦。
- 我现在终于大致了解了为什么在一些库中,都会有一个core.h或者core.py之类的东西,我觉得这里面放的都是一些核心的函数,我们现在做的工作就是写这个core。而基于core,类的作用是封装好这样的核心函数调用,方便他人使用。所以我一开始用类来把这些核心函数的包装起来想法是不好的。
代码(作业二的做出修改的部分以及作业三)在文档最后
第三次作业
1. 滤波前图3-11和滤波后图3-12的标准差变化较明显,为什么滤波前图3-13和滤波后图3-14 的标准差几乎没有变化?
3-11图片像素点周围的突变比较大,边缘信息比较多,进行均值滤波之后,像素被均衡得比较明显。
3-13图片像素点周围的突变比较小,边缘信息比较少,进行均值滤波之后,像素被均衡得不明显。
2. 对一个图像𝐴作5×5的均值滤波得到图像B,对图像𝐵再做5×5的均值滤波得到图像𝐶。那么, 对图像𝐴做多大的均值滤波可以直接得到图像𝐶?
\[一次均值滤波:I_1={\Sigma_i \Sigma_j I(x+i, y+j)\over25}\]\(两次均值滤波:\)\(I_2={\Sigma_i \Sigma_j\Sigma_a \Sigma_b I(x+i+a, y+j+b)\over625}\\={\Sigma_j \Sigma_b\Sigma_i \Sigma_a I(x+i+a, y+j+b)\over625}\\={\Sigma_u\Sigma_v I(x+u, y+v)\over625}\)
所以可以看成是在一个9x9的邻域里做一个均值滤波
3.在图像处理中,彩色到灰度转换公式为:𝑔𝑟𝑦 = 0.299 × 𝑟𝑒𝑑 + 0.587 × 𝑔𝑟𝑒𝑒𝑛 + 0.114 × 𝑏𝑙𝑢𝑒,请用C/C++编程把彩色图像H0301Rgb.bmp转成为灰度图像H0301Gry.bmp,请分别使 用“整数除法或者浮点乘法除法变为整数乘法和移位”的编程技巧和直接计算,分别使用 Debug和Release编译,并各执行1000次,比较它们的时间花费(C/C++中的时间函数为 clock_t t=clock())。
MSVCx86 | 直接做 | 使用trick |
---|---|---|
debug | 2109 | 2113 |
release | 538 | 249 |
MSVCx64 | ||
debug | 3614 | 2047 |
release | 558 | 274 |
单位clock tick
4. 参照算法3-1,采用C/C++编程,实现对于灰度图像H0302Gry.bmp每行上的一维均值滤波 (邻域大小为1行M列,M=21)。
5. 参照算法3-2,采用C/C++编程,实现对于灰度图像H0302Gry.bmp每行上的一维均值滤波 (邻域大小为1行M列,M=21),要使用一维积分图。
列积分实现 | 积分图实现 |
---|---|
图片对于边角也做了滤波,所以没有黑边。做的方法就是,在对于边角进行滤波的时候,将图像外的像素都设置为0。
6. 使用MMX或者SSE指令集,用C/C++编程实现灰度图像H0302Gry.bmp的反相。
使用SSE指令集,结果:
7.使用算法3-6对灰度图像H0302Gry.bmp进行中值滤波,分别使用5 × 5,13 × 13,21 × 21大 小的邻域进行中值滤波,比较这三种邻域时dbgCmpTimes的值,并进行分析。
5x5: 6.248840
13x13: 5.322083
21x21: 5.026718
分析:观察发现,滤波器越大,比较次数就越少。因为滤波器的宽度变大了,相对而言,每一列的值的对于中值的影响变小了,所以在同一行之间推进的时候,增加一列,去掉一列对于中值的影响变小了,中值变化前后,更加接近,比较次数就减少了。
8. 采用高速摄像机(2000fps)对准一个区域进行拍照,该区域有一发炮弹高速飞过。摄像机 得到了𝑀幅场景图像,但𝑀幅图像的每一幅中都有炮弹,请问如何才得到一幅没有炮弹的场 景图像(一般称为背景图像)?
对这M幅图像进行帧间的滤波。对M幅图像的每个像素点对应位置,取出现次数最多的亮度值,或者中值。
9.\(T^3 = \left[ \begin{matrix} 0 & 1 & 0 \\ 1 & 1 & 1 \\ 0 & 1 & 0 \end{matrix} \right]\)和\(T^5 = \left[ \begin{matrix} 0 &0&0&0&0\\0 &0&1&0&0\\ 0 &1&1&1&0\\ 0 &0&1&0&0 \\ 0 &0&0&0&0 \end{matrix} \right]\)等价吗
等价,不过在处理边界的时候可能会造成图片变得更小。
10.\(T^3 = {1 \over{16}}\left[ \begin{matrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \end{matrix} \right]\)有什么特点?使用他进行图像平滑需要乘法和除法运算吗?
对称,而且值都是2的幂次。不需要,乘法和除法可以用位移来代替。
11. 使用标准差等于1.0的高斯函数计算一维卷积模板T(j)(-3<=j<=3),得到T={0.003134, 0.038177,0.171099,0.282095,0.171099,0.038177,0.003134},按式(3-11)实现高斯滤 波时,会有大量的浮点计算,如何消除这些浮点运算?
通过“整数除法或者浮点乘法除法变为整数乘法和移位“的技巧,消除浮点运算,求得高斯核之后乘2^17转化为int,然后在进行卷积运算得到结果之后,右移17位得到结果。
12.使用C/C++编程,对灰度图像H0303Gry.bmp实现标准差𝛿 = 3的高斯滤波,使用两个一维高 斯平滑的串联实现,并且在像素处理循环中不使用浮点运算。
位移23 | 位移17 | opencv的结果 |
---|---|---|
亮度均值 | ||
45 | 60 | 61 |
// 偏移量设置为17的原因,将求和后再位移,因为先位移会损失精度很多次,按照比例来说会有50%的计算损失1亮度
// 所以累积就是 m/2的亮度损失,m为滤波器的宽,在实验之后,放入PS也观察到了相似的结果,是8亮度,相对于m=19的滤波器
// 偏移17的缺点,滤波器的精度下降,滤波器所有值之和只为,0.999931,按照平均亮度是128算(实际上原图的平均亮度只有61),亮度损失只有1不到
// 从宏观上看,相对于23的offset图片整体的亮度提升了
// 亮度下降的另外一个原因是在处理边界的时候使用了周围补0来处理,所以亮度均值会下降
13.算法3-6中threshold取何值时能够实现二值图像的最小值滤波、最大值滤波和中值滤波?
\(\ge\)255时,为最小值滤波
\(\ge\)128时,是中值滤波
\(\ge\)1时为最大值滤波
14.请分别使用超限平滑和𝐾个邻点平均法,对3.2.1节中的数据D1进行邻域为3个像素的均值滤 波,写出滤波结果。
原值 | 超限平滑(设置阈值为3, 邻域3个像素) | 𝐾个邻点平均法(K=5, A=3) |
---|---|---|
3 | 3 | 3 |
3 | 3 | 3 |
3 | 3 | 3 |
9 | 5 | 5 |
3 | 3 | 3 |
3 | 3 | 5 |
9 | 9 | 9 |
9 | 9 | 9 |
9 | 9 | 9 |
3 | 7 | 7 |
9 | 9 | 9 |
9 | 9 | 9 |
9 | 9 | 9 |
15.使用Photoshop中的median Filter或者Gaussian Blur,估计拍摄灰度图像H0304Gry.bmp时光 源的位置,给出不含有药片的光照图像,并通过观察药片的阴影确认得到的光照图像是正确 的。
原图 | 高斯模糊 | 二值图像得到的光源位置 |
---|---|---|
从阴影的方向上看,与二值图像的光源位置是相匹配的。
源码
调试当中的心路历程
对所有的滤波,都对边缘地区做了特殊处理(补0),所以写了很多bug。
一开始的时候,没有相关经验,对于图像错误不会分析。bug多了,逐渐也知道该怎么看错误图像了。比如
- 如果图像出现了横向的拉伸情况,那很有可能,在滤波的时候,处理
for(x = 0; x < width; x++)
的内部出了问题,纵向问题相似。 - 图像处理代码形式一般来说是对称的,如果代码不对称,一般就是俩边处理不一致导致问题。
- 边界问题,在做积分图的均值滤波的时候遇到的问题。由于我在图像周围补了一圈0,计算积分图的时候,补0的宽度和应该是滤波器的宽度+1,否则由积分图得到的目标图像,最右边的边界像素会超积分图的计算范围。
- 浮点运算转化为整数运算的位移问题,位移根据最终结果的值得范围来,如果最大是255,则位移(31 - 8)23位,这个问题在高斯滤波的代码里我做了仔细叙述
- 还有遇到了一个opencv运行的问题,在x86debug模式下,显示“无法定位程序输入点_crtCloseThreadpoolWait于动态链接库C:\WINDOWS\SYSTEN32\CONRT140D.dll上”。网上的解答是DLL确实,或者冲突。问题在于,我在那个路径下,有这个DLL。我搜索了所有这个名字的DLL,放到C:\WINDOWS\SYSTEN32\CONRT140D.dll的这个地方,都没有用。对于冲突的问题,无法解。x86release模式可以运行。
// main.cpp
int main()
{
//opencv_guass_blur();
//hw3_1dGuass();
//hw3_inv_SSE();
//hw3_1dMed_col_cal();
//hw3_1dAvgF_col_cal();
//hw3_1dAvgF_calGraph();
//hw2_time_cmp();
//hw2_24bitHist();
//hw3_time_cmp();
//hw3_1dAvgF();
//bmpConverter bmpCvt = bmpConverter("./pic/H0102Rgb.bmp");
//bmpCvt.Img2Bmp("./pic/test.bmp");
//hw2_time_cmp();
//hw2_14bit_convert();
//bmpCvt.Img2Bmp("./pic/Fig0201Grey.bmp", 8);
return 0;
}
// homework.cpp
void hw3_time_cmp()
{
bmpConverter bmpCvt("./pic/Fig0201.bmp");
int epoch_num = 1000;
time_t start = clock();
while (epoch_num--)
{
bmpCvt.RGB2Gry2(true, false);
}
time_t end = clock() - start;
cout << "时间: " << end << endl;
epoch_num = 1000;
start = clock();
while (epoch_num--)
{
bmpCvt.RGB2Gry2(false, false);
}
end = clock() - start;
cout << "时间: " << end << endl;
bmpCvt.RGB2Gry(true, true);
bmpCvt.Img2Bmp("./pic/Fig0201Grey.bmp", 8);
}
void hw3_1dAvgF_col_cal()
{
bmpConverter bmpCvt("./pic/H0302Gry.bmp"); // H0302Gry.bmp
bmpCvt.AavgFilter2d1c(1, 21, true);
bmpCvt.Img2Bmp("./pic/H0302Gry_F_col_cal.bmp");
}
void hw3_1dAvgF_calGraph()
{
bmpConverter bmpCvt("./pic/H0302Gry.bmp"); // H0302Gry.bmp
bmpCvt.AavgFilter2d1c(1, 21, true, 'g');
bmpCvt.Img2Bmp("./pic/H0302Gry_F_calGraph.bmp");
}
void hw3_inv_SSE()
{
bmpConverter bmpCvt("./pic/H0302Gry.bmp");
bmpCvt.PInvert1c('s');
bmpCvt.Img2Bmp("./pic/H0302Gry_inv_SSE.bmp");
}
void hw3_1dMed_col_cal()
{
bmpConverter bmpCvt("./pic/H0302Gry.bmp"); // H0302Gry.bmp
bmpCvt.AMedianFilter2d1c(21, 21, true);
bmpCvt.Img2Bmp("./pic/H0302Gry_MedF_col_cal.bmp");
}
void hw3_1dGuass()
{
bmpConverter bmpCvt("./pic/H0303Gry.bmp"); // H0303Gry.bmp
bmpCvt.AGuassFilter2d1c(3, 19);
bmpCvt.Img2Bmp("./pic/H0303Gry_Guass2.bmp");
}
// 这个函数是用来比较自己实现的高斯模糊和标准的高斯模糊的差距
// 因为自己实现的高斯模糊运用了快速的浮点运算转化为整数运算的技巧,存在误差
void opencv_guass_blur()
{
cv::Mat src, dst;
const char* filename = "./pic/H0303Gry.bmp";
cv::imread(filename).copyTo(src);
if (src.empty()) {
throw("Faild open file.");
}
int ksize1 = 19;
int ksize2 = 19;
double sigma1 = 3;
double sigma2 = 0;
cv::GaussianBlur(src, dst, cv::Size(ksize1, ksize2), sigma1, sigma2);
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::imwrite("./pic/H0303Gry_cvblur.bmp", dst);
cv::waitKey();
cout << getGaussianKernel(ksize1, sigma1, CV_64F);
return ;
}
// Img.h
class Img
{
public:
Img();
Img(int width, int heigth, int channel = 1);
Img(BYTE* p, int w, int h, int channel = 1);
Img(Img && a);
Img(Img & a);
~Img();
protected:
int width, height, channel;
BYTE* pImg=nullptr;
void T();
};
// bmpConverter.h
class bmpConverter : public Img
{
public:
bmpConverter(const char * orgfile);
bmpConverter();
bmpConverter(Img);
bmpConverter(bmpConverter &);
bmpConverter(bmpConverter &&);
~bmpConverter();
// 灰度图像反转
void InvertImg();
// 读取bmp
bool BmpFile2Img(const char * DstFile);
// 保存bmp
bool Img2Bmp(const char * DstFile, int bitCnt=0, char RGBMOD='\0');
// 读取14bit raw数据,已经写死
bool read14bitRaw(const char * DstFile);
// RGB转灰度图
void RGB2Gry(bool table_chk=true, bool inplace=true);
// RGB转灰度图2,只用于测试
void RGB2Gry2(bool table_chk=true, bool inplace=true);
// 线性拉伸(仿射变换)
void PAffine(double k, double b);
// 均值标准差规定化
void P2avgstd8Bit(double mean = 128.0, double stddev = 1);
// 8bit直方图标准化实现
void PHistogramEqualize8bit();
// 24bit直方图标准化实现(三个通道分别实现)
void PHistogramEqualize24bit();
// 24bit直方图标准化实现(三个通道在一起实现)
void PHistogramEqualize24bit1();
// 24bit直方图标准化实现(三个通道的均值实现)
void PHistogramEqualize24bit2();
// 二维维均值滤波单通道
Img AavgFilter2d1c(int m, int n, bool inplace = true, char mod='c');
// 灰度图像反转
Img PInvert1c(char mod = 'n', bool inplace=true);
// 二维中值滤波单通道
Img AMedianFilter2d1c(int m, int n, bool inplace = true, char mod = 'c');
// 二维高斯滤波单通道
Img AGuassFilter2d1c(double dev, int m, bool inplace = true, char mod = 'c');
private:
void PHistogramEqualize14bit(short * pRawImg);
void PHistogramEqualize14bit2(short * pRawImg);
void getHistGram8bit(int * hist);
void getHistGram24bitavg(int * hist);
void getHistGram24bit(int * hist);
bool Img28bitBmp(const char * DstFile, char mod);
bool Img224bitBmp(const char * DstFile);
//void AavgFilter2d_row_cal(BYTE * pSrc, int m, int n, BYTE * pDes);
void AavgFilter2d8bit_col_cal(BYTE * pImg, int width, int height, int m, int n, BYTE * pDes);
void AgetCalGraph(BYTE * pImg, int width, int height, int m, int n, int * pSum);
void AavgFilter2d8bit_calGraph(int * pSum, int m, int n, BYTE * pDes);
void PInvert8bit_SSE(BYTE * pImg, int sum, BYTE * pDes);
void PInvert8bit(BYTE * pImg, int sum, BYTE * pDes);
void AMedianFilter8bit_col_cal(BYTE * pImg, int width, int height, int m, int n, BYTE * pDes);
void AGuassFilter1d(BYTE * pSrc, int width, int height, int * pFilter, int n, BYTE * pDes);
Img T(BYTE * & pSrC, int w, int h, bool inplace = true);
BITMAPFILEHEADER FileHeader;
BITMAPINFOHEADER BmpHeader;
};
// bmpConverter.cpp
//_____________________hw3___________________________
// RGB转灰度的测试函数
void bmpConverter::RGB2Gry2(bool table_chk, bool inplace)
{
if (channel != 3 || !pImg)return;
int sum = width * height;
BYTE * temp_save = new BYTE[sum];
if (!temp_save)
{
#ifdef DEBUG
puts("程序错误,内存申请失败");
#endif // DEBUG
return;
}
#ifdef DEBUG
//cout << (int)(temp_save) <<"113" <<sizeof(temp_save)<< endl;
////cout << (int)pImg << endl;
#endif // DEBUG
BYTE * p1 = temp_save, *p2 = pImg - 1;
if (table_chk)
{
int r_ratio = 0.299 * 1024, g_ratio = 0.587 * 1024, b_ratio = 0.144 * 1024;
while (sum--)*(p1++) = ((b_ratio * int (*(++p2))) + g_ratio * (*(++p2)) + r_ratio * (*(++p2))) >> 10;//blue,green,red
}
else
{
while (sum--)*(p1++) = ((*(++p2)) * 0.114 + 0.587 * (*(++p2)) + 0.299 * (*(++p2)));//blue,green,red
}
if (inplace)
{
swap(temp_save, pImg);
channel = 1;
}
#ifdef DEBUG
//cout << int(temp_save) << endl;
//cout << (int)pImg << endl;
#endif // DEBUG
delete temp_save;
return;
}
// 二维均值滤波,列积分
void bmpConverter::AavgFilter2d8bit_col_cal(BYTE * pImg, int width, int height, int m, int n, BYTE * pDes)
{
BYTE *pAdd, *pDel, *pRes;
int halfx, halfy;
int x, y;
int sum, c;
int sumCol[1<<15]; //约定图像宽度不大于2^15
// step.1------------初始化--------------------------//
m = m | 1; //奇数化
n = n | 1; //奇数化
halfx = m / 2; //滤波器的半径x
halfy = n / 2; //滤波器的半径y
c = (1 << 23) / (m * n); //乘法因子
memset(sumCol, 0, sizeof(int) * width);
for (y = 0, pAdd = pImg; y <= halfy; y++)
{
for (x = 0; x < width; x++) sumCol[x] += *(pAdd++);
}
// step.2------------滤波----------------------------//
for (y = 0, pRes = pDes, pDel = pImg; y < height; y++)
{
for (sum = 0, x = 0; x <= halfx; x++) sum += sumCol[x];
//滤波
//pRes += halfx; //跳过左侧
for (x = 0; x < width; x++)
{
//求灰度均值
*(pRes++) = sum * c >> 23; //用整数乘法和移位代替除法
//换列,更新灰度和
if(x >= halfx)sum -= sumCol[x - halfx]; //减左边列
if(x < width - halfx )sum += sumCol[x + halfx + 1]; //加右边列
}
//pRes += halfx;//跳过右侧
//换行,更新sumCol
if (y >= halfy)
{
for (x = 0; x < width; x++)sumCol[x] -= *(pDel++); //减上一行
}
if (y < height - halfy - 1)
{
for (x = 0; x < width; x++)sumCol[x] += *(pAdd++); //加下一行
}
}
return;
}
// 计算补充滤波器宽度的积分图
void bmpConverter::AgetCalGraph(BYTE * pImg, int width, int height, int m, int n, int * pSum)
{
BYTE *pCur ;
int *pRes;
int x, y, halfx, halfy, w, i, rowsum;
halfx = m / 2;
halfy = n / 2;
pCur = pImg;
pRes = pSum;
w = width + halfx * 2 + 1; // 由于积分图方块特性所以要多加1
// 上侧padding
for (i = 0; i < (halfy+ 1) * w; i++) *(pRes++) = 0;
for (y = 0; y < height; y++)
{
// 左侧padding
for (x = 0; x < halfx + 1; x++) *pRes++ = 0;
// 正常处理
for (x = 0, rowsum = 0; x < width; x++)
{
rowsum += *pCur++;
*(pRes++) = *(pRes - w) + rowsum;
}
// 右侧padding
for (x = 0; x < halfx; x++)*pRes++ = *(pRes - 1);
#ifdef DEBUG
/*printf("%d %d\n", *(pRes - 1));*/
#endif // DEBUG
}
// 下侧padding
for (i = 0; i < (halfy) * w; i++) *(pRes++) = *(pRes - w);
return;
}
// 2d均值滤波 使用积分图
void bmpConverter::AavgFilter2d8bit_calGraph(int * pSum, int m, int n, BYTE * pDes)
{
int *pY1, *pY2;
BYTE *pRes;
int halfx, halfy, w;
int y, x1, x2;
int sum, c;
// step.1------------初始化--------------------------//
m = m | 1; //奇数化
n = n | 1; //奇数化
halfx = m / 2; //滤波器的半径x
halfy = n / 2; //滤波器的半径y
w = width + 2 * halfx + 1;
c = (1 << 23) / (m * n); //乘法因子
// step.2------------滤波----------------------------//
for (
y = 0, pRes = pDes, pY1 = pSum, pY2 = pSum + (n) * w;
y < height;
y++, pY1 += w, pY2 += w
)
{
for (x1 = 0, x2 = m; x1 < width; x1++, x2++) //可以简化如此,但不太容易读
{
sum = *(pY2 + x2) - *(pY2 + x1) - *(pY1 + x2) + *(pY1 + x1);
*(pRes++) = (sum * c) >> 23; //用整数乘法和移位代替除法
#ifdef DEBUG
//if (sum < 0)
//{
// printf("%d %d\n", y, x1);
// printf("%d %d %d %d\n", *(pY2 + x2), *(pY2 + x1), *(pY1 + x2), *(pY1 + x1));
// printf("%d\n", *(pY2 + x2 - 1));
//}
#endif // DEBUG
}
}
// step.3------------返回----------------------------//
return;
}
/*
二维均值滤波
m: filter width
n: filter height
mod: col or row
inplace: inplace
*/
Img bmpConverter::AavgFilter2d1c(int m, int n, bool inplace, char mod)
{
if (!pImg)
{
#ifdef DEBUG
puts("未载入图像,无法操作");
#endif // DEBUG
return Img();
}
if (channel != 1)
{
#ifdef DEBUG
puts("不是单通道图像");
#endif // DEBUG
return Img();
}
if (m > width || n > height)
{
#ifdef DEBUG
puts("滤波器大小错误");
#endif // DEBUG
return Img();
}
BYTE* pSrc = new BYTE[width * height];
switch (mod)
{
default:
case 'c':
case 'C':
AavgFilter2d8bit_col_cal(pImg, width, height, m, n, pSrc);
break;
case 'G':
case 'g':
{
int * pSum = new int[(width + (m | 1)) * (height + (n | 1))];
AgetCalGraph(pImg, width, height, m, n, pSum);
AavgFilter2d8bit_calGraph(pSum, m, n, pSrc);
break;
}
}
if (inplace)
{
delete pImg;
pImg = pSrc;
return Img();
}
else
{
return Img(pSrc, width, height);
}
}
/* 单通道图像反转
* mod: 'n':正常
's':使用SSE
*/
Img bmpConverter::PInvert1c(char mod, bool inplace)
{
if (channel != 1)
{
#ifdef DEBUG
puts("非单通道,无法执行");
#endif // DEBUG
return Img();
}
_declspec(align(16)) BYTE * pDes = new BYTE[((width * height) | 15) + 1];// 凑齐16的整数倍
switch (mod)
{
default:
case 'n':
case 'N':
case '0':
PInvert8bit(pImg, width * height, pDes);
break;
case 's':
case 'S':
case '1':
PInvert8bit_SSE(pImg, width * height, pDes);
break;
}
if (inplace)
{
delete pImg;
pImg = pDes;
return Img();
}
else
{
return Img(pDes, width, height);
}
}
// 不适用SSE的灰度图像反转
void bmpConverter::PInvert8bit(BYTE * pImg, int sum, BYTE * pDes)
{
BYTE * pCur = pImg, * pEnd = pImg + sum, *pRes = pDes;
while (pCur < pEnd) *(pRes++) = ~*pCur++;
}
// 使用SSE的灰度图像反转
void bmpConverter::PInvert8bit_SSE(BYTE * pImg, int sum, BYTE * pDes)
{
BYTE * pCur, * pRes;
__m128i * pCurSSE, *pDesSSE;
__m128i inver;
int res, t;
inver = _mm_set_epi32(-1, -1, -1, -1);
res = sum & 15, t = sum / 16;
pCur = pImg;
pCurSSE = (__m128i *)pCur;
pDesSSE = (__m128i *)pDes;
while (t--)*pDesSSE++ = _mm_sub_epi32(inver, *pCurSSE++);
pCur = pImg + sum, pRes = pDes + sum;
while (res--) *(pRes--) = ~*pCur--;
}
// 二维维均值滤波单通道
Img bmpConverter::AMedianFilter2d1c(int m, int n, bool inplace, char mod)
{
if (!pImg)
{
#ifdef DEBUG
puts("未载入图像,无法操作");
#endif // DEBUG
return Img();
}
if (channel != 1)
{
#ifdef DEBUG
puts("不是单通道图像");
#endif // DEBUG
return Img();
}
if (m > width || n > height)
{
#ifdef DEBUG
puts("滤波器大小错误");
#endif // DEBUG
return Img();
}
BYTE* pSrc;
pSrc = new BYTE[width * height];
switch (mod)
{
default:
case 'c':
case 'C':
AMedianFilter8bit_col_cal(pImg, width, height, m, n, pSrc);
break;
case 'G':
case 'g':
{
break;
}
}
if (inplace)
{
delete pImg;
pImg = pSrc;
return Img();
}
else
{
return Img(pSrc, width, height);
}
}
// 二维维均值滤波单通道使用列积分
void bmpConverter::AMedianFilter8bit_col_cal(BYTE * pImg, int width, int height, int m, int n, BYTE * pDes)
{
BYTE *pCur, *pRes;
int halfx, halfy, x, y, i, j, y1, y2, res;
int histogram[256];
int wSize, j1, j2;
int num, med, v;
int dbgCmpTimes = 0; //搜索中值所需比较次数的调试
m = m | 1; // 奇数化
n = n | 1; // 奇数化
halfx = m / 2; // x半径
halfy = n / 2; // y半径
wSize = m * n; // 邻域内像素总个数
for (y = 0, pRes = pDes; y < height; y++)
{
// step.1----初始化直方图
y1 = y - halfy;
y2 = y + halfy + 1;
memset(histogram, 0, sizeof(int) * 256);
if (y1 < 0) histogram[0] -= y1 * m, y1 = 0;
if (y2 > height) histogram[0] += (y2 - height) * m, y2 = height;
for (i = y1, pCur = pImg + y1 * width; i < y2; i++, pCur += width)
{
for (j = 0; j <= halfx; j++)
{
histogram[pCur[j]]++;
}
}
//step.2-----初始化中值
num = 0; //记录着灰度值从0到中值的个数
for (i = 0; i < 256; i++)
{
num += histogram[i];
if (num * 2 > wSize)
{
med = i;
break;
}
}
// 滤波
// pRes += halfx; // 没有处理图像左边界侧的像素
res = n - y2 + y1;
for (x = 0; x < width; x++)
{
// 赋值
*(pRes++) = med;
#ifdef DEBUG
//if (!x || x == 1 || x == 250)
//{
// printf("%d \n", med);
// printf("num: %d\n", num);
// //int tot = 0;
// //for (int i = 0; i < 256; i++)tot += histogram[i];
// //printf("tot : %d\n", tot);
//}
#endif // DEBUG
// step.3-----直方图递推: 减去当前邻域最左边的一列,添加邻域右侧的一个新列
j1 = x - halfx; // 最左边列
j2 = x + halfx + 1; // 右边的新列
if (j1 < 0) // 如果左边超过了
{
histogram[0] -= n;
num -= n;
}
else
{
for (i = y1, pCur = pImg + y1 * width; i < y2; i++, pCur += width)
{
// 减去最左边列
v = pCur[j1];
histogram[v]--; //更新直方图
if (v <= med) num--; //更新num
}
histogram[0] -= res; // 处理padding部分
num -= res;
}
if (j2 >= width) // 如果右边超过了
{
histogram[0] += n;
num += n;
}
else
{
for (i = y1, pCur = pImg + y1 * width; i < y2; i++, pCur += width)
{
// 加上最右边列
v = pCur[j2];
histogram[v]++; // 更新直方图
if (v <= med) num++; // 更新num
}
histogram[0] += res; // 处理padding部分
num += res;
}
// step.4-----更新中值
if (num * 2 < wSize) // 到上次中值med的个数不够了,则med要变大
{
for (med = med + 1; med < 256; med++)
{
dbgCmpTimes += 2; //总的比较次数,调试用
num += histogram[med];
if (num * 2 >= wSize) break;
}
dbgCmpTimes += 1; //总的比较次数,调试用
}
else //到上次中值med的个数多了,则med要变小
{
while ((num - histogram[med]) * 2 > wSize)//若减去后,仍变小
{
dbgCmpTimes++; //总的比较次数,调试用
num -= histogram[med--];
}
dbgCmpTimes += 2; //总的比较次数,调试用
}
}
//pRes += halfx;//没有处理图像右边界侧的像素
}
printf("dbg: %lf \n", 1.0 * dbgCmpTimes / width / height);
return;
}
// 一维维高斯滤波单通道
void bmpConverter::AGuassFilter1d(BYTE * pSrc, int width, int height, int * pFilter, int m, BYTE * pDes)
{
int offset = 17;
// 偏移量设置为17的原因,将求和后再位移,因为先位移会损失精度很多次,按照比例来说会有50%的计算损失1亮度
// 所以累积就是 m/2的亮度损失,m为滤波器的宽,在实验之后,放入PS也观察到了相似的结果,是8亮度,相对于m=19的滤波器
// 偏移16的缺点,滤波器的精度下降,所有之和只为,0.999931,按照平均亮度是128算(实际上原图的平均亮度只有61),亮度损失只有1不到
// 从宏观上看,相对于23的offset图片整体的亮度提升了
int halfm, y,x, f, sum = 0;
BYTE *pCur, *pRes, *pY;
halfm = m / 2;
for (y = 0, pY = pSrc, pRes = pDes; y < height; y++, pY += width)
{
// 处理左侧
for (x = 0, sum = 0; x < halfm; x++, sum = 0)
{
for (f = halfm - x, pCur = pY; f < m; f++)
{
sum += pFilter[f] * *pCur++ ;
}
sum >>= offset;
#ifdef DEBUG
if (sum < 0 || sum > 255)
{
printf("1%d %d %d\n", sum, y, x);
}
#endif // DEBUG
*pRes++ = sum;
}
// 处理中间
for (x = halfm, sum = 0; x < width - halfm; x++, sum = 0)
{
for (f = 0, pCur = pY + x - halfm; f < m; f++)
{
sum += pFilter[f] * *pCur++;
}
sum >>= offset;
*pRes++ = sum;
#ifdef DEBUG
if (sum < 0 || sum > 255)
{
printf("2%d %d %d\n", sum, y, x);
}
#endif // DEBUG
}
// 处理右侧
for (x = width - halfm, sum = 0; x < width; x++, sum = 0)
{
for (f = 0, pCur = pY + x - halfm; pCur < pY + width; f++)
{
sum += pFilter[f] * *pCur++;
}
sum >>= offset;
*pRes++ = sum;
#ifdef DEBUG
if (sum < 0 || sum > 255)
{
printf("2%d %d %d\n", sum, y, x);
}
#endif // DEBUG
}
}
}
// 计算pSrc的转置
void bmpConverter::T(BYTE * pSrc, int w, int h, BYTE * pDes)
{
BYTE * pX, *pCur;
int x, y;
pCur = pDes;
for (x = 0; x < w; x++)
{
for (y = 0, pX = pSrc + x; y < h; y++, pX += w)
{
*pCur++ = *pX;
}
}
return;
}
// 计算高斯核
void getGuassFilter(double dev, int m, int * pFilter, int offset=17)
{
int halfm;
double sum;
double dF[1024];
halfm = m / 2;
sum = dF[halfm] = 1;
for (int i = 1; i <= halfm; i++)
{
dF[halfm + i] = dF[halfm - i] = exp( - i * i / 2.0 / dev / dev);
sum += dF[halfm + i] + dF[halfm + i];
}
for (int i = 0; i <= halfm; i++)
{
pFilter[halfm + i] = pFilter[halfm - i] = (1 << offset) * dF[halfm + i] / sum;
}
}
// 二维高斯滤波单通道
Img bmpConverter::AGuassFilter2d1c(double dev, int m, bool inplace, char mod)
{
if (!pImg)
{
#ifdef DEBUG
puts("未载入图像,无法操作");
#endif // DEBUG
return Img();
}
if (channel != 1)
{
#ifdef DEBUG
puts("不是单通道图像");
#endif // DEBUG
return Img();
}
if (m > width || m > height || m >= 80)
{
#ifdef DEBUG
puts("滤波器大小错误");
#endif // DEBUG
return Img();
}
BYTE* pSrc, * pSrc2, * pSrcT, * pSrc2T;
int pFilter[128];
m |= 1;
getGuassFilter(dev, m, pFilter);
pSrc = new BYTE[width * height];
#ifdef DEBUG
//double fsum = 0;
//int offset = 17;
//for (int i = 0; i < m; i++)
//{
// fsum += 1.0 * pFilter[i] / (1 << offset);
// printf("%lf \n", 1.0 * pFilter[i] / (1 << offset));
//}
//cout << fsum << endl;
#endif // DEBUG
switch (mod)
{
default:
case 'c':
case 'C':
{
pSrc2 = new BYTE[width * height];
pSrcT = new BYTE[width * height];
pSrc2T = new BYTE[width * height];
AGuassFilter1d(pImg, width, height, pFilter, m, pSrc);
T(pSrc, width, height, pSrcT);
AGuassFilter1d(pSrcT, height, width, pFilter, m, pSrc2);
T(pSrc2, height, width, pSrc2T);
delete pSrc;
delete pSrc2;
delete pSrcT;
pSrc = pSrc2T;
break;
}
case 'G':
case 'g':
{
break;
}
}
if (inplace)
{
delete pImg;
pImg = pSrc;
return Img();
}
else
{
return Img(pSrc, width, height);
}
}
// 第二次作业修改部分
// RGB转灰度图(将浮点乘法转化为整数运算)
void bmpConverter::RGB2Gry(bool table_chk, bool inplace)
{
if (channel != 3 || !pImg)return;
int sum = width * height;
BYTE * temp_save = new BYTE[sum];
if (!temp_save)
{
#ifdef DEBUG
// puts("程序错误,内存申请失败");
#endif // DEBUG
return;
}
#ifdef DEBUG
//cout << (int)(temp_save) <<"113" <<sizeof(temp_save)<< endl;
////cout << (int)pImg << endl;
#endif // DEBUG
BYTE * p1 = temp_save, *p2 = pImg - 1;
if (table_chk)
{
BYTE r8[256], g8[256], b8[256];
int r_ratio = 0.299 * 1024, g_ratio = 0.587 * 1024;
for (int i = 0; i < 256; i++)
{
r8[i] = r_ratio * i >> 10;
g8[i] = g_ratio * i >> 10;
b8[i] = i - r8[i] - g8[i];
}
while (sum--)*(p1++) = (b8[*(++p2)] + g8[*(++p2)] + r8[*(++p2)]);//blue,green,red
}
else
{
while (sum--)*(p1++) = ((*(++p2)) * 0.114 + 0.587 * (*(++p2)) + 0.299 * (*(++p2)));//blue,green,red
}
if (inplace)
{
swap(temp_save, pImg);
channel = 1;
}
#ifdef DEBUG
//cout << int(temp_save) << endl;
//cout << (int)pImg << endl;
#endif // DEBUG
delete temp_save;
return;
}
// 还有一个工作是去掉了函数中的输出语句。