Parallel.For Schleife und Sperren



  • Okay. Musst bedenken, dass das aus einer bestehenden Bibliothek herausgerissen wurde und deshalb etwas umfangreicher ist als für die konkreten Beispielfilter notwendig. Dafür aber erweiterbar.

    Ich poste mal stückchenweise.

    Kleine Testform:
    Es wird ein Bild geladen und dann erst ein Invertierungsfilter und danach ein Glättungsfilter angewandt. Im Beispiel werden so viele Threads verwendet, wie Kerne zur Verfügung stehen

    //Form1.cs

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    
    using ImageLib.Filter.Basic;
    using ImageLib.Filter.Smoothing;
    using ImageLib.Base;
    
    namespace fastimaging
    {
        public partial class Form1 : Form
        {
            PictureBox pb;
    
            public Form1()
            {
                InitializeComponent();
    
                Load += new EventHandler(Form1_Load);
                pb = new PictureBox();
                pb.SizeMode = PictureBoxSizeMode.Zoom;
                pb.Dock = DockStyle.Fill;
    
                Controls.Add(pb);
            }
    
            void Form1_Load(object sender, EventArgs e)
            {
                Bitmap inBitmap = (Bitmap)Image.FromFile(@"C:\xxxxxxxxxx.jpg");
                Rectangle rec = new Rectangle(0, 0, inBitmap.Width, inBitmap.Height);
    
                //Lastverteilung auf so viele Threads, wie Kerne zur Verfügung stehen
                int coreCount = Environment.ProcessorCount;
    
                //Invertierungsfilter
                PixelFilterSingleSource inv = new Invert(ImageLib.Enums.FilterWorkPlace.InBitmap);
                //Gaussscher Glättungsfilter
                PixelFilterSingleSource gauss = new Gaussian(10);
    
                inv.Apply(inBitmap, rec, coreCount);
                pb.Image = gauss.Apply(inBitmap, rec, coreCount);
            }
        }
    }
    


  • Hilfsklassen für das Threading.
    ThreadPoolDispatcher verteilt auf #coreCnt viele Kerne Aufgaben, die von außem mit dem Action<int> Delegat angegeben werden.
    Asynchrone Delegate bedienen sich im Threadpool -> Schnell

    ThreadBarrier ist notwendig um die Berechnungen bei manchen Bildfiltern zu synchronisieren.

    //Parallel.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    
    namespace ImageLib.InternalUtils.Parallel
    {
        internal static class SimpleThreadDispatcherFactory
        {
            public static IThreadDispatcher Create()
            {
                return new ThreadPoolDispatcher();
            }
        }
    
        internal interface IThreadDispatcher
        {
            void Do(int coreCnt, Action<int> indexedAction);
        }
    
        internal class ThreadPoolDispatcher : IThreadDispatcher
        {
            public void Do(int coreCnt, Action<int> indexedAction)
            {
                Action[] action = new Action[coreCnt];
                IAsyncResult[] asynResults = new IAsyncResult[coreCnt];
    
                //dispatch...
                //int i = 0; //Die Berechnung für Testzwecke auf einen Thread beschränken (Auskommentieren der beiden Zeilen "for...")
                for (int i = 0; i < coreCnt; ++i)
                {
                    int index = i;
                    action[i] = () => indexedAction(index);
                    asynResults[i] = action[i].BeginInvoke(null, null);
                }
    
                //...and synchronize
                for (int i = 0; i < coreCnt; ++i)
                    action[i].EndInvoke(asynResults[i]);
            }
        }
    
        //Synchronisierungskonstrukt
        public class ThreadBarrier
        {
            int Counter;
            object threadLocker = new object();
    
            readonly int BarrierMaxCount;
    
            public ThreadBarrier(int BarrierMaxCount)
            {
                this.BarrierMaxCount = BarrierMaxCount;
            }
    
            public void Synchronize()
            {
                if (BarrierMaxCount <= 1)
                    return;
    
                lock (threadLocker)
                {
                    if (++Counter < BarrierMaxCount)
                        Monitor.Wait(threadLocker);
                    else
                    {
                        Counter = 0;
                        Monitor.PulseAll(threadLocker);
                    }
                }
            }
        }
    }
    


  • Ein paar Hilfsklassen und Methoden, die ich auf die schnelle von verschiedenen Dateien im Projekt zusammenkopiert habe.
    Langweiliges Zeug.

    //Util.cs

    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System;
    
    namespace ImageLib.Enums
    {
        public enum ColorComponent
        {
            Blue_Value_Hellwert,
            Green_Saturation_Sättigung,
            Red_Hue_Farbton
        }
    
        public enum FilterWorkPlace
        {
            InBitmap,
            CreateNewBitmap
        }
    }
    
    namespace ImageLib.InternalUtils
    {
        internal class ImageScheme
        {
            public static int PixelformatToStep(PixelFormat pixelFormat)
            {
                if (pixelFormat == PixelFormat.Format24bppRgb)
                    return 3;
                if (pixelFormat == PixelFormat.Format32bppRgb || pixelFormat == PixelFormat.Format32bppArgb)
                    return 4;
                if (pixelFormat == PixelFormat.Format8bppIndexed)
                    return 1;
    
                throw new ArgumentException(string.Format("Unknown Pixelformat in {0} for PixelformatToStep", typeof(ImageScheme).FullName));
            }
    
            public static int ColorComponentToIndex(Enums.ColorComponent color)
            {
                if (color == Enums.ColorComponent.Red_Hue_Farbton)
                    return 2;
                else if (color == Enums.ColorComponent.Green_Saturation_Sättigung)
                    return 1;
                else if (color == Enums.ColorComponent.Blue_Value_Hellwert)
                    return 0;
    
                throw new ArgumentException(string.Format("Unknown ColorComponent in {0} for ColorComponentToIndex", typeof(ImageScheme).FullName));
            }
    
            //Muss für ein neues Pixelformat erweitert werden.
            public static Bitmap CreateBitmap(int width, int height, PixelFormat outPixelFormat)
            {
                Bitmap bmp = new Bitmap(width, height, outPixelFormat);
    
                if (outPixelFormat == PixelFormat.Format24bppRgb
                     || outPixelFormat == PixelFormat.Format32bppRgb
                     || outPixelFormat == PixelFormat.Format32bppArgb)
                {
                    return bmp;
                }
                else if (outPixelFormat == PixelFormat.Format8bppIndexed)
                {
                    ColorPalette palette = bmp.Palette;
    
                    for (int c = 0; c <= 255; ++c)
                        palette.Entries[c] = Color.FromArgb(c, c, c);
    
                    bmp.Palette = palette;
    
                    return bmp;
                }
    
                throw new Exception("Unsupported PixelFormat in ImageLib.InternalUtils.ImageScheme.CreateBitmap()");
            }
        }
    }
    


  • Hier wirds interessant.
    Die Basisklasse für weitere Filter.
    In abgeleiteten Klassen findet in der überschriebenen Worker-Methode die Algorithmik statt.
    Die Methode DoApply kümmert sich um die Lastenverteilung auf verschiedene Threads.

    //PixelFilterSingleSource.cs

    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    
    namespace ImageLib.Base
    {
        //Vorlage nach dem Template-Method-Muster für Filter mit einem Eingabebild
        public abstract class PixelFilterSingleSource
        {
            protected abstract Enums.FilterWorkPlace FilterPlace { get; }
            protected abstract IDictionary<PixelFormat, PixelFormat> PixelFormats { get; }
            protected virtual bool AllowMultiThreading { get { return true; } }
    
            protected InternalUtils.Parallel.ThreadBarrier ThreadBarrier;
    
            public Bitmap Apply(Bitmap bitmap)
            {
                return DoApply(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Environment.ProcessorCount);
            }
    
            public Bitmap Apply(Bitmap bitmap, Rectangle r, int coreCnt)
            {
                return DoApply(bitmap, r, coreCnt);
            }
    
            protected virtual void PreWorkSync(Bitmap bitmap, Rectangle r, int coreCnt) { }
            protected virtual void PostWorkSync() { }
    
            protected abstract void Worker(
                IntPtr inPtr, 
                IntPtr outPtr, 
                PixelFormat inPixelFormat,
                PixelFormat outPixelFormat,
                int width,
                int height,
                int inStride, 
                int inStep, 
                int outStride,
                int outStep,
                int coreCnt, 
                int index);
    
            private Bitmap DoApply(Bitmap bitmap, Rectangle r, int coreCnt)
            {
                if (!PixelFormats.ContainsKey(bitmap.PixelFormat))
                    throw new ArgumentException(
                        string.Format("Unexpected Pixelformat ({0}) for {1}.\nFilter allows {2} as input",
                        bitmap.PixelFormat,
                        this.GetType().FullName,
                        PixelFormats.Select(p => p.Key.ToString()).Aggregate((accu, pxformat) => accu + ", " + pxformat)));
    
                PreWorkSync(bitmap, r, coreCnt);
    
                ThreadBarrier = new ImageLib.InternalUtils.Parallel.ThreadBarrier(coreCnt);
    
                int width = r.Width;
                int height = r.Height;
    
                BitmapData inBitmapData = bitmap.LockBits(r, ImageLockMode.ReadWrite, bitmap.PixelFormat);
    
                Bitmap bitmapResult = bitmap;
                BitmapData outBitmapData = inBitmapData;
    
                if (FilterPlace == Enums.FilterWorkPlace.CreateNewBitmap)
                {
                    bitmapResult = InternalUtils.ImageScheme.CreateBitmap(bitmap.Width, bitmap.Height, PixelFormats[bitmap.PixelFormat]);
                    outBitmapData = bitmapResult.LockBits(r, ImageLockMode.WriteOnly, bitmapResult.PixelFormat);
                }
    
                //Auch der lesende Zugriff auf das Pixelformat scheint nicht threadsicher zu sein => Exception wegen gesperrtem Bitmapbereich.
                PixelFormat inPixelFormat = bitmap.PixelFormat;
                PixelFormat outPixelFormat = bitmapResult.PixelFormat;
    
                if (AllowMultiThreading)
                {
                    var threadDispatcher = InternalUtils.Parallel.SimpleThreadDispatcherFactory.Create();
    
                    threadDispatcher.Do(coreCnt,
                        (index) =>
                            Worker(
                                inBitmapData.Scan0,
                                outBitmapData.Scan0,
                                inPixelFormat,
                                outPixelFormat,
                                width,
                                height,
                                inBitmapData.Stride,
                                InternalUtils.ImageScheme.PixelformatToStep(inPixelFormat),
                                outBitmapData.Stride,
                                InternalUtils.ImageScheme.PixelformatToStep(outPixelFormat),
                                coreCnt,
                                index));
                }
                else
                {
                    Worker(
                        inBitmapData.Scan0,
                        outBitmapData.Scan0,
                        inPixelFormat,
                        outPixelFormat,
                        width,
                        height,
                        inBitmapData.Stride,
                        InternalUtils.ImageScheme.PixelformatToStep(inPixelFormat),
                        outBitmapData.Stride,
                        InternalUtils.ImageScheme.PixelformatToStep(outPixelFormat),
                        1,
                        0);
                }
    
                if (FilterPlace == Enums.FilterWorkPlace.CreateNewBitmap)
                    bitmapResult.UnlockBits(outBitmapData);
    
                bitmap.UnlockBits(inBitmapData);
    
                ThreadBarrier = null;
    
                PostWorkSync();
    
                return bitmapResult;
            }
        }
    }
    


  • Ein Invertierungsfilter.
    Abgeleitet von PixelFilterSingleSource.

    //Invert.cs

    using System.Collections.Generic;
    using System.Drawing.Imaging;
    using System.Drawing;
    using System;
    
    namespace ImageLib.Filter.Basic
    {
        public class Invert : Base.PixelFilterSingleSource
        {
            private Enums.FilterWorkPlace filterWorkPlace;
    
            public Invert(Enums.FilterWorkPlace filterWorkPlace)
            {
                this.filterWorkPlace = filterWorkPlace;
            }
    
            protected override Enums.FilterWorkPlace FilterPlace
            {
                get { return filterWorkPlace; }
            }
    
            protected override IDictionary<PixelFormat, PixelFormat> PixelFormats
            {
                get
                {
                    var pixelFormats = new Dictionary<PixelFormat, PixelFormat>();
                    pixelFormats.Add(PixelFormat.Format24bppRgb, PixelFormat.Format24bppRgb);
                    pixelFormats.Add(PixelFormat.Format32bppRgb, PixelFormat.Format24bppRgb);
                    pixelFormats.Add(PixelFormat.Format32bppArgb, PixelFormat.Format24bppRgb);
                    pixelFormats.Add(PixelFormat.Format8bppIndexed, PixelFormat.Format8bppIndexed);
                    return pixelFormats;
                }
            }
    
            protected unsafe override void Worker(
                 IntPtr inPtr, IntPtr outPtr,
                 PixelFormat inPixelFormat, PixelFormat outPixelFormat,
                 int width, int height,
                 int inStride, int inStep,
                 int outStride, int outStep,
                 int coreCnt, int offset)
            {
                byte* inImagePointer = (byte*)inPtr;
                byte* outImagePointer = (byte*)outPtr;
    
                int inCurrentPos = offset * inStride;
                int inLineStep = coreCnt * inStride - width * inStep;
    
                int outCurrentPos = offset * outStride;
                int outLineStep = coreCnt * outStride - width * outStep;
    
                if (inPixelFormat == PixelFormat.Format8bppIndexed)
                {
                    for (int y = offset; y < height; y += coreCnt, inCurrentPos += inLineStep, outCurrentPos += outLineStep)
                    {
                        for (int x = 0; x < width; ++x, inCurrentPos += inStep, outCurrentPos += outStep)
                        {
                            outImagePointer[outCurrentPos] = (byte)(255 - inImagePointer[inCurrentPos]);
                        }
                    }
                }
                else
                {
                    for (int y = offset; y < height; y += coreCnt, inCurrentPos += inLineStep, outCurrentPos += outLineStep)
                    {
                        for (int x = 0; x < width; ++x, inCurrentPos += inStep, outCurrentPos += outStep)
                        {
                            outImagePointer[outCurrentPos]     = (byte)(255 - inImagePointer[inCurrentPos]);
                            outImagePointer[outCurrentPos + 1] = (byte)(255 - inImagePointer[inCurrentPos + 1]);
                            outImagePointer[outCurrentPos + 2] = (byte)(255 - inImagePointer[inCurrentPos + 2]);
                        }
                    }
                }
            }
        }
    }
    

    PixelFormats gibt an, wie Pixelformate ineinander übertragen werden. Also ein Eingabebild mit PixelFormat X wird in das PixelFormatY übertragen u.s.w.. Die Basisklasse stellt dann ein entsprechendes Bitmap für die Ausgabe bereit.

    In der überschriebenen Worker-Methode ist der eigentliche Invertierungsfilter. Später etwas mehr zur Threadaufteilung .



  • Und zum Schluß noch der Gaußfilter zur Glättung.

    //Gaussian.cs

    using System.Collections.Generic;
    using System.Drawing.Imaging;
    using System.Drawing;
    using System;
    
    namespace ImageLib.Filter.Smoothing
    {
        //Gaußscher Glättungsfilter (separiert und parallelisiert)
        public unsafe class Gaussian : Base.PixelFilterSingleSource
        {
            int filterSize; // ==sigma
            float[] gaussianCoeff;
    
            //Thread-shared temporary arrays for seperated gaussian-filter.
            private float[,] tmpGray;
            private float[,] tmpRed;
            private float[,] tmpGreen;
            private float[,] tmpBlue;
    
            public Gaussian(int FilterSize)
            {
                filterSize = FilterSize;
    
                calcGaussianCoeff();
            }
    
            private void calcGaussianCoeff()
            {
                gaussianCoeff = new float[filterSize + 1];
    
                for (int i = 0; i <= filterSize; i++)
                    gaussianCoeff[i] = (float)Math.Exp((-1.0 * (i * i)) / (filterSize * filterSize));
            }
    
            protected override Enums.FilterWorkPlace FilterPlace
            {
                get { return Enums.FilterWorkPlace.CreateNewBitmap; }
            }
    
            protected override IDictionary<PixelFormat, PixelFormat> PixelFormats
            {
                get
                {
                    var pixelFormats = new Dictionary<PixelFormat, PixelFormat>();
                    pixelFormats.Add(PixelFormat.Format24bppRgb, PixelFormat.Format24bppRgb);
                    pixelFormats.Add(PixelFormat.Format32bppRgb, PixelFormat.Format24bppRgb);
                    pixelFormats.Add(PixelFormat.Format32bppArgb, PixelFormat.Format24bppRgb);
                    pixelFormats.Add(PixelFormat.Format8bppIndexed, PixelFormat.Format8bppIndexed);
                    return pixelFormats;
                }
            }
    
            protected override void PreWorkSync(Bitmap bitmap, Rectangle r, int coreCnt)
            {
                if (bitmap.PixelFormat == PixelFormat.Format8bppIndexed)
                    tmpGray = new float[bitmap.Width, bitmap.Height];
                else
                {
                    tmpRed = new float[bitmap.Width, bitmap.Height];
                    tmpGreen = new float[bitmap.Width, bitmap.Height];
                    tmpBlue = new float[bitmap.Width, bitmap.Height];
                }
            }
    
            protected override void PostWorkSync()
            {
                tmpGray = tmpRed = tmpGreen = tmpBlue = null;
            }
    
            protected unsafe override void Worker(
                  IntPtr inPtr, IntPtr outPtr,
                  PixelFormat inPixelFormat, PixelFormat outPixelFormat,
                  int width, int height,
                  int inStride, int inStep,
                  int outStride, int outStep,
                  int coreCnt, int offset)
            {
                byte* inImagePointer = (byte*)inPtr;
                byte* outImagePointer = (byte*)outPtr;
    
                int outCurrentPos = offset * outStride;
                int outLineStep = coreCnt * outStride - width * outStep;
    
                if (inPixelFormat == PixelFormat.Format8bppIndexed)
                {
                    for (int y = offset; y < height; y += coreCnt)
                    {
                        for (int x = 0; x < width; ++x)
                        {
                            float sum = 0;
                            float coeffSum = 0;
    
                            for (int i = -filterSize; i <= filterSize; ++i)
                            {
                                if (x + i >= 0 && x + i < width)
                                {
                                    float coeff = gaussianCoeff[Math.Abs(i)];
                                    coeffSum += coeff;
                                    int position = y * inStride + (x + i) * inStep;
                                    sum += coeff * inImagePointer[position];
                                }
                            }
    
                            tmpGray[x, y] = sum / coeffSum;
                        }
                    }
    
                    ThreadBarrier.Synchronize();
    
                    for (int y = offset; y < height; y += coreCnt, outCurrentPos += outLineStep)
                    {
                        for (int x = 0; x < width; ++x, outCurrentPos += outStep)
                        {
                            float sum = 0;
                            float coeffSum = 0;
    
                            for (int j = -filterSize; j <= filterSize; ++j)
                            {
                                if (y + j >= 0 && y + j < height)
                                {
                                    float coeff = gaussianCoeff[Math.Abs(j)];
                                    coeffSum += coeff;
                                    sum += coeff * tmpGray[x, y + j];
                                }
                            }
    
                            outImagePointer[outCurrentPos] = (byte)(sum / coeffSum);
                        }
                    }
                }
                else
                {
                    for (int y = offset; y < height; y += coreCnt)
                    {
                        for (int x = 0; x < width; ++x)
                        {
                            float sumRed = 0, sumGreen = 0, sumBlue = 0;
                            float coeffSum = 0;
    
                            for (int i = -filterSize; i <= filterSize; ++i)
                            {
                                if (x + i >= 0 && x + i < width)
                                {
                                    int position = y * inStride + (x + i) * inStep;
    
                                    float coeff = gaussianCoeff[Math.Abs(i)];
                                    coeffSum += coeff;
    
                                    sumRed += coeff * inImagePointer[position + 2];
                                    sumGreen += coeff * inImagePointer[position + 1]; 
                                    sumBlue += coeff * inImagePointer[position];
                                }
                            }
    
                            tmpRed[x, y] = sumRed / coeffSum;
                            tmpGreen[x, y] = sumGreen / coeffSum;
                            tmpBlue[x, y] = sumBlue / coeffSum;
                        }
                    }
    
                    ThreadBarrier.Synchronize();
    
                    for (int y = offset; y < height; y += coreCnt, outCurrentPos += outLineStep)
                    {
                        for (int x = 0; x < width; ++x, outCurrentPos += outStep)
                        {
                            float sumRed = 0, sumGreen = 0, sumBlue = 0;
                            float coeffSum = 0;
    
                            for (int j = -filterSize; j <= filterSize; ++j)
                            {
                                if (y + j >= 0 && y + j < height)
                                {
                                    float coeff = gaussianCoeff[Math.Abs(j)];
                                    coeffSum += coeff;
    
                                    sumRed   += coeff * tmpRed[x, y + j];
                                    sumGreen += coeff * tmpGreen[x, y + j];
                                    sumBlue  += coeff * tmpBlue[x, y + j];
                                }
                            }
    
                            outImagePointer[outCurrentPos]     = (byte)(sumBlue / coeffSum);
                            outImagePointer[outCurrentPos + 1] = (byte)(sumGreen / coeffSum);
                            outImagePointer[outCurrentPos + 2] = (byte)(sumRed / coeffSum);
                        }
                    }
                }
            }
        }
    }
    


  • Das wars.

    Auf welche Art und Weise die Last auf Threads verteilt wird, entscheidet jeweils die abgeleitet Klasse.
    Hier, sowohl bei Invert als auch bei Gaussian, findet eine Zeilenweise Bildverarbeitung statt.
    Zur Verfügung stehen dazu die Parameter: offset und coreCnt in den überschriebenen Worker-Methoden, welche von der Basisklasse aufgerufen werden.

    Es ist: 0<=offset<coreCnt
    offset ist also ein "Index" für jeden Thread, coreCnt die Anzahl der Threads.

    z.B.: offset = 2 und coreCnt = 6.
    -> 6 Threads und die Methode arbeitet auf Thread mit offset/index = 2. Bei zeilenweiser Bearbeitung wird also bei der dritten Bildzeile begonnen (weil offset nullbasiert ist) und dann immer um 6 Zeilen weitergesprungen. So kommen sich Threads niemals in die Quere.



  • Vielen Dank. Ich werde mich mal damit beschäftigen und Rückmeldung geben wenn ich weiter bin.



  • Ok.

    Kannst ja die Dateien einfach mal in ein Projekt kopieren und die Problemlösung der zeilenweisen Bildverarbeitung isolieren.

    Bei Fragen, nur zu.



  • So, hier nochmal die Lösung zum Schluß. Ich hab das nun doch noch anders gelöst. Das Problem war das Set/Get Pixel kein Mehrfachzugriff erlaubt weil diese jeweils auf das Bildobjekt zugreifen. Mein Bild wird nun in ein ByteArray gespeichert und ich ändere die Daten direkt im Array. Wie man das ganze macht, wird hier beschrieben: http://ilab.ahemm.org/tutBitmap.html
    Das problem der Streifen lag am Mehrfachzugriff auf den Klassifikator und nicht wie vorhher beschrieben. Diesen werde ich noch bearbeiten und bis dahin bleibt die Sperre erstmal drinne.

    Für diejenigen die den Beitrag fleißig mitverfolgt haben hier meine Umsetzung:

    //Image Locking
                BitmapData lockData = i.ImageBitmap.LockBits(new System.Drawing.Rectangle(0, 0, i.ImageBitmap.Width, i.ImageBitmap.Height),
                System.Drawing.Imaging.ImageLockMode.ReadWrite,
                System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                //Array erstellen um Bild darin zu speichern
                Int32[] imageData = new Int32[i.ImageBitmap.Width * i.ImageBitmap.Height];
                //benutzen der Marshal Klasse um image data zu kopieren
                System.Runtime.InteropServices.Marshal.Copy(lockData.Scan0, imageData, 0, imageData.Length);
                int w = i.ImageBitmap.Width;
                int h = i.ImageBitmap.Height;
                //Dummy erzeugen und anzeigen:
                object Sperre = new object();
                Parallel.For(0, w, x =>
                {
                    for (ushort y = 0; y < h; y++)
                    {
                        {//Laufindex für ByteArray erzeugen, durch X und Y Werte
                            int j = 0;
                            if (y == 0)
                                j = x;
                            if (y >= 1 && x == 0)
                                j = w * y;
                            if (y >= 1 && x >= 0)
                                j = (w * y) + x;
                            string l;
                            lock (Sperre)
                            {
                                l = CurrentClassificationPipeline.SelectedClassifier.classify(i.PixelValue(Convert.ToUInt16(x), y));
                            }
                            if (Problem.CurrentProblem.Labels.ContainsKey(l))
                            {
                                System.Drawing.Color col = Problem.CurrentProblem.Labels[l];
                                imageData[j] = col.ToArgb();
                            }
                            else
                                imageData[j] = Color.Black.ToArgb();
                        }
                    }
                });
                //Copy Image Data back
                Marshal.Copy(imageData, 0, lockData.Scan0, imageData.Length);
                //Unlock Image
                i.ImageBitmap.UnlockBits(lockData);
                System.Drawing.Bitmap res = i.ImageBitmap.Clone(new System.Drawing.Rectangle(0, 0, i.ImageBitmap.Width, i.ImageBitmap.Height),
                System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                Watch.Stop();
                MessageBox.Show(Watch.ElapsedMilliseconds.ToString() + " ms");
                return res;
    

Anmelden zum Antworten