I want to replace the pixel data of a DICOM file with another one. I used this code:
public bool ImportImage(string imageFile, string newFilePah, string oldDicomFile)
{
try
{
Bitmap bitmap = new Bitmap(imageFile);
bitmap = GetValidImage(bitmap);
int rows, columns;
byte[] pixels = GetPixels(bitmap, out rows, out columns);
MemoryByteBuffer buffer = new MemoryByteBuffer(pixels);
DicomDataset dataset = new DicomDataset();
var df = DicomFile.Open(oldDicomFile);
FillDataset(ref dataset, df);
DicomTransferSyntax dicomTransfer = df.Dataset.Get<DicomTransferSyntax>(DicomTag.TransferSyntaxUID, DicomTransferSyntax.JPEGProcess14);
dataset.AddOrUpdate(DicomTag.PhotometricInterpretation, PhotometricInterpretation.Rgb.Value);
dataset.AddOrUpdate(DicomTag.Rows, (ushort)rows);
dataset.AddOrUpdate(DicomTag.Columns, (ushort)columns);
dataset.AddOrUpdate(DicomTag.BitsAllocated, (ushort)8);
DicomPixelData pixelData = DicomPixelData.Create(dataset, true);
pixelData.BitsStored = 8;
pixelData.SamplesPerPixel = 3;
pixelData.HighBit = 7;
pixelData.PixelRepresentation = 0;
pixelData.PlanarConfiguration = 0;
pixelData.AddFrame(buffer);
DicomFile dicomfile = new DicomFile(dataset.Clone(dicomTransfer));
dicomfile.Save(newFilePah);
return true;
}
catch(Exception ddd) { return false; }
}
private void FillDataset(ref DicomDataset dataset, DicomFile df)
{
foreach(var item in df.Dataset)
{
if(!item.Tag.Group.ToString().Equals("7FE0") && !item.Tag.Group.ToString().Equals("40"))
dataset.Add(item);
}
}
The output DICOM file loses many tags which affect image display.
I referred to this answer. But the AddOrUpdatePixelData method used in that answer is deprecated in version v4.0.0-rc1 that I am using. So that answer does not help me.
Is there any other way to change the pixel data of a DICOM file using fo-DICOM?
Following code does replace the pixel data correctly.
public static bool ImportImage(string imageFile, string newFilePah, string oldDicomFile)
{
Bitmap bitmap = new Bitmap(imageFile);
int rows, columns;
byte[] pixels = GetPixels(bitmap, out rows, out columns);
MemoryByteBuffer buffer = new MemoryByteBuffer(pixels);
DicomDataset dataset = new DicomDataset();
var dicomfile = DicomFile.Open(oldDicomFile);
dataset = dicomfile.Dataset.Clone();
dataset.AddOrUpdate(DicomTag.PhotometricInterpretation, PhotometricInterpretation.Rgb.Value);
dataset.AddOrUpdate(DicomTag.Rows, (ushort)rows);
dataset.AddOrUpdate(DicomTag.Columns, (ushort)columns);
dataset.AddOrUpdate(DicomTag.BitsAllocated, (ushort)8);
DicomPixelData pixelData = DicomPixelData.Create(dataset, true);
pixelData.BitsStored = 8;
pixelData.SamplesPerPixel = 3;
pixelData.HighBit = 7;
pixelData.PhotometricInterpretation = PhotometricInterpretation.Rgb;
pixelData.PixelRepresentation = 0;
pixelData.PlanarConfiguration = 0;
pixelData.Height = (ushort)rows;
pixelData.Width = (ushort)columns;
pixelData.AddFrame(buffer);
dicomfile = new DicomFile(dataset);
dicomfile.Save(newFilePah);
return true;
}
private static byte[] GetPixels(Bitmap bitmap, out int rows, out int columns)
{
using(var stream = new MemoryStream())
{
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
rows = bitmap.Height;
columns = bitmap.Width;
return stream.ToArray();
}
}
You can see I have cleaned up your code much.
But the major change is using System.Drawing.Imaging.ImageFormat.Bmp instead of other formats. This depends on actual input image format. Use the format as that of input image.
For detailed insight, please refer to this source code on github.
Related
I'm trying to convert screenshots taken on an HDR display via IDXGIOutput6.DuplicateOutput1() into SDR GDI+ bitmaps. The color space of the display is DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 and the pixel format is DXGI_FORMAT_R16G16B16A16_FLOAT.
I can't find a series of Direct2D transforms that actually gives me a reasonable result though. I've tried many different combinations of effects and settings, but the output continues to either come out too bright, washed out, low contrast, or oversaturated.
The gist of what I'm doing is:
Grab HDR frame from the output
Bring it into a D2D bitmap
Calculate max content light level
Change color space to scRGB
Apply HDR tone mapping
Change color space to sRGB
Use WIC to convert to GUID_WICPixelFormat32bppPBGRA
Copy raw pixel data to a GDI+ bitmap with format PixelFormat.Format32bppPArgb
Currently I'm seeing the issue of the output being too bright, but I have no idea why. Any help in figuring out what the transform chain actually should be would be appreciated.
private const float NominalRefWhite = 80.0f;
private const int HistNumBins = 400;
private const float HistGamma = 0.1f;
private const int HistMaxNits = 1000000;
private const float OutputNits = 80.0f;
private Bitmap CaptureDxgiOutputFrame(IComObject<IDXGIOutput6> output, IComObject<ID3D11Device> device, IComObject<ID3D11DeviceContext> deviceCtx)
{
output.Object.GetDesc1(out var outputDesc).ThrowOnError();
Debug.WriteLine($"CaptureDxgiOutputFrame - OutputDesc: {ToDebugString(outputDesc)}");
var (frameInfo, frameTex) = DuplicateOutputFrame(output, device, deviceCtx);
using (var d2dDev = D2DFactoryInstance.Object.CreateDevice<ID2D1Device6>(device.As<IDXGIDevice>(true)))
using (var d2dCtx = d2dDev.Object.CreateDeviceContext<ID2D1DeviceContext6>())
{
var frameSurface = frameTex.AsNonOwned<IDXGISurface>();
frameTex.Object.GetDesc(out var frameDesc);
Debug.WriteLine($"CaptureDxgiOutputFrame - FrameDesc: {ToDebugString(frameDesc)}");
using (var inputColorContext = d2dCtx.CreateColorContextFromDxgiColorSpace(outputDesc.ColorSpace))
using (var scRgbColorContext = d2dCtx.CreateColorContext(D2D1_COLOR_SPACE.D2D1_COLOR_SPACE_SCRGB))
using (var sRgbColorContext = d2dCtx.CreateColorContext(D2D1_COLOR_SPACE.D2D1_COLOR_SPACE_SRGB))
using (var frameBmp = d2dCtx.CreateBitmapFromDxgiSurface(frameSurface, frameDesc.Format, inputColorContext))
using (var targetBmp = d2dCtx.CreateBitmap(frameDesc.Width, frameDesc.Height,
DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT, sRgbColorContext,
D2D1_BITMAP_OPTIONS.D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS.D2D1_BITMAP_OPTIONS_CANNOT_DRAW))
using (var inputToScRgbEffect = new D2DColorManagementEffect(d2dCtx))
using (var scRgbToSRgbEffect = new D2DColorManagementEffect(d2dCtx))
using (var hdrToneMapEffect = new D2DHdrToneMapEffect(d2dCtx))
{
d2dCtx.SetTarget(targetBmp);
var maxCll = CalculateMaxCll(d2dCtx, frameBmp);
Debug.WriteLine($"CaptureDxgiOutputFrame - Max CLL: {maxCll}");
inputToScRgbEffect.Quality = D2D1_COLORMANAGEMENT_QUALITY.D2D1_COLORMANAGEMENT_QUALITY_BEST;
inputToScRgbEffect.InputColorContext = inputColorContext;
inputToScRgbEffect.OutputColorContext = scRgbColorContext;
scRgbToSRgbEffect.Quality = D2D1_COLORMANAGEMENT_QUALITY.D2D1_COLORMANAGEMENT_QUALITY_BEST;
scRgbToSRgbEffect.InputColorContext = scRgbColorContext;
scRgbToSRgbEffect.OutputColorContext = sRgbColorContext;
hdrToneMapEffect.InputMaxLuminance = maxCll;
hdrToneMapEffect.OutputMaxLuminance = OutputNits;
hdrToneMapEffect.DisplayMode = D2D1_HDRTONEMAP_DISPLAY_MODE.D2D1_HDRTONEMAP_DISPLAY_MODE_SDR;
inputToScRgbEffect.SetInput(frameBmp);
hdrToneMapEffect.SetInput(inputToScRgbEffect);
scRgbToSRgbEffect.SetInput(hdrToneMapEffect);
d2dCtx.BeginDraw();
d2dCtx.DrawImage(scRgbToSRgbEffect.Effect);
d2dCtx.EndDraw();
var gdiBmp = To32BppGdiBitmap(d2dCtx, targetBmp);
gdiBmp.Save(DebugDir + "gdi.bmp", ImageFormat.Bmp);
return gdiBmp;
}
}
}
private static Bitmap To32BppGdiBitmap(IComObject<ID2D1DeviceContext6> d2dCtx, IComObject<ID2D1Bitmap> srcBmp)
{
var srcSize = srcBmp.GetSize();
var srcD2DPixelFormat = srcBmp.GetPixelFormat();
Debug.WriteLine($"To32BppGdiBitmap - Source D2D format: {Enum.GetName(typeof(DXGI_FORMAT), srcD2DPixelFormat.format)}, {Enum.GetName(typeof(D2D1_ALPHA_MODE), srcD2DPixelFormat.alphaMode)}");
using (var colorContext = d2dCtx.CreateColorContext(D2D1_COLOR_SPACE.D2D1_COLOR_SPACE_SRGB))
using (var srcBmpCpu = d2dCtx.CreateBitmap((uint)srcSize.width, (uint)srcSize.height, srcD2DPixelFormat.format,
colorContext, D2D1_BITMAP_OPTIONS.D2D1_BITMAP_OPTIONS_CPU_READ | D2D1_BITMAP_OPTIONS.D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
srcD2DPixelFormat.alphaMode))
{
srcBmpCpu.Object.CopyFromBitmap(IntPtr.Zero, srcBmp.Object, IntPtr.Zero).ThrowOnError();
var srcWicPixelFormat = D2DPixelFormatToWicPixelFormat(srcD2DPixelFormat);
Debug.WriteLine($"To32BppGdiBitmap - Source WIC format: {WICPixelFormat.GuidToName[srcWicPixelFormat]}");
using (var mappedData = srcBmpCpu.Map(D2D1_MAP_OPTIONS.D2D1_MAP_OPTIONS_READ))
using (var wicBmp = WicFactoryInstance.CreateBitmapFromMemory((uint)srcSize.width,
(uint)srcSize.height,
srcWicPixelFormat, mappedData.Pitch,
(int)mappedData.Pitch * (int)srcSize.height, mappedData.Bits))
using (var formatConverter = WicFactoryInstance.CreateFormatConverter())
{
var targetWicPixelFormat = WICPixelFormat.Format32bppPBGRA;
formatConverter.Object.Initialize(wicBmp.Object, ref targetWicPixelFormat,
WICBitmapDitherType.WICBitmapDitherTypeNone,
null, 0.0, WICBitmapPaletteType.WICBitmapPaletteTypeCustom);
return ToGdiBitmap(formatConverter);
}
}
}
private static float CalculateMaxCll(IComObject<ID2D1DeviceContext6> d2dCtx, IComObject<ID2D1Bitmap> bmp)
{
using (var histogramMatrixEffect = new D2DColorMatrixEffect(d2dCtx))
using (var histogramGammaEffect = new D2DGammaTransferEffect(d2dCtx))
using (var histogramEffect = new D2DHistogramEffect(d2dCtx))
{
var scale = HistMaxNits / NominalRefWhite;
histogramMatrixEffect.ColorMatrix = new D2D_MATRIX_5X4_F
{
_11 = 0.2126f / scale,
_21 = 0.7152f / scale,
_31 = 0.0722f / scale,
_44 = 1.0f,
};
histogramGammaEffect.RedExponent = HistGamma;
histogramGammaEffect.GreenDisable = true;
histogramGammaEffect.BlueDisable = true;
histogramGammaEffect.AlphaDisable = true;
histogramEffect.NumBins = HistNumBins;
histogramMatrixEffect.SetInput(bmp);
histogramGammaEffect.SetInput(histogramMatrixEffect);
histogramEffect.SetInput(histogramGammaEffect);
d2dCtx.BeginDraw();
d2dCtx.DrawImage(histogramEffect.Effect);
d2dCtx.EndDraw();
var buckets = histogramEffect.ReadOutput();
int maxCllBin = 0;
var runningSum = 0.0;
for (int i = buckets.Length - 1; i >= 0; i--)
{
runningSum += buckets[i];
maxCllBin = i;
if (runningSum >= 1.0 - 0.9999)
break;
}
var binNorm = (double)maxCllBin / HistNumBins;
var maxCll = Math.Pow(binNorm, 1 / HistGamma) * HistMaxNits;
return (float)maxCll;
}
}
private static Bitmap To32BppGdiBitmap(IComObject<ID2D1DeviceContext6> d2dCtx, IComObject<ID2D1Bitmap> srcBmp)
{
var srcSize = srcBmp.GetSize();
var srcD2DPixelFormat = srcBmp.GetPixelFormat();
Debug.WriteLine($"To32BppGdiBitmap - Source D2D format: {Enum.GetName(typeof(DXGI_FORMAT), srcD2DPixelFormat.format)}, {Enum.GetName(typeof(D2D1_ALPHA_MODE), srcD2DPixelFormat.alphaMode)}");
using (var colorContext = d2dCtx.CreateColorContext(D2D1_COLOR_SPACE.D2D1_COLOR_SPACE_SRGB))
using (var srcBmpCpu = d2dCtx.CreateBitmap((uint)srcSize.width, (uint)srcSize.height, srcD2DPixelFormat.format,
colorContext, D2D1_BITMAP_OPTIONS.D2D1_BITMAP_OPTIONS_CPU_READ | D2D1_BITMAP_OPTIONS.D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
srcD2DPixelFormat.alphaMode))
{
srcBmpCpu.Object.CopyFromBitmap(IntPtr.Zero, srcBmp.Object, IntPtr.Zero).ThrowOnError();
var srcWicPixelFormat = D2DPixelFormatToWicPixelFormat(srcD2DPixelFormat);
Debug.WriteLine($"To32BppGdiBitmap - Source WIC format: {WICPixelFormat.GuidToName[srcWicPixelFormat]}");
using (var mappedData = srcBmpCpu.Map(D2D1_MAP_OPTIONS.D2D1_MAP_OPTIONS_READ))
using (var wicBmp = WicFactoryInstance.CreateBitmapFromMemory((uint)srcSize.width,
(uint)srcSize.height,
srcWicPixelFormat, mappedData.Pitch,
(int)mappedData.Pitch * (int)srcSize.height, mappedData.Bits))
using (var formatConverter = WicFactoryInstance.CreateFormatConverter(wicBmp, WICPixelFormat.Format32bppPBGRA,
WICBitmapDitherType.WICBitmapDitherTypeNone, null, 0.0, WICBitmapPaletteType.WICBitmapPaletteTypeCustom))
{
return ToGdiBitmap(formatConverter);
}
}
}
private static Bitmap ToGdiBitmap(IComObject<IWICBitmapSource> srcBmp)
{
if (srcBmp.GetPixelFormat() != WICPixelFormat.Format32bppPBGRA)
throw new ArgumentException("Input must be 32bpp BGRA with pre-multiplied alpha.");
var sz = srcBmp.GetSize();
var stride = (int)sz.width * 4;
var gdiBmp = new Bitmap((int)sz.width, (int)sz.height, PixelFormat.Format32bppPArgb);
var buf = new byte[(int)sz.height * stride];
srcBmp.Object.CopyPixels(IntPtr.Zero, (uint)stride, buf.Length, buf).ThrowOnError();
var gdiBmpData = gdiBmp.LockBits(new Rectangle(0, 0, (int)sz.width, (int)sz.height), ImageLockMode.WriteOnly,
PixelFormat.Format32bppPArgb);
try
{
unsafe
{
fixed (byte* src = buf)
{
Buffer.MemoryCopy(src, (void*)gdiBmpData.Scan0, buf.Length, buf.Length);
}
}
}
finally
{
gdiBmp.UnlockBits(gdiBmpData);
}
return gdiBmp;
}
I have an image. I read the text content using ironocr. The following code used to read text.
var Ocr = new AutoOcr();
var Result = Ocr.Read(bmpCrop);
string text = Result.Text;
return text;
But the text trims the space and I couldn't get the exact copy of the text as in the image. Is there way or any other ocr libraries that reads text as an exact copy from the image. Please find the image attached, that I have used to read using ocr.
I have tried the following methode specified in the below url also, This also not working for me.
How to preserve document structure in tesseract
I have found that the latest IronOCR has a detailed document object model of pages, blocks, paragraphs, lines, words and characters
https://ironsoftware.com/csharp/ocr/examples/results-objects/
using IronOcr;
using System.Drawing; //for image export
// We can delve deep into OCR results as an object model of
// Pages, Barcodes, Paragraphs, Lines, Words and Characters
// This allows us to explore, export and draw OCR content using other APIs/
var Ocr = new IronTesseract();
Ocr.Configuration.EngineMode = TesseractEngineMode.TesseractAndLstm;
Ocr.Configuration.ReadBarCodes = true;
using (var Input = new OcrInput(#"example.tiff"))
{
OcrResult Result = Ocr.Read(Input);
foreach (var Page in Result.Pages)
{
// Page object
int PageNumber = Page.PageNumber;
string PageText = Page.Text;
int PageWordCount = Page.WordCount;
// null if we dont set Ocr.Configuration.ReadBarCodes = true;
OcrResult.Barcode[] Barcodes = Page.Barcodes;
System.Drawing.Bitmap PageImage = Page.ToBitmap(Input);
int PageWidth = Page.Width;
int PageHeight = Page.Height;
foreach (var Paragraph in Page.Paragraphs)
{
// Pages -> Paragraphs
int ParagraphNumber = Paragraph.ParagraphNumber;
String ParagraphText = Paragraph.Text;
System.Drawing.Bitmap ParagraphImage = Paragraph.ToBitmap(Input);
int ParagraphX_location = Paragraph.X;
int ParagraphY_location = Paragraph.Y;
int ParagraphWidth = Paragraph.Width;
int ParagraphHeight = Paragraph.Height;
double ParagraphOcrAccuracy = Paragraph.Confidence;
OcrResult.TextFlow paragrapthText_direction = Paragraph.TextDirection;
foreach (var Line in Paragraph.Lines)
{
// Pages -> Paragraphs -> Lines
int LineNumber = Line.LineNumber;
String LineText = Line.Text;
System.Drawing.Bitmap LineImage = Line.ToBitmap(Input); ;
int LineX_location = Line.X;
int LineY_location = Line.Y;
int LineWidth = Line.Width;
int LineHeight = Line.Height;
double LineOcrAccuracy = Line.Confidence;
double LineSkew = Line.BaselineAngle;
double LineOffset = Line.BaselineOffset;
foreach (var Word in Line.Words)
{
// Pages -> Paragraphs -> Lines -> Words
int WordNumber = Word.WordNumber;
String WordText = Word.Text;
System.Drawing.Image WordImage = Word.ToBitmap(Input);
int WordX_location = Word.X;
int WordY_location = Word.Y;
int WordWidth = Word.Width;
int WordHeight = Word.Height;
double WordOcrAccuracy = Word.Confidence;
if (Word.Font != null)
{
// Word.Font is only set when using Tesseract Engine Modes rather than LTSM
String FontName = Word.Font.FontName;
double FontSize = Word.Font.FontSize;
bool IsBold = Word.Font.IsBold;
bool IsFixedWidth = Word.Font.IsFixedWidth;
bool IsItalic = Word.Font.IsItalic;
bool IsSerif = Word.Font.IsSerif;
bool IsUnderLined = Word.Font.IsUnderlined;
bool IsFancy = Word.Font.IsCaligraphic;
}
foreach (var Character in Word.Characters)
{
// Pages -> Paragraphs -> Lines -> Words -> Characters
int CharacterNumber = Character.CharacterNumber;
String CharacterText = Character.Text;
System.Drawing.Bitmap CharacterImage = Character.ToBitmap(Input);
int CharacterX_location = Character.X;
int CharacterY_location = Character.Y;
int CharacterWidth = Character.Width;
int CharacterHeight = Character.Height;
double CharacterOcrAccuracy = Character.Confidence;
// Output alternative symbols choices and their probability.
// Very useful for spellchecking
OcrResult.Choice[] Choices = Character.Choices;
}
}
}
}
}
}
Currently I'm working with a fingerletter recognition using visual studio 2013 C# along with emguCV. i already have a database using ms access and a program which allow me to add images(in graysacle already) to it.
I've got 2 imageBox (imageBox0, imageBox1, imageBox2).., imageBox0 displays the live streaming of my camera., imageBox1 displays the processed image of imageBox0 (contour of my hand, grayscale, rectangle) and imageBox2 displays a selected image from the ms access database.
What I needed is a way to recognize an image from imageBox1 that is similar to imageBox2 or (in the whole images inside the database)
Here are snapshots of what I am doing:
I am using EmguCV version 2.
private Image GetImgFromDB()
{
if (rowNumber >= 0)
{
byte[] FetchedImgBytes = (byte[])LocalDataTable.Rows[rowNumber]["alphaImage"];
MemoryStream stream = new MemoryStream(FetchedImgBytes);
FetchedImg = Image.FromStream(stream);
txtAlphaName.Text = (string)LocalDataTable.Rows[rowNumber]["AlphaName"];
Bitmap FetchedImgCV = (Bitmap)FetchedImg;
normalizedMasterImage = new Image<Gray, Byte>(FetchedImgCV);
return FetchedImg;
}
else
{
MessageBox.Show("There are no images in the dataase yet. add some Please");
//return null;
}
return null;
} `
I've been though this with quiet a good degree of success. Mine compares two images and you set the percentage differences between to two to raise an alert.
The key is to compare hashes, not raw data...
class bitmapCompare
{
public enum CompareResult
{
ciCompareOk,
ciPixelMismatch,
ciSizeMismatch
};
public static CompareResult Compare(bool useHash, Bitmap bmp1, Bitmap bmp2, out double err, out Bitmap diff)
{
CompareResult cr = CompareResult.ciCompareOk;
int er = 0;
err = 0;
diff = new Bitmap(bmp1.Width, bmp1.Height);
//Test to see if we have the same size of image
if (bmp1.Size != bmp2.Size)
{
cr = CompareResult.ciSizeMismatch;
err = 100;
}
else
{
//Convert each image to a byte array
System.Drawing.ImageConverter ic =
new System.Drawing.ImageConverter();
byte[] btImage1 = new byte[1];
btImage1 = (byte[])ic.ConvertTo(bmp1, btImage1.GetType());
byte[] btImage2 = new byte[1];
btImage2 = (byte[])ic.ConvertTo(bmp2, btImage2.GetType());
//Compute a hash for each image
SHA256Managed shaM = new SHA256Managed();
byte[] hash1 = shaM.ComputeHash(btImage1);
byte[] hash2 = shaM.ComputeHash(btImage2);
//Compare the hash values
if (useHash)
{
for (int i = 0; i < hash1.Length && i < hash2.Length; i++)
{
if (hash1[i] != hash2[i])
{
er++;
cr = CompareResult.ciPixelMismatch;
}
}
}
else
{
int totalPixels = 0;
er = 0;
for (int x = 0; x < bmp1.Width; x++)
{
for (int y = 0; y < bmp1.Height; y++)
{
totalPixels++;
if (bmp1.GetPixel(x, y) != bmp2.GetPixel(x, y))
{
diff.SetPixel(x, y, Color.Black);
er++;
cr = CompareResult.ciPixelMismatch;
}
else
diff.SetPixel(x, y, Color.White);
}
}
System.Diagnostics.Debug.WriteLine("Total pixels:{0}", totalPixels);
System.Diagnostics.Debug.WriteLine("Diff pixels:{0}", er);
if (er > 0)
err = (double)er / ((double)bmp1.Height * (double)bmp1.Width);
else
err = 0;
if (err > 0) err = Math.Round(err*100, 1);
if (err > 100) err = 100;
}
}
return cr;
}
Feel free to change, this was only a POC, nothing production.
I want to create a gif file with several frames.
I want to use the Method which Microsoft support--Image.SaveAdd
But I don't know how to set the EncoderParameters Paramater to make up a gif file.
I can't find documents to refer. So how to create a gif file with Image.SaveAdd
Probably, too late to be useful for the original poster, but I managed to create a proper gif using just System.Drawing. The code below is based on jschroedl's answer, but also sets the frame delays and number of animation loops.
// Gdi+ constants absent from System.Drawing.
const int PropertyTagFrameDelay = 0x5100;
const int PropertyTagLoopCount = 0x5101;
const short PropertyTagTypeLong = 4;
const short PropertyTagTypeShort = 3;
const inr UintBytes = 4;
//...
var gifEncoder = GetEncoder(ImageFormat.Gif);
// Params of the first frame.
var encoderParams1 = new EncoderParameters(1);
encoderParams1.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
// Params of other frames.
var encoderParamsN = new EncoderParameters(1);
encoderParamsN.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.FrameDimensionTime);
// Params for the finalizing call.
var encoderParamsFlush = new EncoderParameters(1);
encoderParamsFlush.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.Flush);
// PropertyItem for the frame delay (apparently, no other way to create a fresh instance).
var frameDelay = (PropertyItem)FormatterServices.GetUninitializedObject(typeof(PropertyItem));
frameDelay.Id = PropertyTagFrameDelay;
frameDelay.Type = PropertyTagTypeLong;
// Length of the value in bytes.
frameDelay.Len = Bitmaps.Count * UintBytes;
// The value is an array of 4-byte entries: one per frame.
// Every entry is the frame delay in 1/100-s of a second, in little endian.
frameDelay.Value = new byte[Bitmaps.Count * UintBytes];
// E.g., here, we're setting the delay of every frame to 1 second.
var frameDelayBytes = BitConverter.GetBytes((uint)100);
for (int j = 0; j < Bitmaps.Count; ++j)
Array.Copy(frameDelayBytes, 0, frameDelay.Value, j * UintBytes, UintBytes);
// PropertyItem for the number of animation loops.
var loopPropertyItem = (PropertyItem)FormatterServices.GetUninitializedObject(typeof(PropertyItem));
loopPropertyItem.Id = PropertyTagLoopCount;
loopPropertyItem.Type = PropertyTagTypeShort;
loopPropertyItem.Len = 1;
// 0 means to animate forever.
loopPropertyItem.Value = BitConverter.GetBytes((ushort)0);
using (var stream = new FileStream("animation.gif", FileMode.Create))
{
bool first = true;
Bitmap firstBitmap = null;
// Bitmaps is a collection of Bitmap instances that'll become gif frames.
foreach (var bitmap in Bitmaps)
{
if (first)
{
firstBitmap = bitmap;
firstBitmap.SetPropertyItem(frameDelay);
firstBitmap.SetPropertyItem(loopPropertyItem);
firstBitmap.Save(stream, gifEncoder, encoderParams1);
first = false;
}
else
{
firstBitmap.SaveAdd(bitmap, encoderParamsN);
}
}
firstBitmap.SaveAdd(encoderParamsFlush);
}
// ...
private ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
I had success with these parameters. img1,2,3,4... are the images I want to combine.
ULONG parameterValue;
EncoderParameters encoderParameters;
encoderParameters.Count = 1;
encoderParameters.Parameter[0].Guid = EncoderSaveFlag;
encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParameters.Parameter[0].NumberOfValues = 1;
encoderParameters.Parameter[0].Value = ¶meterValue;
// Save the first frame
parameterValue = EncoderValueMultiFrame;
rc = img1->Save(L"Output.gif", &encoderClsid, &encoderParameters);
assert(rc == Ok);
// Add the second frame
parameterValue = EncoderValueFrameDimensionTime;
rc = img1->SaveAdd(img2, &encoderParameters);
assert(rc == Ok);
// etc...adding frames img3,4,5...
// Done...
parameterValue = EncoderValueFlush;
rc = img1->SaveAdd(&encoderParameters);
assert(rc == Ok);
Edit: I just realized that you asked for C# and I have C++ code. Hopefully the parameters still apply.
If you want create gif with many picture you can use ngif. see this
//you should replace filepath
String [] imageFilePaths = new String[]{"c:\\01.png","c:\\02.png","c:\\03.png"};
String outputFilePath = "c:\\test.gif";
AnimatedGifEncoder e = new AnimatedGifEncoder();
e.Start( outputFilePath );
e.SetDelay(500);
//-1:no repeat,0:always repeat
e.SetRepeat(0);
for (int i = 0, count = imageFilePaths.Length; i < count; i++ )
{
e.AddFrame( Image.FromFile( imageFilePaths[i] ) );
}
e.Finish();
/* extract Gif */
string outputPath = "c:\\";
GifDecoder gifDecoder = new GifDecoder();
gifDecoder.Read( "c:\\test.gif" );
for ( int i = 0, count = gifDecoder.GetFrameCount(); i < count; i++ )
{
Image frame = gifDecoder.GetFrame( i ); // frame i
frame.Save( outputPath + Guid.NewGuid().ToString()
+ ".png", ImageFormat.Png );
}
How could I create a dynamic forum signature generator using ASP.NET MVC. I currently have a Stats fetcher that retrieves the user info to be used in the Forum Signature.
I'm trying to create a forum signature generator where a user can enter their user name and generate an image that they can put in their forum signature that will show everyone the users stats.
something like this http://www.xfire.com/miniprofile
I must have lost track of what I was doing I didn't mean supply such little info, But I think you will have an idea of what im trying to do now..
i would use abcPdf component, the image would be a hi-res pdf document.
you would then just need to pass text, font, color, x, y, w, h
then your render the PDF out as a jpg stream
a basic idea to get you going could be like this;
private void addTextToPDF(string cmyk, int fs, string fontname, Double posx,
Double posY, Double mWidth, Double mHeight, String text, Double hpos)
{
text = secure.reverseCleanup(text);
int lettercount1 = 0;
foreach (char c in text)
{ lettercount1 ++; }
TheDoc.Color.String = cmyk;
TheDoc.FontSize = fs;
var theFont = fontname;
TheDoc.Rect.Position(posx, posY);
TheDoc.Rect.Width = mWidth;
TheDoc.Rect.Height = mHeight;
TheDoc.HPos = hpos;
TheDoc.Font = TheDoc.EmbedFont(theFont, "Latin", false, true, true);
int didwrite = TheDoc.AddText(text);
string addedchars = TheDoc.GetInfo(didwrite, "Characters");
var oldid = didwrite;
if (addedchars != lettercount1.ToString())
didwrite = 0;
while (didwrite==0) // hits this if first run did not add text
{
TheDoc.Delete(oldid);
fs = fs - 2;
TheDoc.Color.String = cmyk;
TheDoc.FontSize = fs;
theFont = fontname;
TheDoc.Rect.Position(posx, posY);
TheDoc.Rect.Width = mWidth;
TheDoc.Rect.Height = mHeight;
TheDoc.HPos = hpos;
TheDoc.Font = TheDoc.EmbedFont(theFont, "Latin", false, true, true);
didwrite = TheDoc.AddText(secure.reverseCleanup(text));
addedchars = TheDoc.GetInfo(didwrite, "Characters");
oldid = didwrite;
if (addedchars != lettercount1.ToString())
didwrite = 0;
}
}
public byte[] convertPDFToImageStream()
{
byte[] jpgBytes = null;
byte[] theData = null;
theData = TheDoc.GetData();
TheDoc.Clear();
TheDoc.Read(theData);
TheDoc.Rendering.DotsPerInch = getDPI();
TheDoc.Rendering.ColorSpace = "RGB";
jpgBytes = TheDoc.Rendering.GetData("preview.jpg");
return jpgBytes;
}
that is the code to add text and also to render the PDF out as a stream JPG
very very good component.