Как разбить байтовый массив изображения на несколько байтовых массивов с помощью задач - PullRequest
0 голосов
/ 14 апреля 2020

Цель:

Я создаю простое приложение для обработки изображений, использующее задачи для многопоточности. Пользователь выбирает изображение из папки и отображает его в PicBox. Когда он вводит количество потоков, цвет для изменения и значение (0-255) этого цвета (R, G, B) и нажимает кнопку редактирования, изображение выглядит так:

Процедура

  1. Преобразовано в байтовый массив
  2. Возвращается байтовый массив и вычисляется сводная таблица в соответствии с номером потока.
  3. Список задач создан, и каждой задаче назначен начальный и конечный индексы байтового массива большего размера
  4. В методе небольшая часть байта большего размера (от начального до конечного индекса) сохраняется в байтовом массиве меньшего размера
  5. Затем метод преобразует Небольшой байтовый массив для Image и возвращает изображение

Проблема :

Все идет хорошо, пока на 5-м шаге я не попытаюсь преобразовать байтовый массив в изображение. Это особенно происходит, когда начальный индекс больше 0, что во время выполнения 2-й задачи. Это работает для первого задания. Может ли быть так, что он не может принять Start Index> 0?

Пожалуйста, смотрите следующий код:

Код

                List<Task<Image>> processImgTask = new List<Task<Image>>(threadCount);

                threadCount = Convert.ToInt32(threadCombox.SelectedItem);
                interval = imgArray.Length / threadCount;

                for (int i = 0; i < threadCount; i++)
                {
                    Start = End;
                    End += interval;
                    if (i == threadCount - 1)
                    {
                        End = imgArray.Length;
                    }
                    object data = new object[3] { Start, End, imgArray };
                    processImgTask.Add(new Task<Image>(ImgProcess, data));
                }
                //Task.WaitAll(processImgTask);

                //EDIT followed by comments and answer
                Parallel.ForEach(processImgTask, task =>
                {
                    task.Start();
                    taskPicbox.Image = task.Result;
                });



    private Image ImgProcess(object data)
    {
        object[] indexes = (object[])data;
        int Start=(int)indexes[0];
        int End = (int)indexes[1];            
        byte[] img = (byte[])indexes[2];

        List<byte> splitArray = new List<byte>();
        for (int i =Start;i<End;i++)
        {
            splitArray.Add(img[i]);
        }
        byte[] b = splitArray.ToArray();

        //Error occurs here when task 2 (thread 2) is being executed->
            Image x = (Bitmap)((new ImageConverter()).ConvertFrom(b));
        //System.ArgumentException: 'Parameter is not valid.'                    
        return x;
    }

Ответы [ 2 ]

3 голосов
/ 15 апреля 2020

См. Этот ответ на , как преобразовать массив байтов с необработанными пиксельными данными в растровое изображение .

Я также настоятельно рекомендую использовать Parallel.For вместо задач. Задачи предназначены для асинхронного выполнения кода, то есть позволяют компьютеру делать что-то, пока он ожидает данных. Parallel.For / Foreach предназначен для одновременного запуска кода, то есть использования нескольких ядер для повышения производительности. Asyn c и параллель - это не одно и то же .

Я также рекомендовал бы начать с простой однопоточной реализации того, что вы пытаетесь сделать. Процессоры быстрые , если только вы не делаете что-то очень требовательное, накладные расходы могут быть значительными. И хотя распараллеливание может привести к тому, что ваш код будет работать в четыре раза быстрее (или сколько у вас процессорных ядер), довольно часто бывает, что другая оптимизация может повысить производительность в сотни и более раз. И вы всегда можете распараллелить позже, если это необходимо.

Для изображений типичным способом распараллеливания является параллелизм. Для каждой строки в изображении.

1 голос
/ 15 апреля 2020

В ответ на Джона sH я сделал для вас пример. В этом примере используется тип Span. Это может быть сделано непосредственно с массивом или ArraySegment<byte>.

Это пример того, как вы можете обрабатывать многопоточные строки:

private void Main()
{
    int width = 1024;
    int height = 768;
    int channelCount = 3;

    // create a buffer
    var data = new byte[width * height * channelCount];

    // fill with some data
    // example: 0, 1, 2, 3, 4, 5, 6
    PutSomeValuesInThatBuffer(data);

    // process the image and specify a line-edit callback
    // transforms to: 255, 254, 253, 252, 251, 250
    ProcessImage(data, width, height, channelCount, linePixels =>
    {
        int offset = 0;

        // we need to loop all pixel on this line
        while (offset < linePixels.Length)
        {
            // for RGB | R = channel[0], G = channel[1], B = channel[2], etc...

            // lets invert the colors, this loop isn't quite necessary
            // but it shows the use of channels  (R, G, B)  
            for (int i = 0; i < channelCount; i++)
            {
                linePixels[offset] = 255 - linePixels[offset];
                offset++;
            }
        }
    });
}

public delegate void LineProcessorAction(Span<byte> line);

// this is the process method which will split the data into lines 
// and process them over multiple threads.
private void ProcessImage(
    byte[] data,
    int width, int height, int channelCount,
    LineProcessorAction lineProcessor)
{
    var rowSizeInBytes = width * channelCount;

    Parallel.For(0, height, index => 
        lineProcessor(new Span<byte>(data, index * rowSizeInBytes, rowSizeInBytes)));
}

private static void PutSomeValuesInThatBuffer(byte[] data)
{
    for (int i = 0; i < data.Length; i++)
        data[i] = (byte)i;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...