图像投影技术实现与可视化(基于OpenCV)
·
图像投影技术实现与可视化(基于OpenCV)
在图像处理领域,投影技术是一种重要的分析手段,广泛应用于字符识别、目标检测等场景。通过将图像的像素信息投影到某个方向上,可以简化图像数据,提取关键特征。本文将详细介绍如何使用OpenCV实现图像的水平投影、垂直投影以及对角线投影,并对投影结果进行可视化。
本文软件及opencv版本:vs2022 opencv4.5
1. 图像预处理
在进行投影之前,通常需要对图像进行预处理,以增强投影的效果。常见的预处理步骤包括灰度化和二值化。灰度化是将彩色图像转换为灰度图像,而二值化则是将灰度图像转换为只有黑白两种颜色的图像。二值化后的图像更便于进行投影计算。
Mat preprocessImage(const Mat& src) {
Mat gray, binary;
if (src.channels() == 3) {
cvtColor(src, gray, COLOR_BGR2GRAY);
} else {
gray = src.clone();
}
adaptiveThreshold(gray, binary, 255, ADAPTIVE_THRESH_GAUSSIAN_C,
THRESH_BINARY, 11, 2);
return binary;
}
2. 水平投影
水平投影是将图像的像素信息沿着水平方向进行投影,得到一个表示每一行像素密度的向量。在二值图像中,通常计算每一行中白色像素的数量。
Mat getHorizonProjection(const Mat& src) {
Mat dst(src.rows, 1, CV_32F, Scalar(0));
for (int i = 0; i < src.rows; i++) {
float sum = 0;
for (int j = 0; j < src.cols; j++) {
sum += int(src.at<uchar>(i, j) / 255);
}
dst.at<float>(i, 0) = sum;
}
return dst;
}
3. 垂直投影
垂直投影是将图像的像素信息沿着垂直方向进行投影,得到一个表示每一列像素密度的向量。同样,在二值图像中,计算每一列中白色像素的数量。
Mat getVerticalProjection(const Mat& src) {
Mat dst(1, src.cols, CV_32F, Scalar(0));
for (int j = 0; j < src.cols; j++) {
float sum = 0;
for (int i = 0; i < src.rows; i++) {
sum += int(src.at<uchar>(i, j) / 255);
}
dst.at<float>(0, j) = sum;
}
return dst;
}
4. 对角线投影
对角线投影是将图像的像素信息沿着对角线方向进行投影。常见的对角线方向包括45度和135度。通过对角线投影可以提取图像在对角方向上的特征。
Mat getDiagonalProjection(const Mat& src, int direction = 0) {
int diagonalSize = min(src.rows, src.cols);
Mat dst;
switch (direction) {
case 0: // 45度方向
dst = Mat(src.rows + src.cols - 1, 1, CV_32F, Scalar(0));
for (int d = 0; d < src.rows + src.cols - 1; d++) {
float sum = 0;
for (int i = max(0, d - src.cols + 1); i <= min(d, src.rows - 1); i++) {
int j = d - i;
sum += int(src.at<uchar>(i, j) / 255);
}
dst.at<float>(d, 0) = sum;
}
break;
case 1: // 135度方向
dst = Mat(src.rows + src.cols - 1, 1, CV_32F, Scalar(0));
for (int d = 0; d < src.rows + src.cols - 1; d++) {
float sum = 0;
for (int i = max(0, d - src.cols + 1); i <= min(d, src.rows - 1); i++) {
int j = src.cols - 1 - (d - i);
sum += int(src.at<uchar>(i, j));
}
dst.at<float>(d, 0) = sum;
}
break;
default:
dst = Mat(diagonalSize, 1, CV_32F, Scalar(0));
break;
}
return dst;
}
5. 投影结果可视化
为了更直观地展示投影结果,我们可以将投影向量可视化为图像。通过绘制线段,可以清晰地看到每一行或每一列的像素密度。
Mat visualizeProjection(const Mat& projection, int type) {
Mat vis;
if (type == 0) { // 水平投影
normalize(projection, vis, 0, 255, NORM_MINMAX);
vis.convertTo(vis, CV_8U);
Mat display(256, projection.rows, CV_8U, Scalar(255));
for (int i = 0; i < projection.rows; i++) {
line(display, Point(i, 255), Point(i, 255 - vis.at<uchar>(i, 0)), Scalar(0), 1);
}
return display;
} else if (type == 1) { // 垂直投影
normalize(projection, vis, 0, 255, NORM_MINMAX);
vis.convertTo(vis, CV_8U);
Mat display(256, projection.cols, CV_8U, Scalar(255));
for (int j = 0; j < projection.cols; j++) {
line(display, Point(j, 255), Point(j, 255 - vis.at<uchar>(0, j)), Scalar(0), 1);
}
return display;
} else { // 对角线投影
normalize(projection, vis, 0, 255, NORM_MINMAX);
vis.convertTo(vis, CV_8U);
Mat display(256, projection.rows, CV_8U, Scalar(255));
for (int k = 0; k < projection.rows; k++) {
line(display, Point(k, 255), Point(k, 255 - vis.at<uchar>(k, 0)), Scalar(0), 1);
}
return display;
}
}
6. 完整代码
以下是完整的代码实现,包括图像的读取、预处理、投影计算以及结果的可视化和保存。
#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
// 图像预处理:灰度化+二值化
Mat preprocessImage(const Mat& src) {
Mat gray, binary;
// 如果输入是彩色图像,转为灰度图
if (src.channels() == 3) {
cvtColor(src, gray, COLOR_BGR2GRAY);
}
else {
gray = src.clone();
}
// 二值化处理
adaptiveThreshold(gray, binary, 255, ADAPTIVE_THRESH_GAUSSIAN_C,
THRESH_BINARY, 11, 2);
return binary;
}
// 水平投影
Mat getHorizonProjection(const Mat& src) {
Mat dst(src.rows, 1, CV_32F, Scalar(0));
for (int i = 0; i < src.rows; i++) {
float sum = 0;
for (int j = 0; j < src.cols; j++) {
sum += int(src.at<uchar>(i, j)/255); //目前投影都以白色即255为前景黑色0为背景
//sum += src.cols-int(src.at<uchar>(i, j) / 255); //注释上一行代码,取消注释本行则计算黑色零值的投影
}
dst.at<float>(i, 0) = sum;
}
return dst;
}
// 垂直投影
Mat getVerticalProjection(const Mat& src) {
Mat dst(1, src.cols, CV_32F, Scalar(0));
for (int j = 0; j < src.cols; j++) {
float sum = 0;
for (int i = 0; i < src.rows; i++) {
sum += int(src.at<uchar>(i, j)/255); //目前投影都以白色即255为前景黑色0为背景
// sum += src.rows-int(src.at<uchar>(i, j) / 255); //注释上一行代码,取消注释本行则计算黑色零值的投影
}
dst.at<float>(0, j) = sum;
}
return dst;
}
// 对角线投影(支持多个方向)
Mat getDiagonalProjection(const Mat& src, int direction = 0) {
// direction: 0-主对角线, 1-副对角线, 2-45度方向, 3-135度方向
int diagonalSize = min(src.rows, src.cols);
Mat dst;
switch (direction) {
case 0: // 45度方向(类似主对角线但更宽)
dst = Mat(src.rows + src.cols - 1, 1, CV_32F, Scalar(0));
for (int d = 0; d < src.rows + src.cols - 1; d++) {
float sum = 0;
int count = 0;
for (int i = max(0, d - src.cols + 1); i <= min(d, src.rows - 1); i++) {
int j = d - i;
sum += int(src.at<uchar>(i, j)/255);
count++;
}
dst.at<float>(d, 0) = sum; // 平均值
}
break;
case 1: // 135度方向(类似副对角线但更宽)
dst = Mat(src.rows + src.cols - 1, 1, CV_32F, Scalar(0));
for (int d = 0; d < src.rows + src.cols - 1; d++) {
float sum = 0;
int count = 0;
for (int i = max(0, d - src.cols + 1); i <= min(d, src.rows - 1); i++) {
int j = src.cols - 1 - (d - i);
sum += int(src.at<uchar>(i, j));
count++;
}
dst.at<float>(d, 0) = sum; // 平均值
}
break;
default:
dst = Mat(diagonalSize, 1, CV_32F, Scalar(0));
break;
}
return dst;
}
// 将投影结果可视化
Mat visualizeProjection(const Mat& projection, int type) {
Mat vis;
if (type == 0) { // 水平投影
normalize(projection, vis, 0, 255, NORM_MINMAX);
vis.convertTo(vis, CV_8U);
Mat display(256, projection.rows, CV_8U, Scalar(255));
for (int i = 0; i < projection.rows; i++) {
line(display, Point(i, 255), Point(i, 255 - vis.at<uchar>(i, 0)), Scalar(0), 1);
}
return display;
}
else if (type == 1) { // 垂直投影
normalize(projection, vis, 0, 255, NORM_MINMAX);
vis.convertTo(vis, CV_8U);
Mat display(256, projection.cols, CV_8U, Scalar(255));
for (int j = 0; j < projection.cols; j++) {
line(display, Point(j, 255), Point(j, 255 - vis.at<uchar>(0, j)), Scalar(0), 1);
}
return display;
}
else { // 对角线投影
normalize(projection, vis, 0, 255, NORM_MINMAX);
vis.convertTo(vis, CV_8U);
Mat display(256, projection.rows, CV_8U, Scalar(255));
for (int k = 0; k < projection.rows; k++) {
line(display, Point(k, 255), Point(k, 255 - vis.at<uchar>(k, 0)), Scalar(0), 1);
}
return display;
}
}
int main() {
// 读取输入图像
Mat src = imread("./test.jpg");
if (src.empty()) {
cout << "无法加载图像文件" << endl;
return -1;
}
// 图像预处理
Mat binary = preprocessImage(src); // 使用自适应阈值
// 计算各种投影
Mat horizonProj = getHorizonProjection(binary);
Mat verticalProj = getVerticalProjection(binary);
// 计算多种对角线投影
Mat diagProj3 = getDiagonalProjection(binary, 0); // 45度方向
Mat diagProj4 = getDiagonalProjection(binary, 1); // 135度方向
// 可视化投影结果
Mat horizonVis = visualizeProjection(horizonProj, 0);
Mat verticalVis = visualizeProjection(verticalProj, 1);
Mat diagVis3 = visualizeProjection(diagProj3, 2);
Mat diagVis4 = visualizeProjection(diagProj4, 2);
// 保存结果
imwrite("./result/binary_image.jpg", binary);
imwrite("./result/horizon_projection.jpg", horizonVis);
imwrite("./result/vertical_projection.jpg", verticalVis);
imwrite("./result/diagonal_projection_45deg.jpg", diagVis3);
imwrite("./result/diagonal_projection_135deg.jpg", diagVis4);
// 显示结果
imshow("原始图像", src);
imshow("二值化图像", binary);
imshow("水平投影", horizonVis);
imshow("垂直投影", verticalVis);
imshow("45度方向投影", diagVis3);
imshow("135度方向投影", diagVis4);
waitKey(0);
return 0;
}
更多推荐



所有评论(0)