标签(空格分隔): 算法学习
……(留在日后填写,原理以及实现细节,这里先直接贴程序吧)
在做卫星图像质量提高的时候,相机噪声通常都是很大的,在项目里面采用了5-3提升方向小波,为了提高程序的运行速度,将原来的9个方向缩减为现在的3个方向,这么做的好处是什么呢?当采用9个方向的方向提升小波时,需要对图像进行预处理插值,9个方向时需要分别插出1/2像素点和1/4像素点。依次类推,采用5个方向预测时,则只需要插出1/2像素点。预测3个方向时,则不需要提前对图像进行插值。 在写c语言的时候,我也重新整理了一下3方向提升小波,下面我按照思路将程序一块块的贴上来:
方向提升小波的正变换对应于小波正变换,会得到图像的LL,LH,HL,HH四个分量,分别对应于图像的低频分量,水平方向上的高频分量,垂直方向上的高频分量以及45度角上的高频分量。
cout << "----------------------wavelet denoise start---------------------" << endl; Mat * img_in = new Mat(); Mat * img_out = new Mat(); *img_in = Mat::zeros(rows_ori, cols_ori, CV_64FC1); *img_out = Mat::zeros(rows_ori, cols_ori, CV_64FC1); // double * img_ori_ptr ---> Mat::img_ori int ptr_bias = 0; for (int i = 0; i < rows_ori; ++i) { for (int j = 0; j < cols_ori; ++j) { img_in->at<double>(i, j) = *(image_in + ptr_bias); ptr_bias++; } } int rows = img_in->size().height; int cols = img_in->size().width; clock_t start, end; start = clock(); cout << "------step1: Forward Transform-----" << endl; ////////////////////////////////小波一级分解/////////////////////////////// Mat* LL = new Mat(); LL->create(rows / 2, cols / 2, CV_64FC1); Mat* LH = new Mat(); LH->create(rows / 2, cols / 2, CV_64FC1); Mat* HL = new Mat(); HL->create(rows / 2, cols / 2, CV_64FC1); Mat* HH = new Mat(); HH->create(rows / 2, cols / 2, CV_64FC1); int *dir_mat11 = new int[rows * cols]; int *dir_mat12 = new int[(rows / 2) * cols]; ADL97(img_in, 1, LL, LH, HL, HH, dir_mat11, dir_mat12, blk_size);上述函数分别完成了将double型的图像指针转换到mat矩阵中,并提前为LL,LH,HL,HH分配好内存空间,其中dirMat变量用来存放预测的方向矩阵,方向矩阵中的取值为,-1,0,1分别对应于三个方向(左上-右下,上-下,右上-左下)。
方向预测的过程是对图像块进行处理,在程序中对512*512的图像进行方向预测时默认的块大小为32*32,小波二级分解时图像块的大小变为16*16,三级分解变为8*8。在此处编程时,应用到了一个常用的技巧,即就是取出图像块,对图像块进行处理,再将处理后的图像块放置回原图对应的位置。 下面的getBlock和setBlock函数就是进行上述取块放块操作:
void getBlock(Mat* img, double * blk_img, int* blk_position) { /*对图像进行分块 输入: img:输入的待分块图像 win_size : 块的大小 输入图像的长和宽必须为win_size的整数倍大小 输出: blk_img:图像块 double* */ int p_rows_f = blk_position[0];// 首行 int p_rows_l = blk_position[1];// 末行 int p_cols_f = blk_position[2];// 首列 int p_cols_l = blk_position[3];// 末列 int r_rows = p_rows_l - p_rows_f; int r_cols = p_cols_l - p_cols_f; for (int i = p_rows_f, r_i = 0; i < p_rows_l; i++, r_i++) { for (int j = p_cols_f, r_j = 0; j < p_cols_l; j++, r_j++) { blk_img[r_i*r_cols + r_j] = img->at<double>(i,j); } } }void setBlock(int* dir_mat, int* blk_img, int* blk_position,int img_cols){ /* 输入: blk_img:图像块 double*块中的数据位当前块的预测方向,取值应为(-1,0,1,); blk_position:当前块四个顶点的左边索引 img_cols:输入图像的列数 输出: dir_mat:由所有块拼接而成的方向矩阵,大小与输入图像一致; */ //将小图像块拼接称为整个图像 int p_rows_f = blk_position[0];// 首行 int p_rows_l = blk_position[1];// 末行 int p_cols_f = blk_position[2];// 首列 int p_cols_l = blk_position[3];// 末列 int r_rows = p_rows_l - p_rows_f; int r_cols = p_cols_l - p_cols_f; for (int i = p_rows_f, r_i = 0; i<p_rows_l; i++, r_i++) { for (int j = p_cols_f, r_j = 0; j<p_cols_l; j++, r_j++) { dir_mat[i * img_cols + j] = blk_img[r_i * r_cols + r_j]; /*cout << i << endl;*/ } }}下面贴出方向预测的主函数:
void getDirMat(Mat* img, int flag, int decom_level, int* dir_mat,int blk_size){ /* 输入: img:待处理图像 Mat* flag:当前处理标志 flag == 1:列变换 flag == 2:行变换 decom_level:分解级数 输出: dir_mat:方向矩阵 int* 大小与输入图像一致 */ int win_size = blk_size / (pow(2, (decom_level - 1))); int rows = img->size().height; int cols = img->size().width; int count_rows = rows / win_size; int count_cols = cols / win_size; //当前块的位置 double* blk_img = new double[win_size * win_size]; int blk_position[4] = { 0 }; int temp; int* temp_mat = new int[win_size * win_size]; //将图像分解成win*win的块进行计算 for (int i = 0; i < count_rows; i++) { for (int j = 0; j < count_cols; j++) { //当前块的四个角坐标索引 blk_position[0] = i * win_size; blk_position[1] = (i + 1) * win_size; blk_position[2] = j * win_size; blk_position[3] = (j + 1) * win_size; //读取到当前块 getBlock(img, blk_img, blk_position); //对当前块进行方向预测 temp = dirEstimation2(blk_img, flag, win_size); for (int k = 0; k < win_size * win_size; k++) { temp_mat[k] = temp; } setBlock(dir_mat, temp_mat, blk_position, cols); } } delete[] blk_img; delete[] temp_mat;}其中子函数dirEstimation2对输入的图像块用奇数行预测偶数行,偶数行就包含了高频信息,分别计算是哪个方向预测出的高频矩阵,取高频矩阵二范数最小,即能说明该方向的预测是准确的,块中所有点都为此方向。下面贴出方向预测的代码:
void matTrans(int* mat, int* mat_T, int rows, int cols){ for (int i = 0; i < cols; i++) { for (int j = 0; j < rows; j++) { mat_T[i*rows + j] = mat[j*cols + i]; } } }int minIdx(double* power, int data_size){ double min = power[0]; int idx = 0; for (int i = 1; i < data_size; i++) { if (power[i] < min) { min = power[i]; idx = i; } } return idx;}double calcuPower(double* high_freq, int win_size){ double sum = 0; for (int i = 0; i < win_size / 2 ; i++) { for (int j = 0; j < win_size ; j++) { sum = sum + pow(high_freq[i*win_size + j] ,2); } } return sum;}void downSampling(double* img, int win_size, double* high_freq,int flag){ if (flag == 1)//列变换(行下2采样) { for (int i = 0; i < win_size / 2; i++) { for (int j = 0; j < win_size; j++) { high_freq[i*win_size + j] = img[(2 * i)*win_size + j]; } } } if (flag == 2)//行变换(列下2采样) { for (int i = 0; i < win_size; i++) { for (int j = 0; j < win_size / 2; j++) { high_freq[i * win_size / 2 + j] = img[i*win_size + (2*j)]; } } }}int dirEstimation2(double* blk_img, int flag, int win_size){ double power[3] = { 0 }; double* high_freq_mat = new double[win_size * (win_size / 2)]; memset(high_freq_mat, 0, sizeof(double)*win_size*(win_size / 2)); //把图像块的内容暂存在blk的Mat* 里面 Mat* blk_mat_img = new Mat(); blk_mat_img->create(win_size, win_size, CV_64FC1); //边界扩展后的图像块 Mat* blk_expanded_rows = new Mat(); blk_expanded_rows->create(win_size + 2, win_size , CV_64FC1); Mat* blk_expanded_cols = new Mat(); blk_expanded_cols->create(win_size + 2, win_size + 2, CV_64FC1); //把double*的图像块数据存放在Mat矩阵里面 for (int i = 0; i < win_size; i++) { for (int j = 0; j < win_size; j++) { blk_mat_img->at<double>(i, j) = blk_img[i*win_size + j]; } } if (flag == 1)//当前为列操作 { for (int k = -1; k < 2; k++) { //循环之前,需要将blk_mat_img的数据重新赋值给blk_expanded,并填充边框 copyMakeBorder(*blk_mat_img, *blk_expanded_rows, 1, 1, 0, 0, BORDER_REFLECT_101); copyMakeBorder(*blk_expanded_rows, *blk_expanded_cols, 0, 0, 1, 1, BORDER_REFLECT); //数据准备完毕,进行方向预测 for (int i = 1; i < win_size + 1; i = i + 2) { for (int j = 1; j < win_size + 1; j++) { blk_expanded_cols->at<double>(i, j) = blk_expanded_cols->at<double>(i, j) - 0.5*( blk_expanded_cols->at<double>(i-1,j+k) + blk_expanded_cols->at<double>(i+1,j-k) ); } } for (int i = 0; i < win_size; i++) { for (int j = 0; j < win_size; j++) { blk_img[i*win_size + j] = blk_expanded_cols->at<double>(i + 1, j + 1); } } downSampling(blk_img, win_size, high_freq_mat, flag); power[k + 1] = calcuPower(high_freq_mat, win_size); } } if (flag == 2) { Mat* blk_img_T = new Mat(); blk_img_T->create(win_size, win_size, CV_64FC1); //图像直接转置 for (int i = 0; i < win_size; i++) { for (int j = 0; j < win_size; j++) { blk_img_T->at<double>(i, j) = blk_mat_img->at<double>(j, i); } } for (int k = -1; k < 2; k++) { //循环之前,需要将blk_mat_img的数据重新赋值给blk_expanded,并填充边框 copyMakeBorder(*blk_img_T, *blk_expanded_rows, 1, 1, 0, 0, BORDER_REFLECT_101); copyMakeBorder(*blk_expanded_rows, *blk_expanded_cols, 0, 0, 1, 1, BORDER_REFLECT); //数据准备完毕,进行方向预测 for (int i = 1; i < win_size + 1; i = i + 2) { for (int j = 1; j < win_size + 1; j++) { blk_expanded_cols->at<double>(i, j) = blk_expanded_cols->at<double>(i, j) - 0.5*(blk_expanded_cols->at<double>(i - 1, j + k) + blk_expanded_cols->at<double>(i + 1, j - k)); } } for (int i = 0; i < win_size; i++) { for (int j = 0; j < win_size; j++) { blk_img[i*win_size + j] = blk_expanded_cols->at<double>(i + 1, j + 1); } } downSampling(blk_img, win_size, high_freq_mat, 1); power[k + 1] = calcuPower(high_freq_mat, win_size); } delete blk_img_T; } int dir = 0; dir = minIdx(power, 3) - 1; delete[] high_freq_mat; delete blk_mat_img; delete blk_expanded_rows; delete blk_expanded_cols; return dir;}下面是方向小波核心程序
void singleLifting(Mat* img, int flag, int step, double coe, int* dir_mat){ /*小波变换单步提升,核心步骤 输入: img:输入图像 Mat* flag: 行列处理标志 flag == 1列变换, flag ==2 行变换 step: 小波处理步骤标志 step == 1预测 step == 2更新 coe:小波提升系数 dir_mat: 方向矩阵,大小与输入图像一致 输出: img: 输出图像,在函数内部对图像进行处理 */ int rows = img->size().height; int cols = img->size().width; if (flag == 1)//列变换 { //预处理,图像对称延拓一圈 Mat *extend_img = new Mat(); extend_img->create(rows + 1, cols + 1, CV_64FC1); copyMakeBorder(*img, *extend_img, 1, 1, 1, 1, BORDER_REFLECT_101); int extend_rows = extend_img->size().height; int extend_cols = extend_img->size().width; //第一步,确认输入数据的正确性 //输出扩展后的图像数据和进行预测的方向矩阵 if (step == 1)//预测 { for (int i = 1; i < extend_rows - 2; i = i + 2)//剔除原图最后一行,再2:2:rows-1 { for (int j = 1; j < extend_cols - 1; j++)//2:cols-1 { extend_img->at<double>(i, j) = extend_img->at<double>(i, j) + coe*( extend_img->at<double>(i - 1, j + dir_mat[(i - 1)*cols + j - 1]) + extend_img->at<double>(i + 1, j - dir_mat[(i - 1)*cols + j - 1]) ); } } for (int i = 0; i < rows; i++) { for (int j = 0; j < cols ; j++) { img->at<double>(i, j) = extend_img->at<double>(i + 1, j + 1); } } delete extend_img; } if (step == 2)//更新 { for (int i = 2; i < extend_rows - 1; i = i + 2)//剔除原图第一行,再2:2:rows-1 { for (int j = 1; j < extend_cols - 1; j++)//2:cols-1 { extend_img->at<double>(i, j) = extend_img->at<double>(i, j) + coe*(extend_img->at<double>(i - 1, j + dir_mat[(i - 1)*cols + j - 1]) + extend_img->at<double>(i + 1, j - dir_mat[(i - 1)*cols + j - 1])); } } for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { img->at<double>(i, j) = extend_img->at<double>(i + 1, j + 1); //outFile << img->at<double>(i, j) << endl; } } delete extend_img; } } if (flag == 2)//行变换 { Mat* img_T = new Mat(); img_T->create(cols, rows, CV_64FC1); int* dir_mat_T = new int[rows*cols]; for (int i = 0; i < cols; i++) { for (int j = 0; j < rows; j++) { img_T->at<double>(i, j) = img->at<double>(j, i); } } ////cvTranspose这一opencv内置函数用于矩阵的转置 matTrans(dir_mat, dir_mat_T, rows, cols); ////预处理,图像对称延拓一圈 Mat *extend_img = new Mat(); extend_img->create(rows + 1, cols + 1, CV_64FC1); copyMakeBorder(*img_T, *extend_img, 1, 1, 1, 1, BORDER_REFLECT_101); int extend_rows = extend_img->size().height; int extend_cols = extend_img->size().width; if (step == 1) { for (int i = 1; i < extend_rows - 2; i = i + 2)//剔除原图最后一行,再2:2:rows-1 { for (int j = 1; j < extend_cols - 1; j++)//2:cols-1 { extend_img->at<double>(i, j) = extend_img->at<double>(i, j) + coe*(extend_img->at<double>(i - 1, j + dir_mat_T[(i - 1)*rows + j - 1]) + extend_img->at<double>(i + 1, j - dir_mat_T[(i - 1)*rows + j - 1])); //此时的dir_mat_T是cols行,rows列 } } for (int i = 0; i < cols; i++) { for (int j = 0; j < rows; j++) { img_T->at<double>(i, j) = extend_img->at<double>(i + 1, j + 1); } } //将图像转置回去 //cvTranspose(img_T, img); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { img->at<double>(i, j) = img_T->at<double>(j, i); } } delete extend_img; delete img_T; delete[] dir_mat_T; } if (step == 2) { for (int i = 2; i < extend_rows - 1; i = i + 2)//剔除原图第一行,再2:2:rows-1 { for (int j = 1; j < extend_cols - 1; j++)//2:cols-1 { //extend_img[i*extend_cols + j] = extend_img[i*extend_cols + j] extend_img->at<double>(i, j) = extend_img->at<double>(i, j) + coe*(extend_img->at<double>(i - 1, j + dir_mat_T[(i - 1)*rows + j - 1]) + extend_img->at<double>(i + 1, j - dir_mat_T[(i - 1)*rows + j - 1])); } } for (int i = 0; i < cols; i++) { for (int j = 0; j < rows; j++) { img_T->at<double>(i, j) = extend_img->at<double>(i + 1, j + 1); } } //将图像转置回去 //cvTranspose(img_T, img); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { img->at<double>(i, j) = img_T->at<double>(j, i); } } delete extend_img; delete img_T; delete[] dir_mat_T; } }}新闻热点
疑难解答