废话:

有时候我们是从物品的斜上方拍摄的图片,看起来不直观,需要把视角拉正,这样的一个操作就叫做 梯度矫正,需要用到的技术是 Opencv 的 透视变换。

这个只是一个简单的演示demo,如果完善一下,比如物品检测,可以应用更多的场景,比如常见的:文件、资料上传,软管摄像头的应用等,怎么说也是一个技术点吧

重要代码:

/**
* @brief hDLL_gradientAuto 梯度矫正
* @param src 输入图像
* @param dst 输出图像
* @param flag 方向,[0(左),1(上),2(右),3(下)]
* @param val 矫正度数,像素,[10 ~ 100]
* @return 0(成功),-1(失败)
*/
int MainWindow::hDLL_gradientAuto(Mat &src, Mat &dst, int flag, int val)
{
if(flag != 0 && flag != 1 && flag != 2 && flag != 3) return -1;
if(val < 10 || val > 100) return -1; int width = src.cols;
int height = src.rows;
Mat M;
// flag 方向,[0(左),1(上),2(右),3(下)]
switch (flag) {
case 0:
{
Point2f pts_src[] = { Point(val,val), Point(width, 0), Point(width, height), Point(val, height-val)};
Point2f pts_dst[] = { Point(0, 0), Point(width, 0), Point(width, height) ,Point(0, height) };
M = cv::getPerspectiveTransform(pts_src, pts_dst);
}break;
case 1:
{
Point2f pts_src[] = { Point(val,val), Point(width-val, val), Point(width, height), Point(0, height)};
Point2f pts_dst[] = { Point(0, 0), Point(width, 0), Point(width, height) ,Point(0, height) };
M = cv::getPerspectiveTransform(pts_src, pts_dst);
}break;
case 2:
{
Point2f pts_src[] = { Point(0,0), Point(width-val, val), Point(width-val, height-val), Point(0, height)};
Point2f pts_dst[] = { Point(0, 0), Point(width, 0), Point(width, height) ,Point(0, height) };
M = cv::getPerspectiveTransform(pts_src, pts_dst);
}break;
case 3:
{
Point2f pts_src[] = { Point(0,0), Point(width, 0), Point(width-val, height-val), Point(val, height-val)};
Point2f pts_dst[] = { Point(0, 0), Point(width, 0), Point(width, height) ,Point(0, height) };
M = cv::getPerspectiveTransform(pts_src, pts_dst);
}break;
} cv::warpPerspective(src, dst, M, dst.size(), cv::INTER_LINEAR , cv::BORDER_REPLICATE);
return 0;
}

Demo演示:

完整代码:

.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H #include <QMainWindow>
#include <QImage>
#include <QDebug>
#include <QtMath> #include "opencv2/opencv.hpp"
using namespace cv; QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE class MainWindow : public QMainWindow
{
Q_OBJECT public:
MainWindow(QWidget *parent = nullptr);
~MainWindow(); void updateQLabelImage(); Mat QImage2Mat(QImage &img);
QImage Mat2QImage(Mat &img); /**
* @brief hDLL_gradientAuto 梯度矫正
* @param src 输入图像
* @param dst 输出图像
* @param flag 方向,[0(左),1(上),2(右),3(下)]
* @param val 矫正度数,像素,[10 ~ 100]
* @return 0(成功),-1(失败)
*/
int hDLL_gradientAuto(Mat &src, Mat &dst, int flag, int val); public slots:
void horChange(int index);
void verChange(int index); private:
Ui::MainWindow *ui; QImage m_img; // 原图
QImage m_img_dst; // 处理过的图像
};
#endif // MAINWINDOW_H

.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this); connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), this, SLOT(horChange(int)));
connect(ui->verticalSlider, SIGNAL(valueChanged(int)), this, SLOT(verChange(int))); m_img = QImage("F:1.jpg");
m_img_dst = m_img;
updateQLabelImage();
} MainWindow::~MainWindow()
{
delete ui;
} // 更新QLabel里面的图像
void MainWindow::updateQLabelImage()
{
// m_img = m_img.scaled(ui->label->width(), ui->label->height());
QImage img_show = m_img_dst.scaled(ui->label->width(), ui->label->height());
ui->label->setPixmap(QPixmap::fromImage(img_show));
} Mat MainWindow::QImage2Mat(QImage &img)
{
cv::Mat mat;
switch (img.format())
{
case QImage::Format_RGB32: //一般Qt读入彩色图后为此格式
mat = cv::Mat(img.height(), img.width(), CV_8UC4, (void*)img.constBits(), img.bytesPerLine());
cv::cvtColor(mat,mat,cv::COLOR_BGRA2BGR); //转3通道
break;
case QImage::Format_RGB888:
mat = cv::Mat(img.height(), img.width(), CV_8UC3, (void*)img.constBits(), img.bytesPerLine());
cv::cvtColor(mat,mat,cv::COLOR_RGB2BGR);
break;
case QImage::Format_Indexed8:
mat = cv::Mat(img.height(), img.width(), CV_8UC1, (void*)img.constBits(), img.bytesPerLine());
break;
}
return mat;
} QImage MainWindow::Mat2QImage(Mat &img)
{
if(img.type()==CV_8UC1 || img.type()==CV_8U)
{
QImage image((const uchar *)img.data, img.cols, img.rows, img.step, QImage::Format_Grayscale8);
return image;
}
else if(img.type()==CV_8UC3)
{
QImage image((const uchar *)img.data, img.cols, img.rows, img.step, QImage::Format_RGB888);
return image.rgbSwapped(); //r与b调换
}
} int MainWindow::hDLL_gradientAuto(Mat &src, Mat &dst, int flag, int val)
{
if(flag != 0 && flag != 1 && flag != 2 && flag != 3) return -1;
if(val < 10 || val > 100) return -1; int width = src.cols;
int height = src.rows;
Mat M;
// flag 方向,[0(左),1(上),2(右),3(下)]
switch (flag) {
case 0:
{
Point2f pts_src[] = { Point(val,val), Point(width, 0), Point(width, height), Point(val, height-val)};
Point2f pts_dst[] = { Point(0, 0), Point(width, 0), Point(width, height) ,Point(0, height) };
M = cv::getPerspectiveTransform(pts_src, pts_dst);
}break;
case 1:
{
Point2f pts_src[] = { Point(val,val), Point(width-val, val), Point(width, height), Point(0, height)};
Point2f pts_dst[] = { Point(0, 0), Point(width, 0), Point(width, height) ,Point(0, height) };
M = cv::getPerspectiveTransform(pts_src, pts_dst);
}break;
case 2:
{
Point2f pts_src[] = { Point(0,0), Point(width-val, val), Point(width-val, height-val), Point(0, height)};
Point2f pts_dst[] = { Point(0, 0), Point(width, 0), Point(width, height) ,Point(0, height) };
M = cv::getPerspectiveTransform(pts_src, pts_dst);
}break;
case 3:
{
Point2f pts_src[] = { Point(0,0), Point(width, 0), Point(width-val, height-val), Point(val, height-val)};
Point2f pts_dst[] = { Point(0, 0), Point(width, 0), Point(width, height) ,Point(0, height) };
M = cv::getPerspectiveTransform(pts_src, pts_dst);
}break;
} cv::warpPerspective(src, dst, M, dst.size(), cv::INTER_LINEAR , cv::BORDER_REPLICATE);
return 0;
} // 横向改变
void MainWindow::horChange(int index)
{
qDebug() << "hor:" << index; if(index == 0)
{
m_img_dst = m_img;
}
else if(index < 0)
{
int val = abs(index) * 10;
Mat src = QImage2Mat(m_img);
Mat dst;
hDLL_gradientAuto(src, dst, 0, val);
m_img_dst = Mat2QImage(dst);
}
else if (index > 0)
{
int val = abs(index) * 10;
Mat src = QImage2Mat(m_img);
Mat dst;
hDLL_gradientAuto(src, dst, 2, val);
m_img_dst = Mat2QImage(dst);
}
updateQLabelImage();
} // 竖向改变
void MainWindow::verChange(int index)
{
qDebug() << "ver:" << index; if(index == 0)
{
m_img_dst = m_img;
}
else if(index < 0)
{
int val = abs(index) * 10;
Mat src = QImage2Mat(m_img);
Mat dst;
hDLL_gradientAuto(src, dst, 3, val);
m_img_dst = Mat2QImage(dst);
}
else if (index > 0)
{
int val = abs(index) * 10;
Mat src = QImage2Mat(m_img);
Mat dst;
hDLL_gradientAuto(src, dst, 1, val);
m_img_dst = Mat2QImage(dst);
} updateQLabelImage();
}

代码下载:

我的环境是:Qt 5.15.2 + Opencv V4.8.0,如果需要下载代码,自己调试,自己配置环境即可

代码仓库:https://gitee.com/vvvj/qt-test-gradient-auto

Qt加Opencv实现 梯度矫正 功能的更多相关文章

  1. windows平台下基于QT和OpenCV搭建图像处理平台

        在之前的博客中,已经分别比较详细地阐述了"windows平台下基于VS和OpenCV"以及"Linux平台下基于QT和OpenCV"搭建图像处理框架,并 ...

  2. Qt:&OpenCV—Q图像处理基本操作(Code)

    原文链接:http://www.cnblogs.com/emouse/archive/2013/03/31/2991333.html 作者写作一系列:http://www.cnblogs.com/em ...

  3. QT与openCV,与PCL结合!

    (1):详解QT多媒体框架:给予视频播放器 原文链接:http://mobile.51cto.com/symbian-271123.htm 对于使用主框架的QT程序,实现Qimage的转换可借鉴下面程 ...

  4. CUDA加opencv复现导向滤波算法

    CUDA是GPU通用计算的一种,其中现在大热的深度学习底层GPU计算差不多都选择的CUDA,在这我们先简单了解下其中的一些概念,为了好理解,我们先用DX11里的Compute shader来和CUDA ...

  5. QT+OPENCV实现录屏功能

    本文使用QT+opencv来实现对指定窗体画面录制,并保存为avi文件. (1)获取窗体界面 QScreen类有一个grabWindow函数,可以用来获取窗体的画面,这个函数使用很简单,就是传入窗体句 ...

  6. 使用QT显示OpenCV读取的图片

    目录 1. 概述 2. 实现 2.1. 代码 2.2. 解析 3. 结果 1. 概述 OpenCV自带了一部分常用的GUI功能,但是更多的图像处理功能需要其他GUI框架来辅助实现,这里通过QT来显示O ...

  7. 基于PI+QT实现OpenCV图像处理操作(基本环境搭建)

    这篇博客就是在PI上直接写出来的!cheers!! PI3的性能已经非常强劲,而作为一个能够独立运行的运算单元,使用它来做图像处理,将是非常适合的.为了挖掘机器的最大潜能,我没有采用比较常见的pyth ...

  8. 基于QT和OpenCV的人脸检測识别系统(2)

    紧接着上一篇博客的讲 第二步是识别部分 人脸识别 把上一阶段检測处理得到的人脸图像与数据库中的已知 人脸进行比对,判定人脸相应的人是谁(此处以白色文本显示). 人脸预处理 如今你已经得到一张人脸,你能 ...

  9. 【OpenCV】透视变换矫正

    演示结果参考: 功能实现:运行程序,会显示图片的尺寸,按回车键后,依次点击需矫正的图片的左上.右上.左下.右下角,并能显示其坐标,结果弹出矫正后的图片,如图上的PIC2对话框.可以继续选择图片四个点进 ...

  10. 项目实战:Qt+Ffmpeg+OpenCV相机程序(打开摄像头、支持多种摄像头、分辨率调整、翻转、旋转、亮度调整、拍照、录像、回放图片、回放录像)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

随机推荐

  1. 【Azure 环境】ARM部署模板大于4MB的解决方案及Linked Template遇见存储账号防火墙无法访问

    问题一:在ADF Pipeline部署ARM Template报错"Deployment failed -- the request content size exceeds the max ...

  2. Nebula Graph 源码解读系列 | Vol.02 详解 Validator

    整体架构 Nebula Graph Query Engine 主要分为四个模块,分别是 Parser.Validator.Optimizer 和 Executor. Parser 完成对语句的词法语法 ...

  3. RocketMQ(1) 基础介绍和单机-集群安装

    1. MQ简单介绍 1.1 应用场景 应用解耦 系统的耦合性越高,容错性就越低.以电商应用为例,用户创建订单后,如果耦合调用库存系统.物流系统.支付系统,任何一个子系统出了故障或者因为升级等原因暂时不 ...

  4. Codeforces Round 922 (Div. 2)(A~D)补题

    A题考虑贪心,要使使用的砖头越多,每块转的k应尽可能小,最小取2,最后可能多出来,多出来的就是最后一块k=3,我们一行内用到的砖头就是\(\frac{m}{2}\)下取整,然后乘以行数就是答案. #i ...

  5. Delete `␍`

    新电脑遇到的问题 Delete `␍`eslint(prettier/prettier) 网上一搜,一堆解决办法,没有一个说到点子上,都是表面上如何避免,如何设置VSCODE... 都知道是换行符的问 ...

  6. Python面向对象之面向对象编程

    [一]什么是面向过程 [1]面向过程介绍 面向过程,核心在于 "过程" 二字 过程的终极奥义就是将程序 "流程化" 过程是 "流水线" ,用 ...

  7. 春风吹又生的开源项目「GitHub 热点速览」

    随着上周知名 Switch 开源模拟器 Yuzu(柚子)被任天堂起诉,该项目作者就删库了,但还是要赔偿任天堂数百万美元.此事还在 GitHub 上掀起了一波 Yuzu fork 项目的小浪潮,正所谓野 ...

  8. SoftCnKiller 更新程序 bat 调用vbs 更新,下载gitee文件 更新自身数据

    bat @echo off title 更新流氓软件黑名单 cd /d "%~dp0" echo 请选择更新源,默认使用1.GitCode更新. echo.&choice ...

  9. ElasticSearch 准实时原理

    Elasticsearch 是一个基于 Lucene 库的搜索引擎.它提供了一个准实时的.分布式.支持多租户的全文搜索引擎. ----维基百科 那么问题来了,为啥 Elasticsearch 不是实时 ...

  10. Spring Boot学习日记

    学习了springboot 的优点 为所有Spring开发者更快的入门 开箱即用,提供各种默认配置来简化项目配置 内嵌式容器简化Web项目 没有冗余代码生成和XML配置的要求 Spring开发-Hel ...