WPF C# 我在使用线程时更新位图时遇到问题

问题描述 投票:0回答:1

我在 asm 中过滤图像,并使用 WPF C# 作为 GUI。当我有过滤数据并且想将其转换为位图时,我在这行 Marshal.Copy(imageBytesPtr, imageBytes, 0, imageWidth * imageHeight * 4); 中遇到内存访问错误最奇怪的事情是随机发生的,有时我在 1,2,3,4,5 个线程上过滤图像,它会压垮 6 个线程,但是当我再次运行程序并在第 6 个线程上启动应用程序时,一切都很好

这是完整的代码:

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Diagnostics;
using System.Threading;
using System.Windows.Ink;
using System.Security.Permissions;
using System.Windows.Markup;
using System.ComponentModel;
using System.Reflection;
using System.Linq;

/*
 * POMYSLY 
 * -narzie zmienic tak zeby asm sie dodawal ten offset i zobaczymy co sie stanie 
 */
namespace LaplaceFilter
{
 
    public partial class MainWindow : Window
    {
        public CroppedBitmap finalBitmap;
        public List<byte> processedImage;
        public int imageWidth;
        public int imageHeight;
        private List<WriteableBitmap> processedImages = new List<WriteableBitmap>();

        private BitmapImage sourceImage;
        private int parts;
        private string selectedFilePath;
        private Thread[] ThreadsData;
        private byte[] finalImage;
        public WriteableBitmap resultBitmap;
        public Int32Rect rect;
        private static int counter;

        private int offset = 0;

        [DllImport("C:\\Users\\jakub\\source\\repos\\LaplaceFilter\\x64\\Debug\\laplaceCpp.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void FiltrImage(IntPtr arrayPtr, int size, int width, int height, int bytesPerPixel,int start, IntPtr resultArray);

        [DllImport("C:\\Users\\jakub\\source\\repos\\LaplaceFilter\\x64\\Debug\\LaplaceAsm.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern void LaplaceApply(IntPtr arrayPtr, int width, int height, IntPtr resultArray,int shift);
        public MainWindow()
        {
            InitializeComponent();

        }
        private void SelectFile_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Title = "Wybierz plik graficzny",
                Filter = "Pliki graficzne (*.png, *.jpg)|*.png;*.jpg",
            };

            if (openFileDialog.ShowDialog() == true)
            {
                selectedFilePath = openFileDialog.FileName;

                sourceImage = new BitmapImage(new Uri(selectedFilePath));
                EntryImage.Source = sourceImage;

                filtr.IsEnabled = true;
                ExecutingTime.Content = "Czas wykonywania algorytmu: ";
            }
        }

        private void Filtr_Click(object sender, RoutedEventArgs e)
        {
            

            parts = (int)MySlider.Value;

            ThreadsData = new Thread[parts];
            splitData(sourceImage, parts);
            counter = 0;
            filtr.IsEnabled = false;
        }
        private void splitData(BitmapImage sourceImage, int parts)
        {
            int sourceWidth = (int)sourceImage.PixelWidth;
            int sourceHeight = (int)sourceImage.PixelHeight;
            int partHeight = sourceHeight / parts;
            imageHeight = sourceHeight;
            imageWidth = sourceWidth;

            

            byte[] bitmapData = new byte[sourceHeight * sourceWidth * 4];

            byte[] AsmResult = new byte[sourceHeight * sourceWidth * 4];
            IntPtr AsmResultPtr = Marshal.UnsafeAddrOfPinnedArrayElement(AsmResult, 0);

            CroppedBitmap croppedBitmap = new CroppedBitmap(sourceImage, new Int32Rect(0, 0, sourceWidth, sourceHeight));
            int stride = croppedBitmap.PixelWidth * 4; // 4 bajty na piksel (RGBA)


            croppedBitmap.CopyPixels(bitmapData, stride, 0);
            IntPtr bitmapDataPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bitmapData, 0);

            int[] partsArray = new int[parts];

            for (int i = 0; i < parts; i++)
            {
                if (i == parts - 1)
                {
                    partsArray[i] = sourceHeight - (i * (int)(sourceHeight / parts));
                }
                else
                {
                    partsArray[i] = (int)(sourceHeight / parts);
                }

            }

            int startY = 0;
            
            for (int i = 0; i < parts; i++)
            {
                int tempPartHeight = partsArray[i];
                if (parts == 1)
                {
                    tempPartHeight = partsArray[i];
                }
                if (i == 0)
                {
                    startY = 0;
                }
                else if (i == parts - 1)
                {
                    startY = sourceHeight - tempPartHeight;
                    
                }
                else
                {
                    startY += partsArray[i];
                }
                int o = startY * imageWidth * 4 ;
             
                int index = i;
                if (CPlusPlusRadioButton.IsChecked == true)
                {
                    ThreadsData[index] = new Thread(() => ProcessImagesLaPlaceCpp(bitmapDataPtr, sourceWidth, tempPartHeight, o, AsmResultPtr, i, parts));
                }

                else if (ASMRadioButton.IsChecked == true)
                {
                    ThreadsData[index] = new Thread(() => ProcessImagesLaPlace(bitmapDataPtr, sourceWidth, tempPartHeight, o, AsmResultPtr, i, parts));
                }
                else
                {
                    if (i == 0)
                    {
                        MessageBox.Show("Wybierz filtr");
                    }
                    ThreadsData[index] = new Thread(() => Thread.Sleep(1));


                }
                    int a = 1;
            }
            
            for (int i = 0; i < parts; i++)
            {

                int index = i;
                ThreadsData[index].Start();
                //Thread.Sleep(100);
            }

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            for (int i = 0; i < parts; i++)
            {
                int index = i;
                ThreadsData[index].Join();
            }
            stopwatch.Stop();
            long elapsedTicks = stopwatch.ElapsedTicks;
            double elapsedMilliseconds = stopwatch.ElapsedMilliseconds;
            TimeSpan elapsed = stopwatch.Elapsed;
            ExecutingTime.Content = $"Czas wykonywania algorytmu: {elapsedTicks} ";
            
        }
        private readonly object lockObject = new object();

        private void ProcessImagesLaPlace(IntPtr bitmapDataPtr, int width, int height, int startY, IntPtr AsmResultPtr, int i, int parts)
        {
            if (width > 0 && height > 0)
            {
                // int stride = width * 4;
                lock (lockObject)
                {
                    Dispatcher.InvokeAsync(() =>
                    {
                        height--;
                        counter++;
                        LaplaceApply(bitmapDataPtr, width * 4, height, AsmResultPtr, startY);
                        if (counter == parts)
                        {
                            DisplayLastProcessedImage(AsmResultPtr);
                        }
                        filtr.IsEnabled = true;
                    }, DispatcherPriority.Background);
                }
            }
            else
            {
                Dispatcher.Invoke(() =>
                {
                    MessageBox.Show("Obrazek jest pusty.");
                });
            }
        }

        private void ProcessImagesLaPlaceCpp(IntPtr bitmapDataPtr, int width, int height, int startY, IntPtr AsmResultPtr, int i, int parts)
        {
            if (width > 0 && height > 0)
            {
                // int stride = width * 4;
                Dispatcher.InvokeAsync(() =>
                {
                    counter++;
                    FiltrImage(bitmapDataPtr, width * height, width * 4, height, 3, startY, AsmResultPtr);
                    if (counter == parts)
                    {
                        DisplayLastProcessedImage(AsmResultPtr);
                    }
                    filtr.IsEnabled = true;
                }, DispatcherPriority.Background);
            }
            else
            {
                Dispatcher.Invoke(() =>
                {
                    MessageBox.Show("Obrazek jest pusty.");
                });
            }
        }

        private void DisplayLastProcessedImage(IntPtr imageBytesPtr)
        {
            Dispatcher.InvokeAsync(() =>
            {
                resultBitmap = new WriteableBitmap(imageWidth, imageHeight, 96, 96, PixelFormats.Bgr32, null);
                rect = new Int32Rect(0, 0, imageWidth, imageHeight);

                byte[] imageBytes = new byte[imageWidth * imageHeight * 4];
                Marshal.Copy(imageBytesPtr, imageBytes, 0, imageWidth * imageHeight * 4);

                resultBitmap.WritePixels(rect, imageBytes, imageWidth * 4, 0);

                FinalImage.Source = resultBitmap;
            }, DispatcherPriority.Background);
        }


    }

}

我尝试放入原始位图(未过滤),但出现了一些问题。我尝试在线程上运行它,但是当我单击多次来过滤图像时,它又发生了

c# wpf multithreading image-processing
1个回答
0
投票

尚不清楚到底是什么导致了您所观察到的错误。

但是,您的代码似乎太复杂了。根据我对这个问题的评论,我想展示一个简单的程序,它可以异步更新 WriteableBitmap 的

BackBuffer
部分,而无需显式使用线程或制作原始图像数据的许多副本。

代码运行一定数量的

Task
,每个
BackBuffer
中的像素变亮或变暗。您可以用拉普拉斯滤波器方法替换
WritePart
方法的实现。

public MainWindow()
{
    InitializeComponent();

    Loaded += OnWindowLoaded;
}

private async void OnWindowLoaded(object sender, RoutedEventArgs e)
{
    var source = new BitmapImage(new Uri(...)); // you source image
    var bitmap = new WriteableBitmap(source);

    bitmap.Lock();

    var buffer = bitmap.BackBuffer;
    var width = bitmap.PixelWidth;
    var height = bitmap.PixelHeight;
    var parts = 6;
    var partHeight = height / parts;
    var tasks = new Task[parts];

    for (var i = 0; i < parts; i++)
    {
        var y = i * partHeight;

        if (i == parts - 1)
        {
            partHeight = height - y;
        }

        tasks[i] = Task.Run(() => WritePart(buffer, width, y, partHeight));
    }

    await Task.WhenAll(tasks);

    bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
    bitmap.Unlock();

    image.Source = bitmap; // <Image x:Name="image"/> in XAML
}

private static readonly Random random = new Random();

private static void WritePart(IntPtr buffer, int width, int y, int height)
{
    var f = 0.5 + random.NextDouble(); // 0.5 .. 1.5

    unsafe
    {
        var stride = 4 * width;
        var buf = (byte*)buffer.ToPointer() + stride * y;

        for (var i = 0; i < stride * height; i += 4)
        {
            buf[i] = Math.Min((byte)0xFF, (byte)(f * buf[i]));
            buf[i + 1] = Math.Min((byte)0xFF, (byte)(f * buf[i + 1]));
            buf[i + 2] = Math.Min((byte)0xFF, (byte)(f * buf[i + 2]));
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.