基於qml創建最簡單的圖像處理程序(2)-使用c++&qml進行圖像處理


 《基於qml創建最簡單的圖像處理程序》系列課程及配套代碼
基於qml創建最簡單的圖像處理程序(1)-基於qml創建界面
http://www.cnblogs.com/jsxyhelu/p/8343310.html
課程1附件
https://files.cnblogs.com/files/jsxyhelu/%E9%98%B6%E6%AE%B5%E4%BB%A3%E7%A0%811.zip
基於qml創建最簡單的圖像處理程序(2)-使用c++&qml進行圖像處理
http://www.cnblogs.com/jsxyhelu/p/8361441.html
課程2附件
https://files.cnblogs.com/files/jsxyhelu/%E9%98%B6%E6%AE%B5%E4%BB%A3%E7%A0%812.zip
基於qml創建最簡單的圖像處理程序(3)-使用opencv&qml進行圖像處理
http://www.cnblogs.com/jsxyhelu/p/8361443.html
課程3附件
https://files.cnblogs.com/files/jsxyhelu/%E9%98%B6%E6%AE%B5%E4%BB%A3%E7%A0%813.zip
 
            qml實現了不錯的界面,但是圖像處理這塊不是它強項,它能夠提供的就是qimage這樣的圖像,對像素的處理就是對圖像的處理,而最簡單、最直接的方法就是使用c++的代碼進行像素處理。
        這里就涉及到 c++&qml 聯合程序設計的具體內容了。這方面涉及到的東西有點多,首先我們需要解決的是qml和c++的聯合問題。當然我們在搭建這個框架的時候,可能會有比較多一些的問題—另一個方面,當框架搭建好了以后,使用起來會比較方便。
一、編寫按鈕觸發事件
         現有的例子,已經實現了qml的界面,並且能夠打開、顯示一個新的圖像;我們甚至為后面的圖像處理預留了幾個按鈕的位置,下面我們首先就是要觸發這些按鈕的事件。
  //灰度效果d
        Button {
            text : "灰度";
            style : btnStyle;
            onPressedChanged : {
                busy.running = true;
              //  processor.process(fileDialog.fileUrl, ImageProcessor.Gray);
            }
        }
        //浮雕效果
        Button {
            text : "浮雕";
            style : btnStyle;
            onPressedChanged : {
                busy.running = true;
              //  processor.process(fileDialog.fileUrl, ImageProcessor.Emboss);
            }
        }
        //黑白效果
        Button {
            text : "黑白";
            style : btnStyle;
            onPressedChanged : {
                busy.running = true;
              //  processor.process(fileDialog.fileUrl, ImageProcessor.Binarize);
            }
        }
   
這個地方有一個小細節,那就是
onPressedChanged()
onClick()
的區別。這兩者表示的都是“按下”這個事件,他們的不同在於onClick()要多了一個“按下后抬起”的動作。所以一般來說,對於桌面運用,click用的多一點,而對於Android, onPressedChanged多一點。
 
  二、觸發圖像處理算法
         我們可以看到上面代碼中,每一種處理后面,都有一行注釋,調用的是processor對象的一個函數,那么這個函數是從哪里來的了?它的定義來自這里
 
    //需要特別注意,這個組件來自imageprocessor這個類,通過這種方法進行集成
    ImageProcessor {
        id : processor;
        onFinished : {
            imageViewer.source = "file:///" +newFile;
        }
    }
它的意思是processor對象是ImageProcessor類的一個實例,那么ImageProcessor又是從哪里來的了?
它的定義來自於
//將c++中定義的類給接過來,在這個定義中,ImageProcessor是c++中函數名,GO.ImageProcessor是你在qml中使用的名稱
qmlRegisterType <ImageProcessor >( "GO.ImageProcessor", 1, 0, "ImageProcessor");
 
其中qmlRegisterType是類的引入,類似dllimport之類,而
< ImageProcessor >
"ImageProcessor"
一般來說,都是來自於你的c++的函數名稱;而
"GO.ImageProcessor"
是你在qml中的頭文件.
 
此外,1,0是你的版本號。
 
所以這個地方有兩個頭文件,在main.cpp中,引入
 
# include "imageProcessor.h"
而在 qml中,這樣引入
 
import GO.ImageProcessor 1. 0
 
那么我們在這里主要討論的就是如何從c++中將行數引接過來的。
 
 三 、C++中具體算法實現
         那我們就來看c++類中的 ImageProcessor  是如何實現的,它的代碼地址來自:
 
 
它的原型為:
void process(QString sourceFile, ImageProcessor : :ImageAlgorithm algorithm)
{
QFileInfo fi(sourceFile);
QString destFile = QString( "%1/%2_%3").arg(m_tempPath).arg(( int)algorithm).arg(fi.fileName());
AlgorithmRunnable *r = new AlgorithmRunnable(sourceFile,destFile,algorithm, this);
m_runnables.append(r);
r - >setAutoDelete( false);
QThreadPool : :globalInstance() - >start(r);
}
 
它設定了一系列參數,主要是創建了AlgorithmRunnalbe,然后通過 QThreadPool 打開新線程運行算法。那么主要的函數體在 AlgorithmRunnable 中的。由於涉及到較多的具體內容,這里不展開說明,大家可以參考全部代碼。
我們關注的是圖像處理函數,在這個項目中實現了以下內容:
 
//具體的圖像處理算法,注意圖片處理的結果直接保存到了destFile中去//
static void _gray(QString sourceFile, QString destFile)
{
    QImage image(sourceFile);
    if(image.isNull())
    {
        qDebug() << "load " << sourceFile << " failed! ";
        return;
    }
    qDebug() << "depth - " << image.depth();

    int width = image.width();
    int height = image.height();
    QRgb color;
    int gray;
    for( int i = 0; i < width; i ++)
    {
        for( int j = 0; j < height; j ++)
        {
            color = image.pixel(i, j);
            gray = qGray(color);
            image.setPixel(i, j, qRgba(gray, gray, gray, qAlpha(color)));
        }
    }

    image.save(destFile);
}

static void _binarize(QString sourceFile, QString destFile)
{
    QImage image(sourceFile);
    if(image.isNull())
    {
        qDebug() << "load " << sourceFile << " failed! ";
        return;
    }
    int width = image.width();
    int height = image.height();
    QRgb color;
    QRgb avg;
    QRgb black = qRgb( 0, 0, 0);
    QRgb white = qRgb( 255, 255, 255);
    for( int i = 0; i < width; i ++)
    {
        for( int j = 0; j < height; j ++)
        {
            color = image.pixel(i, j);
            avg = (qRed(color) + qGreen(color) + qBlue(color)) / 3;
            image.setPixel(i, j, avg > = 128 ? white : black);
        }
    }
    image.save(destFile);
}

static void _emboss(QString sourceFile, QString destFile)
{
    QImage image(sourceFile);
    if(image.isNull())
    {
        qDebug() << "load " << sourceFile << " failed! ";
        return;
    }
    int width = image.width();
    int height = image.height();
    QRgb color;
    QRgb preColor = 0;
    QRgb newColor;
    int gray, r, g, b, a;
    for( int i = 0; i < width; i ++)
    {
        for( int j = 0; j < height; j ++)
        {
            color = image.pixel(i, j);
            r = qRed(color) - qRed(preColor) + 128;
            g = qGreen(color) - qGreen(preColor) + 128;
            b = qBlue(color) - qBlue(preColor) + 128;
            a = qAlpha(color);
            gray = qGray(r, g, b);
            newColor = qRgba(gray, gray, gray, a);
            image.setPixel(i, j, newColor);
            preColor = newColor;
        }
    }
    image.save(destFile);
}
//END 具體的圖像處理算法,注意圖片處理的結果直接保存到了destFile中去//
 
 
甚至不用去看算法的實現(因為最后我們肯定是要使用OpenCV來做圖像處理的),這3個圖像處理的函數都是非常統一的
 
static  void _binarize(QString sourceFile, QString destFile)
 
比如返回類型為void,第一個參數為輸入圖像,第二個參數為輸出圖片。從這里也可以看出,在我們這個項目中,是通過地址(而不是內存)來傳遞數據的。如果我們想要添加新的算法,修改現有算法,直接修改這幾個函數、添加相關界面就可以。
 
而下一步,最關鍵的一步,也是原書例子沒有實現的,就是添加OpenCV的代碼,使用它來替代進行圖像處理。
 
 





附件列表

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM