Я пытаюсь реализовать фильтр Лапласа, но мой вывод всегда неверен:
Я отлаживал 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;
}
Понятия не имею, что вы подразумеваете под «неправильным», но вижу, что вы смотрите на соседние пиксели. Однако вы изменяете то же изображение, с которого читаете, это повлияет на то, какими будут соседние пиксели для следующего пикселя. Попробуйте читать с img вместо image.
Фильтры должны считывать пиксели исходного изображения.
GetPixel, SetPixel - отстой. Не делай этого. Считайте растровое изображение в массив байтов и измените их, а затем преобразуйте обратно в изображение, используя данные растрового изображения. docs.microsoft.com/en-us/dotnet/api/…
@AccessDenied Вы должны написать это как ответ.
Думаю, @ LasseVågsætherKarlsen должен написать ответ.
Я думаю, что проблема в том, что вы читаете и обновляете одно и то же растровое изображение. В итоге вы читаете только что обновленные пиксели. Если «изображение» является вашим выходным растровым изображением, тогда все ваши вызовы GetPixel () должны использовать ваше входное растровое изображение, «img».
Возможно, вы захотите рассмотреть библиотеку, чтобы сделать это, манипуляции с изображениями трудно отладить и сложно сделать быстро. Интерполяция соседних пикселей называется «преобразованием размытия». Удаление цвета - это «преобразование в оттенки серого». Библиотека AForge может легко сделать это, например, и сделать это лучше.
Если это так, я бы рассмотрел emguCV, управляемую оболочку OpenCV.
@ LasseVågsætherKarlsen Спасибо за ответ , Я нашел ошибку !!
Спасибо вам всем !!





Прежде всего (из практических соображений) используйте логические имена. 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)), вы увидите, что оно действительно возвращает цвет. Так что имена в порядке.
Как отмечалось выше, ваша проблема в том, что вы обновляете то же изображение, с которого читаете.
Ниже приводится программа 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);
}
И результат,
Спасибо, я обнаружил ошибку, в которой я должен использовать img.GetPixel вместо image.GetPixel.
Я отредактировал ваш образец с помощью быстрой обработки изображений. Конечно, это еще далеко не идеальный код. Должны быть методы загрузки, обработки и сохранения. Потребовалось время, чтобы понять, что шаг иногда не равен ширине * 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);
}
Можно 40 часов отлаживать и ни к чему не привести. Вы пробовали делать некоторые вычисления вручную и сравнивать их с результатами вашего кода? "Порядок действий" вам что-то говорит? Что именно вы хотите умножить на минус четыре?