koffice/krita/image

Dmitry Kazakov dimula73 at gmail.com
Sun Nov 14 10:22:30 CET 2010


SVN commit 1196807 by dkazakov:

Added a benchmark for KisConvolutionPainter

Adjusted the workers as well. Now the type of the worker depends not
the available engine only, but on the size of the kernel as well. Even
from theoretical perspective, KisConvolutionWorker is faster when the
size of the kernel is less than 5x5px.

Some conclusions from benchmarking the FFTW convolution worker:
1) FFT and IFFT of the image take 30% of the time each.
2) FFT of the kernel (even though the kernel has the same size as the
   image, but it is very sparse) takes 7.5% only.
3) Creation of a plan for FFT and IFFT take 4% each.

4) There is a possibility to make the worker faster. That is to use
   the "wisdom" (FFTW_MEASURE flag) recomended by FFTW3
   documentation. In case of activation of this flag the convolution
   becomes about 20% faster. But the use of this flag will make us
   change our system a bit: we will have to use descrete sizes of
   the convolution areas and we will need to save these "words of
   wisdom" somewhere.

CCMAIL:kimageshop at kde.org


 M  +30 -16    kis_convolution_painter.cc  
 M  +54 -6     tests/kis_convolution_painter_test.cpp  
 M  +1 -0      tests/kis_convolution_painter_test.h  


--- trunk/koffice/krita/image/kis_convolution_painter.cc #1196806:1196807
@@ -66,6 +66,33 @@
 #endif
 
 
+template<class factory>
+KisConvolutionWorker<factory>* createWorker(const KisConvolutionKernelSP kernel,
+                                           KisPainter *painter,
+                                           KoUpdater *progress)
+{
+    KisConvolutionWorker<factory> *worker;
+
+#ifdef HAVE_FFTW3
+    #define THRESHOLD_SIZE 5
+
+    if(kernel->width() <= THRESHOLD_SIZE &&
+       kernel->height() <= THRESHOLD_SIZE) {
+
+        worker = new KisConvolutionWorkerSpatial<factory>(painter, progress);
+    }
+    else {
+        worker = new KisConvolutionWorkerFFT<factory>(painter, progress);
+    }
+#else
+    Q_UNUSED(kernel);
+    worker = new KisConvolutionWorkerSpatial<factory>(painter, progress);
+#endif
+
+    return worker;
+}
+
+
 KisConvolutionPainter::KisConvolutionPainter()
         : KisPainter()
 {
@@ -81,7 +108,6 @@
 {
 }
 
-
 void KisConvolutionPainter::applyMatrix(const KisConvolutionKernelSP kernel, const KisPaintDeviceSP src, QPoint srcPos, QPoint dstPos, QSize areaSize, KisConvolutionBorderOp borderOp)
 {
 
@@ -104,11 +130,7 @@
 
         if(dataRect.isValid()) {
             KisConvolutionWorker<RepeatIteratorFactory> *worker;
-            #ifdef HAVE_FFTW3
-            worker = new KisConvolutionWorkerFFT<RepeatIteratorFactory>(this, progressUpdater());
-            #else
-            worker = new KisConvolutionWorkerSpatial<RepeatIteratorFactory>(this, progressUpdater());
-            #endif
+            worker = createWorker<RepeatIteratorFactory>(kernel, this, progressUpdater());
             worker->execute(kernel, src, srcPos, dstPos, areaSize, dataRect);
             delete worker;
         }
@@ -117,11 +139,7 @@
     return;
     case BORDER_DEFAULT_FILL : {
         KisConvolutionWorker<StandardIteratorFactory> *worker;
-        #ifdef HAVE_FFTW3
-        worker = new KisConvolutionWorkerFFT<StandardIteratorFactory>(this, progressUpdater());
-        #else
-        worker = new KisConvolutionWorkerSpatial<StandardIteratorFactory>(this, progressUpdater());
-        #endif
+        worker = createWorker<StandardIteratorFactory>(kernel, this, progressUpdater());
         worker->execute(kernel, src, srcPos, dstPos, areaSize, QRect());
         delete worker;
         break;
@@ -140,11 +158,7 @@
         areaSize -= QSize(kw - 1, kh - 1);
 
         KisConvolutionWorker<StandardIteratorFactory> *worker;
-        #ifdef HAVE_FFTW3
-        worker = new KisConvolutionWorkerFFT<StandardIteratorFactory>(this, progressUpdater());
-        #else
-        worker = new KisConvolutionWorkerSpatial<StandardIteratorFactory>(this, progressUpdater());
-        #endif
+        worker = createWorker<StandardIteratorFactory>(kernel, this, progressUpdater());
         worker->execute(kernel, src, srcPos, dstPos, areaSize, QRect());
         delete worker;
     }
--- trunk/koffice/krita/image/tests/kis_convolution_painter_test.cpp #1196806:1196807
@@ -55,7 +55,7 @@
     return dev;
 }
 
-Matrix<qreal, 3, 3> initSymmFilter(qreal &factor)
+Matrix<qreal, 3, 3> initSymmFilter(qreal &offset, qreal &factor)
 {
     Matrix<qreal, 3, 3> filter;
     filter(0,0) = 1.0 / 21;
@@ -70,12 +70,13 @@
     filter(2,1) = 3.0 / 21;
     filter(2,2) = 1.0 / 21;
 
+    offset = 0.0;
     factor = 1.0;
 
     return filter;
 }
 
-Matrix<qreal, 3, 3> initAsymmFilter(qreal &factor)
+Matrix<qreal, 3, 3> initAsymmFilter(qreal &offset, qreal &factor)
 {
     Matrix<qreal, 3, 3> filter;
     filter(0,0) = -1.0;
@@ -90,6 +91,7 @@
     filter(1,2) = 2.0;
     filter(2,2) = 1.0;
 
+    offset = 0.5;
     factor = 1.0;
 
     return filter;
@@ -139,8 +141,9 @@
 
 void KisConvolutionPainterTest::testSymmConvolution()
 {
+    qreal offset = 0.0;
     qreal factor = 1.0;
-    Matrix<qreal, 3, 3> filter = initSymmFilter(factor);
+    Matrix<qreal, 3, 3> filter = initSymmFilter(offset, factor);
 
     QRect imageRect;
     int pixelSize = 0;
@@ -149,7 +152,7 @@
 
 
     KisConvolutionKernelSP kernel =
-        KisConvolutionKernel::fromMatrix(filter, 0, factor);
+        KisConvolutionKernel::fromMatrix(filter, offset, factor);
     KisConvolutionPainter gc(dev);
     gc.beginTransaction("");
     gc.applyMatrix(kernel, dev, imageRect.topLeft(), imageRect.topLeft(),
@@ -164,8 +167,9 @@
 
 void KisConvolutionPainterTest::testAsymmConvolutionImp(QBitArray channelFlags)
 {
+    qreal offset = 0.0;
     qreal factor = 1.0;
-    Matrix<qreal, 3, 3> filter = initAsymmFilter(factor);
+    Matrix<qreal, 3, 3> filter = initAsymmFilter(offset, factor);
 
     QRect imageRect;
     int pixelSize = -1;
@@ -174,7 +178,7 @@
 
 
     KisConvolutionKernelSP kernel =
-        KisConvolutionKernel::fromMatrix(filter, 0.5, factor);
+        KisConvolutionKernel::fromMatrix(filter, offset, factor);
     KisConvolutionPainter gc(dev);
     gc.beginTransaction("");
     gc.setChannelFlags(channelFlags);
@@ -258,5 +262,49 @@
     testAsymmConvolutionImp(channelFlags);
 }
 
+
+// #include <valgrind/callgrind.h>
+void KisConvolutionPainterTest::benchmarkConvolution()
+{
+    QImage referenceImage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
+    QRect imageRect(QPoint(), referenceImage.size());
+
+    KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
+    dev->convertFromQImage(referenceImage, "", 0, 0);
+
+    qreal offset = 0.0;
+    qreal factor = 1.0;
+    Matrix<qreal, 3, 3> filter = initAsymmFilter(offset, factor);
+
+    int diameter = 1;
+
+    for (int i = 0; i < 10; i++) {
+
+        KisCircleMaskGenerator* kas = new KisCircleMaskGenerator(diameter, 1.0, 5, 5, 2);
+        KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMaskGenerator(kas);
+
+        KisConvolutionPainter gc(dev);
+
+        QTime timer; timer.start();
+
+        // CALLGRIND_START_INSTRUMENTATION;
+
+        gc.beginTransaction("");
+        gc.applyMatrix(kernel, dev, imageRect.topLeft(), imageRect.topLeft(),
+                       imageRect.size());
+        gc.deleteTransaction();
+
+        // CALLGRIND_STOP_INSTRUMENTATION;
+
+        qDebug() << "Diameter:" << diameter << "time:" << timer.elapsed();
+
+        if(diameter < 10) {
+            diameter += 2;
+        } else {
+            diameter += 8;
+        }
+    }
+}
+
 QTEST_KDEMAIN(KisConvolutionPainterTest, GUI)
 #include "kis_convolution_painter_test.moc"
--- trunk/koffice/krita/image/tests/kis_convolution_painter_test.h #1196806:1196807
@@ -41,6 +41,7 @@
     void testAsymmSkipBlue();
     void testAsymmSkipAlpha();
 
+    void benchmarkConvolution();
 };
 
 #endif


More information about the kimageshop mailing list