yolo-obb旋转框的保存图片


// 手动截取旋转矩形区域
cv::Mat getRotatedRegion(cv::Mat srcMat, const cv::RotatedRect& obb)
{
	// 解析旋转矩形参数
	float cx = obb.center.x;
	float cy = obb.center.y;
	float w = (int)obb.size.width / 2 * 2; //奇数会导致 tag_pos 的位置出错
	float h = (int)obb.size.height / 2 * 2;
	float theta = obb.angle * acos(-1) / 180.0f;  //obb.angle是角度

	int srcWidth = srcMat.cols;
	int srcHeight = srcMat.rows; 
	int channels = srcMat.channels();

	// 计算旋转矩阵的余弦和正弦
	float cosTheta = std::cos(theta);
	float sinTheta = std::sin(theta);

	// 创建空白图像用于保存截取的区域
	Mat cropped(h, w, CV_8UC3);
	uint8_t* cropped_ptr = cropped.data;
	uint8_t* src = srcMat.data;
	// 遍历旋转矩形内的所有像素
	for (int px = -w / 2; px <= w / 2; ++px) {
		for (int py = -h / 2; py <= h / 2; ++py) {
			// 将局部坐标转换为原始图像坐标
			int srcX = static_cast(cx + (px * cosTheta - py * sinTheta));
			int srcY = static_cast(cy + (px * sinTheta + py * cosTheta));

			// 边界检查
			if (srcX >= 0 && srcX < srcWidth && srcY >= 0 && srcY < srcHeight) {
				int tag_pos = (1.0f * (py + h / 2) * w + 1.0f * (px + w / 2)) * channels;
				if (tag_pos >= 0 && tag_pos < w * h * channels)
				{
					// 获取像素值
					for (int c = 0; c < channels; ++c)
					{
						cropped_ptr[tag_pos + c] = src[(srcY * srcWidth + srcX) * channels + c];
					}
				}
				
			}
		}
	}

	return cropped;
}

思路:先基于原点旋转,在加上中心点的偏移量

tag_pos计算准确性很重要

cuda实现  无论大小,每个框由32*32个线程来处理


// CUDA 核函数:将多个旋转矩形内的图像提取到对应的正矩形中
__global__ void extractMultipleOBBToAABB_Kernel(const uchar* src, int src_width, int src_height, OBB_IMG* obb_ptr,
	 int num_boxes) 
{
	
	int boxIdx = blockIdx.x; // 每个 block 处理一个检测框
	if (boxIdx >= num_boxes) return;

	// 读取旋转检测框参数
	float cx = obb_ptr[boxIdx].center_x; // 中心点 x
	float cy = obb_ptr[boxIdx].center_y; // 中心点 y
	float w = (int)obb_ptr[boxIdx].w / 2 * 2;  // 宽度
	float h = (int)obb_ptr[boxIdx].h / 2 * 2;  // 高度
	float theta = obb_ptr[boxIdx].angle * acosf(-1) / 180; // 旋转角度(弧度)

	// 线程坐标
	int tx = threadIdx.x;
	int ty = threadIdx.y;

	//printf("ty == %d && tx == %d\n",ty,tx);

	// 固定 block 大小,例如 32x32
	int blockW = blockDim.x; // 线程块宽度
	int blockH = blockDim.y; // 线程块高度


	// 计算每个线程需要处理的像素步长
	int stepX = (w + blockW - 1) / blockW; // 向上取整
	int stepY = (h + blockH - 1) / blockH;

	// 预计算旋转矩阵
	float cosTheta = cos(theta);
	float sinTheta = sin(theta);

	// 获取当前框的输出缓冲区
	unsigned char* outputBuffer = obb_ptr[boxIdx].gpu_ptr;

	int channels = 3;

	// 每个线程处理一个矩形区域
	for (int dy = 0; dy < stepY; ++dy) {
		float py = ty * stepY + dy; // 裁剪区域的 y 坐标(相对于输出矩形)
		py -= h / 2;
		for (int dx = 0; dx < stepX; ++dx) {
			float px = tx * stepX + dx; // 裁剪区域的 x 坐标(相对于输出矩形)
			px -= w / 2;
			if (px > w/2 || py > h/2) continue; // 超出检测框范围
			
			// 应用逆旋转矩阵将局部坐标转换到源图像坐标
			int srcX_int = static_cast(cx + (px * cosTheta - py * sinTheta));
			int srcY_int = static_cast(cy + (px * sinTheta + py * cosTheta));


			// 边界检查
			if (srcX_int >= src_width || srcY_int >= src_height || srcX_int < 0 || srcY_int < 0) continue;

			// 计算输入和输出索引
			int srcIdx = (srcY_int * src_width + srcX_int) * channels;
			int dstIdx = ((py + h / 2) * w + (px + w / 2)) * channels;
			// 确保输出索引在有效范围内
			if (dstIdx >= 0 && dstIdx < w * h * channels) 
			{
				// 复制像素
				for (int c = 0; c < channels; ++c) {
					outputBuffer[dstIdx + c] = src[srcIdx + c];
				}
			}
			
		}
	}
}


void extractMultipleOBBToAABB(const uchar* src, int src_width, int src_height, OBB_IMG* obb_ptr,
	 int num_boxes, cudaStream_t stream)
{
	dim3 Dg(num_boxes);
	dim3 Db(32, 32);
	extractMultipleOBBToAABB_Kernel << > > (
		src,
		src_width,
		src_height,
		obb_ptr,
		num_boxes
		);
}

struct OBB_IMG
{
	float center_x;
	float center_y;
	float w;
	float h;
	float angle;
	uchar* gpu_ptr;  //申请好的显存地址
};

你可能感兴趣的:(YOLO,开发语言)