Qt图像处理技术一:对QImage图片美颜,使用双指数滤波
一、效果图

二、demo源码地址(除了磨皮还有一些基本的滤镜)
如果你觉得有用的话,期待你的小星星
实战应用项目:
github :https://github.com/dependon/simple-image-filter //纯qt图像处理项目
三、接口代码
img参数是原始图像,返回的是处理后的图像
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <math.h>
#include <QVector>
#include <QColor>
#include <QImage>
#include <QPainter>
QImage QImageAPI::QImageD_RunBEEPSHorizontalVertical(const QImage &img, double spatialDecay, double photometricStandardDeviation)
{
QImage imgCopy = QImage(img);
double c = -0.5 / (photometricStandardDeviation * photometricStandardDeviation); //-1/2 *光度标准偏差的平方
double mu = spatialDecay / (2 - spatialDecay);
double *exptable = new double[256];
double *g_table = new double[256];
for (int i = 0; i <= 255; i++) {
exptable[i] = (1 - spatialDecay) * exp(c * i * i);
g_table[i] = mu * i;
}
int width = img.width();
int height = img.height();
int length = width * height;
double *data2Red = new double[length];
double *data2Green = new double[length];
double *data2Blue = new double[length];
int i = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
QRgb rgb = imgCopy.pixel(x, y);
data2Red[i] = qRed(rgb);
data2Green[i] = qGreen(rgb);
data2Blue[i] = qBlue(rgb);
i++;
}
}
double *gRed = new double[length];
double *pRed = new double[length];
double *rRed = new double[length];
double *gGreen = new double[length];
double *pGreen = new double[length];
double *rGreen = new double[length];
double *gBlue = new double[length];
double *pBlue = new double[length];
double *rBlue = new double[length];
memcpy(pRed, data2Red, sizeof(double) * length);
memcpy(rRed, data2Red, sizeof(double) * length);
memcpy(pGreen, data2Green, sizeof(double) * length);
memcpy(rGreen, data2Green, sizeof(double) * length);
memcpy(pBlue, data2Blue, sizeof(double) * length);
memcpy(rBlue, data2Blue, sizeof(double) * length);
double rho0 = 1.0 / (2 - spatialDecay);
for (int k2 = 0; k2 < height; ++k2) {
int startIndex = k2 * width;
double mu = 0.0;
for (int k = startIndex + 1, K = startIndex + width; k < K; ++k) {
int div0Red = fabs(pRed[k] - pRed[k - 1]);
mu = exptable[div0Red];
pRed[k] = pRed[k - 1] * mu + pRed[k] * (1.0 - mu);//公式1
int div0Green = fabs(pGreen[k] - pGreen[k - 1]);
mu = exptable[div0Green];
pGreen[k] = pGreen[k - 1] * mu + pGreen[k] * (1.0 - mu);//公式1
int div0Blue = fabs(pBlue[k] - pBlue[k - 1]);
mu = exptable[div0Blue];
pBlue[k] = pBlue[k - 1] * mu + pBlue[k] * (1.0 - mu);//公式1
}
for (int k = startIndex + width - 2; startIndex <= k; --k) {
int div0Red = fabs(rRed[k] - rRed[k + 1]);
double mu = exptable[div0Red];
rRed[k] = rRed[k + 1] * mu + rRed[k] * (1.0 - mu);//公式3
int div0Green = fabs(rGreen[k] - rGreen[k + 1]);
mu = exptable[div0Green];
rGreen[k] = rGreen[k + 1] * mu + rGreen[k] * (1.0 - mu);//公式3
int div0Blue = fabs(rBlue[k] - rBlue[k + 1]);
mu = exptable[div0Blue];
rBlue[k] = rBlue[k + 1] * mu + rBlue[k] * (1.0 - mu);//公式3
}
for (int k = startIndex, K = startIndex + width; k < K; k++) {
rRed[k] = (rRed[k] + pRed[k]) * rho0 - g_table[(int)data2Red[k]];
rGreen[k] = (rGreen[k] + pGreen[k]) * rho0 - g_table[(int)data2Green[k]];
rBlue[k] = (rBlue[k] + pBlue[k]) * rho0 - g_table[(int)data2Blue[k]];
}
}
int m = 0;
for (int k2 = 0; k2 < height; k2++) {
int n = k2;
for (int k1 = 0; k1 < width; k1++) {
gRed[n] = rRed[m];
gGreen[n] = rGreen[m];
gBlue[n] = rBlue[m];
m++;
n += height;
}
}
memcpy(pRed, gRed, sizeof(double) * height * width);
memcpy(rRed, gRed, sizeof(double) * height * width);
memcpy(pGreen, gGreen, sizeof(double) * height * width);
memcpy(rGreen, gGreen, sizeof(double) * height * width);
memcpy(pBlue, gBlue, sizeof(double) * height * width);
memcpy(rBlue, gBlue, sizeof(double) * height * width);
for (int k1 = 0; k1 < width; ++k1) {
int startIndex = k1 * height;
double mu = 0.0;
for (int k = startIndex + 1, K = startIndex + height; k < K; ++k) {
int div0Red = fabs(pRed[k] - pRed[k - 1]);
mu = exptable[div0Red];
pRed[k] = pRed[k - 1] * mu + pRed[k] * (1.0 - mu);
int div0Green = fabs(pGreen[k] - pGreen[k - 1]);
mu = exptable[div0Green];
pGreen[k] = pGreen[k - 1] * mu + pGreen[k] * (1.0 - mu);
int div0Blue = fabs(pBlue[k] - pBlue[k - 1]);
mu = exptable[div0Blue];
pBlue[k] = pBlue[k - 1] * mu + pBlue[k] * (1.0 - mu);
}
for (int k = startIndex + height - 2; startIndex <= k; --k) {
int div0Red = fabs(rRed[k] - rRed[k + 1]);
mu = exptable[div0Red];
rRed[k] = rRed[k + 1] * mu + rRed[k] * (1.0 - mu);
int div0Green = fabs(rGreen[k] - rGreen[k + 1]);
mu = exptable[div0Green];
rGreen[k] = rGreen[k + 1] * mu + rGreen[k] * (1.0 - mu);
int div0Blue = fabs(rBlue[k] - rBlue[k + 1]);
mu = exptable[div0Blue];
rBlue[k] = rBlue[k + 1] * mu + rBlue[k] * (1.0 - mu);
}
}
double init_gain_mu = spatialDecay / (2 - spatialDecay);
for (int k = 0; k < length; ++k) {
rRed[k] = (rRed[k] + pRed[k]) * rho0 - gRed[k] * init_gain_mu;
rGreen[k] = (rGreen[k] + pGreen[k]) * rho0 - gGreen[k] * init_gain_mu;
rBlue[k] = (rBlue[k] + pBlue[k]) * rho0 - gBlue[k] * init_gain_mu;
}
m = 0;
for (int k1 = 0; k1 < width; ++k1) {
int n = k1;
for (int k2 = 0; k2 < height; ++k2) {
data2Red[n] = rRed[m];
data2Green[n] = rGreen[m];
data2Blue[n] = rBlue[m];
imgCopy.setPixel(k1, k2, qRgb(data2Red[n], data2Green[n], data2Blue[n]));
m++;
n += width;
}
}
delete []data2Red;
data2Red = nullptr;
delete []data2Green ;
data2Green = nullptr;
delete []data2Blue;
data2Blue = nullptr;
delete []pRed;
pRed = nullptr;
delete []rRed;
rRed = nullptr;
delete []gRed;
gRed = nullptr;
delete []pGreen;
pGreen = nullptr;
delete []rGreen;
rGreen = nullptr;
delete []gGreen;
gGreen = nullptr;
delete []pBlue;
pBlue = nullptr;
delete []rBlue;
rBlue = nullptr;
delete []gBlue;
gBlue = nullptr;
delete []exptable;
exptable = nullptr;
delete []g_table;
g_table = nullptr;
return imgCopy;
}
四、原始代码(上面接口是根据原始代码修改的)
//原始代码
static void RunBEEPSVerticalHorizontal(double *data,int width,int height,double spatialDecay,double *exp_table,double *g_table)
{
int length0=height*width;
double* g= new double[length0];
int m = 0;
for (int k2 = 0;k2<height;++k2)
{
int n = k2;
for (int k1 = 0;k1<width;++k1)
{
g[n]=data[m++];
n += height;
}
}
double*p = new double[length0];
double*r = new double[length0];
memcpy(p, g, sizeof(double) * length0);
memcpy(r, g, sizeof(double) * length0);
for (int k1 = 0;k1<width; ++k1)
{
int startIndex=k1 * height;
double mu = 0.0;
for (int k=startIndex+1,K =startIndex+height;k<K;++k)
{
int div0=fabs(p[k]-p[k-1]);
mu =exp_table[div0];
p[k] = p[k - 1] * mu + p[k] * (1.0 - mu);//文献中的公式1,这里做了一下修改,效果影响不大
}
for (int k =startIndex+height-2;startIndex <= k;--k)
{
int div0=fabs(r[k]-r[k+1]);
mu =exp_table[div0];
r[k] = r[k+1] * mu + r[k] * (1.0-mu) ;//文献公式3
}
}
double rho0=1.0/(2-spatialDecay);
for (int k = 0;k <length0;++k)
{
r[k]= (r[k]+p[k])*rho0-g_table[(int)g[k]];
}
m = 0;
for (int k1=0;k1<width;++k1)
{
int n = k1;
for (int k2 =0;k2<height;++k2)
{
data[n] = r[m++];
n += width;
}
}
memcpy(p,data, sizeof(double) * length0);
memcpy(r,data, sizeof(double) * length0);
for (int k2 = 0; k2<height;++k2)
{
int startIndex=k2 * width;
double mu = 0.0;
for (int k=startIndex+1, K=startIndex+width;k<K;++k)
{
int div0=fabs(p[k]-p[k-1]);
mu =exp_table[div0];
p[k] = p[k - 1] * mu + p[k] * (1.0 - mu);
}
for (int k=startIndex+width-2;startIndex<=k;--k)
{
int div0=fabs(r[k]-r[k+1]);
mu =exp_table[div0];
r[k] = r[k + 1] * mu + r[k] * (1.0 - mu) ;
}
}
double init_gain_mu=spatialDecay/(2-spatialDecay);
for (int k = 0; k <length0; k++)
{
data[k]=(p[k]+r[k])*rho0-data[k]*init_gain_mu;//文献中的公式5
}
delete[] p;
delete[] r;
delete[] g;
}
四、公式原理
The first recursion is progressive. Letting x[k] be the current sample of an input sequence x at location k ∈ Z,were cursively compute the elements of an auxiliary sequence ϕ as:
where:

The second recursion is regressive and very similar to thefirst one, except for a reversal of the order in which the indices are traversed. We recursively compute a second auxiliary sequence φ as:
where:

We complete our algorithm by merging the resulting progressive sequence ϕ and regressive sequence φ to produce the samples of the output sequence y as:
在这里我们主要使用到了上面的公式1和公式3,并在关键代码使用里面有所标注。
联系我
liuminghang0821@qq.com
有问必答,期待点赞,github星星
Qt图像处理技术一:对QImage图片美颜,使用双指数滤波的更多相关文章
- 搭建Android+QT+OpenCV环境,实现“单色图片着色”效果
OpenCV是我们大家非常熟悉的图像处理开源类库:在其新版本将原本在Contrib分库中的DNN模块融合到了主库中,并且更新了相应文档.这样我们就能够非常方便地利用OpenCV实 ...
- HTML5图形图像处理技术研究
摘要:图形图像处理平台大部分是传统的C/S架构的桌面应用程序,维护困难,共享性差,而B/S架构的Web程序具有易维护.易共享的优点.本文研究了基于HTML5的Web图形图像处理技术,用HTML5实现了 ...
- GDI+图形图像处理技术中Pen和Brush的简单使用和简单图形的绘制(C#)
1.Graphics Graphics对象是GDI+绘图表面,因此在Windows窗体应用程序中要使用GDI+创建绘图,必须要先创建Graphics.在给窗体注册一个Paint事件后,Graphics ...
- 数字图像处理技术在TWaver可视化中的应用
数字图像处理(Digital Image Processing)又称为计算机图像处理,它是指将图像信号转换成数字信号并利用计算机对其进行处理的过程.常用的图像处理方法有图像增强.复原.编码.压缩等,数 ...
- php图形图像处理技术
图形图像处理技术,gd库的强大支持,PHP的图像可以是PHP的强项,PHP图形化类库,jpgraph是一款非常好用的强大的图形处理工具. 在PHP中加载GD库 gd官方网址下载: http://www ...
- FPGA与数字图像处理技术
数字图像处理方法的重要性源于两个主要应用领域: 改善图像信息以便解释. 为存储.传输和表示而对图像数据进行处理,以便于机器自动理解. 图像处理(image processing): 用计算机对图像进行 ...
- Qt 打开安卓相冊选择图片并获取图片的本地路径
Qt 打开安卓相冊选择图片并获取图片的本地路径 过程例如以下: 通过 Intent 打开安卓的系统相冊. 推荐使用 QAndroidJniObject::getStaticObjectField 获取 ...
- Qt开发技术:QCharts(三)QCharts样条曲线图介绍、Demo以及代码详解
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- Qt开发技术:图形视图框架(二)场景QGraphicsScene、QGraphicsItem与QGraphicsView详解
前话 Qt的图形视图框架,最核心的三个类为:QGraphicsScene.QGraphicsItem与QGraphicsView. 基于图形框架的高级白板软件Demo QGraphicsSce ...
- Qt QPixmap QImage 图片等比例缩放到指定大小
QPixmap pixmap(path); //pixmap=QPixmap::fromImage(imgShow); pixmap = pixmap.scaled(, , Qt::KeepAspec ...
随机推荐
- 通过fetch_mcp,让Cline能够获取网页内容。
fetch_mcp介绍 GitHub地址:https://github.com/zcaceres/fetch-mcp 此MCP服务器提供了以多种格式(包括HTML.JSON.纯文本和Markdown) ...
- Windows 提权-MSSQL
本文通过 Google 翻译 MSSQL – Windows Privilege Escalation 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校正及个别注释补充. 导航 0 前言 ...
- Anaconda使用记录
1 安装 windows下,安装完添加环境变量(哦安装时勾选添加环境变量选项就是加这些变量的) ## (记anaconda软件目录为%ANACONDA3%) %ANACONDA3%\ %ANACOND ...
- 【Bug记录】defineEmits 在 TS 项目报错
前言 单文件组件 <script setup> 是 Vue3.2 版本后非常流行的写法. 但是在开发过程中,按照官网使用 defineEmits ,结果项目报错,无法运行. 错误截图 Un ...
- JMeter 性能优化
Jmeter 性能优化:(3优化 + 1补充) 1.在 jmx 文件中 Disable 所有的结果输出,如: View Results Tree / Graph Results / Aggrega ...
- Xshell连接有跳板机(堡垒机)的服务器
一.Xshell直连有跳板机的服务器 跳板机IP:112.74.163.161 端口号: 22 服务器IP:47.244.217.66 端口号:22 1. 新建跳板机会话 点击连接,主机和端口 ...
- Oracle操作审计
因为信安的要求,要对Oracle加审计.看了一下,原来是有开的,类型为DB: SQL> show parameter audit; NAME TYPE VALUE --------------- ...
- .NET周刊【3月第2期 2025-03-09】
国内文章 记一次.NET内存居高不下排查解决与启示 https://www.cnblogs.com/huangsheng/p/18731382 本文讲述了一个ASP.NET Core gRPC服务迁移 ...
- Netty源码—1.服务端启动流程
大纲 1.服务端启动整体流程及关键方法 2.服务端启动的核心步骤 3.创建服务端Channel的源码 4.初始化服务端Channel的源码 5.注册服务端Channel的源码 6.绑定服务端端口的源码 ...
- Lua程序设计笔记
未学:第10章URL编码及以后的示例 13章位和字节 Lua语言基础 一组命令或表达式组成的序列叫chunk程序段,因为Lua语言可以被用作数据定义语言,chunk的大小没有限制,几MB的程序段也很常 ...