《基於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創建最簡單的圖像處理程序(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);
}
}
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;
}
}
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 <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);
}
{
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中去//
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的代碼,使用它來替代進行圖像處理。
附件列表