我正在尝试对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。编译时没有错误,但应用后我看不到图像的差异。而且,它缺少矩阵大小功能。
所以我的问题是:
ToPGM()
,将位图(应用了中值滤波器)转换回PGM P2 format
?我的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;
}