基于Qt的图像处理技术和算法

原文链接: http://developer.nokia.com/community/wiki/Image_editing_techniques_and_algorithms_using_Qt

这篇文章主要阐述了如何使用Qt在像素级别上对图像进行操作,并实现了一些图像效果,这些效果主要有:灰度,模糊,锐化,添加相框,金属质感,改变图像饱和度,亮度还有白平衡。

介绍

文章中,我们将讨论在Qt中修改图像的一些技术和算法,在这之前,你必须知道在Qt中操作图像的一些方法。

.在Qt中有两种表示图像的类,Qt:QImage和QPixmap,还有QBitmap来存储单色的图像,比如遮罩,QPicture在存储QPainter的一些操作指令。

当我们想要在屏幕上绘制图像的时候,最快的方法就是使用QPixmap,不过坏处就是无法访问和修改像素;

QImage在IO操作中有很快的速度,并且给出了访问像素的接口,这篇文章中我们就使用这个类。

.如果你是要处理大的图片,比如摄像头拍摄的照片,这种情况最好是将原图缩小之后作为预览图显示在屏幕上,除非我们允许用户缩放图像。有两种加载并缩放图像的方法。

。将图像加载进QImage或者QPixmap,然后调整大小:

QImage image("sample.png");
image = image.scaled(width, height);

使用QImageReader来读取和缩放图片,然后再加载进QImage中。QImageReader无法将一张图片加载进QPixmap中去,但是可以使用静态方法 QPixmap::fromImage(QImage img)从QImage中加载进QPixmap。这个方法非常快,并且不需要加载大图的内存开销:

QImageReader imgReader("sample.png");
imgReader.setScaledSize(QSize(width, height));
QImage * image;
imgReader.read(image);

。每一张图片都是由像素点组成,每一个像素都有三个通道:红,绿,蓝,还有一个alpha通道来保存透明度(JPEG格式的图片不支持透明)。每个通道的值是0-255,三个通道都是0的话,表示黑色,都是255表示白色。这篇文章中我们用RGB来表示一种颜色,也就是三个通道的值。

。相比于一个像素一个像素地读取,uchar *  QImage::scanLine(int i)可以一次读取整行的像素值,会更加高效,下面的例子就是按行读取的例子,也是我们将要讲的第一个例子,转灰度图。

QImage * MainWindow::greyScale(QImage * origin){
    QImage * newImage = new QImage(origin->width(), origin->height(), QImage::Format_ARGB32);

    QRgb * line;

    for(int y = 0; y<newImage->height(); y++){
        QRgb * line = (QRgb *)origin->scanLine(y);

        for(int x = 0; x<newImage->width(); x++){
            int average = (qRed(line[x]) + qGreen(line[x]) + qRed(line[x]))/3;
            newImage->setPixel(x,y, qRgb(average, average, average));
        }

    }

    return newImage;
}

灰度

我们要学习的第一个技术就是将彩色图转换成灰度图,我们首先要明白的一点就是,其实标准的灰度图就是每个像素点的三个通道的值一样或者近似,我们的策略就是将每个像素的每个通道的值都调成一样,取R,G,B值为三者的算数平均数就可以了,比如原色是RGB(169,204,69), 那么最终的RGB就是(169+204+69)/3 = 147.

QImage * MainWindow::greyScale(QImage * origin){
    QImage * newImage = new QImage(origin->width(), origin->height(), QImage::Format_ARGB32);

    QColor oldColor;

    for(int x = 0; x<newImage->width(); x++){
        for(int y = 0; y<newImage->height(); y++){
            oldColor = QColor(origin->pixel(x,y));
            int average = (oldColor.red()+oldColor.green()+oldColor.blue())/3;
            newImage->setPixel(x,y,qRgb(average,average,average));
        }
    }

    return newImage;
}

原始图

灰度图

亮度调节

就如之前我们提到的,白色用RGB(255,255,255)表示,黑色用RGB(0,0,0)表示,所以如果我们需要提高图片的亮度(颜色接近白色),我们需要同时增加三个通道的数值,反之就是变暗。

在这里我们添加了一个函数参数来决定要提高多少亮度,如果参数是负数的话就是减少亮度了。在每个通道都加上delta值之后,需要做的就是让它不要低于0且不要高于255.

原图

加亮图 Delta = 30

暖色调

当我们说一一幅暖色调的图片的时候通常是因为这张图色调偏黄。我们没有黄色的通道,但是红色和绿色混合起来就是黄色,所以我们增加这两个通道值,然后蓝色通道值不变就好了。

我们使用一个delta参数来决定增加红色和绿色通道的值。一张暖色的图片能够给人一种复古效果,如果是有沙子的图片,图片将会更加生动。

QImage * MainWindow::warm(int delta, QImage * origin){
    QImage *newImage = new QImage(origin->width(), origin->height(), QImage::Format_ARGB32);

    QColor oldColor;
    int r,g,b;

    for(int x=0; x<newImage->width(); x++){
        for(int y=0; y<newImage->height(); y++){
            oldColor = QColor(origin->pixel(x,y));

            r = oldColor.red() + delta;
            g = oldColor.green() + delta;
            b = oldColor.blue();

            //we check if the new values are between 0 and 255
            r = qBound(0, r, 255);
            g = qBound(0, g, 255);

            newImage->setPixel(x,y, qRgb(r,g,b));
        }
    }

    return newImage;
}

原图

暖色图 Delta = 30

冷色调

如果说暖色调的图片偏黄色,那么冷色调的图片应该就是偏蓝色了。在这个方法里面我们只增加蓝色通道的值,红色和绿色的值不变。

冷色调的图片可以联想到未来,死亡或者,冷。

QImage * MainWindow::cool(int delta, QImage * origin){
    QImage *newImage = new QImage(origin->width(), origin->height(), QImage::Format_ARGB32);

    QColor oldColor;
    int r,g,b;

    for(int x=0; x<newImage->width(); x++){
        for(int y=0; y<newImage->height(); y++){
            oldColor = QColor(origin->pixel(x,y));

            r = oldColor.red();
            g = oldColor.green();
            b = oldColor.blue()+delta;

            //we check if the new value is between 0 and 255
            b = qBound(0, b, 255);

            newImage->setPixel(x,y, qRgb(r,g,b));
        }
    }

    return newImage;
}

原图

冷色调图 Delta = 30

饱和度

我们已经说了,颜色由三个通道组成:红,绿,蓝,尽管如此,RGB不是唯一一个表示色彩的方式,在这里,我们使用HSL格式表示色彩 - hue(色相), saturation(饱和度), lightness(明度)。

饱和的图像拥有更加生动的颜色,通常会比较好看,但是有一点要记住:不要滥用饱和度,因为很容易出现失真。

QImage * MainWindow::saturation(int delta, QImage * origin){
    QImage * newImage = new QImage(origin->width(), origin->height(), QImage::Format_ARGB32);

    QColor oldColor;
    QColor newColor;
    int h,s,l;

    for(int x=0; x<newImage->width(); x++){
        for(int y=0; y<newImage->height(); y++){
            oldColor = QColor(origin->pixel(x,y));

            newColor = oldColor.toHsl();
            h = newColor.hue();
            s = newColor.saturation()+delta;
            l = newColor.lightness();

            //we check if the new value is between 0 and 255
            s = qBound(0, s, 255);

            newColor.setHsl(h, s, l);

            newImage->setPixel(x, y, qRgb(newColor.red(), newColor.green(), newColor.blue()));
        }
    }

    return newImage;
}

原图

饱和的图片 Delta=30

模糊

这个效果相对于之前的有一点点复杂。我们会用到一个卷积滤波器,根据当前像素的颜色和相邻像素的颜色来获得一个新的颜色。同时还有一个kernel的矩阵来决定计算中相邻像素的影响程度。

原像素会在矩阵的中心,因此我们会使用基数行的行和列。我们不会修改边缘的像素点,因为那些点没有我们需要的相邻像素点,虽然我们也可以只使用有效的像素点。

举了例子,让我们来看看如何计算像素的RGB值。下面的三个举证代表着当前像素和邻接像素的RGB值,最中间的是当前像素。

R = 20 102 99
150 200 77
170 210 105

G = 22 33 40
17 21 33
8 15 24

B = 88 70 55
90 72 59
85 69 50

Kenel =  0 2 0
2 5 2
0 2 0

使用滤波器进行计算:

r = ( (102*2) + (150*2) + (200*5) + (77*2) + (210*2) ) / (2+2+5+2+2) = 159
g = ( (33*2) + ( 17*2) + (21*5) + (33*2) + (15*2) ) / (2+2+5+2+2) = 23
b = ( (70*2) + (90*2) + (72*5) + (59*2) + (69*2) ) / (2+2+5+2+2) = 72

由原始的RGB(200, 21, 72)得到了RGB(159, 23, 72).  发现最大的变化是红色的通道,因为红色通道的值差距最大。

在修改肖像照片的时候通常会使用到模糊的技术,它能后掩盖住皮肤的瑕疵。

QImage * MainWindow::blur(QImage * origin){
    QImage * newImage = new QImage(*origin);

    int kernel [5][5]= {{0,0,1,0,0},
                        {0,1,3,1,0},
                        {1,3,7,3,1},
                        {0,1,3,1,0},
                        {0,0,1,0,0}};
    int kernelSize = 5;
    int sumKernel = 27;
    int r,g,b;
    QColor color;

    for(int x=kernelSize/2; x<newImage->width()-(kernelSize/2); x++){
        for(int y=kernelSize/2; y<newImage->height()-(kernelSize/2); y++){

            r = 0;
            g = 0;
            b = 0;

            for(int i = -kernelSize/2; i<= kernelSize/2; i++){
                for(int j = -kernelSize/2; j<= kernelSize/2; j++){
                    color = QColor(origin->pixel(x+i, y+j));
                    r += color.red()*kernel[kernelSize/2+i][kernelSize/2+j];
                    g += color.green()*kernel[kernelSize/2+i][kernelSize/2+j];
                    b += color.blue()*kernel[kernelSize/2+i][kernelSize/2+j];
                }
            }

            r = qBound(0, r/sumKernel, 255);
            g = qBound(0, g/sumKernel, 255);
            b = qBound(0, b/sumKernel, 255);

            newImage->setPixel(x,y, qRgb(r,g,b));

        }
    }
    return newImage;
}

原图

模糊图

锐化

像模糊中一样,锐化一张图片也会使用一个卷积滤波器,但是kernel矩阵是不一样的,相邻像素对应的值是负的。

锐化能够处理模糊的照片,能够提升细节。

QImage * MainWindow::sharpen(QImage * origin){
    QImage * newImage = new QImage(* origin);

    int kernel [3][3]= {{0,-1,0},
                        {-1,5,-1},
                        {0,-1,0}};
    int kernelSize = 3;
    int sumKernel = 1;
    int r,g,b;
    QColor color;

    for(int x=kernelSize/2; x<newImage->width()-(kernelSize/2); x++){
        for(int y=kernelSize/2; y<newImage->height()-(kernelSize/2); y++){

            r = 0;
            g = 0;
            b = 0;

            for(int i = -kernelSize/2; i<= kernelSize/2; i++){
                for(int j = -kernelSize/2; j<= kernelSize/2; j++){
                    color = QColor(origin->pixel(x+i, y+j));
                    r += color.red()*kernel[kernelSize/2+i][kernelSize/2+j];
                    g += color.green()*kernel[kernelSize/2+i][kernelSize/2+j];
                    b += color.blue()*kernel[kernelSize/2+i][kernelSize/2+j];
                }
            }

            r = qBound(0, r/sumKernel, 255);
            g = qBound(0, g/sumKernel, 255);
            b = qBound(0, b/sumKernel, 255);

            newImage->setPixel(x,y, qRgb(r,g,b));

        }
    }
    return newImage;
}

原图

锐化图

添加相框

绘制一个相框是非常见到那的,我们只需要把相框在原图上面绘制就可以了。这里假设我们已经有一个和图片一样大小的相框了,不一样的话要resize到一样大。

QImage * MainWindow::drawFrame(QImage * origin){
    QImage * newImage = new QImage(* origin);
    QPainter painter;

    painter.begin(newImage);

    painter.drawImage(0,0, QImage(":images/frame.png"));

    painter.end();

    return newImage;
}

原图

相框

添加相框之后

金属效果

这个例子中我们会结合几种技术来获得一种效果。下面是处理的步骤:

1.调整图像的亮度,获得一个较暗的图片。

2.将图像转成灰度。

3.将灰度图绘制在金属的纹理上,透明度50%。

QImage * MainWindow::metal(QImage * origin){
    QImage * newImage = new QImage(":images/metal.png");
    QImage * darkImage = brightness(-100, origin);
    QImage * greyImage = greyScale(darkImage);
    QPainter painter;

    painter.begin(newImage);

    painter.setOpacity(0.5);
    painter.drawImage(0, 0, * greyImage);

    painter.end();

    delete greyImage;
    delete darkImage;

    return newImage;
}

原图

金属纹理

最终效果

模糊的边框

最后再来学习一个融合的效果,这次我们想要做的是模糊图片外延的部分,让视线的焦点聚集在图片的中间。

我们将会使用一张遮罩图片,来决定需要模糊的部分,具体的操作步骤如下:

1.从原图获取一张完全模糊的图片。

2.使用QPainter的一种融合模式,通过遮罩图片截取出一个模糊的相框。点这里可以学习到更多的QPainter的融合模式。

3.在原图上绘制出模糊的边框。

QImage * MainWindow::blurFrame(QImage * origin){
    QImage * newImage = new QImage(* origin);
    QImage * blurredImage = blur(newImage);
    QImage * mask = new QImage(":images/mask.png");
    QPainter painter;

    //Using the composition mode SourceAtop we get a blurred frame stored in QImage mask
    painter.begin(mask);

    painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
    painter.drawImage(0, 0, * blurredImage);

    painter.end();

    //With our new frame we simply draw it over the original image
    painter.begin(newImage);

    painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
    painter.drawImage(0, 0, * mask);

    painter.end();

    delete mask;
    delete blurredImage;

    return newImage;
}

原图

遮罩

模糊的边框

最终效果

手机Demo

你可以下载这个手机Demo的源码,里面包含了文章中的源码,在这个应用中,包含了3张462*260的图片。测试应用的时候,你只要选择其中一张并应用下面的效果就可以了。

总结

这篇文章应该可以成为你图像处理的入门,但是一切皆有可能。你可以修改这些方法,整合这些方法,使用其他的技术等等。想象力才是你唯一的限制。

时间: 2024-11-08 20:17:00

基于Qt的图像处理技术和算法的相关文章

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

在之前的博客中,已经分别比较详细地阐述了"windows平台下基于VS和OpenCV"以及"Linux平台下基于QT和OpenCV"搭建图像处理框架,并且生成了相应的免费视频.这篇博客的主要内容,就是基于最新版本的相应工具,在windows平台下,"基于QT和OpenCV搭建图像处理平台",并且进一步研究如何基于QT所见即所得的便利,进行图像处理操作,最终还要和vs做一个比较,进行初步小结. 主要分为3个部分,一个是当前模式下,windows+Q

基于大数据技术推荐系统算法案例实战视频教程(项目实战)

38套大数据,云计算,架构,数据分析师,Hadoop,Spark,Storm,Kafka,人工智能,机器学习,深度学习,项目实战视频教程 视频课程包含: 38套大数据和人工智能精品高级课包含:大数据,云计算,架构,数据挖掘实战,实时推荐系统实战,电视收视率项目实战,实时流统计项目实战,离线电商分析项目实战,Spark大型项目实战用户分析,智能客户系统项目实战,Linux基础,Hadoop,Spark,Storm,Docker,Mapreduce,Kafka,Flume,OpenStack,Hiv

基于QT和OpenCV的人脸识别系统

1 系统方案设计 1.1 引言 人脸是一个常见而复杂的视觉模式,人脸所反映的视觉信息在人与人的交流和交往中有着重 要的作用和意义,对人脸进行处理和分析在视频监控.出入口控制.视频会议以及人机交互等领 域都有着广泛的应用前景,因此是模式识别和计算机视觉领域持续的研究热点. 本系统在 FriendlyARM Tiny6410 开发板基础上,利用 OpenCV 计算机视觉库和 QT 图形库,通 过普通的 USB 摄像头实现了自动人脸识别,准确率较高,方便易用. 1.2 系统总体架构 "人脸识别&quo

基于HSV分块颜色直方图的图像检索算法

引 言 随着多媒体技术及[nternet技术的迅速发展,各行各业对图像的使用越来越广泛,图像信息资源的管理和检索显得越来越重要.传统的通过手工标记和索引图像(即基于文本的图像检索)的方法已经不能满足人们的需求,随之而来的问题是:随着图像数据的剧增和人们对图像的理解具有不同的侧重点,不同的人从不同的角度对同一幅图像的认识可能存在很大的差异性,因此无法准确反映图像信息.基于内容的图像检索方法(Content-Based Image Retrieval,CBIR)由此应运而生. 在基于内容的图像检索中

HTML5图形图像处理技术研究

摘要:图形图像处理平台大部分是传统的C/S架构的桌面应用程序,维护困难,共享性差,而B/S架构的Web程序具有易维护.易共享的优点.本文研究了基于HTML5的Web图形图像处理技术,用HTML5实现了一个Web图形图像处理程序,并通过理论分析和实验得出:HTML5在Web图形图像处理上具有优势,存在基于HTML5实现Web处理图形图像的可能性. 关键词:HTML5:Web:Canvas:图形图像处理 引言 传统C/S架构的桌面图形图像处理程序,虽然处理速度较快,但由于界面和算法逻辑都集中在客户端

基于内容的图像检索技术

图像检索的本质是对图像特征的提取与基于特征的匹配技术,图像的特征包括图像的文本特征.视觉特征,所谓图像的文本特征是指与图像相关的文本信息,比如图像的名称.对图像的注解文字等,而目前比较成熟应用于网络环境下的图像检索系统比如Google.百度等均属于这类.图像的视觉特征是指图像本身所拥有的视觉信息,又可以进一步分为通用的视觉特征和领域特征,如颜色.纹理.形状等属于图像通用特征,而光谱特征则属于地理科学中遥感影像独有的特征. 从发展演变历程来看,根据图像检索系统所提取图像特征可分为两类,第一类即基于

AACOS:基于编译器和操作系统内核的算法设计与实现

AACOS:基于编译器和操作系统内核的算法设计与实现 [计算机科学技术] 谢晓啸 湖北省沙市中学 [关键词]: 编译原理,操作系统内核实现,算法与数据结构,算法优化 0.索引 1.引论 1.1研究内容 1.2研究目的 1.3研究提要 正文 2.1研究方法 2.2编译器部分 2.2.1从计算器程序中得到的编译器制作启示 2.2.2在编译器中其它具体代码的实现 2.2.3编译器中栈的高级应用 2.2.3编译器中树的高级应用 2.2.4编译器与有限状态机 2.3操作系统内核部分 2.3.1操作系统与底

基于Qt的跨平台应用开发

1 Qt简介 Qt是1991年奇趣科技开发的一个跨平台的C++图形用户界面应用程序框架.它提供给应用程序开发者建立艺术级的图形用户界面所需的所有功能.Qt很容易扩展,并且允许真正地组件编程.基本上,Qt 同 X Window 上的 Motif,Openwin,GTK 等图形界 面库和 Windows 平台上的 MFC,OWL,VCL,ATL 是同类型的东西. 2008年,奇趣科技被诺基亚公司收购,QT也因此成为诺基亚旗下的编程语言工具.2012年,Qt被Digia收购.2014年4月,跨平台集成

基于Qt有限状态机人工智能的一种实现及改进方法

基于Qt有限状态机人工智能的一种实现及改进方法 人工智能在今年是一个非常火的方向,当然了,不仅仅是今年,它一直火了很多年,有关人工智能的一些算法层出不穷.人工智能在很多领域都有应用,就拿我熟悉的游戏领域来说吧,一些寻路算法,比如说A*算法(我的<十日驱鬼记>就曾经使用了A*算法进行寻路),还有一些高级的算法,比如说决策树等,都在游戏中得以了广泛的应用.我目前想制作的项目和人工智能也有一定的关系,因此,我这个月开始学习搭建一些简单的人工智能框架. 蒋彩阳原创文章,首发地址:http://blog