PGM P2格式应用中值过滤器并保存所做的更改问题

时间:2018-12-17 19:43:01

标签: c# p2 pgm

我正在尝试对PGM P2图像应用中值滤镜,然后将图像另存为PGM。我的解析器工作正常。问题是我的中值滤波器功能需要位图,但是将我的PGM图像转换成位图并不难,但是我无法将位图(使用中值滤波器)转换回PGM格式。解决方案是在应用中值函数后将位图转换回PGM(下面的代码),或者在PGM图像上使用中值滤波器函数代替位图。

public class PGMImage
{
    public string Format { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
    public int MaxGrayLevel { get; set; }
    public byte[] PixelData { get; set; }

    public bool HasValidMetadata
    {
        get
        {
            return Width > 0 && Height > 0 && MaxGrayLevel > 0 && !string.IsNullOrWhiteSpace(Format);
        }
    }
}

public static PGMImage LoadFromFile(string path)
{
    PGMImage image = new PGMImage();

    using (StreamReader reader = File.OpenText(path))
    {
        string line = string.Empty;

        while ((line = reader.ReadLine()) != null)
        {
            if (string.IsNullOrEmpty(line) || line.StartsWith("#"))
            {
                continue;
            }

            // Read format
            if (image.Format == null)
            {
                if (line.Trim() != "P2")
                {
                    throw new PGMFormatException("Unsupported file format.");
                }

                image.Format = line;
                continue;
            }

            // Read width and height
            string[] widthAndHeight = line.Split(new string[] { " ", "\n", "\t" }, StringSplitOptions.RemoveEmptyEntries);
            if (image.Width == 0 || image.Height == 0)
            {
                image.Width = Convert.ToInt32(widthAndHeight[0]);
                image.Height = Convert.ToInt32(widthAndHeight[1]);
                continue;
            }

            // Read max gray value
            if (image.MaxGrayLevel == 0)
            {
                image.MaxGrayLevel = Convert.ToInt32(line);
                break;
            }
        }

        // Validate metadata
        if (!image.HasValidMetadata)
        {
            throw new PGMFormatException("Metadata could not be read or it is invalid.");
        }

        // Read pixel data
        byte[] pixelData = new byte[image.Width * image.Height];
        int readPixels = 0;

        while ((line = reader.ReadLine()) != null)
        {
            byte[] pixels = line.Split(new string[] { " ", "\n", "\t" }, StringSplitOptions.RemoveEmptyEntries).Select(e => Convert.ToByte(e)).ToArray();

            for (int i = 0; i < pixels.Length; i++)
            {
                pixelData[readPixels + i] = pixels[i];
            }

            readPixels += pixels.Length;
        }

        image.PixelData = pixelData;
    }

    return image;
}

public static void Save(this PGMImage image, string path)
{
    using (StreamWriter writer = new StreamWriter(path))
    {
        // Writing format
        writer.WriteLine(image.Format);

        // Writing width and height
        writer.WriteLine($"{image.Width} {image.Height}");

        // Writing max gray level
        writer.WriteLine(image.MaxGrayLevel);

        // Writing pixel data
        string pixelLine = string.Empty;

        for (int i = 0; i < image.PixelData.Length; i++)
        {
            string currentPixel = $"{image.PixelData[i]} ";

            if (pixelLine.Length + currentPixel.Length >= 70) // 70 is line's max length
            {
                writer.WriteLine(pixelLine);
                pixelLine = currentPixel;
            }
            else
            {
                pixelLine += currentPixel;
            }
        }

        if (pixelLine.Length > 0)
        {
            writer.WriteLine(pixelLine);
        }
    }
}

public Bitmap ToBitmap()
{
    Bitmap bitmap = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);

    ColorPalette palette = bitmap.Palette;
    for (int i = 0; i < 256; i++)
        palette.Entries[i] = Color.FromArgb(255, i, i, i);
    bitmap.Palette = palette;

    BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
    Marshal.Copy(PixelData, 0, bmpData.Scan0, PixelData.Length);
    bitmap.UnlockBits(bmpData);

    return bitmap;
}

我的中值过滤器功能如下。您会看到它是位图图像的扩展方法,这很不好,因为我想将应用了中值滤镜的加载的PGM图像保存为PGM P2格式,因此我需要将接收到的位图转换回PGM,但我无法要做。

public static Bitmap MedianFilter(this Bitmap sourceBitmap, int matrixSize, int bias = 0, bool grayscale = false)
{
    BitmapData sourceData = sourceBitmap.LockBits(new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

    byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
    byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];

    Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);

    sourceBitmap.UnlockBits(sourceData);

    if (grayscale == true)
    {
        float rgb = 0;

        for (int k = 0; k < pixelBuffer.Length; k += 4)
        {
            rgb = pixelBuffer[k] * 0.11f;
            rgb += pixelBuffer[k + 1] * 0.59f;
            rgb += pixelBuffer[k + 2] * 0.3f;

            pixelBuffer[k] = (byte)rgb;
            pixelBuffer[k + 1] = pixelBuffer[k];
            pixelBuffer[k + 2] = pixelBuffer[k];
            pixelBuffer[k + 3] = 255;
        }
    }

    int filterOffset = (matrixSize - 1) / 2;
    int calcOffset = 0;

    int byteOffset = 0;

    List<int> neighbourPixels = new List<int>();
    byte[] middlePixel;

    for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++)
    {
        for (int offsetX = filterOffset; offsetX < sourceBitmap.Width - filterOffset; offsetX++)
        {
            byteOffset = offsetY * sourceData.Stride + offsetX * 4;

            neighbourPixels.Clear();

            for (int filterY = -filterOffset; filterY <= filterOffset; filterY++)
            {
                for (int filterX = -filterOffset; filterX <= filterOffset; filterX++)
                {
                    calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
                    neighbourPixels.Add(BitConverter.ToInt32(pixelBuffer, calcOffset));
                }
            }

            neighbourPixels.Sort();

            middlePixel = BitConverter.GetBytes(neighbourPixels[filterOffset]);

            resultBuffer[byteOffset] = middlePixel[0];
            resultBuffer[byteOffset + 1] = middlePixel[1];
            resultBuffer[byteOffset + 2] = middlePixel[2];
            resultBuffer[byteOffset + 3] = middlePixel[3];
        }
    }

    Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
    BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0, resultBitmap.Width, resultBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

    Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);

    resultBitmap.UnlockBits(resultData);

    return resultBitmap;
}

我试图实现与上述相同的功能,但是这次是针对PGM图像,因为这样就无需将位图转换为PGM,因为它已经是PGM。编译时没有错误,但应用后我看不到图像的差异。而且,它缺少矩阵大小功能。

所以我的问题是:

  1. 如何制作扩展方法ToPGM(),将位图(应用了中值滤波器)转换回PGM P2 format
  2. 如何制作PGM图像的中值函数 (包括矩阵大小选项)。

我的PGM图像中值过滤器代码在下面,但是如上所述,它缺少矩阵大小功能,我不确定是否可以工作,因为我无法真正找到图像的差异(可能是因为矩阵大小还不够大(如7x7),无法获得更明显的结果,但我不确定)。

public static PGMImage ApplyMedianFilter(this PGMImage inputImage, int matrixSize)
{
    PGMImage outputImage = inputImage;

    int readBytes = 0;
    byte[,] pixelsData = new byte[inputImage.Width, inputImage.Height];
    for (int y = 0; y < inputImage.Height; y++)
    {
        int counter = 0;

        for (int x = 0; x < inputImage.Width; x++)
        {
            pixelsData[x, y] = inputImage.PixelData[x + readBytes];
            counter++;
        }

        readBytes += counter;
    }

    byte[,] tempArray = new byte[inputImage.Width, inputImage.Height];

    for (int x = 0; x < inputImage.Width - 1; x++)
    {
        for (int y = 0; y < inputImage.Height - 1; y++)
        {
            List<int> validNeighbors = inputImage.GetAllNeighbors(pixelsData, x, y);

            validNeighbors.Sort();
            int medianIndex = validNeighbors.Count / 2;
            byte medianPixel = (byte)validNeighbors[medianIndex];

            tempArray[x, y] = medianPixel;
        }
    }

    outputImage.PixelData = new byte[inputImage.Width * inputImage.Height];

    for (int y = 0; y < inputImage.Height; y++)
    {
        for (int x = 0; x < inputImage.Width; x++)
        {
            outputImage.PixelData[y * inputImage.Width + x] = tempArray[x, y];
        }
    }

    return outputImage;
}

0 个答案:

没有答案