Повышение производительности инвертирования цвета для изображений Tiff - С#

Я использую Винформс. В моей форме у меня есть картинка, куда я загружаю tiff-документы. Я использую приведенный ниже код, чтобы инвертировать цвета в документе вперед и назад при нажатии кнопки. Проблема с моим кодом в том, что он очень медленный. Как быстрее преобразовать цвета изображений?

Информация об изображении:

  • Размер изображения: 8,5 х 11 дюймов
  • Плотность пикселей: 300 x 300 пикселей/дюйм
  • Размеры в пикселях — 2550 x 3300 пикселей (обычно потому, что я открываю разные документы для просмотра)

    private void button1_Click(object sender, EventArgs e)
    {
        Bitmap pic = new Bitmap(pictureBox1.Image);
        for (int y = 0; (y <= (pic.Height - 1)); y++)
        {
            for (int x = 0; (x <= (pic.Width - 1)); x++)
            {
                Color inv = pic.GetPixel(x, y);
                inv = Color.FromArgb(255, (255 - inv.R), (255 - inv.G), (255 - inv.B));
                pic.SetPixel(x, y, inv);
            }
        }
        pictureBox1.Image = pic;
    
    }
    

person taji01    schedule 09.10.2015    source источник
comment
Вы пытались найти что-то вроде быстро инвертировать цвет c#? Нажмите.   -  person Sinatr    schedule 09.10.2015
comment
Сколько времени это занимает? Какова в среднем высота и ширина изображения?   -  person Yacoub Massad    schedule 09.10.2015
comment
Ну, ваш код перебирает каждый пиксель, чтобы снова инвертировать после нажатия кнопки. Вы когда-нибудь хранили (в памяти) обе версии одного и того же изображения (нормальную и перевернутую) и переключали их нажатием кнопки?   -  person RvdK    schedule 09.10.2015


Ответы (2)


GetPixel и SetPixel очень медленные из-за слишком многих причин. Кроме того, вложение циклов часто является плохой идеей. Взгляните на следующую ссылку для более быстрой реализации (не оптимальной, но ближе)

http://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part

Это связано с использованием небезопасного кода и метода LockPixel для изображений.

Код по ссылке (Необходимо разрешить небезопасный код в настройках проекта)

public static bool Invert(Bitmap b)
{
    // GDI+ still lies to us - the return format is BGR, NOT RGB. 
    BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), 
    ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 
    int stride = bmData.Stride; 
    System.IntPtr Scan0 = bmData.Scan0; 
    unsafe 
    { 
        byte * p = (byte *)(void *)Scan0;
        int nOffset = stride - b.Width*3; 
        int nWidth = b.Width * 3;
        for(int y=0;y < b.Height;++y)
        {
            for(int x=0; x < nWidth; ++x )
            {
                p[0] = (byte)(255-p[0]);
                ++p;
            }
            p += nOffset;
        }
    }

    b.UnlockBits(bmData);

    return true;
}

Если этот код не работает, поскольку у вас разные форматы изображений, вам необходимо изменить некоторые части с точки зрения размера пикселя. (в примере это константа 3). Вы можете получить это путем вычисления следующим образом:

int pixelSize = imageData.Stride/image.Width;

и заменить

PixelFormat.Format24bppRgb 

с участием

b.PixelFormat
person CSharpie    schedule 09.10.2015
comment
Как мне использовать код с событием нажатия кнопки? @CSharpie - person taji01; 09.10.2015
comment
Просто вызовите Invert((Bitmap)pictureBox1.Image); и если это не обновит pictureBox1.Refresh(); - person CSharpie; 09.10.2015
comment
Мне пришлось добавить немного Refresh(); после Invert((Bitmap)pictureBox1.Image); Спасибо за помощь. Код преформируется очень быстро. - person taji01; 09.10.2015

Взгляните на этот вопрос:

Инвертирование изображения возвращает черное изображение

Дэн использует цветовую матрицу с атрибутами изображения в своем решении.

Вот как вы будете его использовать (непроверено):

Объявите свою цветовую матрицу, чтобы вам не приходилось каждый раз переопределять ее:

    System.Drawing.Imaging.ColorMatrix m_colorMatrix;

    private void Init()
    {
        // create the negative color matrix
        m_colorMatrix = new System.Drawing.Imaging.ColorMatrix(new float[][] {
            new float[] {-1, 0, 0, 0, 0},
            new float[] {0, -1, 0, 0, 0},
            new float[] {0, 0, -1, 0, 0},
            new float[] {0, 0, 0, 1, 0},
            new float[] {1, 1, 1, 0, 1}
        });
    }

Теперь в событии Click кнопки укажите элемент управления Picture Box:

    private void Button_Click(object sender, EventArgs e) {
        var source = new Bitmap(pictureBox1.Image);
        //create a blank bitmap the same size as original
        var bmpInvert = new Bitmap(source.Width, source.Height);

        //get a graphics object from the new image
        using (var g = Graphics.FromImage(bmpInvert))
        {

            // create some image attributes
            var attributes = new System.Drawing.Imaging.ImageAttributes();

            attributes.SetColorMatrix(m_colorMatrix);

            g.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height),
                        0, 0, source.Width, source.Height, GraphicsUnit.Pixel, attributes);

            pictureBox1.Image = bmpInvert;
        }
    }

Поскольку я включил объект Graphics в блок using, вам не следует вызывать метод Dispose, как это делает он.

Это может дать вам то, что вам нужно, но мне не на чем это проверить.

person jp2code    schedule 09.10.2015
comment
Код Дэна выглядит хорошо, но я не знаю, как я могу вызвать его код нажатием кнопки. - person taji01; 09.10.2015
comment
@ taji01, взгляните на отредактированную версию с кодом. +1, если поможет; отметьте как ответ позже, если это решит вашу проблему. - person jp2code; 09.10.2015
comment
Когда я запускаю код и нажимаю кнопку. Произошло необработанное исключение типа «System.ArgumentException» в System.Drawing.dll. - person taji01; 09.10.2015
comment
На какой строке возникает исключение? Каков текст сообщения? Это может быть простое решение. - person jp2code; 09.10.2015