Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# How to crop an image without using lots of memory? [duplicate]

Possible Duplicate:
How to crop huge image

There is a question asking how to crop an image here

Ive read it and used the answers. But they all seem to require loading the image into a Bitmap or Image.

This is causing memory issues for me. As Im trying to crop 5 images (8000 x 8000) into tiles. One at a time.

Correct me if im wrong, but thats 8000x8000x4 bytes = 244 MB. per image.

And randomly I get out of memory problem.

How can I get a 1000x1000 image from another image, with reduced memory consumption.

like image 980
IAmGroot Avatar asked Dec 05 '12 17:12

IAmGroot


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

Is C language easy?

Compared to other languages—like Java, PHP, or C#—C is a relatively simple language to learn for anyone just starting to learn computer programming because of its limited number of keywords.

What is C language?

C is an imperative procedural language supporting structured programming, lexical variable scope, and recursion, with a static type system. It was designed to be compiled to provide low-level access to memory and language constructs that map efficiently to machine instructions, all with minimal runtime support.


1 Answers

So, this is a decidedly non-trivial thing to do - basically, you'd have to re-implement the image decoder for a given image format. This is not simple.

For the "simple" Windows BMP format, there are these beasts to contend with:

  • BMP File Format
  • DIB Header Structure

That said, I had to give it a try over my lunch break...here's what I was able to come up with, in a nice LINQPad-ready script.

(NOTE: Windows BMP only!)

void Main()
{
    // Carve out a 100x100 chunk
    var top = 100;
    var left = 100;
    var bottom = 300;
    var right = 300;

    // For BMP only - open input
    var fs = File.OpenRead(@"c:\temp\testbmp.bmp");

    // Open output
    if(File.Exists(@"c:\temp\testbmp.cropped.bmp")) File.Delete(@"c:\temp\testbmp.cropped.bmp");
    var output = File.Open(@"c:\temp\testbmp.cropped.bmp", FileMode.CreateNew);
    var bw = new BinaryWriter(output);

    // Read out the BMP header fields
    var br = new BinaryReader(fs);
    var headerField = br.ReadInt16();
    var bmpSize = br.ReadInt32();
    var reserved1 = br.ReadInt16();
    var reserved2 = br.ReadInt16();
    var startOfData = br.ReadInt32();   

    // Read out the BMP DIB header
    var header = new BITMAPV5Header();  
    var headerBlob = br.ReadBytes(Marshal.SizeOf(header));
    var tempMemory = Marshal.AllocHGlobal(Marshal.SizeOf(header));
    Marshal.Copy(headerBlob, 0, tempMemory, headerBlob.Length);
    header = (BITMAPV5Header)Marshal.PtrToStructure(tempMemory, typeof(BITMAPV5Header));
    Marshal.FreeHGlobal(tempMemory);

    // This file is a 24bpp rgb bmp, 
    var format = PixelFormats.Bgr24;
    var bytesPerPixel = (int)(format.BitsPerPixel / 8);
    Console.WriteLine("Bytes/pixel:{0}", bytesPerPixel);

    // And now I know its dimensions
    var imageWidth = header.ImageWidth;
    var imageHeight = header.ImageHeight;
    Console.WriteLine("Input image is:{0}x{1}", imageWidth, imageHeight);

    var fromX = left;
    var toX = right;
    var fromY = imageHeight - top;
    var toY = imageHeight - bottom;

    // How "long" a horizontal line is
    var strideInBytes = imageWidth * bytesPerPixel;
    Console.WriteLine("Stride size is:0x{0:x}", strideInBytes);

    // new size
    var newWidth = Math.Abs(toX - fromX);
    var newHeight = Math.Abs(toY - fromY);
    Console.WriteLine("New slice dimensions:{0}x{1}", newWidth, newHeight);

    // Write out headers to output file 
    {
        // header = "BM" = "Windows Bitmap"
        bw.Write(Encoding.ASCII.GetBytes("BM"));    
        var newSize = 14 + Marshal.SizeOf(header) + (newWidth * newHeight * bytesPerPixel);
        Console.WriteLine("New File size: 0x{0:x} bytes", newSize);
        bw.Write((uint)newSize);    
        // 2 reserved shorts
        bw.Write((ushort)0);    
        bw.Write((ushort)0);            
        // offset to "data"
        bw.Write(header.HeaderSize + 14);

        // Tweak size in header to cropped size
        header.ImageWidth = newWidth;
        header.ImageHeight = newHeight;

        // Write updated DIB header to output
        tempMemory = Marshal.AllocHGlobal(Marshal.SizeOf(header));
        Marshal.StructureToPtr(header, tempMemory, true);
        byte[] asBytes = new byte[Marshal.SizeOf(header)];
        Marshal.Copy(tempMemory, asBytes, 0, asBytes.Length);
        Marshal.FreeHGlobal(tempMemory);
        bw.Write(asBytes);
        asBytes.Dump();
    }

    // Jump to where the pixel data is located (on input side)
    Console.WriteLine("seeking to position: 0x{0:x}", startOfData);
    fs.Seek(startOfData, SeekOrigin.Begin);

    var sY = Math.Min(fromY, toY);
    var eY = Math.Max(fromY, toY);
    for(int currY = sY; currY < eY; currY++)
    {
        long offset =  startOfData + ((currY * strideInBytes) + (fromX * bytesPerPixel));
        fs.Seek(offset, SeekOrigin.Begin);      

        // Blast in each horizontal line of our chunk
        var lineBuffer = new byte[newWidth * bytesPerPixel];
        int bytesRead = fs.Read(lineBuffer, 0, lineBuffer.Length);
        output.Write(lineBuffer, 0, lineBuffer.Length);
    }

    fs.Close();
    output.Close();
}

[StructLayout(LayoutKind.Sequential, Pack=0)]
public struct BITMAPV5Header 
{
    public uint HeaderSize;
    public int ImageWidth;
    public int ImageHeight;
    public ushort Planes;
    public ushort BitCount;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst=36)]
    public byte[] DontCare;
}
like image 94
JerKimball Avatar answered Sep 20 '22 12:09

JerKimball