MENU

分类 Linux开发 下的文章

使用pthread_create()创建线程

可以通过 pthread_create()函数创建新线程。

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,
                              const pthread_attr_t *restrict attr,
                              void *(*start_rtn)(void *),
                              void *restrict arg);

阅读全文

C语言关键字static

<h4>1.作用于变量</h4>
用static声明局部变量

用static声明局部变量时,则改变变量的存储方式(生命期),使变量成为静态的局部变量,即编译时就为变量分配内存,直到程序退出才释放存储单元。这样,使得该局部变量有记忆功能,可以记忆上次的数据,不过由于仍是局部变量,因而只能在代码块内部使用(作用域不变)。


阅读全文

Linux系统错误代码

在Linux下,系统请求失败后会返回错误代码。/usr/include/asm-generic/errno.h这个文件解释了错误代码的含义。

文件版本:2017/04/22 on CentOS 6.5

#ifndef _ASM_GENERIC_ERRNO_H
#define _ASM_GENERIC_ERRNO_H

#include <asm-generic/errno-base.h>

#define	EDEADLK		35	/* Resource deadlock would occur */
#define	ENAMETOOLONG	36	/* File name too long */
#define	ENOLCK		37	/* No record locks available */
#define	ENOSYS		38	/* Function not implemented */
#define	ENOTEMPTY	39	/* Directory not empty */
#define	ELOOP		40	/* Too many symbolic links encountered */
#define	EWOULDBLOCK	EAGAIN	/* Operation would block */
#define	ENOMSG		42	/* No message of desired type */
#define	EIDRM		43	/* Identifier removed */
#define	ECHRNG		44	/* Channel number out of range */
#define	EL2NSYNC	45	/* Level 2 not synchronized */
#define	EL3HLT		46	/* Level 3 halted */
#define	EL3RST		47	/* Level 3 reset */
#define	ELNRNG		48	/* Link number out of range */
#define	EUNATCH		49	/* Protocol driver not attached */
#define	ENOCSI		50	/* No CSI structure available */
#define	EL2HLT		51	/* Level 2 halted */
#define	EBADE		52	/* Invalid exchange */
#define	EBADR		53	/* Invalid request descriptor */
#define	EXFULL		54	/* Exchange full */
#define	ENOANO		55	/* No anode */
#define	EBADRQC		56	/* Invalid request code */
#define	EBADSLT		57	/* Invalid slot */

#define	EDEADLOCK	EDEADLK

#define	EBFONT		59	/* Bad font file format */
#define	ENOSTR		60	/* Device not a stream */
#define	ENODATA		61	/* No data available */
#define	ETIME		62	/* Timer expired */
#define	ENOSR		63	/* Out of streams resources */
#define	ENONET		64	/* Machine is not on the network */
#define	ENOPKG		65	/* Package not installed */
#define	EREMOTE		66	/* Object is remote */
#define	ENOLINK		67	/* Link has been severed */
#define	EADV		68	/* Advertise error */
#define	ESRMNT		69	/* Srmount error */
#define	ECOMM		70	/* Communication error on send */
#define	EPROTO		71	/* Protocol error */
#define	EMULTIHOP	72	/* Multihop attempted */
#define	EDOTDOT		73	/* RFS specific error */
#define	EBADMSG		74	/* Not a data message */
#define	EOVERFLOW	75	/* Value too large for defined data type */
#define	ENOTUNIQ	76	/* Name not unique on network */
#define	EBADFD		77	/* File descriptor in bad state */
#define	EREMCHG		78	/* Remote address changed */
#define	ELIBACC		79	/* Can not access a needed shared library */
#define	ELIBBAD		80	/* Accessing a corrupted shared library */
#define	ELIBSCN		81	/* .lib section in a.out corrupted */
#define	ELIBMAX		82	/* Attempting to link in too many shared libraries */
#define	ELIBEXEC	83	/* Cannot exec a shared library directly */
#define	EILSEQ		84	/* Illegal byte sequence */
#define	ERESTART	85	/* Interrupted system call should be restarted */
#define	ESTRPIPE	86	/* Streams pipe error */
#define	EUSERS		87	/* Too many users */
#define	ENOTSOCK	88	/* Socket operation on non-socket */
#define	EDESTADDRREQ	89	/* Destination address required */
#define	EMSGSIZE	90	/* Message too long */
#define	EPROTOTYPE	91	/* Protocol wrong type for socket */
#define	ENOPROTOOPT	92	/* Protocol not available */
#define	EPROTONOSUPPORT	93	/* Protocol not supported */
#define	ESOCKTNOSUPPORT	94	/* Socket type not supported */
#define	EOPNOTSUPP	95	/* Operation not supported on transport endpoint */
#define	EPFNOSUPPORT	96	/* Protocol family not supported */
#define	EAFNOSUPPORT	97	/* Address family not supported by protocol */
#define	EADDRINUSE	98	/* Address already in use */
#define	EADDRNOTAVAIL	99	/* Cannot assign requested address */
#define	ENETDOWN	100	/* Network is down */
#define	ENETUNREACH	101	/* Network is unreachable */
#define	ENETRESET	102	/* Network dropped connection because of reset */
#define	ECONNABORTED	103	/* Software caused connection abort */
#define	ECONNRESET	104	/* Connection reset by peer */
#define	ENOBUFS		105	/* No buffer space available */
#define	EISCONN		106	/* Transport endpoint is already connected */
#define	ENOTCONN	107	/* Transport endpoint is not connected */
#define	ESHUTDOWN	108	/* Cannot send after transport endpoint shutdown */
#define	ETOOMANYREFS	109	/* Too many references: cannot splice */
#define	ETIMEDOUT	110	/* Connection timed out */
#define	ECONNREFUSED	111	/* Connection refused */
#define	EHOSTDOWN	112	/* Host is down */
#define	EHOSTUNREACH	113	/* No route to host */
#define	EALREADY	114	/* Operation already in progress */
#define	EINPROGRESS	115	/* Operation now in progress */
#define	ESTALE		116	/* Stale NFS file handle */
#define	EUCLEAN		117	/* Structure needs cleaning */
#define	ENOTNAM		118	/* Not a XENIX named type file */
#define	ENAVAIL		119	/* No XENIX semaphores available */
#define	EISNAM		120	/* Is a named type file */
#define	EREMOTEIO	121	/* Remote I/O error */
#define	EDQUOT		122	/* Quota exceeded */

#define	ENOMEDIUM	123	/* No medium found */
#define	EMEDIUMTYPE	124	/* Wrong medium type */
#define	ECANCELED	125	/* Operation Canceled */
#define	ENOKEY		126	/* Required key not available */
#define	EKEYEXPIRED	127	/* Key has expired */
#define	EKEYREVOKED	128	/* Key has been revoked */
#define	EKEYREJECTED	129	/* Key was rejected by service */

/* for robust mutexes */
#define	EOWNERDEAD	130	/* Owner died */
#define	ENOTRECOVERABLE	131	/* State not recoverable */

#define ERFKILL		132	/* Operation not possible due to RF-kill */

#define EHWPOISON	133	/* Memory page has hardware error */

#endif

 

基于Qt的图像处理算法和实现——索贝尔边缘检测

索贝尔算子(Sobel operator)主要用作边缘检测,在技术上,它是一离散性差分算子,用来运算图像亮度函数的灰度之近似值。在图像的任何一点使用此算子,将会产生对应的灰度矢量或是其法矢量

Sobel卷积因子为:

$$\begin{matrix}
\left [ \begin{matrix}
-1 & 0 & +1\\
-2 & 0 & +2 \\
-2 & 0 & +1
\end{matrix} \right ]& \left [ \begin{matrix}
+1 & +2 & +1\\
0 & 0 & 0 \\
-1 & -2 & -1
\end{matrix}\right ]\\
Gx & Gy
\end{matrix}$$

该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以A代表原始图像,GxGy分别代表经横向及纵向边缘检测的图像灰度值,其公式如下:

$$\begin{align*}
G_{x} &= \left [ \begin{matrix}
-1 & 0 & +1\\
-2 & 0 & +2\\
-1 & 0 & +1
\end{matrix} \right ]*A
\\
G_{y} &= \left [ \begin{matrix}
+1 & +2 & +1\\
0 & 0 & 0\\
-1 & -2 & -1
\end{matrix} \right ]*A
\end{align*}$$

图像的每一个像素的横向及纵向灰度值通过以下公式结合,来计算该点灰度的大小:

$$G = \sqrt{ G_{x}^{2} + G_{y}^{2} }$$

通常,为了提高效率 使用不开平方的近似值:

$$\left | G \right | = \left | G_{x} \right | + \left | G_{y} \right |$$

如果梯度G大于某一阀值 则认为该点(x,y)为边缘点。

然后可用以下公式计算梯度方向:

$$\theta = arctan(\frac{G_{y}}{G_{x}})$$

Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。

算法实现:

QImage Tools::SobelEdge(const QImage &origin)
{
    double *Gx = new double[9];
    double *Gy = new double[9];

    /* Sobel */
    Gx[0] = 1.0; Gx[1] = 0.0; Gx[2] = -1.0;
    Gx[3] = 2.0; Gx[4] = 0.0; Gx[5] = -2.0;
    Gx[6] = 1.0; Gx[7] = 0.0; Gx[8] = -1.0;

    Gy[0] = -1.0; Gy[1] = -2.0; Gy[2] = - 1.0;
    Gy[3] = 0.0; Gy[4] = 0.0; Gy[5] = 0.0;
    Gy[6] = 1.0; Gy[7] = 2.0; Gy[8] = 1.0;

    QRgb pixel;
    QImage grayImage = GreyScale(origin);
    int height = grayImage.height();
    int width = grayImage.width();
    QImage newImage = QImage(width, height,QImage::Format_RGB888);

    float sobel_norm[width*height];
    float max = 0.0;
    QColor my_color;

    for (int x=0; x<width; x++)
    {
        for( int y=0; y<height; y++)
        {
            double value_gx = 0.0;
            double value_gy = 0.0;

            for (int k=0; k<3;k++)
            {
                for(int p=0; p<3; p++)
                {
                    pixel=grayImage.pixel(x+1+1-k,y+1+1-p);
                    value_gx += Gx[p*3+k] * qRed(pixel);
                    value_gy += Gy[p*3+k] * qRed(pixel);
                }
//                sobel_norm[x+y*width] = sqrt(value_gx*value_gx + value_gy*value_gy)/1.0;
                sobel_norm[x+y*width] = abs(value_gx) + abs(value_gy);

                max=sobel_norm[x+y*width]>max ? sobel_norm[x+y*width]:max;
            }
        }
    }

    for(int i=0;i<width;i++){
        for(int j=0;j<height;j++){
            my_color.setHsv( 0 ,0, 255-int(255.0*sobel_norm[i + j * width]/max));
            newImage.setPixel(i,j,my_color.rgb());
        }
    }
    return newImage;
}

处理效果:

[caption id="attachment_1689" align="aligncenter" width="225"] 原图[/caption]

[caption id="attachment_1690" align="aligncenter" width="225"] 处理后[/caption]

 

 

基于Qt的图像处理算法和实现——拉普拉斯锐化

锐化的目的是为了突出图像的边缘和细节,图像的二阶导数反映了图像的细节。

当邻域中心像素灰度低于它所在的领域内其它像素的平均灰度时,此中心像素的灰度应被进一步降低,当邻域中心像素灰度高于它所在的邻域内其它像素的平均灰度时,此中心像素的灰度应被进一步提高,以此实现图像的锐化处理。

操作原理

  1. 基于一阶微分的图像增强原理

对于二元函数的一阶偏微分微分表示如下:

$$\frac{\partial f}{\partial x} = f(x,y) - f(x-1, y) $$

$$\frac{\partial f}{\partial y} = f(x,y) - f(x, y-1)$$

定义二元函数的微分为:

$$\begin{align*}
\bigtriangledown f &= \frac{\partial f}{\partial x} + \frac{\partial f}{\partial y} \\
&= f(x,y)-f(x-1,y)+f(x,y)-f(x,y-1)\\
&=2f(x,y)-f(x-1,y)-f(x,y-1)
\end{align*}$$

这就描述了图像中一个像素点(x,y)的一阶微分变化。简单的总结为一个算子的话就是一个2*2的矩阵如下:

$$\begin{matrix}
-1 & 2 \\
0 & -1
\end{matrix}$$

将用这个算子处理后的值与(x,y)处原有的值相加,就可以将使这种变化更明显,也就是放大这种变化,从而达到图像锐化的目的。

2. 基于二阶微分的图像增强处理

类似上面的推导过程我们得到二阶微分:

$$\bigtriangledown ^2 f=f(x+1,y)+f(x-1,y)+f(x,y+1)+f(x,y-1)-4f(x,y)$$

从而得到拉普拉斯算子:

$$\begin{matrix}
0 & -1 & 0\\
-1 & 4 & -1\\
0 & -1 & 0
\end{matrix}$$

用这个算子对图像的每个像素进行处理得到的是图像的变化程度,应该将这种变化叠加到原图像:

$$g(x,y)=
\left\{\begin{matrix}
f(x,y)-\bigtriangledown ^{2} f(x,y), & \text{ A }<0 \\
f(x,y)+\bigtriangledown ^{2} f(x,y), & \text{ A }>0
\end{matrix}\right.$$

上式中A为拉普拉斯掩模中心系数,如上面的拉普拉斯算子中该系数为4.

注:每个像素R、G、B应该分别做如上处理。

算法实现(Qt/C++):

QImage LaplaceSharpen(const QImage &origin)
{
    int width = origin.width();
    int height = origin.height();
    QImage newImage = QImage(width, height,QImage::Format_RGB888);
    int window[3][3] = {0,-1,0,-1,4,-1,0,-1,0};

    for (int x=1; x<width; x++)
    {
        for(int y=1; y<height; y++)
        {
            int sumR = 0;
            int sumG = 0;
            int sumB = 0;

            //对每一个像素使用模板
            for(int m=x-1; m<= x+1; m++)
                for(int n=y-1; n<=y+1; n++)
                {
                    if(m>=0 && m<width && n<height)
                    {
                        sumR += QColor(origin.pixel(m,n)).red()*window[n-y+1][m-x+1];
                        sumG += QColor(origin.pixel(m,n)).green()*window[n-y+1][m-x+1];
                        sumB += QColor(origin.pixel(m,n)).blue()*window[n-y+1][m-x+1];
                    }
                }


            int old_r = QColor(origin.pixel(x,y)).red();
            sumR += old_r;
            sumR = qBound(0, sumR, 255);

            int old_g = QColor(origin.pixel(x,y)).green();
            sumG += old_g;
            sumG = qBound(0, sumG, 255);

            int old_b = QColor(origin.pixel(x,y)).blue();
            sumB += old_b;
            sumB = qBound(0, sumB, 255);


            newImage.setPixel(x,y, qRgb(sumR, sumG, sumB));
        }
    }
    return newImage;
}

效果截图:

[caption id="attachment_1685" align="aligncenter" width="650"] 原图[/caption]

[caption id="attachment_1686" align="aligncenter" width="650"] 处理后[/caption]

 

基于Qt的图像处理算法和实现——线性灰度变换

灰度的线性变换使用的变换函数是一个一维的线性函数:y = ax + b

x为原灰度值,y即得到的新灰度。

当a>1时,输出图像的对比度将增大;

当a<1时,输出图像的对比度将减小;

当a=1时且b≠0时,变换将使灰度值上移或下移,其效果是使整个图像变亮或变暗(我们在《基于Qt的图像处理算法和实现——灰度图像、冷暖色调、亮度》这篇文章中改变图像亮度时就是直接对各颜色分量的值进行加减操作);

当a<0时,暗区域将变亮,亮区域将变暗。

特殊情况:a=01,d=255时,输出图像的灰度相反。

示例代码

下面的代码针对所有类型的图像文件,不仅仅是位图,所以第一步操作是根据RGB值计算灰度值。对于彩色转灰度,有一个很著名的心理学公式:

$$Gray = R*0.299 + G*0.587 + B*0.114$$

至于用这个公式而不是直接取平均值,是因为人眼对RGB颜色的感知程度并不相同,所以在转换的时候应该分别为RGB设置不同的权重。至于其背后的详细说明及物理上的原理,请移步知乎的这个帖子:

传送门:知乎 RGB 转为灰度值的心理学公式 Gray = 0.30 * R + 0.59 * G + 0.11 * B 是怎么来的?

考虑到一幅图像的每个像素都需要使用上面的公式,计算量比较大,我们这里采用了:

$$ Gray = (R*299 + G*587 + B*114 + 500) / 1000$$

这个公式,注意后面那个除法是整数除法,所以需要加上500来实现四舍五入。

/*****************************************************************************
 *                           线性灰度变换 y = ax + b
 * **************************************************************************/
QImage Tools::LinearLevelTransformation(const QImage &origin, double _a, double _b)
{
    QImage *newImage = new QImage(origin.width(), origin.height(),
                                   QImage::Format_ARGB32);
    QColor oldColor;
    int grayLevel = 0;

    for (int x=0; x<newImage->width(); x++) {
        for (int y=0; y<newImage->height(); y++) {
            oldColor = QColor(origin.pixel(x,y));
            grayLevel = (oldColor.red()*299+oldColor.green()*587+oldColor.blue()*114+500)/1000;
            int _y = _a*grayLevel + _b;
            // Make sure that the new values are between 0 and 255
            _y = qBound(0, _y, 255);

            newImage->setPixel(x,y,qRgb(_y,_y,_y));
        }
    }
//    qDebug()<<"a:"<<_a<<"\tb:"<<_b;

    return *newImage;
}

注意,变换之后得到的灰度应该控制在 0~255之间。

效果截图

[caption id="attachment_1672" align="aligncenter" width="985"] 线性灰度变换[/caption]

上图中是 a =1.2, b=0时的效果

基于Qt的图像处理算法和实现——绘制图像直方图

关于图像直方图的介绍可以看我以前的一篇文章《图像处理中的直方图均衡化

这篇文章介绍了使用Qt绘制图像的直方图,包括灰度直方图和各颜色分量的直方图。

具体算法实现

#ifndef HISTOGRAM_H
#define HISTOGRAM_H

#include <QWidget>
#include <QLabel>
#include <QPainter>
#include <QDebug>

class Histogram : public QLabel
{
public:
    Histogram(QWidget* parent = 0);
    Histogram(QWidget*, Histogram*);

    void computeHstgrm(QImage img);
    void paintEvent(QPaintEvent *e);
    void drawBwHstgrm(int xBase, int yBase, int height);
    void drawRedHstgrm(int xBase, int yBase, int height);
    void drawGreenHstgrm(int xBase, int yBase, int height);
    void drawBlueHstgrm(int xBase, int yBase, int height);
    int getBwHstgrm(int index);
    int getRedHstgrm(int index);
    int getGreenHstgrm(int index);
    int getBlueHstgrm(int index);

private:
    // index 0 to 255 => count of image's pixels for this value
    // index 256 => maximum value
    // index 257 => total value of the dark component
    // index 258 => total value of the light component
    int bwHstgrm[259];

    // index 0 to 255 => count of image's pixels for this value
    // index 256 => maximum value
    // index 257 => total value of the component
    int redHstgrm[258];
    int greenHstgrm[258];
    int blueHstgrm[258];
};

#endif // HISTOGRAM_H
#include "histogram.h"

#include <sstream>
#include <iostream>


Histogram::Histogram(QWidget * parent) : QLabel(parent)
{
    for(int i = 0;i<256;i++)
    {
        bwHstgrm[i] = 0;
        redHstgrm[i] = 0;
        greenHstgrm[i] = 0;
        blueHstgrm[i] = 0;
    }

    bwHstgrm[256] = -1;
    redHstgrm[256] = -1;
    greenHstgrm[256] = -1;
    blueHstgrm[256] = -1;

    redHstgrm[257] = 0;
    greenHstgrm[257] = 0;
    blueHstgrm[257] = 0;

    bwHstgrm[257] = 0;
    bwHstgrm[258] = 0;
}



Histogram::Histogram(QWidget * parent, Histogram * hstgrm) : QLabel(parent)
{
    for(int i = 0;i<258;i++)
    {
        bwHstgrm[i] = hstgrm->bwHstgrm[i];
        redHstgrm[i] = hstgrm->redHstgrm[i];
        greenHstgrm[i] = hstgrm->greenHstgrm[i];
        blueHstgrm[i] = hstgrm->blueHstgrm[i];
    }

    bwHstgrm[258] = hstgrm->bwHstgrm[258];
}



void Histogram::computeHstgrm(QImage img)
{
    if (!img.isNull())
    {
        for(int i = 0;i<img.height();i++)
        {
            for(int j = 0;j<img.width();j++)
            {
                int bwValue = qGray(img.pixel(j, i));

                int redValue = qRed(img.pixel(j, i));
                int greenValue = qGreen(img.pixel(j, i));
                int blueValue = qBlue(img.pixel(j, i));

                bwHstgrm[bwValue]++;
                redHstgrm[redValue]++;
                greenHstgrm[greenValue]++;
                blueHstgrm[blueValue]++;
            }
        }

        for(int i = 0;i<256;i++)
        {
            // maximum values
            if (bwHstgrm[256] < bwHstgrm[i])
                bwHstgrm[256] = bwHstgrm[i];

            if (redHstgrm[256] < redHstgrm[i])
                redHstgrm[256] = redHstgrm[i];

            if (greenHstgrm[256] < greenHstgrm[i])
                greenHstgrm[256] = greenHstgrm[i];

            if (blueHstgrm[256] < blueHstgrm[i])
                blueHstgrm[256] = blueHstgrm[i];

            // values of colour components
            redHstgrm[257] += i*redHstgrm[i];
            greenHstgrm[257] += i*greenHstgrm[i];
            blueHstgrm[257] += i*blueHstgrm[i];

            // values of dark and light component
            if (i <= 127)
                bwHstgrm[257] += (127-i)*bwHstgrm[i];
            else
                bwHstgrm[258] += (i-127)*bwHstgrm[i];
        }
    }
}



void Histogram::paintEvent(QPaintEvent * event)
{
    Q_UNUSED(event);

    int step = 100;              // distance between histograms
    int height = 255+10;        // histograms height
    int xBase = 99;             // x coordinate of the first histogram origin
    int yBase = 30+height+1;    // y coordinate of the first histogram origin

    QPainter painter(this);
    painter.setPen(Qt::black);


    // 显示在第一行
    // bw hstgrm
    if (bwHstgrm[256] != -1)
        drawBwHstgrm(xBase, yBase, height);
    else
        painter.drawText(xBase, yBase-height/2+5, tr("Can't load the gray levels histogram."));

    // red hstgrm
    if (redHstgrm[256] != -1)
        drawRedHstgrm(xBase+step+height, yBase, height);
    else
        painter.drawText(xBase+step+height+1-height/2+5, yBase, tr("Can't load the red component histogram."));


    // 显示在第二行
    // green hstgrm
    if (greenHstgrm[256] != -1)
        drawGreenHstgrm(xBase, yBase+step+height+1, height);
    else
        painter.drawText(xBase, yBase+(step+height+1), tr("Can't load the green component histogram."));
    // blue hstgrm
    if (blueHstgrm[256] != -1)
        drawBlueHstgrm(xBase+step+height, yBase+step+height+1, height);
    else
        painter.drawText(xBase+step+height, yBase+step+height+1, tr("Can't load the blue component histogram."));

}



void Histogram::drawBwHstgrm(int xBase, int yBase, int height)
{
    QPainter painter(this);

    painter.setPen(Qt::darkGray);

    float max = bwHstgrm[256];

    if (max < redHstgrm[256])
        max = redHstgrm[256];

    if (max < greenHstgrm[256])
        max = greenHstgrm[256];

    if (max < blueHstgrm[256])
        max = blueHstgrm[256];

    // drawing the histogram
    for(int i = 0;i<256;i++)
    {
        painter.drawLine(xBase+1+i, yBase, xBase+1+i,
            yBase-(float)(256./max)*(float)bwHstgrm[i]);
    }

    painter.drawText(xBase, yBase+25, tr("black"));
    painter.drawText(xBase+220, yBase+25, tr("white"));

    painter.setPen(Qt::black);

    painter.drawText(xBase+40, yBase-height-10, tr("GRAY LEVELS HISTOGRAM"));

    painter.drawText(xBase+100, yBase+15, tr("Intensity"));
    painter.drawText(xBase-84, yBase-height/2+5, tr("Pixels count"));

    // abscissa
    painter.drawLine(xBase, yBase, xBase+256+1, yBase);
    painter.drawLine(xBase, yBase+1, xBase+256+1, yBase+1);

    // left ordinate
    painter.drawLine(xBase, yBase, xBase, yBase-height);
    painter.drawLine(xBase-1, yBase, xBase-1, yBase-height);

    // right ordinate
    painter.drawLine(xBase+256+1, yBase, xBase+256+1, yBase-height);
    painter.drawLine(xBase+256+2, yBase, xBase+256+2, yBase-height);

    // left ordinate arrow
    painter.drawLine(xBase, yBase-height, xBase+4, yBase-height+7);
    painter.drawLine(xBase-1, yBase-height, xBase-1-4, yBase-height+7);

    // right ordinate arrow
    painter.drawLine(xBase+256+1, yBase-height, xBase+256+1-4, yBase-height+7);
    painter.drawLine(xBase+256+2, yBase-height, xBase+256+2+4, yBase-height+7);
}



void Histogram::drawRedHstgrm(int xBase, int yBase, int height)
{
    QPainter painter(this);

    painter.setPen(Qt::darkRed);

    float max = bwHstgrm[256];

    if (max < redHstgrm[256])
        max = redHstgrm[256];

    if (max < greenHstgrm[256])
        max = greenHstgrm[256];

    if (max < blueHstgrm[256])
        max = blueHstgrm[256];

    // drawing the histogram
    for(int i = 0;i<256;i++)
    {
        painter.drawLine(xBase+1+i, yBase, xBase+1+i,
            yBase-(float)(256./max)*(float)redHstgrm[i]);
    }

    painter.drawText(xBase, yBase+25, tr("dark"));
    painter.drawText(xBase+225, yBase+25, tr("light"));

    painter.setPen(Qt::black);

    painter.drawText(xBase+25, yBase-height-10, tr("RED COMPONENT HISTOGRAM"));

    painter.drawText(xBase+100, yBase+15, tr("Intensity"));
    painter.drawText(xBase-84, yBase-height/2+5, tr("Pixels count"));

    // abscissa
    painter.drawLine(xBase, yBase, xBase+256+1, yBase);
    painter.drawLine(xBase, yBase+1, xBase+256+1, yBase+1);

    // left ordinate
    painter.drawLine(xBase, yBase, xBase, yBase-height);
    painter.drawLine(xBase-1, yBase, xBase-1, yBase-height);

    // right ordinate
    painter.drawLine(xBase+256+1, yBase, xBase+256+1, yBase-height);
    painter.drawLine(xBase+256+2, yBase, xBase+256+2, yBase-height);

    // left ordinate arrow
    painter.drawLine(xBase, yBase-height, xBase+4, yBase-height+7);
    painter.drawLine(xBase-1, yBase-height, xBase-1-4, yBase-height+7);

    // right ordinate arrow
    painter.drawLine(xBase+256+1, yBase-height, xBase+256+1-4, yBase-height+7);
    painter.drawLine(xBase+256+2, yBase-height, xBase+256+2+4, yBase-height+7);
}



void Histogram::drawGreenHstgrm(int xBase, int yBase, int height)
{
    QPainter painter(this);

    painter.setPen(Qt::darkGreen);

    float max = bwHstgrm[256];

    if (max < redHstgrm[256])
        max = redHstgrm[256];

    if (max < greenHstgrm[256])
        max = greenHstgrm[256];

    if (max < blueHstgrm[256])
        max = blueHstgrm[256];

    // drawing the histogram
    for(int i = 0;i<256;i++)
    {
        painter.drawLine(xBase+1+i, yBase, xBase+1+i,
            yBase-(float)(256./max)*(float)greenHstgrm[i]);
    }

    painter.drawText(xBase, yBase+25, tr("dark"));
    painter.drawText(xBase+225, yBase+25, tr("light"));

    painter.setPen(Qt::black);

    painter.drawText(xBase+15, yBase-height-10, tr("GREEN COMPONENT HISTOGRAM"));

    painter.drawText(xBase+100, yBase+15, tr("Intensity"));
    painter.drawText(xBase-84, yBase-height/2+5, tr("Pixels count"));

    // abscissa
    painter.drawLine(xBase, yBase, xBase+256+1, yBase);
    painter.drawLine(xBase, yBase+1, xBase+256+1, yBase+1);

    // left ordinate
    painter.drawLine(xBase, yBase, xBase, yBase-height);
    painter.drawLine(xBase-1, yBase, xBase-1, yBase-height);

    // right ordinate
    painter.drawLine(xBase+256+1, yBase, xBase+256+1, yBase-height);
    painter.drawLine(xBase+256+2, yBase, xBase+256+2, yBase-height);

    // left ordinate arrow
    painter.drawLine(xBase, yBase-height, xBase+4, yBase-height+7);
    painter.drawLine(xBase-1, yBase-height, xBase-1-4, yBase-height+7);

    // right ordinate arrow
    painter.drawLine(xBase+256+1, yBase-height, xBase+256+1-4, yBase-height+7);
    painter.drawLine(xBase+256+2, yBase-height, xBase+256+2+4, yBase-height+7);
}



void Histogram::drawBlueHstgrm(int xBase, int yBase, int height)
{
    QPainter painter(this);

    painter.setPen(Qt::darkBlue);

    float max = bwHstgrm[256];

    if (max < redHstgrm[256])
        max = redHstgrm[256];

    if (max < greenHstgrm[256])
        max = greenHstgrm[256];

    if (max < blueHstgrm[256])
        max = blueHstgrm[256];

    // drawing the histogram
    for(int i = 0;i<256;i++)
    {
        painter.drawLine(xBase+1+i, yBase, xBase+1+i,
            yBase-(float)(256./max)*(float)blueHstgrm[i]);
    }

    painter.drawText(xBase, yBase+25, tr("dark"));
    painter.drawText(xBase+225, yBase+25, tr("light"));

    painter.setPen(Qt::black);

    painter.drawText(xBase+20, yBase-height-10, tr("BLUE COMPONENT HISTOGRAM"));

    painter.drawText(xBase+100, yBase+15, tr("Intensity"));
    painter.drawText(xBase-84, yBase-height/2+5, tr("Pixels count"));

    // abscissa
    painter.drawLine(xBase, yBase, xBase+256+1, yBase);
    painter.drawLine(xBase, yBase+1, xBase+256+1, yBase+1);

    // left ordinate
    painter.drawLine(xBase, yBase, xBase, yBase-height);
    painter.drawLine(xBase-1, yBase, xBase-1, yBase-height);

    // right ordinate
    painter.drawLine(xBase+256+1, yBase, xBase+256+1, yBase-height);
    painter.drawLine(xBase+256+2, yBase, xBase+256+2, yBase-height);

    // left ordinate arrow
    painter.drawLine(xBase, yBase-height, xBase+4, yBase-height+7);
    painter.drawLine(xBase-1, yBase-height, xBase-1-4, yBase-height+7);

    // right ordinate arrow
    painter.drawLine(xBase+256+1, yBase-height, xBase+256+1-4, yBase-height+7);
    painter.drawLine(xBase+256+2, yBase-height, xBase+256+2+4, yBase-height+7);
}



int Histogram::getBwHstgrm(int index)
{
    if (index >= 0 && index <= 258)
        return bwHstgrm[index];
    else
        return -2;
}



int Histogram::getRedHstgrm(int index)
{
    if (index >= 0 && index <= 257)
        return redHstgrm[index];
    else
        return -2;
}



int Histogram::getGreenHstgrm(int index)
{
    if (index >= 0 && index <= 257)
        return greenHstgrm[index];
    else
        return -2;
}



int Histogram::getBlueHstgrm(int index)
{
    if (index >= 0 && index <= 257)
        return blueHstgrm[index];
    else
        return -2;
}

调用部分

主窗口中建立了一个Histogram菜单项(Action),以下是触发Action后的操作

/******************************************************************************
 *                           绘制图像直方图
 *****************************************************************************/
void MainWindow::on_actionHistogram_triggered()
{

    QDialog * hstgrmDialog = new QDialog(this);
    QScrollArea * scrollArea = new QScrollArea(hstgrmDialog);
    Histogram * hstgrm = new Histogram(scrollArea);
    hstgrm->computeHstgrm(rightImage->imageObject());

    if (hstgrm == NULL)
        return;


    scrollArea->setWidget(hstgrm);

    QHBoxLayout * layout = new QHBoxLayout;
    layout->addWidget(scrollArea);
    hstgrmDialog->setLayout(layout);

    hstgrm->resize(800, 780);
    hstgrmDialog->setFixedWidth(820);
    scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    scrollArea->adjustSize();

    hstgrmDialog->setWindowTitle("Histogram - ImageQt");

    hstgrmDialog->show();
}

效果图

[caption id="attachment_1667" align="aligncenter" width="848"] 直方图[/caption]

基于Qt的图像处理算法和实现——灰度图像、冷暖色调、亮度

由于学校的课程需要,开始学习基于Qt的图像处理,所有的代码已经公开在Github,这篇文章是《Qt 实现的一个图片查看器》的后续更新,由于代码量越来越大,以后的更新不在放出所有的代码,查看完整代码请移步Github。

Github项目地址:https://github.com/jiangjinteng/ImageQt

这篇文章主要内容包括灰度、温度、亮度

概述

Qt提供了四个图像QImage、QPixmap、QBitmap和QPicture,图像处理时比较常用的是QImage和QPixmap。

QPixmap继承了QPaintDevice,因此,你可以使用QPainter直接在上面绘制图形。QPixmap主要用于绘图,但是不方便访问和修改像素。QPixmap是针对屏幕进行特殊优化的,因此,它与实际的底层显示设备息息相关。注意,这里说的显示设备并不是硬件,而是操作系统提供的原生的绘图引擎。所以,在不同的操作系统平台下,QPixmap的显示可能会有所差别。

QImage在IO操作中有很快的速度,并且给出了访问像素的接口,有时我们也需要借助QPainter来完成某些操作,如后面的添加相框。这篇文章中将大量使用QImage。

 

QBitmap只是一个继承于QPixmap的简单类,它可以确保图片深度为1,所以说,QBitmap实际上是只有黑白两色的图像数据。QBitmap提供单色图像,可以用来制作游标(QCursor)或者笔刷(QBrush)。

QPicture是一个绘画设备类,它记录了并可以重演QPainter的命令。你可以使用QPainter的begin()方法,指定在QPicture上绘图,使用end()方法结束绘图,使用QPicture的save()方法將QPainter所使用过的绘图指令存至档案。要重播绘图指令的话,建立一個QPicture,使用load()方法载入绘图指令的档案,然后在指定的绘图裝置上绘制QPicture。

代码结构

在正式开始之前先介绍下我们的代码结构:

所有的图像处理函数在

tools.h

中声明,在

tools.cpp

中实现,

tools.h

中声明了一个新的命名空间 “Tools”。我们在

mainwindow.cpp

中调用这些图像处理的工具。

tools.h

的部分代码

#ifndef TOOLS_H
#define TOOLS_H

#include <QImage>
#include <QDebug>
#include <QPainter>

namespace Tools {
QImage GreyScale(QImage origin);
QImage Warm(int delta, QImage origin);
QImage Cool(int delta, QImage origin);
QImage DrawFrame(QImage origin, QImage &frame);
}

#endif // TOOLS_H

 

灰度图像

标准的灰度图就是每个像素点的三个通道的值一样或者近似,我们采用的策略是每个通道的值都取三个通道的平均值,比如原色是RGB(100,200,300), 那么最终的RGB就是(100+200+300)/3 = 200

严格意义上来说,我们不应该直接计算平均值。从RGB颜色转灰度值有一个著名的心理学公式:

$$Gray = R*0.299 + G*0.587 + B*0.114$$

对它的更多解释请移步我的这篇文章《基于Qt的图像处理算法和实现——线性灰度变

这个处理的实现:

QImage Tools::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;

}

下面是对 GreyScale() 的调用:

void MainWindow::on_actionGrayscale_triggered()
{   
    QImage newImage = Tools::GreyScale(rightImage->imageObject());
    QPixmap tmpPixmap = QPixmap::fromImage(newImage);

    rightImage->updateImage(newImage);
    rightImage->updatePixmap(tmpPixmap);

    repaintRightScene(tmpPixmap);

}

如上,第3行是正式的调用,其它代码是在屏幕上显示结果的相关处理。效果如下:

[caption id="attachment_1651" align="aligncenter" width="297"] 原始图[/caption]

[caption id="attachment_1652" align="aligncenter" width="297"] 灰度图[/caption]

亮度调节

白色用RGB(255,255,255)表示,黑色用RGB(0,0,0)表示,提高图像亮度即提高像素接近白色的程度,我们需要同时增加三个通道的数值,反之就是变暗。

[caption id="attachment_1662" align="aligncenter" width="560"] 原图[/caption]

[caption id="attachment_1661" align="aligncenter" width="560"] delta=30,调整后[/caption]

我们的函数接收一个QImage参数作为要调整亮度的图像,一个int值delta作为调整范围,正数为加亮,负数为变暗。该函数返回调整后的新图像。

/*****************************************************************************
 *                          Adjust image brightness
 * **************************************************************************/
QImage Tools::Brightness(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() + delta;

            // 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;
}

冷/暖色调

[caption id="attachment_1663" align="aligncenter" width="510"] 原图[/caption]

暖色调是比较复古的风格,视觉效果上比较偏黄,数据上,同时增加红色分量和绿色分量可以使图像“偏黄”

[caption id="attachment_1665" align="aligncenter" width="510"] 暖色调[/caption]

/*****************************************************************************
 *                           Adjust color temperature
 * **************************************************************************/
QImage Tools::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();
//            qDebug()<<r<<" "<<g<<""<<b;

            // 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;
}

和暖色调对应,冷色调即偏蓝色。我们可以只增加蓝色分量的值,不改变其他分量。

[caption id="attachment_1664" align="aligncenter" width="510"] 冷色调[/caption]

QImage Tools::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;

            // 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;
}

 

Qt 实现的一个图片查看器

为《数字图像处理》这门课程的上机实验写的小工具,后续会继续更新。当前功能只是对图片的查看以及放大、旋转等操作,其他界面上显示的功能都还是半成品。

2017年3月26日更新:代码已经开源在Github:https://github.com/jiangjinteng/ImageQt

功能说明

为了适应课程需要(图片处理),程序窗口分为左右两栏,右栏会实时显示图像处理的结果。

对于图像的缩放,添加了对鼠标滚轮的监听,可以通过鼠标滚轮对图像进行缩放。

使用 Qt 5.7.1 开发,测试环境为Fedora 25 with kernel 4.9

软件截图

窗口布局做了自适应

[caption id="attachment_1647" align="aligncenter" width="635"] 程序初始化[/caption]

[caption id="attachment_1648" align="aligncenter" width="635"] 图片显示及缩放[/caption]

源代码

#ifndef IMAGE_H
#define IMAGE_H

#include <QPixmap>
#include <QFile>
#include <QFileInfo>

class Image
{
private:
    QPixmap pixmap;
    QFileInfo *info;

public:
    Image(QString path);
    ~Image();

    void load(QString path);
    void save(QString path);

    QPixmap pixmapObject();
    QString path();         // /home/ttj/Pictures/logo.png
    QString name();         // logo.png
    QString baseName();     //logo
    QString extension();    //.png
    QString directory();    // /home/ttj/Pictures

    int width();
    int height();
};

#endif // IMAGE_H
#include "image.h"


Image::Image(QString path)
{
    pixmap.load(path);
    QFile file(path);
    info = new QFileInfo(file.fileName());
}

Image::~Image()
{

    if (info)
    {
        delete info;
        info = NULL;
    }
}

void Image::load(QString path)
{
    pixmap.load(path);
    QFile file(path);
    if (info)
    {
        delete info;
        info = new QFileInfo(file.fileName());
    }
}

void Image::save(QString path)
{
    pixmap.save(path, 0, 60);

}

QPixmap Image::pixmapObject()
{
    return pixmap;
}

QString Image::path()
{
    return info->absoluteFilePath();

}

QString Image::name()
{
    return info->fileName();
}

QString Image::baseName()
{
    return info->baseName();
}

QString Image::extension()
{
    return "." + info->suffix();
}

QString Image::directory()
{
    return info->path() + "/";
}

int Image::width()
{
    pixmap.width();
}

int Image::height()
{
    pixmap.height();
}
#ifndef GRAPHICSVIEW_H
#define GRAPHICSVIEW_H

#include <QGraphicsView>
#include <QWheelEvent>
#include <QtMath>

class GraphicsView : public QGraphicsView
{
private:
    int factor;

public:
    GraphicsView(QWidget* parent = 0);
    ~GraphicsView();

    int getFactor();
    void setFactor(int _factor);

protected:
    virtual void wheelEvent(QWheelEvent *e);
};

#endif // GRAPHICSVIEW_H
#include "graphicsview.h"

GraphicsView::GraphicsView(QWidget *parent) : QGraphicsView(parent)
{
    factor = 0;
}

GraphicsView::~GraphicsView()
{
    // to do
}

int GraphicsView::getFactor()
{
    return factor;
}

void GraphicsView::setFactor(int _factor)
{
    if (_factor = 0)
    {
        factor = 0;
    }
    else
    {
        factor += _factor;
    }
}

void GraphicsView::wheelEvent(QWheelEvent *e)
{
    // Calculate wheel movement
    int distance = e->delta()/15/8;
    qreal val;

    if (distance != 0)
    {
        val = pow(1.2, distance);
        this->scale(val, val);
        if (distance > 0)
        {
            factor ++;
        }
        else
        {
            factor --;
        }
    }
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QProcess>
#include <QFileDialog>
#include <QMessageBox>
#include <QInputDialog>
#include <QLabel>
#include <QDebug>
#include "image.h"
#include "graphicsview.h"

#define WINDOW_TITLE    "ImageQt"
#define WINDOW_CRITICAL "Error - ImageQt"
#define WINDOW_WARNING  "Notice - ImageQt"
#define WINDOW_ABOUT    "About - ImageQt"


namespace Ui {
    class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    void cleanImage();
    void setActionStatus(bool status);
    void adjustZoom();

private slots:
    void on_actionExit_triggered();

    void on_actionAdjust_triggered();

    void on_actionClose_triggered();

    void on_actionOpen_triggered(QString imagePath = "");

    void on_actionSave_As_triggered();

    void on_actionRestore_triggered();

    void on_actionRight_triggered();

    void on_actionLeft_triggered();

    void on_actionNormal_triggered();

    void on_actionAbout_triggered();

    void on_actionZoom_Out_triggered();

    void on_actionZoom_In_triggered();

private:
    Ui::MainWindow *ui;
    QGraphicsScene *leftScene;
    QGraphicsScene *rightScene;
    Image *image;
    Image *rightImage;
    QLabel *size, *zoom;
};

#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"



MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QVBoxLayout *mainWindowLayout = new QVBoxLayout;
    setLayout(mainWindowLayout);

    leftScene = new QGraphicsScene;
    rightScene = new QGraphicsScene;
    image = NULL;
    rightImage = NULL;
    size = new QLabel;
    zoom = new QLabel;

    leftScene->setBackgroundBrush(QColor::fromRgb(224,224,224));
    ui->leftGraphicsView->setScene(leftScene);
    rightScene->setBackgroundBrush(QColor::fromRgb(224,224,224));
    ui->rightGraphicsView->setScene(leftScene);

    ui->statusBar->addPermanentWidget(size);
    ui->statusBar->addWidget(zoom);

    connect(ui->openBtn, SIGNAL(clicked(bool)),
            this, SLOT(on_actionOpen_triggered()));
    connect(ui->closeBtn, SIGNAL(clicked(bool)),
            this, SLOT(on_actionClose_triggered()));
    connect(ui->saveAsBtn, SIGNAL(clicked(bool)),
            this, SLOT(on_actionSave_As_triggered()));

    connect(ui->zoomInBtn, SIGNAL(clicked(bool)),
            this, SLOT(on_actionZoom_In_triggered()));
    connect(ui->zoomOutBtn, SIGNAL(clicked(bool)),
            this, SLOT(on_actionZoom_Out_triggered()));

    connect(ui->normalBtn, SIGNAL(clicked()),
            this, SLOT(on_actionNormal_triggered()));

    ui->menuZoom->setIcon(QIcon(":/img/src/edit-find.png"));
    setActionStatus(false);
    setWindowTitle("ImageQt");

}

MainWindow::~MainWindow()
{
    delete ui;

    if (leftScene)
    {
        delete leftScene;
        leftScene = NULL;
    }
    if (image)
    {
        delete image;
        image = NULL;

    }
    if (size)
    {
        delete size;
        size = NULL;
    }

    if (zoom)
    {
        delete zoom;
        zoom = NULL;
    }
}

/******************************************************************************
 *                        Exit the program
 *
 *****************************************************************************/
void MainWindow::on_actionExit_triggered()
{
    qApp->quit();
}



void MainWindow::setActionStatus(bool status)
{
    ui->actionSave->setEnabled(status);
    ui->saveAsBtn->setEnabled(status);
    ui->closeBtn->setEnabled(status);
    ui->zoomInBtn->setEnabled(status);
    ui->zoomOutBtn->setEnabled(status);
    ui->normalBtn->setEnabled(status);
    ui->actionSave_As->setEnabled(status);
}

/******************************************************************************
 *                      Clean image of both Scene
 *
 *****************************************************************************/
void MainWindow::cleanImage()
{
    if (leftScene)
    {
        delete leftScene;
        leftScene = new QGraphicsScene;
        leftScene->setBackgroundBrush(QColor::fromRgb(224,224,224));
        ui->leftGraphicsView->setScene(leftScene);
    }
    if (rightScene)
    {
        delete rightScene;
        rightScene = new QGraphicsScene;
        rightScene->setBackgroundBrush(QColor::fromRgb(224,224,224));
        ui->rightGraphicsView->setScene(rightScene);
    }

    if (image)
    {
        delete image;
        image = NULL;
    }
    if (rightImage)
    {
        delete rightImage;
        rightImage = NULL;
    }

    if (size)
    {
        delete size;
        size = new QLabel;
        ui->statusBar->addPermanentWidget(size);
    }

    if (zoom)
    {
        delete zoom;
        zoom = new QLabel;
        ui->statusBar->addWidget(zoom);
    }

    this->setWindowTitle(WINDOW_TITLE);
    ui->leftGraphicsView->resetTransform();
    ui->rightGraphicsView->resetTransform();
    setActionStatus(false);

}

/******************************************************************************
 *                  Adjust the image size to fit the window
 *
 *****************************************************************************/
void MainWindow::on_actionAdjust_triggered()
{
    // left
    int height = image->height();
    int width = image->width();
    int max_height = ui->leftGraphicsView->height();
    int max_width = ui->leftGraphicsView->width();
    int size,max_size,fact=0;
    double val=0;


    size = qMin(width,height);
    max_size = qMin(max_width,max_height);


    if (size < max_size) {
        while ((size*val) < max_size)
            val = pow(1.2,fact++);
        val = pow(1.2,fact-2);
        ui->leftGraphicsView->setFactor(fact-2);
    }

    else {
        val = 1;
        while ((size*val) > max_size)
            val = pow(1.2,fact--);
        val = pow(1.2,fact+1);
        ui->leftGraphicsView->setFactor(fact+1);
    }

    ui->leftGraphicsView->scale(val,val);


    // right
    height = rightImage->height();
    width = rightImage->width();
    max_height = ui->rightGraphicsView->height();
    max_width = ui->rightGraphicsView->width();
    size = max_size = fact = 0;
    val=0;


    size = qMin(width,height);
    max_size = qMin(max_width,max_height);


    if (size < max_size) {
        while ((size*val) < max_size)
            val = pow(1.2,fact++);
        val = pow(1.2,fact-2);
        ui->rightGraphicsView->setFactor(fact-2);
    }

    else {
        val = 1;
        while ((size*val) > max_size)
            val = pow(1.2,fact--);
        val = pow(1.2,fact+1);
        ui->rightGraphicsView->setFactor(fact+1);
    }

    ui->rightGraphicsView->scale(val,val);


    adjustZoom();
}

/******************************************************************************
 *                            Adjust zoom
 *
 *****************************************************************************/
void MainWindow::adjustZoom()
{
    int num = 100*pow(1.2,ui->rightGraphicsView->getFactor());
    QString percentage = QString::number(num);
    zoom->setText(percentage + "%");
}




/******************************************************************************
 *                      Clean image of both Scene
 *
 *****************************************************************************/
void MainWindow::on_actionClose_triggered()
{
    cleanImage();
}




/******************************************************************************
 *                      Open a image file and show it
 ******************************************************************************
 * Args:
 *      QString imagePath: The abslute path of a image
 *****************************************************************************/
void MainWindow::on_actionOpen_triggered(QString imagePath)
{
    if(imagePath.isEmpty())
    {
        // open path and select image type
        imagePath = QFileDialog::getOpenFileName(this, tr("Open image"), QString(),
                                                 tr("All Files (*);;"
                                                    "All Images (*.bpm *.gif *.jpg *.jpeg *.png *.ppm *.xbm *.xpm);;"
                                                    "Image BPM (*.bpm);;"
                                                    "Image GIF (*.gif);;"
                                                    "Image JPG (*.jpg);;"
                                                    "Image JPEG (*.jpeg);;"
                                                    "Image PNG (*.png);;"
                                                    "Image PPM (*.ppm);;"
                                                    "Image XBM (*.xbm);;"
                                                    "Image XPM (*.xpm);;"));
    }

    if (!imagePath.isEmpty())
    {
        QFile file(imagePath);
        if (!file.open(QIODevice::ReadOnly)) {
            QMessageBox::critical(this, tr(WINDOW_CRITICAL),
                                  tr("Unable to open image."));
            return;
        }

        // delete previous image
        cleanImage();

        // upload image
        image = new Image(imagePath);


        rightImage = new Image(imagePath);

        leftScene->addPixmap(image->pixmapObject());

        rightScene->addPixmap(rightImage->pixmapObject());


        // settings
        this->setWindowTitle(image->name() + " - ImageQt");

        setActionStatus(true);

        size->setText(QString::number(image->width())
                      + " x " + QString::number(image->height()));
        if (qMax(image->width(),
                 image->height()) > qMin(ui->leftGraphicsView->width(),
                                         ui->leftGraphicsView->height()))
            on_actionAdjust_triggered();
        else
            adjustZoom();

    }
}




/******************************************************************************
 *                          Action : Save as
 *****************************************************************************/
void MainWindow::on_actionSave_As_triggered()
{
    QString newPath = QFileDialog::getSaveFileName(this, tr("Save image"), QString(),
            tr("All files (*);;"
               "Image BPM (*.bpm);;"
               "Image GIF (*.gif);;"
               "Image JPG (*.jpg);;"
               "Image JPEG (*.jpeg);;"
               "Image PNG (*.png);;"
               "Image PPM (*.ppm);;"
               "Image XBM (*.xbm);;"
               "Image XPM (*.xpm);;"));

    if (!newPath.isEmpty()) {

        QFile file(newPath);
        if (!file.open(QIODevice::WriteOnly)) {
            QMessageBox::critical(this, tr(WINDOW_CRITICAL), tr("Unable to save image."));
            return;
        }

        //Save image to new path
        image->save(newPath);
    }
}



/******************************************************************************
 *                   Restore the image, both size and rotate
 *****************************************************************************/
void MainWindow::on_actionRestore_triggered()
{
    ui->rightGraphicsView->resetTransform();
    ui->rightGraphicsView->setFactor(0);

    ui->leftGraphicsView->resetTransform();
    ui->leftGraphicsView->setFactor(0);

    adjustZoom();
}


/******************************************************************************
 *                              Rotate Right
 *****************************************************************************/
void MainWindow::on_actionRight_triggered()
{
    ui->rightGraphicsView->rotate(90);
}





/******************************************************************************
 *                              Rotate Left
 *****************************************************************************/
void MainWindow::on_actionLeft_triggered()
{
    ui->rightGraphicsView->rotate(-90);
}


/******************************************************************************
 *                              To do
 *****************************************************************************/
void MainWindow::on_actionNormal_triggered()
{
    qreal val = pow(1.2, -ui->rightGraphicsView->getFactor());
    ui->rightGraphicsView->scale(val,val);
    ui->rightGraphicsView->setFactor(0);
    adjustZoom();
}



/******************************************************************************
 *                              Dialog: About
 *****************************************************************************/
void MainWindow::on_actionAbout_triggered()
{
    QMessageBox::about(this, tr(WINDOW_ABOUT), tr("ImageQt for Linux. Powered by Qt.\n"
                                                  "<h3>Author:</h3>\nLu Hongli\n"
                                                  "Liu Zheng\n"
                                                  "Ou Hanyang\n"));
}

/******************************************************************************
 *                              Action: Zoom Out
 *****************************************************************************/
void MainWindow::on_actionZoom_Out_triggered()
{
    ui->rightGraphicsView->scale(1/1.2,1/1.2);
    ui->rightGraphicsView->setFactor(-1);
    adjustZoom();
}
/******************************************************************************
 *                              Action: Zoom In
 *****************************************************************************/
void MainWindow::on_actionZoom_In_triggered()
{
    ui->rightGraphicsView->scale(1.2, 1.2);
    ui->rightGraphicsView->setFactor(-1);
    adjustZoom();
}
#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

 

RAID种类对比及软RAID制作过程

一、前言

常用的RAID存储类别主要包括RAID0、RAID1、RAID5、RAID6,下面给出详细的RAID原理和软RAID制作过程。

二、RAID类别

1、RAID0

图1 RAID0存储方式图

如图1所示,RAID0最少使用两块硬盘,在读写是将数据分成硬盘个数的分数,分别在每个硬盘上进行读写,通过这种方式来提高读写性能。

空间利用率:所有空间的和(空间利用率最高的一种方式)。

性能:所有硬盘速度之和。

冗余性:没有冗余能力。

2、RAID1

图2 RAID1存储方式图

RAID1最少使用两块盘硬盘,在读写数据时,将数据复制到多块硬盘上,每个硬盘备份一份,在读数据时,大大提高到了数据的冗余性。同时从多块盘读取数据,以提高读性能。

空间利用率:所有磁盘当中最下的一块。

性能:读性能是所有性能之和,写性能会有所减弱。

冗余性:只要有一块硬盘正常,数据就正常。

3、RAID5

图3 RAID5存储方式图

RAID5少使用三块盘,raid5和raid0类似,在写数据的时候,会分别将数据写到所有硬盘上,但是在写数据的同时会进行奇偶校验运算,将校验后的信息同时保存到硬盘上,校验的信息可以用于恢复数据。

空间利用率:1-1/n(n为磁盘的个数)。

性能:读性能接近于raid0,写性能有比raid0弱一些。

冗余能力:可接受一块磁盘损坏。

4、RAID6

图4 RAID6存储方式图

RAID6最少使用四块盘,raid6和raid5相似,读写数据的时候讲数据分布式的读写在所有硬盘上,并保存奇偶性校验信息,但是会比raid5多保存一份校验信息,所有冗余性较raid5有所提升。

空间利用率:1-2/n(n磁盘个数)。

性能:读写接近raid5,读写性能比raid5还要弱一些。

冗余能力:可接受两块盘损坏。

三、软RAID的制作

1、RAID的创建

创建命令:mdadm

-C 创建一个新的RAID

-a 自动创建对应设备

-l  指定要创建的RAID级别

-n  指定要创建的磁盘

-x  指定备份磁盘

#创建RAID0
mdadm –C  /dev/md0 –a yes –l 0 –n 2 /dev/sdb  /dev/sdc
#创建RAID1
mdadm –C  /dev/md0 –a yes –l 1 –n 2 /dev/sdb  /dev/sdc
#创建RAID5
mdadm –C  /dev/md0 –a yes –l 5 –n 3 /dev/sdb  /dev/sdc  /dev/sdd
#创建RAID6
mdadm –C  /dev/md0 –a yes –l 6 –n 4 /dev/sdb  /dev/sdc  /dev/sdd  /dev/sde
#创建RAID5并指定一块备份磁盘,当有磁盘坏掉时,它自动补上。
mdadm –C  /dev/md0 –a yes –l 5 –n 3 –x 1 /dev/sdb  /dev/sdc  /dev/sdd  /dev/sde

 

2、保存配置

 

#创建好以后,若要保存当前所创建的RAID,修改配置文件:
mdadm –C –scan > /etc/mdadm.conf

 

3、安装文件系统并挂载

以ext4为例

 

mkfs.ext4 /dev/md0              #格式化所创建的磁盘
mount /dev/md0  /mnt/       #将/mnt/目录挂载到该磁盘

 

4、RAID的管理

#查看RAID信息

 

mdadm –D /dev/md0
cat /proc/mdstat

 

#RAID的开启和关闭

 

mdadm –S /dev/md0            #关闭对应的RAID(关闭前先卸载目录)
mdadm –R /dev/md0           #重启对应的RAID

 

#清除RAID信息

 

mdadm –zero-superblock  /dev/sdb
mdadm –zero-superblock  /dev/sdb

 

#替换RAID坏掉的磁盘

 

mdadm /dev/md0 –f  /dev/sdb    #指定这块盘为坏掉的磁盘
mdadm /dev/md0 –r  /dev/sdb    #将这块磁盘移除
mdadm /dev/md0 –a  /dev/sdb   #重新添加到RAID当中