0

Bitmap Image Multiplication I Found this Memory stream option to merge the files with given no of repeats. All the things are working file but in the End, the generated BMP file is not as per enter repetition. Need help. Commented lines in code is another way which I tried. enter code here public partial class Form1 : Form { FileStream bitmapfileload; Bitmap bmp; int NoOfRepeats; public Form1() { InitializeComponent(); }

    private void btnOpenFile_Click(object sender, EventArgs e)
    {
        DoubleBuffered = true;
        OpenFileDialog openFileDialog1 = new OpenFileDialog
        {
            InitialDirectory = @"D:\",
            Title = "Browse Design Files",
            CheckFileExists = true,
            CheckPathExists = true,
            DefaultExt = "tif",
            Filter = "Images (*.BMP;*.TIF)|*.BMP;*.TIF|" + "All files (*.*)|*.*",
            FilterIndex = 1,
            RestoreDirectory = true,
            Multiselect = false,
            ReadOnlyChecked = false,
            ShowReadOnly = true
        };

        if (openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            byte[] ba = null;
            foreach (String file in openFileDialog1.FileNames)
            {
                Image temp = new Bitmap(file);
                LblSourceWidth.Text = Convert.ToString(temp.Width);
                LblSourHeight.Text = Convert.ToString(temp.Height);
                temp.Dispose();
                bitmapfileload = new FileStream(file,FileMode.Open,FileAccess.Read,FileShare.ReadWrite);
                ba = streamToByteArray(bitmapfileload);
            }
            NoOfRepeats = int.Parse(ltbRepeat.Text); // Text box will give no of repeat for writing proceedure
            // List <byte[]> ListBa = new List<byte[]>();
            MemoryStream MergeFile = new MemoryStream();
            using (MemoryStream allFrameStream = new MemoryStream())
            {
                for (int i = 0; i < NoOfRepeats; i++)
                {
                    allFrameStream.Write(ba, 0, ba.Length);
                    allFrameStream.Position = ba.Length * i;
                    //ListBa.Add(allFrameStream.ToArray());
                    //ListBa.Add(ba);
                }
                MergeFile = allFrameStream;
                bmp = new Bitmap(MergeFile);
            }
            //byte[] finaleba = Combine(ListBa.ToArray());
            //byte[] finaleba = ListBa.Cast<byte[]>().SelectMany(a => a).ToArray();
            //LblListCount.Text = Convert.ToString(finaleba.Length); // List Count = no of repeats for FOR-LOOP
            //Stream Finalfile = new MemoryStream(MergeFile);
            //richTextBox1.Text = Convert.ToString(Finalfile);
          
           
            LblDestiWidth.Text = Convert.ToString(bmp.Width); // no change here
            LblDestiHeight.Text = Convert.ToString(bmp.Height); // no change here
            pictureBox1.Image = bmp;
            pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
        }
    }
    private static byte[] Combine(byte[][] arrays)
    {
        byte[] bytes = new byte[arrays.Sum(a => a.Length)];
        int offset = 0;

        foreach (byte[] array in arrays)
        {
            Buffer.BlockCopy(array, 0, bytes, offset, array.Length);
            offset += array.Length;
        }

        return bytes;
    }
    public static byte[] streamToByteArray(Stream input)
    {
        MemoryStream ms = new MemoryStream();
        input.CopyTo(ms);
        return ms.ToArray();
    }
  • The images are different sizes. So when you add an image to the memory stream you need to precede with the number of bytes. then when removing each image read the number of bytes and then get image based on byte size. – jdweng Jun 21 '21 at 10:34
  • Thanks for Prompt reply @jdweng. i am repeating same image using multiple time with For loop of no of repeats. i checked array length also & found array length is increasing according to no of repeats but displayed image size is same as original. – jaydeep6720 Jun 21 '21 at 10:39
  • That make perfect sense. If you add multiple images to an array the size of each image doesn't change. But the size of the entire array gets larger. – jdweng Jun 21 '21 at 10:44
  • what about writing all the image to stream by the length ??? which is actually i am doing it. So if stream length is increased then image length should also be increased but it didnt. can you suggest any correction in code ??? – jaydeep6720 Jun 21 '21 at 11:14
  • Please explain the actual end goal you want here, it seems you expect simply concatenating all the bytes from saving those files into one big byte array will somehow result in 1 image that has all those pixels? That's not how image file formats work. – Lasse V. Karlsen Jun 21 '21 at 11:17
  • @LasseV.Karlsen i want to read all bytes from one file , repeatedly appending them to one new file with size multiplied by no of repetition. Other way of image editing takes huge time limit & huge memory load as my files are in (18144X60000) pixels with 720 DPI resolution. is it like i need to apply some conversion when i convert stream to bitmap again ??? image is converting but problem is it is same as original size. – jaydeep6720 Jun 21 '21 at 11:23
  • The issue is, again, that image file formats doesn't work this way. You can't just append the bytes from several images into one big file and get one big image with all the pixels out the other end. You *will* have to process these as *images* and create one image with enough width and height for all the pixels, and then draw all those pixels into the image before finally saving it. There are some image file formats that are "raw" enough that you can read and write chunks from them on disk to avoid keeping it all in memory. – Lasse V. Karlsen Jun 21 '21 at 11:29
  • What you get from your approach is one file that has several individual images, and unless you do special handling of that when reading it in, the image decoder will likely read the first image only, then stop. – Lasse V. Karlsen Jun 21 '21 at 11:29
  • The approach I would use: 1. Convert each image into such a "raw" file format so that you can read chunks from it on disk without having to load the entire image. 2. Create a similar output image on disk, large enough to hold all your source images. 3. Copy chunks from each source image into the right chunk in the target image. 4. If needed, convert the resulting image back into the required target file format. You will have to find an external program to do the image conversion at the start and end, that is capable of handling large images. – Lasse V. Karlsen Jun 21 '21 at 11:50
  • Why would the size of one image change if you are adding additional images? – jdweng Jun 21 '21 at 11:59
  • @jdweng i am appending same image for multiple times to create one large image. – jaydeep6720 Jun 21 '21 at 12:04
  • You can't!!! An image has a header and you can't just modify the image without updating the header. – jdweng Jun 21 '21 at 13:04

1 Answers1

0

The example is not very obvious. I would assume the end goal is to produce a larger image, with multiple copies of the source image stacked on top of each other.

It looks like you are trying to do this by concatenating the streams for an image. This will not work since images have headers. This will cause only the first image to be decoded.

The correct approach would be to create a new, larger, bitmap, and copy all the pixel-values to this bitmap. For example:

    public static unsafe Bitmap Repeat(Bitmap source, int count)
    {
        var targetHeight = source.Height * count;
        var target = new Bitmap(source.Width, targetHeight, source.PixelFormat);
        var sourceData = source.LockBits(new Rectangle(0,0, source.Width, source.Height), ImageLockMode.ReadOnly, source.PixelFormat);
        var targetData = target.LockBits(new Rectangle(0, 0, target.Width, target.Height), ImageLockMode.ReadOnly,
            target.PixelFormat);
        try
        {
            var frameLength = sourceData.Stride * sourceData.Height;
            for (int i = 0; i < count; i++)
            {
                var targetPtr = targetData.Scan0 + i * frameLength;
                Buffer.MemoryCopy(sourceData.Scan0.ToPointer(), targetPtr.ToPointer(), frameLength * count, frameLength);
            }
        }
        finally
        {
            source.UnlockBits(sourceData);
            target.UnlockBits(targetData);
        }
        return target;
    }

This could be adopted to merge images of different size, but it would require copying the image row by row instead, and some more math regarding height, indices etc. Note that this code is untested, use at own risk.

Another approach would be to use the Graphics api with FromImage and DrawImageUnscaled. This would perhaps be easier if you have multiple images.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • Unfortunately his images is 18k x 60k, which you can't even create the first Bitmap object for, he will likely need to write the image processing from the ground up for his images. – Lasse V. Karlsen Jun 21 '21 at 11:48
  • 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.' it worked only for 2 repeats, after 3rd repeats i got above error. Repetition was in Horizonatal direction which i changed. According to my Target size, i need atleast 40 repeats to be accepted. actually i tried the same proceedure with many efforts but got size error in the last on each trials so i decided to play with some memory streams> – jaydeep6720 Jun 21 '21 at 12:01
  • You cannot use the .NET Image/Bitmap classes for this, they will not be able to handle these massive images. You will need to write the image processing for this from the ground up, or find a 3rd party library that can deal with huge images. – Lasse V. Karlsen Jun 21 '21 at 12:11
  • i tried magickimage, LibTiff.net but worked for me, but timings they took was arroung 15 sec which way too slow for my process. i think i need to develop entire project in C++ – jaydeep6720 Jun 21 '21 at 12:13
  • @jaydeep6720 images in horizontal direction would require copying row by row. And you need to be careful when calculating the indices to get the start of each row. Also according to [this](https://stackoverflow.com/questions/29175585/what-is-the-maximum-resolution-of-c-sharp-net-bitmap) the maximum size of GDI bitmaps is 2Gb, so you would need an external library to work with larger images. Still, the same approach could probably be used. – JonasH Jun 21 '21 at 12:15
  • @JonasH your code did work but the problem is it changed DPI. Just tested right now. if i increase DPI then it give GDI memory error. Yes GDI Bitmaps is 2GB max limit. – jaydeep6720 Jun 21 '21 at 13:02
  • I have Edited your code @JonasH with following part & its working fine now for even 720dpi files. ` finally { source.UnlockBits(sourceData); target.SetResolution(source.HorizontalResolution, source.VerticalResolution); target.UnlockBits(targetData); } ` – jaydeep6720 Dec 30 '21 at 05:19