0

there is a folder named "1"
"1" folder has around 1-100 image files in there
example file name: 000001.jpg 0000012.jpg 0000013.jpg ---> 000100.jpg

i want to combine all image from that folder (named 1)
and extract to one file (output_1.jpg) from 100 file to 1 file (Vertical)

but if there is 10 folder, combine all image from folder 1,2,3 .....
and extract to one file per folder
output_1.jpg (all image from folder 1)
output_2.jpg (all image from folder 2)
output_3.jpg (all image from folder 3)
and more

folder structure:
-----c:\
----------folder1\
---------------------000001.jpg
---------------------000002.jpg

the combined image file will be extract to c:\ (not in folder1 or 2 or 3 ....)

this is my code, i have to add more code file by file to combine them (headache attack)

    Dim Img1 As Bitmap = Image.FromFile("C:\1\000001.jpg")
    Dim Img2 As Bitmap = Image.FromFile("C:\1\000002.jpg")
    Dim Img3 As Bitmap = Image.FromFile("C:\1\000003.jpg")
    Dim Img4 As Bitmap = Image.FromFile("C:\1\000004.jpg")
    Dim Img5 As Bitmap = Image.FromFile("C:\1\000005.jpg")

    Dim extract_img As Bitmap
    Dim Width As Integer
    Dim Height As Integer
    Dim x As Integer
    Dim y As Integer

    If Img1.Width > Img2.Width Then
        Width = Img1.Width
    Else
        Width = Img2.Width
    End If

    Height = Img1.Height + Img2.Height + Img3.Height + Img4.Height + Img5.Height
    extract_img = New Bitmap(Width, Height)

    For x = 0 To Img1.Width - 1
        For y = 0 To Img1.Height - 1
            extract_img.SetPixel(x, y, Img1.GetPixel(x, y))
        Next
    Next
    For x = 0 To Img2.Width - 1
        For y = 0 To Img2.Height - 1
            extract_img.SetPixel(x, y + Img1.Height, Img2.GetPixel(x, y))
        Next
    Next
    For x = 0 To Img3.Width - 1
        For y = 0 To Img3.Height - 1
            extract_img.SetPixel(x, y + Img1.Height + Img2.Height, Img3.GetPixel(x, y))
        Next
    Next
    For x = 0 To Img4.Width - 1
        For y = 0 To Img4.Height - 1
            extract_img.SetPixel(x, y + Img1.Height + Img2.Height + Img3.Height, Img4.GetPixel(x, y))
        Next
    Next
    For x = 0 To Img5.Width - 1
        For y = 0 To Img5.Height - 1
            extract_img.SetPixel(x, y + Img1.Height + Img2.Height + Img3.Height + Img4.Height, Img5.GetPixel(x, y))
        Next
    Next

    extract_img.Save("C:\output.jpg", System.Drawing.Imaging.ImageFormat.Jpeg)
DREAM
  • 426
  • 2
  • 14
  • It appears you want to create a new Bitmap (from the word *Vertical*, entered in the description), which combines all source Bitmaps vertically. What is the average size of these Bitmaps? Or, if you have already measured it, what is the overall height of the destination Bitmap? – Jimi Apr 17 '20 at 08:54
  • @Jimi one image = width:1920px height:780px, i set width 1920px... so, after combine 5 file it will be width:1920px height:3900px – DREAM Apr 17 '20 at 11:12
  • Do you know that the Bitmap object has max dimensions of `32767` pixels (in both dimensions), but it must occupy a contiguous section of memory when it's created? So the actual maximum size depends on the memory status and availability. With an average height of `780px` per Image, you can merge a maximum of ~`41-42` images. – Jimi Apr 17 '20 at 11:34
  • @Jimi oh i dont know that... do you have better way to do it? – DREAM Apr 17 '20 at 11:49
  • @dmpower007 What are the combined images going to be used for? Would it be acceptable to make several combined images from one directory? – Andrew Morton Apr 17 '20 at 12:00

1 Answers1

1

You can get the image sizes from each file and work out how large the final image will be.

Then, as pointed out in a comment, you need to check that the final size is not too large.

Make a new bitmap to contain them all, then iterate over the files to draw them into the big one.

Finally, wrap it all up in an iteration over the subdirectories, something like this:

Imports System.Drawing.Imaging
Imports System.IO

Public Class Form1

    Function GetImageSize(fileName As String) As SizeF
        ' From "Getting image dimensions without reading the entire file"
        ' https://stackoverflow.com/questions/111345/getting-image-dimensions-without-reading-the-entire-file

        Dim dims As SizeF

        Using fs As New FileStream(fileName, FileMode.Open, FileAccess.Read)
            Using img = Image.FromStream(fs, False, False)
                dims = New SizeF(img.PhysicalDimension.Width, img.PhysicalDimension.Height)
            End Using
        End Using

        Return dims

    End Function

    Function GetOverallImageSize(srcDir As String) As Size
        Dim largestWidth As Single
        Dim overallHeight As Single

        For Each f In Directory.EnumerateFiles(srcDir, "*.jpg")
            Dim dims = GetImageSize(f)
            largestWidth = Math.Max(largestWidth, dims.Width)
            overallHeight += dims.Height
        Next

        Return New Size(CInt(largestWidth), CInt(overallHeight))

    End Function

    Sub MakeSprites(rootDir As String)
        For Each srcDir In New DirectoryInfo(rootDir).EnumerateDirectories

            Dim overallSize = GetOverallImageSize(srcDir.FullName)

            If overallSize.Width = 0 OrElse overallSize.Height = 0 Then
                MsgBox("No suitable image files found in " & srcDir.FullName)
                Continue For
            End If

            If overallSize.Width > 32767 OrElse overallSize.Height > 32767 Then
                MsgBox("Combined size of " & overallSize.ToString() & " is too large in " & srcDir.Name)
                Continue For
            End If

            Using bmp As New Bitmap(overallSize.Width, overallSize.Height, PixelFormat.Format24bppRgb)
                Dim g = Graphics.FromImage(bmp)
                Dim y = 0

                For Each f In Directory.EnumerateFiles(srcDir.FullName, "*.jpg")
                    Using im = Image.FromFile(f)
                        g.DrawImage(im, 0, y)
                        y += im.Height
                    End Using
                Next

                bmp.Save(Path.Combine(rootDir, srcDir.Name & ".png"), ImageFormat.Png)

            End Using

        Next

    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim rootDir = "C:\temp\sprites"
        MakeSprites(rootDir)

    End Sub

End Class

So from this directory content:

enter image description here

I got the file "1.png":

enter image description here

If you really want to save the end result as a JPEG, you will probably want to read How to: Set JPEG Compression Level because the default is less than good quality. You would need to add

Private Shared Function GetEncoder(ByVal format As ImageFormat) As ImageCodecInfo

    Dim codecs As ImageCodecInfo() = ImageCodecInfo.GetImageDecoders()

    Dim codec As ImageCodecInfo
    For Each codec In codecs
        If codec.FormatID = format.Guid Then
            Return codec
        End If
    Next codec
    Return Nothing

End Function

and modify MakeSprites to be

Sub MakeSprites(rootDir As String)
    Dim jpegEncoder As ImageCodecInfo = GetEncoder(ImageFormat.Jpeg)
    Dim myEncoder As Imaging.Encoder = Imaging.Encoder.Quality
    Dim myEncoderParameters As New EncoderParameters(1)
    Dim myEncoderParameter As New EncoderParameter(myEncoder, 85L)
    myEncoderParameters.Param(0) = myEncoderParameter

    For Each srcDir In New DirectoryInfo(rootDir).EnumerateDirectories

        Dim overallSize = GetOverallImageSize(srcDir.FullName)

        If overallSize.Width = 0 OrElse overallSize.Height = 0 Then
            MsgBox("No suitable image files found in " & srcDir.FullName)
            Continue For
        End If

        If overallSize.Width > 32767 OrElse overallSize.Height > 32767 Then
            MsgBox("Combined size of " & overallSize.ToString() & " is too large in " & srcDir.Name)
            Continue For
        End If

        Using bmp As New Bitmap(overallSize.Width, overallSize.Height, PixelFormat.Format24bppRgb)
            Dim g = Graphics.FromImage(bmp)
            Dim y = 0

            For Each f In Directory.EnumerateFiles(srcDir.FullName, "*.jpg")
                Using im = Image.FromFile(f)
                    g.DrawImage(im, 0, y)
                    y += im.Height
                End Using
            Next

            bmp.Save(Path.Combine(rootDir, srcDir.Name & ".jpg"), jpegEncoder, myEncoderParameters)

        End Using

    Next

End Sub

You should be disappointed with the result - especially at the borders between images. It's just down to how JPEG-encoding works.

You will probably want to add some Try...Catches too for when things go wrong. And the MakeSprites method should probably be a function which returns a list of the problems it encountered rather than showing message boxes itself.

Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
  • sir why i can use another code to combine image up to 80 image in 1 one file? https://i.ibb.co/JCDcTyx/123-Portrait.jpg – DREAM Apr 17 '20 at 12:09
  • @dmpower007 Were the individual images resized? The overall size of the image you linked to is 640x19733 pixels. – Andrew Morton Apr 17 '20 at 12:11
  • it's 1920x59200 http://www.mediafire.com/file/l3um79l8pwft6cx/123_Portrait.jpg/file – DREAM Apr 17 '20 at 12:21
  • @dmpower007 The 32767 limit is from GDI+, a part of Windows from a long time ago. If you do some research, you may find that using the [System.Windows.Media.Imaging Namespace](https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.imaging?view=netframework-4.8) instead allows for images of the size you need. Note that the next limit would be from JPEG itself, at 65 535 × 65 535 pixels. – Andrew Morton Apr 17 '20 at 12:30
  • hey sir can you help me again. i cant set image quality https://i.imgur.com/Cqp85L6.png – DREAM Apr 17 '20 at 14:36
  • @dmpower007 You should have studied the documentation more closely ;) Please see my updated answer and compare it to what you have to see where it went wrong. – Andrew Morton Apr 17 '20 at 14:52