废话:

有时候我们是从物品的斜上方拍摄的图片,看起来不直观,需要把视角拉正,这样的一个操作就叫做 梯度矫正,需要用到的技术是 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. Taurus.MVC WebMVC 入门开发教程4:数据列表绑定List<Model>

    前言: 在本篇 Taurus.MVC WebMVC 入门开发教程的第四篇文章中, 我们将学习如何实现数据列表的绑定,通过使用 List<Model> 来展示多个数据项. 我们将继续使用 T ...

  2. AI 让观众成为 3D 版《老友记》的导演了?

    <老友记>上线 3D 版了? 允许用户旋转镜头,且从近景切换到全景观看故事? 今年出炉的 3D 方向 AI 项目 SitCom3D,能够自动补齐<老友记>原剧中的三维拍摄空间, ...

  3. 51从零开始用Rust编写nginx,江湖救急,TLS证书快过期了

    wmproxy wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 负载均衡, 静态文件服务器,websocket代理,四层TCP/UDP转发,内网穿透等,会将实 ...

  4. Nebula Graph 源码解读系列 | Vol.00 序言

    本文首发于 Nebula Graph Community 公众号 Nebula Graph 是由杭州欧若数网科技有限公司(官网:https://www.vesoft.com/cn/)开源的一款分布式图 ...

  5. Java 多线程------例子(1) --创建 三个窗口 买票 总票数为 100张 使用继承Thread类的方式

    1 package com.bytezero.threadexer; 2 3 4 5 /** 6 * 7 * 创建 三个窗口 买票 总票数为 100张 使用继承Thread类的方式 8 * @auth ...

  6. 独家消息:阿里云悄然推出RPA云电脑,已与多家RPA厂商开放合作

    独家消息:阿里云悄然推出RPA云电脑,已与多家RPA厂商开放合作 RPA云电脑,让RPA开箱即用算力无限? 文/王吉伟 这几天,王吉伟频道通过业内人士获得独家消息,阿里云近期推出了一个名为「RPA云电 ...

  7. Docker下搭建Redis集群并实现动态扩/缩容

    一.Redis集群方案的简单介绍 Redis集群的方案一般分为三种: 哈希取余分区 一致性哈希算法分区 哈希槽分区(本文介绍的就是这种) 1.哈希取余分区: 优点: 简单粗暴,只要提前预估好数据量,然 ...

  8. C++ Qt开发:QNetworkAccessManager网络接口组件

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QNe ...

  9. 【mac】Alfred 无法调用Terminal

    原因:是从mac app商店安装的Alfred class 解决方案:建议从官网下载安装:https://www.alfredapp.com/ 参考:https://www.alfredforum.c ...

  10. day18--Java集合01

    Java集合01 1.什么是集合? 前面我们保存数据使用的是数组,数组有不足的地方,我们来分析一下: 长度开始时必须指定,而且一但指定不能更改 保存的必须是同一类型的元素 使用数组进行增加/删除元素的 ...