卷积的C++代码实现(same、full、valid)

介绍卷积的作用和原理的文章很多,此处就不再赘述,以防自己的理解不够而误解了别人。此篇文章主要是介绍C++实现卷积的三种操作。

需要注意的是,‘same’和‘full’只是将被卷积的矩阵做了相应尺寸的0填充后再进行‘valid’卷积即可。

此处默认步长1。

三种卷积操作的示意图:

Valid:

Same:

Full:

源码:

#include <iostream>
#include <map>
#include <math.h>

class OpMatrix {
private:
	const enum CONV_TYPE {
		ID_full = 0,
		ID_valid,
		ID_same
	};
	std::map<std::string, int> convtype_str_id = std::map<std::string, int>();
	int matrix_w, matrix_h, corner_length;
public:

	OpMatrix() {
		convtype_str_id.insert(std::pair<std::string, int>("full", ID_full));
		convtype_str_id.insert(std::pair<std::string, int>("same", ID_same));
		convtype_str_id.insert(std::pair<std::string, int>("valid", ID_valid));
	}

	// 矩阵乘法运算
	template <typename _Tp> 
		_Tp* mul(_Tp* matrix_a, _Tp* matrix_b, int a_w, int a_h, int b_w, int b_h) {
		// 申请矩阵乘法运算结果的内存空间
		_Tp* result = (_Tp*)malloc(sizeof(_Tp)*a_h*b_w);
		// 矩阵相乘需满足前矩阵的列数等于后矩阵的行数
		if (a_w != b_h) {
			throw "不符合矩阵相乘规则";
			return result;
		}
		else {
			// 遍历前矩阵的行
			for (int i = 0; i < a_h; i++) {
				// 遍历后矩阵的列
				for (int j = 0; j < b_w; j++) {
					result[i*b_w + j] = 0;
					// 前矩阵的每一行与后矩阵的每一列内积,将结果存放在result中
					for (int z = 0; z < a_w; z++) {
						result[i*b_w + j] += (matrix_a[i*a_w + z] * matrix_b[z*b_w + j]);
					}

				}
			}
		}
		// 打印计算结果
		for (int i = 0; i < a_h*b_w; i++) {
			std::cout << result[i] << std::endl;
		}
		return result;
	}

	// 矩阵逆时针旋转
	template <typename _Tp> 
		_Tp* rot90(_Tp* matrix, int length, int nums,bool show) {

		// 输入合法性检测
		if (nums < 0) {
			throw "请输入非负整数!";
		}

		// 逆时针旋转90度次数整理
		nums = nums % 4;

		// 为了提高计算效率,对四次不同的旋转次数做直接变换,不使用递归调用逆时针90度旋转的函数
		switch (nums)
		{
		case 0:
			break;
		case 1:
			break;
		case 2:
			for (int i = 0; i < length / 2; i++) {
				for (int j = 0; j < length; j++) {
					_Tp temp = matrix[i*length + j];
					matrix[i*length + j] = matrix[(length - i - 1)*length + (length - j - 1)];
					matrix[(length - i - 1)*length + (length - j - 1)] = temp;
				}
			}

			// 如果corner的边长为奇数,则需将中间的那行数据颠倒
			if (length % 2 != 0) {
				for (int i = 0; i < length / 2; i++) {
					_Tp temp = matrix[(length / 2)*length + i];
					matrix[(length / 2)*length + i] = matrix[(length / 2)*length + (length - i - 1)];
					matrix[(length / 2)*length + (length - i - 1)] = temp;
				}
			}
			break;
		case 3:
			break;
		default:
			break;
		}

		if (show) {
			showMatrix(matrix, length, length);
		}

		return matrix;
	}

	// 矩阵卷积运算
	template <typename _Tp> 
		void conv(_Tp* matrix, _Tp* corner, int matrix_w, int matrix_h, int corner_length,std::string conv_type, _Tp* result,bool show,bool is_rot) {
			int result_w = matrix_w - corner_length + 1;
			int result_h = matrix_h - corner_length + 1;

			// 卷积核逆时针旋转180度
			if (is_rot) {
				corner = rot90(corner, corner_length, 2, false);
			}
			

			int conv_key = convtype_str_id.at(conv_type);
			switch (conv_key)
			{
			case ID_valid:
				// 卷积结果的尺寸((matrix_h - corner_length + 1),(matrix_w - corner_length + 1))
				result_w = matrix_w - corner_length + 1;
				result_h = matrix_h - corner_length + 1;

				// 申请矩阵卷积的运算结果的内存空间
				result = (_Tp*)malloc(sizeof(_Tp)*result_w*result_h);
				for (int i = 0; i < result_h; i++) {
					for (int j = 0; j < result_w; j++) {
						result[i*result_w + j] = 0;
						for (int z = 0; z < corner_length; z++) {
							for (int q = 0; q < corner_length; q++) {
								result[i*result_w + j] += matrix[(i + z)*matrix_w + j + q] * corner[z*corner_length + q];
							}
						}

					}
				}
				break;
			case ID_full:
				result = (_Tp*)malloc(sizeof(_Tp)*(matrix_w + corner_length - 1)*(matrix_h + corner_length - 1));
				// 如果corner的边长为奇数,则用corner的中心点对应matrix的扫描到的点进行卷积运算
				if (corner_length % 2 == 0) {
					std::cout << "same型卷积要求卷积核尺寸为奇数" << std::endl;
					return;
				}
				else {
					// 创建新的matrix_new,用0填充原始matrix
					_Tp* matrix_new = padMatrix(matrix, matrix_w, matrix_h, matrix_w + 2 * corner_length - 2, matrix_h + 2 * corner_length - 2, false);
					// 对matrix_new进行full卷积,变相实现same型卷积
					conv(matrix_new, corner, matrix_w + 2 * corner_length - 2, matrix_h + 2 * corner_length - 2, corner_length, "valid", result, show, false);

					// 释放原始matrix
					// free(matrix);

					return;
				}
				break;
			case ID_same:
				result = (_Tp*)malloc(sizeof(_Tp)*matrix_w*matrix_h);
				// 如果corner的边长为奇数,则用corner的中心点对应matrix的扫描到的点进行卷积运算
				if (corner_length % 2 == 0) {
					std::cout << "same型卷积要求卷积核尺寸为奇数" << std::endl;
					return;
				}
				else {
					// 创建新的matrix_new,用0填充原始matrix
					_Tp* matrix_new = padMatrix(matrix, matrix_w, matrix_h, matrix_w + corner_length - 1, matrix_h + corner_length - 1, false);
					// 对matrix_new进行full卷积,变相实现same型卷积
					conv(matrix_new, corner, matrix_w + corner_length - 1, matrix_h + corner_length - 1, corner_length, "valid", result, show, false);

					// 释放原始matrix
					// free(matrix);

					return;
				}
				break;
			default:
				throw "请输入正确的卷积操作类型!";
				return;
			}

			// 打印计算结果
			if (show) {
				showMatrix(result, result_w, result_h);
			}
		
	}

	template <typename _Tp>
	_Tp* padMatrix(_Tp* matrix, int matrix_w, int matrix_h, int matrix_w_new, int matrix_h_new , bool show) {
		if (matrix_w_new - matrix_w != matrix_h_new - matrix_h || (matrix_h_new - matrix_h) % 2 != 0) {
			std::cout << "目标矩阵增加的长和宽需相同,且增加的行和列需要能被2整除" << std::endl;
			exit(0);
		}
		// 根据目标矩阵的长宽申请内存
		_Tp* matrix_new = (_Tp*)malloc(sizeof(_Tp)*matrix_w_new*matrix_h_new);

		// 计算原matrix在matrix_new中的位置
		int top = (matrix_h_new - matrix_h) / 2;
		int bottom = matrix_h_new - 1 - top;
		int left = top;
		int right = matrix_w_new - 1 - top;

		int matrix_index = 0;

		// 遍历新数组,对每个元素进行赋值
		for (int i = 0; i < matrix_h_new; i++) {
			for (int j = 0; j < matrix_w_new; j++) {
				// 如果遍历到原matrix在matrix_new中的位置,进行赋值
				if (i >= top && i <= bottom && j >= left && j <= right) {
					matrix_new[i*matrix_w_new + j] = matrix[matrix_index];
					matrix_index++;
				}
				else {
					// 填充0
					matrix_new[i*matrix_w_new + j] = 0;
				}
			}
		}

		if (show) {
			showMatrix(matrix_new, matrix_w_new, matrix_h_new);
		}

		return matrix_new;
	}

	template <typename _Tp>
	void showMatrix(_Tp matrix, int matrix_w, int matrix_h) {
		for (int i = 0; i < matrix_h; i++) {
			for (int j = 0; j < matrix_w; j++) {
				std::cout << matrix[i*matrix_w + j] << " ";
			}
			std::cout << std::endl;
		}
	}

	void test_mul() {
		int a[8] = { 1,2,3,4,4,5,6,8 };
		int b[8] = { 1,2,1,2,1,2,1,2 };
		std::cout << mul<int>(a, b, 4, 2, 2, 4) << std::endl;

		double a1[8] = { 1.1,2.2,3.3,4.4,4.4,5.5,6.6,8.8 };
		double b1[8] = { 1.1,2.2,1.1,2.2,1.1,2.2,1.1,2.2 };
		std::cout << mul<double>(a1, b1, 4, 2, 2, 4) << std::endl;
	}

	void test_conv() {
		int matrix[25] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 };
		int corner[9] = { 1,2,1,1,2,1,1,2,1 };
		int* result=new int();
		conv<int>(matrix, corner, 5, 5, 3, "valid", result, true, true);
	}

	void test_rot() {
		int matrix[25] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 };
		rot90<int>(matrix, 5, 2, true);
	}
};
  • 10
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值