Лаплас с использованием C# Где я ошибся?

Я пытаюсь реализовать фильтр Лапласа, но мой вывод всегда неверен:

Лаплас с использованием C# Где я ошибся?

Я отлаживал 4 часа ... Кто-нибудь может мне подсказать? Спасибо!!

Это мой код:

private void button1_Click(object sender, EventArgs e)
    {
        Lap1();
    }
    void Lap1()
    {
        Bitmap img = new Bitmap(pictureBox1.Image);
        Bitmap image = new Bitmap(img);
        for (int x = 1; x < image.Width - 1; x++)
        {
            for (int y = 1; y < image.Height - 1; y++)
            {
                Color color2, color4, color5, color6, color8;
                color2 = image.GetPixel(x, y - 1);
                color4 = image.GetPixel(x - 1, y);
                color5 = image.GetPixel(x, y);
                color6 = image.GetPixel(x + 1, y);
                color8 = image.GetPixel(x, y + 1);
                int r = color2.R + color4.R + color5.R * (-4) + color6.R + color8.R;
                int g = color2.G + color4.G + color5.G * (-4) + color6.G + color8.G;
                int b = color2.B + color4.B + color5.B * (-4) + color6.B + color8.B;




                int avg = (r + g + b) / 3;
                if (avg > 255) avg = 255;
                if (avg < 0) avg = 0;
                image.SetPixel(x, y, Color.FromArgb(avg, avg, avg));
            }
        }
        pictureBox2.Image = image;


    }

Вот как это должно работать.

Можно 40 часов отлаживать и ни к чему не привести. Вы пробовали делать некоторые вычисления вручную и сравнивать их с результатами вашего кода? "Порядок действий" вам что-то говорит? Что именно вы хотите умножить на минус четыре?

CodeCaster 09.10.2018 11:04

Понятия не имею, что вы подразумеваете под «неправильным», но вижу, что вы смотрите на соседние пиксели. Однако вы изменяете то же изображение, с которого читаете, это повлияет на то, какими будут соседние пиксели для следующего пикселя. Попробуйте читать с img вместо image.

Lasse V. Karlsen 09.10.2018 11:05

Фильтры должны считывать пиксели исходного изображения.

Access Denied 09.10.2018 11:10

GetPixel, SetPixel - отстой. Не делай этого. Считайте растровое изображение в массив байтов и измените их, а затем преобразуйте обратно в изображение, используя данные растрового изображения. docs.microsoft.com/en-us/dotnet/api/…

Access Denied 09.10.2018 11:13

@AccessDenied Вы должны написать это как ответ.

Paul Kertscher 09.10.2018 11:15

Думаю, @ LasseVågsætherKarlsen должен написать ответ.

Access Denied 09.10.2018 11:18

Я думаю, что проблема в том, что вы читаете и обновляете одно и то же растровое изображение. В итоге вы читаете только что обновленные пиксели. Если «изображение» является вашим выходным растровым изображением, тогда все ваши вызовы GetPixel () должны использовать ваше входное растровое изображение, «img».

CooncilWorker 09.10.2018 13:24

Возможно, вы захотите рассмотреть библиотеку, чтобы сделать это, манипуляции с изображениями трудно отладить и сложно сделать быстро. Интерполяция соседних пикселей называется «преобразованием размытия». Удаление цвета - это «преобразование в оттенки серого». Библиотека AForge может легко сделать это, например, и сделать это лучше.

Hans Passant 09.10.2018 14:56

Если это так, я бы рассмотрел emguCV, управляемую оболочку OpenCV.

Access Denied 10.10.2018 08:00

@ LasseVågsætherKarlsen Спасибо за ответ , Я нашел ошибку !!

TC code 10.10.2018 14:43

Спасибо вам всем !!

TC code 10.10.2018 14:43
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
11
658
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Прежде всего (из практических соображений) используйте логические имена. GetPixel () возвращает пиксель, а не цвет. И было бы полезно добавить суффикс, относящийся к относительному положению пикселя, с которым вы хотите сравнить (N, NW, W, SW, S, SE, E).

Вы хотите получить что-то вроде этого:

var detlaRed = Math.Abs((pixel_E.R + pixel_W.R + pixel_N.R + pixel_S.R) - 4* pixel);

Итак, я думаю, вы хотите иметь абсолютную ценность.

Что касается логические имена: если вы проверите объявление метода (public System.Drawing.Color GetPixel (int x, int y)), вы увидите, что оно действительно возвращает цвет. Так что имена в порядке.

Lukas Körfer 09.10.2018 14:17
Ответ принят как подходящий

Как отмечалось выше, ваша проблема в том, что вы обновляете то же изображение, с которого читаете.

Ниже приводится программа Linqpad, показывающая, как работает ваша функция при обновлении копии изображения:

void Main()
{
    Bitmap image = LoadPicture("https://i.stack.imgur.com/ti7Ij.png");

    using (Graphics g = Graphics.FromImage(image))
    {
        g.DrawRectangle(new Pen(Color.DarkOliveGreen), new Rectangle(11, 11, 33, 44));
        g.DrawRectangle(new Pen(Color.DarkOliveGreen), new Rectangle(33, 33, 33, 22));
        g.DrawRectangle(new Pen(Color.DarkOliveGreen), new Rectangle(33, 11, 22, 44));
    }

    image.Dump();
    Bitmap image2 = Lap1(image);
    image2.Dump();
}

Bitmap Lap1(Bitmap image)
{
    var image2 = new Bitmap(image);
    for (int x = 1; x < image.Width - 1; x++)
    {
        for (int y = 1; y < image.Height - 1; y++)
        {
            Color color2, color4, color5, color6, color8;
            color2 = image.GetPixel(x, y - 1);
            color4 = image.GetPixel(x - 1, y);
            color5 = image.GetPixel(x, y);
            color6 = image.GetPixel(x + 1, y);
            color8 = image.GetPixel(x, y + 1);
            int r = color2.R + color4.R + color5.R * (-4) + color6.R + color8.R;
            int g = color2.G + color4.G + color5.G * (-4) + color6.G + color8.G;
            int b = color2.B + color4.B + color5.B * (-4) + color6.B + color8.B;
            int avg = (r + g + b) / 3;
            if (avg > 255) avg = 255;
            if (avg < 0) avg = 0;
            image2.SetPixel(x, y, Color.FromArgb(avg, avg, avg));
        }
    }
    return image2;
}

private Bitmap LoadPicture(string url)
{
    HttpWebRequest wreq;
    HttpWebResponse wresp;
    Stream mystream;
    Bitmap bmp;

    bmp = null;
    mystream = null;
    wresp = null;
    try
    {
        wreq = (HttpWebRequest)WebRequest.Create(url);
        wreq.AllowWriteStreamBuffering = true;
        wresp = (HttpWebResponse)wreq.GetResponse();

        if ((mystream = wresp.GetResponseStream()) != null)
            bmp = new Bitmap(mystream);
    }
    finally
    {
        if (mystream != null)
            mystream.Close();

        if (wresp != null)
            wresp.Close();
    }
    return (bmp);
}

И результат,

Перед фильтром: before

После фильтра: after

Спасибо, я обнаружил ошибку, в которой я должен использовать img.GetPixel вместо image.GetPixel.

TC code 10.10.2018 14:47

Я отредактировал ваш образец с помощью быстрой обработки изображений. Конечно, это еще далеко не идеальный код. Должны быть методы загрузки, обработки и сохранения. Потребовалось время, чтобы понять, что шаг иногда не равен ширине * bytesPerPixel.

Кроме того, усреднение r, g, b не является правильным преобразованием в серый цвет, поскольку глаза по-разному воспринимают цвета.

В соответствии с вашим кодом вы должны GetPixel from img, устанавливая пиксель для изображения.

Также рекомендуется обрабатывать изображения в нормализованных двойных значениях от 0 до 1, но это выходит за рамки вопроса.

Также странно получить пиксели для реализации фильтра ядра. Лучше передавать матрицу и вычислять значения на основе матрицы, но не жестко кодировать.

Также плохо блокировать поток пользовательского интерфейса. Например, используйте TaskFactory.

(byte r, byte g, byte b) GetPixel(byte[] image,int x, int y, int bytesPerPixel,int stride)
{
      if (bytesPerPixel < 3) throw new ArgumentException(nameof(bytesPerPixel));
      byte b = image[x * bytesPerPixel + y * stride + 0];
      byte g = image[x * bytesPerPixel + y * stride + 1];
      byte r = image[x * bytesPerPixel + y * stride + 2];
      return (r, g, b);
}

byte ToGray((byte r, byte g, byte b) pixel)
{
   return NearestByte((int)(pixel.r * 0.3 + pixel.g * 0.59 + pixel.b * 0.11));
}

byte NearestByte(int a)
{
   return (byte)(Math.Min(255, Math.Max(0, a)));
}

void SetPixel(byte[] image, byte intensity, int x, int y, int bytesPerPixel, int stride)
{
    if (bytesPerPixel < 3) throw new ArgumentException(nameof(bytesPerPixel));
    image[x * bytesPerPixel + y * stride + 0] = intensity;
    image[x * bytesPerPixel + y * stride + 1] = intensity;
    image[x * bytesPerPixel + y * stride + 2] = intensity;
}

Bitmap ApplyLaplacianFilter(Bitmap input)
{            
    Bitmap img = new Bitmap(input);         
    // Lock the bitmap's bits.  
    Rectangle rect = new Rectangle(0, 0, img.Width, img.Height);
    System.Drawing.Imaging.BitmapData bmpData =
     img.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly,
     img.PixelFormat);

    IntPtr ptr = bmpData.Scan0;
    var stride = bmpData.Stride;
    int bytesPerPixel = bmpData.Stride / img.Width;
    int bytes = img.Width * img.Height * bytesPerPixel;
    byte[] rgbValues = new byte[bytes];
    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
    img.UnlockBits(bmpData);
    int resultBytesPerPixel = 3;
    Bitmap resultBitmap = new Bitmap(img.Width, img.Height, PixelFormat.Format24bppRgb);            
    var bitmapData = resultBitmap.LockBits(rect, ImageLockMode.ReadWrite, resultBitmap.PixelFormat);

    int resultStride = bitmapData.Stride;              
    int resultBytes = resultStride * img.Height;    
    byte[] result = new byte[resultBytes];

    for (int y = 1; y < img.Height - 1; y++)
    {
       for (int x = 1; x < img.Width - 1; x++)
       {

         var left = GetPixel(rgbValues, x - 1, y, bytesPerPixel, stride);
         var right = GetPixel(rgbValues, x + 1, y, bytesPerPixel, stride);
         var top = GetPixel(rgbValues, x, y - 1, bytesPerPixel, stride);
         var bottom = GetPixel(rgbValues, x, y + 1, bytesPerPixel, stride);
         var center = GetPixel(rgbValues, x, y, bytesPerPixel, stride);

         var resultIntensity = NearestByte(
             ToGray(left) + ToGray(top) + ToGray(center) * (-4) + ToGray(right) + ToGray(bottom));
         SetPixel(result, resultIntensity, x, y, resultBytesPerPixel, resultStride);
       }
    }


   IntPtr resultPtr = bitmapData.Scan0;
   System.Runtime.InteropServices.Marshal.Copy(result, 0, resultPtr, resultBytes);
   resultBitmap.UnlockBits(bitmapData);            
   return resultBitmap;
}

private async void button1_Click(object sender, EventArgs e)
{
   pictureBox2.Image = await new TaskFactory().StartNew((image) => ApplyLaplacianFilter((Bitmap)image), pictureBox1.Image);                       
}

Другие вопросы по теме