Unsupported PixelFormat - SetPixelData - c#

I have this function which converts the array of ushort to a grayscale image. I do the converts to convert the values to a byte-array and then use BitmapEncoder.
public static async Task<StorageFile> WriteableBitmapToStorageFile(ushort[,] image, bool isScaleValues, List<KeyValuePair<string, BitmapTypedValue>> metadata)
{
//Setup image
var imgHeight = image.GetLength(0);
var imgWidth = image.GetLength(1);
float maxVal = 1;
if (isScaleValues)
{
for (int i = 0; i < imgHeight; i++)
{
for (int j = 0; j < imgWidth; j++)
{
if (maxVal < image[i, j])
{
maxVal = image[i, j];
}
}
}
}
byte[] data = new byte[imgWidth * imgHeight];
if (image != null)
{
if (isScaleValues)
{
for (int x = 0; x < imgHeight; x++)
for (int y = 0; y < imgWidth; y++)
data[x * imgWidth + y] = (byte)(((double)UInt16.MaxValue * (double)image[x, y]) / (double)maxVal);
}
else
{
for (int x = 0; x < imgHeight; x++)
for (int y = 0; y < imgWidth; y++)
data[x * imgWidth + y] = (byte)image[x, y];
}
}
string FileName = "MyFile.png";
var file =
await
Windows.Storage.ApplicationData.Current.TemporaryFolder.CreateFileAsync(FileName,
CreationCollisionOption.GenerateUniqueName);
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Gray16, BitmapAlphaMode.Ignore,
(uint) imgWidth,
(uint) imgHeight,
2822.222222222222,
2822.222222222222,
data);
await encoder.BitmapProperties.SetPropertiesAsync(metadata);
await encoder.FlushAsync();
}
return file;
}
I get this exception at SetPixelData:
An exception of type 'System.ArgumentException' occurred in mscorlib.ni.dll but was not handled in user code
WinRT information: Windows.Graphics.Imaging: The bitmap pixel format is unsupported.
Additional information: The parameter is incorrect.
Windows.Graphics.Imaging: The bitmap pixel format is unsupported.
The 2D ushort array is already 16-bit grayscale image with first dimension being the height and the 2nd being the width. Since grayscale is apparently not supported, I will need to save it as Rgba16, so the question is: How to convert grayscale to RBG?
AFAIK, I just need to set all R,G,B to the same value but how would I place the values in the array.
What is the RGBA16 format in binary?

Since you have had a 2D ushort array contains 16 bpp grayscale pixels. I'd suggest you use this array to create a SoftwareBitmap with Gray16 pixel format first and then use SoftwareBitmap.Convert method to convert it to Rgba16 pixel format.
BitmapPixelFormat.Gray16 represent 16 bpp grayscale pixel format which means each pixel takes two bytes. So we need a byte array whose length is twice of the ushort array and use BitConverter.GetBytes(UInt16) method to convert ushort to byte[]. Following is a simple sample:
ushort[,] image = { { ushort.MinValue, ushort.MaxValue, ushort.MaxValue }, { ushort.MaxValue, ushort.MinValue, ushort.MaxValue }, { ushort.MaxValue, ushort.MaxValue, ushort.MinValue } };
var imgHeight = image.GetLength(0);
var imgWidth = image.GetLength(1);
byte[] data = new byte[image.Length * 2];
for (int i = 0; i < imgHeight; i++)
{
for (int j = 0; j < imgWidth; j++)
{
byte[] byteArray = BitConverter.GetBytes(image[i, j]);
data[i * imgWidth * 2 + j * 2] = byteArray[0];
data[i * imgWidth * 2 + j * 2 + 1] = byteArray[1];
}
}
//Create a SoftwareBitmap with 16 bpp grayscale pixel format
var softwareBitmap = new SoftwareBitmap(BitmapPixelFormat.Gray16, imgWidth, imgHeight);
softwareBitmap.CopyFromBuffer(data.AsBuffer());
//Convert pixel format to Rgba16 so that we can save it to the file
softwareBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Rgba16);
var outputFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("MyFile.png", CreationCollisionOption.GenerateUniqueName);
//Save softwareBitmap to file
using (IRandomAccessStream stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
// Create an encoder with the desired format
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
// Set the software bitmap
encoder.SetSoftwareBitmap(softwareBitmap);
await encoder.FlushAsync();
}

Related

How to set/get pixel from Softwarebitmap

I am trying to change pixels from a Softwarebitmap.
I want to know if there is an equivalent for Bitmap.Get/SetPixel(x,y,color) in Softwarebitmap UWP.
If you want to read and write softwareBitmap that you should use unsafe code.
To use softwareBitmap is hard that you should write some code.
First using some code.
using System.Runtime.InteropServices;
Then create an interface
[ComImport]
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
void GetBuffer(out byte* buffer, out uint capacity);
}
You can use this code to change the pixel.
Creating the soft bitmap.
var softwareBitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8, 100, 100, BitmapAlphaMode.Straight);
Writing pixel.
using (var buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.ReadWrite))
{
using (var reference = buffer.CreateReference())
{
unsafe
{
((IMemoryBufferByteAccess) reference).GetBuffer(out var dataInBytes, out _);
// Fill-in the BGRA plane
BitmapPlaneDescription bufferLayout = buffer.GetPlaneDescription(0);
for (int i = 0; i < bufferLayout.Height; i++)
{
for (int j = 0; j < bufferLayout.Width; j++)
{
byte value = (byte) ((float) j / bufferLayout.Width * 255);
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 0] = value; // B
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 1] = value; // G
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 2] = value; // R
dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 3] = (byte) 255; // A
}
}
}
}
}
You can write the pixel for write the dataInBytes and you should use byte.
For the pixel is BGRA and you should write this byte.
If you want to show it, you need to Convert when the BitmapPixelFormat isnt Bgra8 and the BitmapAlphaMode is Straight and you can use this code.
if (softwareBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8 ||
softwareBitmap.BitmapAlphaMode == BitmapAlphaMode.Straight)
{
softwareBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
}
This code can show it to Image.
var source = new SoftwareBitmapSource();
await source.SetBitmapAsync(softwareBitmap);
Image.Source = source;
See: Create, edit, and save bitmap images - UWP app developer

How to translate Tiff.ReadEncodedTile to elevation terrain matrix from height map in C#?

I'm new with working with reading tiff images and I'm trying to get the elevation terrain values from a tiff map by using LibTiff. The maps I need to decode are tile organized. Below the fragment of the code I'm using currently to get these values, based on the library documentation and research on the web:
private void getBytes()
{
int numBytes = bitsPerSample / 8; //Number of bytes depending the tiff map
int stride = numBytes * height;
byte[] bufferTiff = new byte[stride * height]; // this is the buffer with the tiles data
int offset = 0;
for (int i = 0; i < tif.NumberOfTiles() - 1; i++)
{
int rawTileSize = (int)tif.RawTileSize(i);
offset += tif.ReadEncodedTile(i, bufferTiff, offset, rawTileSize);
}
values = new double[height, width]; // this is the matrix to save the heigth values in meters
int ptr = 0; // pointer for saving the each data bytes
int m = 0;
int n = 0;
byte[] byteValues = new byte[numBytes]; // bytes of each height data
for (int i = 0; i < bufferTiff.Length; i++)
{
byteValues[ptr] = bufferTiff[i];
ptr++;
if (ptr % numBytes == 0)
{
ptr = 0;
if (n == height) // tiff map Y pixels
{
n = 0;
m++;
if (m == width) // tiff map X pixels
{
m = 0;
}
}
values[m, n] = BitConverter.ToDouble(byteValues, 0); // Converts each byte data to the height value in meters. If the map is 32 bps the method I use is BitConverter.ToFloat
if (n == height - 1 && m == width - 1)
break;
n++;
}
}
SaveArrayAsCSV(values, "values.txt");
}
//Only to show results in a cvs file:
public void SaveArrayAsCSV(double[,] arrayToSave, string fileName) // source: http://stackoverflow.com/questions/8666518/how-can-i-write-a-general-array-to-csv-file
{
using (StreamWriter file = new StreamWriter(fileName))
{
WriteItemsToFile(arrayToSave, file);
}
}
//Only to show results in a cvs file:
private void WriteItemsToFile(Array items, TextWriter file) // source: http://stackoverflow.com/questions/8666518/how-can-i-write-a-general-array-to-csv-file
{
int cont = 0;
foreach (object item in items)
{
if (item is Array)
{
WriteItemsToFile(item as Array, file);
file.Write(Environment.NewLine);
}
else {
file.Write(item + " | ");
cont++;
if(cont == width)
{
file.Write("\n");
cont = 0;
}
}
}
}
I've been testing two different maps (32 and 64 bits per sample) and the results are similar: At the begining, the data seems to be consistent, but there is a point in which all the other values are corrupted (even zero at the end of data results). I deduce there are some bytes that need to be ignored, but I don't know how identify them to depurate my code. The method Tiff.ReadScanline does not work for me, because the maps I need to decode are tiles organized, and this method is not for working with these kind of images (according to BitMiracle.LibTiff documentation). The method Tiff.ReadRGBATile is not valid neither, because the tiff images are not RGB. I can read these values with Matlab, but my project needs to be built in C#, so I can compare the expected results with mine. As reference (I think it could be helpful), these are some data extracted from one of the tiff files with LibTiff tag reading methods:
ImageWidth: 2001
ImageLength: 2001
BitsPerSample: 32
Compression: PackBits (aka Macintosh RLE)
Photometric: MinIsBlack
SamplesPerPixel: 1
PlanarConfig: Contig
TileWidth: 208
TileLength: 208
SampleFormat: 3
Thanks in advance by your help guys!
Ok, Finally I found the solution: My mistake was the parameter "count" in the function Tiff.ReadEncodedTile(tile, buffer, offset, count). The Tiff.RawTileSize(int) function, returns the compressed bytes size of the tile (different for each tile, depending of the compression algorithm), but Tiff.ReadEncodedTile returns the decompressed bytes (bigger and constant for all tiles). That's why not all the information was been saved properly, but just a part of data. Below the correct code with the terrain elevation matrix (need optimization but it works, I think it could be helpful)
private void getBytes()
{
int numBytes = bitsPerSample / 8;
int numTiles = tif.NumberOfTiles();
int stride = numBytes * height;
int bufferSize = tileWidth * tileHeight * numBytes * numTiles;
int bytesSavedPerTile = tileWidth * tileHeight * numBytes; //this is the real size of the decompressed bytes
byte[] bufferTiff = new byte[bufferSize];
FieldValue[] value = tif.GetField(TiffTag.TILEWIDTH);
int tilewidth = value[0].ToInt();
value = tif.GetField(TiffTag.TILELENGTH);
int tileHeigth = value[0].ToInt();
int matrixSide = (int)Math.Sqrt(numTiles); // this works for a square image (for example a tiles organized tiff image)
int bytesWidth = matrixSide * tilewidth;
int bytesHeigth = matrixSide * tileHeigth;
int offset = 0;
for (int j = 0; j < numTiles; j++)
{
offset += tif.ReadEncodedTile(j, bufferTiff, offset, bytesSavedPerTile); //Here was the mistake. Now it works!
}
double[,] aux = new double[bytesHeigth, bytesWidth]; //Double for a 64 bps tiff image. This matrix will save the alldata, including the transparency (the "blank zone" I was talking before)
terrainElevation = new double[height, width]; // Double for a 64 bps tiff image. This matrix will save only the elevation values, without transparency
int ptr = 0;
int m = 0;
int n = -1;
int contNumTile = 1;
int contBytesPerTile = 0;
int i = 0;
int tileHeigthReference = tileHeigth;
int tileWidthReference = tileWidth;
int row = 1;
int col = 1;
byte[] bytesHeigthMeters = new byte[numBytes]; // Buffer to save each one elevation value to parse
while (i < bufferTiff.Length && contNumTile < numTiles + 1)
{
for (contBytesPerTile = 0; contBytesPerTile < bytesSavedPerTile; contBytesPerTile++)
{
bytesHeigthMeters[ptr] = bufferTiff[i];
ptr++;
if (ptr % numBytes == 0 && ptr != 0)
{
ptr = 0;
n++;
if (n == tileHeigthReference)
{
n = tileHeigthReference - tileHeigth;
m++;
if (m == tileWidthReference)
{
m = tileWidthReference - tileWidth;
}
}
double heigthMeters = BitConverter.ToDouble(bytesHeigthMeters, 0);
if (n < bytesWidth)
{
aux[m, n] = heigthMeters;
}
else
{
n = -1;
}
}
i++;
}
if (i % tilewidth == 0)
{
col++;
if (col == matrixSide + 1)
{
col = 1;
}
}
if (contNumTile % matrixSide == 0)
{
row++;
n = -1;
if (row == matrixSide + 1)
{
row = 1;
}
}
contNumTile++;
tileHeigthReference = tileHeight * (col);
tileWidthReference = tileWidth * (row);
m = tileWidth * (row - 1);
}
for (int x = 0; x < height; x++)
{
for (int y = 0; y < width; y++)
{
terrainElevation[x, y] = aux[x, y]; // Final result. Each position of matrix has saved each pixel terrain elevation of the map
}
}
}
Regards!
Here is an improved code, works with non-square tiles:
int imageWidth = tiff.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
int imageHeight = tiff.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
int bytesPerSample = (int)tiff.GetField(TiffTag.BITSPERSAMPLE)[0].ToInt() / 8;
SampleFormat format = (SampleFormat)tiff.GetField(TiffTag.SAMPLEFORMAT)[0].ToInt();
//Array to return
float[,] decoded = new float[imageHeight, imageWidth];
//Get decode function (I only want a float array)
Func<byte[], int, float> decode = GetConversionFunction(format, bytesPerSample);
if (decode == null)
{
throw new ArgumentException("Unsupported TIFF format:"+format);
}
if(tiff.IsTiled())
{
//tile dimensions in pixels - the image dimensions MAY NOT be a multiple of these dimensions
int tileWidth = tiff.GetField(TiffTag.TILEWIDTH)[0].ToInt();
int tileHeight = tiff.GetField(TiffTag.TILELENGTH)[0].ToInt();
//tile matrix size
int numTiles = tiff.NumberOfTiles();
int tileMatrixWidth = (int)Math.Ceiling(imageWidth / (float)tileWidth);
int tileMatrixHeight = (int)Math.Ceiling(imageHeight / (float)tileHeight);
//tile dimensions in bytes
int tileBytesWidth = tileWidth * bytesPerSample;
int tileBytesHeight = tileHeight * bytesPerSample;
//tile buffer
int tileBufferSize = tiff.TileSize();
byte[] tileBuffer = new byte[tileBufferSize];
int imageHeightMinus1 = imageHeight - 1;
for (int tileIndex = 0 ; tileIndex < numTiles; tileIndex++)
{
int tileX = tileIndex / tileMatrixWidth;
int tileY = tileIndex % tileMatrixHeight;
tiff.ReadTile(tileBuffer, 0, tileX*tileWidth, tileY*tileHeight, 0, 0);
int xImageOffset = tileX * tileWidth;
int yImageOffset = tileY * tileHeight;
for (int col = 0; col < tileWidth && xImageOffset+col < imageWidth; col++ )
{
for(int row = 0; row < tileHeight && yImageOffset+row < imageHeight; row++)
{
decoded[imageHeightMinus1-(yImageOffset+row), xImageOffset+col] = decode(tileBuffer, row * tileBytesWidth + col * bytesPerSample);
}
}
}
}

Converting a multi-band 16-bit tiff image to an 8-bit tiff image

I got some pixel data from 16-bit(range 0-65535) tif image as an integer array. I got the value using gdal readraster. How do I convert them to 8-bit(0-225) and convert it (the array) to 8-bit tif image ?
Here is some of my code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OSGeo.GDAL;
using OSGeo.OSR;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
Gdal.AllRegister();
Dataset data1;
int xsize, ysize;
int bandsize;
data1 = Gdal.Open("F:\\po_1473547_bgrn_0000000.tif", Access.GA_ReadOnly);
bandsize = data1.RasterCount;
xsize = data1.RasterXSize; //cols
ysize = data1.RasterYSize; //rows
Console.WriteLine("cols : "+xsize+", rows : "+ysize);
Band[] bands = new Band[bandsize];
for (int i = 0; i < bandsize; i++) {
bands[i] = data1.GetRasterBand(i+1);
}
int[,,] pixel = new int[bandsize,xsize,ysize];
int[] pixtemp = new int[xsize * ysize];
for (int i = 0; i < bandsize; i++)
{
bands[i].ReadRaster(0, 0, xsize, ysize, pixtemp, xsize, ysize, 0, 0);
for (int j = 0; j < xsize; j++)
{
for (int k = 0; k < ysize; k++)
{
pixel[i,j,k] = pixtemp[j + k * xsize];
}
}
}
Console.WriteLine("");
for (int i = 0; i < bandsize; i++)
{
Console.WriteLine("some pixel from band " + (i+1));
for (int j = 0; j < 100; j++)
{
Console.Write(" " + pixel[i,100,j]);
}
Console.WriteLine("\n\n");
}
}
}
}
I was searching Google on how to do that but I only found how to do that if the data type is a byte. Someone please give me a hint.
I don't know about GEO Tiff format, but to convert a regular 16 bit tiff image file to an 8 bit one, you need to scale the 16 bit channel values to 8 bits. The example below shows how this can be achieved for gray scale images.
public static class TiffConverter
{
private static IEnumerable<BitmapSource> Load16BitTiff(Stream source)
{
var decoder = new TiffBitmapDecoder(source, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
for (int i = 0; i < decoder.Frames.Count; i++)
// return all frames that are present in the input.
yield return decoder.Frames[i];
}
private static BitmapSource NormalizeTiffTo8BitImage(BitmapSource source)
{
// allocate buffer & copy image bytes.
var rawStride = source.PixelWidth * source.Format.BitsPerPixel / 8;
var rawImage = new byte[rawStride * source.PixelHeight];
source.CopyPixels(rawImage, rawStride, 0);
// get both max values of first & second byte of pixel as scaling bounds.
var max1 = 0;
int max2 = 1;
for (int i = 0; i < rawImage.Length; i++)
{
if ((i & 1) == 0)
{
if (rawImage[i] > max1)
max1 = rawImage[i];
}
else if (rawImage[i] > max2)
max2 = rawImage[i];
}
// determine normalization factors.
var normFactor = max2 == 0 ? 0.0d : 128.0d / max2;
var factor = max1 > 0 ? 255.0d / max1 : 0.0d;
max2 = Math.Max(max2, 1);
// normalize each pixel to output buffer.
var buffer8Bit = new byte[rawImage.Length / 2];
for (int src = 0, dst = 0; src < rawImage.Length; dst++)
{
int value16 = rawImage[src++];
double value8 = ((value16 * factor) / max2) - normFactor;
if (rawImage[src] > 0)
{
int b = rawImage[src] << 8;
value8 = ((value16 + b) / max2) - normFactor;
}
buffer8Bit[dst] = (byte)Math.Min(255, Math.Max(value8, 0));
src++;
}
// return new bitmap source.
return BitmapSource.Create(
source.PixelWidth, source.PixelHeight,
source.DpiX, source.DpiY,
PixelFormats.Gray8, BitmapPalettes.Gray256,
buffer8Bit, rawStride / 2);
}
private static void SaveTo(IEnumerable<BitmapSource> src, string fileName)
{
using (var stream = File.Create(fileName))
{
var encoder = new TiffBitmapEncoder();
foreach (var bms in src)
encoder.Frames.Add(BitmapFrame.Create(bms));
encoder.Save(stream);
}
}
public static void Convert(string inputFileName, string outputFileName)
{
using (var inputStream = File.OpenRead(inputFileName))
SaveTo(Load16BitTiff(inputStream).Select(NormalizeTiffTo8BitImage), outputFileName);
}
}
Usage:
TiffConverter.Convert(#"c:\temp\16bit.tif", #"c:\temp\8bit.tif");
Interpolate pixels from 16 bit to 8 bit, some resampling methods could perform.
Linear Interpolation may help.
//Convert tiff from 16-bit to 8-bit
byte[,,] ConvertBytes(int[,,] pixel, bandsize, xsize, ysize)
{
byte[,,] trgPixel = new byte[bandsize,xsize,ysize];
for (int i = 0; i < bandsize; i++)
{
for (int j = 0; j < xsize; j++)
{
for (int k = 0; k < ysize; k++)
{
//Linear Interpolation
trgPixel[i,j,k] = (byte)((65535-pixel[i,j,k])/65536.0*256);
}
}
}
return trgPixel;
}
//Save 8-bit tiff to file
void SaveBytesToTiff(string destPath, byte[,,] pixel, bandsize, xsize, ysize)
{
string fileformat = "GTiff";
Driver dr = Gdal.getDriverByName(fileformat);
Dataset newDs = dr.Create(destPath, xsize, ysize, bandsize, DateType.GDT_Byte, null);
for(int i=0; i< bandsize;i++)
{
byte[] buffer = new byte[xsize * ysize];
for (int j = 0; j < xsize; j++)
{
for (int k = 0; k < ysize; k++)
{
buffer[j+k*xsize] = pixel[i,j,k];
}
}
newDs.WriteRaster(0, 0, xsize, ysize, buffer, xsize, ysize, i+1, null, 0, 0, 0);
newDs.FlushCache();
}
newDs.Dispose();
}

copy data from bitmap array into new array store data of bmp

I work on bitmap i read header of bmp 24 bit per pixel ,width 320 height 240,offset start of image data in location 54,use c#
in location 54 in bmp array i access to data ,i store data in new array :
struct pix //structure for pixel in bmp image
{
public byte r;//Red
public byte g;//Green
public byte b;//Blue
};
Bitmap img = new Bitmap(opendialog1.FileName);
string filename = opendialog1.FileName;
byte[] bmp = File.ReadAllBytes(filename);
int i=54;
pix[ , ] bmpdata = new pix[img.Height, img.Width]; //create array of structure
for (int row = 0; row < img.Height; row++)
{
for (int col = 0; col < img.Width; col++)
{
bmpdata[row, col].r = bmp[i];
bmpdata[row, col].g = bmp[i + 1];
bmpdata[row, col].b = bmp[i + 2];
i += 3;
}
is this correct method for copy data from bmp array into new array for data only ,i use c# window form ?
No this is not how you get the pixel data (color information).
Compare it yourself with the color defined at each pixel by calling GetPixel(x,y)
Also you could compare with this method:
var pixels = new Color[img.Height*img.Width];
for (int row = 0; row < img.Height; row++)
{
for (int col = 0; col < img.Width; col++)
{
pixels[row + col] = img.GetPixel(col, row);
}
}
If you just want the byte data, use img.Save() to save to a new MemoryStream for example...

split a splitted string

I wanted to split a splitted string in C#.
Basically, I want to save an image in a form of numbers, here are the code to save it to a number in C# using GetPixel
TextWriter textWriter = new StreamWriter(fileName);
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
Color pixel = bmp.GetPixel(x, y);
textWriter.Write(pixel.R + "," + pixel.G + "," + pixel.B + " ");
}
}
and here is a possible result: 252,255,192 252,255,192 252,255,192.
The pixel is separated by a space, and the rgb is separated by a comma.
the problem is when i tried to convert this again into an image, here is my current code to get the rgb of a pixel.
TextReader textReader = new StreamReader(fileName);
string allLine = textReader.ReadLine();
string[] splitPixel = allLine.Split(' ');
string[] splitRGB = null;
for (int i=0; i<splitPixel.Length; i++) {
splitRGB[i] = splitPixel[i].Split(',');
}
and here is the code to set the pixel color
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
byte tempR, tempG, tempB = 0;
tempR = Convert.ToByte(splitRGB[x]);
tempG = Convert.ToByte(splitRGB[x+1]);
tempB = Convert.ToByte(splitRGB[x+2]);
Color pixel = Color.FromArgb(tempR,tempG,tempB);
bmp.SetPixel(x, y, pixel);
}
}
Right now it's only telling me this error "Cannot implicitly convert type 'string[]' to 'string'" for the
splitRGB[i] = splitPixel[i].Split(',')
string[] splitPixel = allLine.Split(' ');
string[] splitRGB = new string[splitPixel.Length * 3];
for (int i=0; i<splitRGB.Length; i+=3) {
string[] splitted = splitPixel[i].Split(',');
splitRGB[i] = splitted[0];
splitRGB[i + 1] = splitted[1];
splitRGB[i + 2] = splitted[2];
}
EDIT: Here is a better version:
You should save the width and height of your image in the file (ie: 2x2 images have the same file format as 4*1 images), here I suggest that you save them in the first line as width height
using (TextReader textReader = new StreamReader(fileName)){
string sizeLine = textReader.ReadLine();
if (sizeLine == null)
throw new /*UnexpectedData*/Exception("invalid file!");
string[] widthHeightStr = sizeLine.Split(' ');
if (widthHeightStr.Length != 2)
throw new /*UnexpectedData*/Exception("invalid file!");
int width = int.Parse(widthHeightStr[0]);
int height = int.Parse(widthHeightStr[1]);
string pixelsLine = textReader.ReadLine();
if (onlyLine == null)
throw new /*UnexpectedData*/Exception("invalid file!");
string[] splitPixel = pixelsLine.Split(' ');
var bmp = new Bitmap(width, height);
for (int i=0; i<splitPixel.Length; i++) {
string[] splitted = splitPixel[i].Split(',');
if (splitted.Length != 3)
throw new /*UnexpectedData*/Exception("invalid file!");
int tempR = int.Parse(splitted[0]);
int tempG = int.Parse(splitted[1]);
int tempB = int.Parse(splitted[2]);
Color pixel = Color.FromArgb(tempR,tempG,tempB);
bmp.SetPixel(i / width, i % width, pixel);
}
}
EDIT:
using (TextWriter textWriter = new StreamWriter(fileName){
textWriter.WriteLine(bmp.Width + " " + bmp.Height);
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
Color pixel = bmp.GetPixel(x, y);
textWriter.Write(pixel.R + "," + pixel.G + "," + pixel.B + " ");
}
}
}
the error is simply explained you are trying to put an array (since split returns an array) in a string since splitRGB[i] is a string where as splitRGB[] = splitPixel[i].Split(','); or splitRGB[i][] = splitPixel[i].Split(','); <- where splitRGB[][] = new splitRGB[10][3] would work
so your code:
for (int i=0; i<splitPixel.Length; i++)
{
splitRGB[i] = splitPixel[i].Split(',');
}
if you want to put an array in a array you need multi-dimensional arrays like so:
string[][] splitRGB = new string[splitPixel.Length][3];
and the for loop to get your RGB values in
for (int i=0; i
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
byte tempR, tempG, tempB = 0;
tempR = Convert.ToByte(splitRGB[x][0]);
tempG = Convert.ToByte(splitRGB[x][1]);
tempB = Convert.ToByte(splitRGB[x][2]);
Color pixel = Color.FromArgb(tempR,tempG,tempB);
bmp.SetPixel(x, y, pixel);
}
}
the changes are very subtle and i've got the feeling i have made a mistake my self (i never use multi dimensional arrays i create arrays of structs or classes as much as i can)
Since i dont know what you do with the bitmap,i present you with another way to get the bitmap data to file and back in one go(you can split this to acomodate your needs).
I replace setPixel and GetPixel because they are too slow of a way to access the data,this will provide much faster outcome:
//assume you have a class member variable of type bitmap called sourceBitmap.
BitmapData sourceData = sourceBitmap.LockBits(new Rectangle(0, 0,
sourceBitmap.Width, sourceBitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
File.WriteAllBytes(#"C:\..\..\test.txt", pixelBuffer);
//here is where you can split the code...
byte[] fs = File.ReadAllBytes(#"C:\..\..\test.txt");
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(fs, 0, resultData.Scan0, fs.Length);
resultBitmap.UnlockBits(resultData);
pictureBox1.Image = resultBitmap;

Categories