Resizing multiple images and saving them to a separate folder - vb.net

I need to resize and compress 200 images that I have stored in a folder.
I am getting these images in a list using this code that I got from another question:
Dim dir = New IO.DirectoryInfo("C:\\Users\\Charbel\\Desktop\\Images")
Dim images = dir.GetFiles("*.jpg", IO.SearchOption.AllDirectories).ToList
Dim pictures As New List(Of PictureBox)
For Each img In images
Dim picture As New PictureBox
picture.Image = Image.FromFile(img.FullName)
pictures.Add(picture)
Next
Now, I need to compress and reduce each image to (500x374) and then save them in another folder on my PC.

Well, let me first point out a couple of points about your code:
PictureBox doesn't serve any purpose here. You shouldn't create a PictureBox to use the Image.
Always remember to dispose the Image object (e.g., by wrapping it in a Using block) so you don't run into memory issues.
Unlike C#, VB.NET doesn't require escaping the \ character, therefore, you can write your path like this "C:\Users...".
Now, for resizing the image, you can simply create an instance of the Bitmap class with the constructor that takes an image and a size argument: Bitmap(Image, Size) or Bitmap(Image, Int32, Int32).
Here:
Dim sourcePath As String = "C:\Users\Charbel\Desktop\Images"
Dim outputPath As String = "C:\Users\Charbel\Desktop\Images\Resized"
IO.Directory.CreateDirectory(outputPath)
Dim dir = New IO.DirectoryInfo(sourcePath)
Dim files As IO.FileInfo() = dir.GetFiles("*.jpg", IO.SearchOption.AllDirectories)
For Each fInfo In files
Using img As Bitmap = Image.FromFile(fInfo.FullName)
Using resizedImg As New Bitmap(img, 500, 374)
resizedImg.Save(IO.Path.Combine(outputPath, fInfo.Name),
Imaging.ImageFormat.Jpeg)
End Using
End Using
Next

Related

VB.NET Display Image in PictureBox from Binary DB Value

I am trying to take a database binary entry where images are stored and convert it back to display in a PictureBox on a Windows Form. I have looked around and tried to piece a couple methods together however i get no errors and also no image in the PictureBox when trying to use the below code.
Dim MyByte = varReader("BinaryData")
Dim MyImg As Image
If MyByte IsNot Nothing Then
MyImg = bytesToImage(MyByte)
PictureBox1.Image = MyImg
End If
Public Function bytesToImage(ByVal byteArrayIn As Byte()) As Image
Dim ms As MemoryStream = New MemoryStream(byteArrayIn)
Dim returnImage As Image = Image.FromStream(ms)
Return returnImage
End Function
I know the Binary data is good as i have a different piece of code that has created it in the first place to store in the DB and the image displays fine on the Website which uses this data.
Any help or advice where i have gone wrong would be much appreciated!!
Many Thanks
EDITED::
The database column holding the information is varbinary(MAX)
This is the code i use to convert the image to the binary (I won't include the DB update part as this is just a basic update to the DB Column)
Dim varPictureBinary As Byte()
Dim filePath As String = "ImageFilePath"
Dim fStream As FileStream = New FileStream(filePath, FileMode.Open, FileAccess.Read)
Dim br As BinaryReader = New BinaryReader(fStream)
Dim fileInfo As FileInfo = New FileInfo(filePath)
varPictureBinary = br.ReadBytes(fileInfo.Length)
I then want to take a binary entry from this column and turn it back to an image and display it in a picture box in my program.

Is it possible to view multi page .Tif files in vb.net application?

I am hoping to be able to view .Tif files in my vb.net application - is there a component that can be used to do that. I've been messing around with Tiff Viewer but for some reason the file is stretched beyond anything - you can barely make out what it says. I tried to adjust the width of the viewer but it did not do much, it is still stretched horizontally.
Based on https://stackoverflow.com/a/401579/741136, this will load a multiframe tif into a collection of single frames:
Function LoadTif(filename As String) As List(Of Image)
Dim lstTif As New List(Of Image)
Dim bmp As Bitmap = DirectCast(Image.FromFile(filename), Bitmap)
For i As Integer = 0 To bmp.GetFrameCount(Imaging.FrameDimension.Page) - 1
bmp.SelectActiveFrame(Imaging.FrameDimension.Page, i)
Dim ms As New System.IO.MemoryStream
bmp.Save(ms, Imaging.ImageFormat.Tiff)
lstTif.Add(Image.FromStream(ms))
ms.Dispose()
Next i
Return lstTif
End Function

Upload and replace the same image file if it already exist

I have a picturebox named PB_Company_Logo and I have a button named btn_Save and within this button I have this function which saves the image in PB_Company_Logo to current_directory/images
Public Sub save_PB(PB_Name As PictureBox)
Dim filename As String = "company_logo.png"
Dim path As String = Directory.GetCurrentDirectory() & "\images"
Dim filename_path As String = System.IO.Path.Combine(path, filename)
If (Not System.IO.Directory.Exists(path)) Then
System.IO.Directory.CreateDirectory(path)
PB_Name.Image.Save(filename_path)
Else
PB_Name.Image.Save(filename_path)
End If
End Sub
The problem is, there are cases where the user will upload a new company_logo.png. I want the system to treat the uploading of new image as replacing the former company_logo.png.
I think the error in this line of code means that the file is currently in used (locked) and therefore cannot be replaced.
Else
PB_Name.Image.Save(filename_path)
End If
When you load a pictureBox control with an image file the ide (vs) puts a lock on the file. This happens when you set the image property of the pictureBox control to a file at design time.
You can use the FileStream object.
Example:
Dim fs As System.IO.FileStream
' Specify a valid picture file path on your computer.
fs = New System.IO.FileStream("C:\WINNT\Web\Wallpaper\Fly Away.jpg",
IO.FileMode.Open, IO.FileAccess.Read)
PictureBox1.Image = System.Drawing.Image.FromStream(fs)
fs.Close()
After you have done the workaround you can then use the System.IO.File.Exists(img) namespace to check wether or not a picture exists.
Example: This checks if the image passed through already exists and if it does then it will replace it.
Dim imageStr As String = "~/images/name.jpg"
If System.IO.File.Exists(imageStr) Then
Image1.ImageUrl = "~/images/name.jpg"
End If

how to prevent the Image.FromFile() method to lock the file

I am using following code to put JPG's into a DataGridView's Image cell.
If strFileName.ToLower.EndsWith(".jpg") Then
Dim inImg As Image = Image.FromFile(strFileName)
DataGridView4.Rows.Add()
DataGridView4.Rows(DataGridView4.Rows().Count - 1).Cells(0).Value = inImg
End If
The problem is that I need to save this file from within the program, but i get the message that the file is beeing used by another program.
So i tried to add inImg.Dispose() before the end if, but then the program doesnt display the images anymore in the DataGridView.
How can i add images in the DataGridView without locking them?
thanks
When you use the Image.FromFile(strFileName) method to create the Image, the method locks the file until you release the Image. The exact reason is explained below. And it's why you can't access more than one time to the same image file with this method.
You could instead:
use the Image.FromStream(stream) method.
that you use with a New FileStream or a MemoryStream that you create from the image file.
Here are possible implementation of a custom SafeImageFromFile method that doesn't lock the image file:
Public Shared Function SafeImageFromFile(path As String) As Image
Using fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim img = Image.FromStream(fs)
Return img
End using
End Function
Or
Public Shared Function SafeImageFromFile(path As String) As Image
Dim bytes = File.ReadAllBytes(path)
Using ms As New MemoryStream(bytes)
Dim img = Image.FromStream(ms)
Return img
End Using
End Function
Usage
If strFileName.ToLower.EndsWith(".jpg") Then
Dim inImg As Image = SafeImageFromFile(strFileName)
Dim index as integer = DataGridView4.Rows.Add()
DataGridView4.Rows(index).Cells(0).Value = inImg
End If
Important note
Here I create the FileStream or a MemoryStream using a Using statement to make sure the stream is released. It works fine on my system and it seems it work for you too, though MSDN says about Image.FromStream(stream) method:
You must keep the stream open for the lifetime of the Image.
The reason of this sentence is explain here: KB814675 Bitmap and Image constructor dependencies
GDI+, and therefore the System.Drawing namespace, may defer the
decoding of raw image bits until the bits are required by the image.
Additionally, even after the image has been decoded, GDI+ may
determine that it is more efficient to discard the memory for a large
Bitmap and to re-decode later. Therefore, GDI+ must have access to the
source bits for the image for the life of the Bitmap or the Image
object.
To retain access to the source bits, GDI+ locks any source file, and
forces the application to maintain the life of any source stream, for
the life of the Bitmap or the Image object.
So know the code above could generate GDIexceptions because of releasing the stream using Using. It could happen when you save the image from the file or during the image creation. From this thread Loading an image from a stream without keeping the stream open and Hans Passant's comment they fixed several problems with indexed pixel formats in the Vista version of gdiplus.dll., it would happen only on XP.
To avoid this you need to keep the stream open. The methods would be:
Public Shared Function SafeImageFromFile(path As String) As Image
Dim fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim img = Image.FromStream(fs)
Return img
End Function
Or
Public Shared Function SafeImageFromFile(path As String) As Image
Dim bytes = File.ReadAllBytes(path)
Dim ms = New MemoryStream(bytes)
Dim img = Image.FromStream(ms)
Return img
End Function
But those last methods have some disadvantage like not releasing the stream (memory issue) and they violate rule CA2000 Dispose objects before losing scope .
The KB article gives some workarounds:
Create a Non-Indexed Image
This approach requires that the new image be in a non-indexed pixel
format (more than 8 bits-per-pixel), even if the original image was in
an indexed format. This workaround uses the Graphics.DrawImage()
method to copy the image to a new Bitmap object:
Construct the original Bitmap from the stream, from the memory, or from the file.
Create a new Bitmap of the same size, with a pixel format of more than 8 bits-per-pixel (BPP).
Use the Graphics.FromImage() method to obtain a Graphics object for the second Bitmap.
Use Graphics.DrawImage() to draw the first Bitmap onto the second Bitmap.
Use Graphics.Dispose() to dispose of the Graphics.
Use Bitmap.Dispose() to dispose of the first Bitmap.
Create an Indexed Image
This workaround creates a Bitmap object in an indexed format:
Construct the original Bitmap from the stream, from the memory, or from the file.
Create a new Bitmap with the same size and pixel format as the first Bitmap.
Use the Bitmap.LockBits() method to lock the whole image for both Bitmap objects in their native pixel format.
Use either the Marshal.Copy function or another memory copying function to copy the image bits from the first Bitmap to the second Bitmap.
Use the Bitmap.UnlockBits() method to unlock both Bitmap objects.
Use Bitmap.Dispose() to dispose of the first Bitmap.
Here is an implementation of Non-Indexed Image creation, based on KB article and this answer https://stackoverflow.com/a/7972963/2387010 Your best bet is creating a pixel-perfect replica of the image -- though YMMV (with certain types of images there may be more than one frame, or you may have to copy palette data as well.) But for most images, this works:
Private Shared Function SafeImageFromFile(path As String) As Bitmap
Dim img As Bitmap = Nothing
Using fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fs)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
End Using
Return img
End Function
Someone indicated that what is important is that the FileStream is opened in read mode (FileAccess.Read).
True, but it makes more sens if you don't use Using statement and so you don't release the stream, or in multi threads context: FileAccess.Write is inappropriate, and FileAccess.ReadWrite is not required, but open the stream with FileAccess.Read mode won't prevent to have an IO.Exception if another program (or yours in multi threads context) has opened the file with another mode than FileAccess.Read.
If you want to be able to display the image and at the same time be able to save data to the file, Since you don't lock the file with those methods, you should be able to save the image (delete/overwrite the previous file) using the Image.Save method.
# Chris: Opening approximately 100 large (3400x2200) images with your final code, I was receiving an invalid argument crash on [img = new bitmap(...], I have seen this before opening an image of zero size, but that was not the case here. I added fs.dispose and successfully opened thousands of images of the same size of the same set as the first test without issue. I'm interested in your comments on this.
Private Function SafeImageFromFile(FilePath As String) As Image
Dim img As Bitmap = Nothing
Using fs As New FileStream(FilePath, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fs)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
fs.Dispose()
End Using
Return img
End Function
This works without issue, ran 4189 images 3400x2200 through it (twice) without issue, this moves the filestream outside of the function and re-uses it. Im closing the file to release the write lock. Im pointing a picturebox at this image in a loop for my test.
Private fsIMG As FileStream
Private Function SafeImageFromFile(FilePath As String) As Image
'Ref: http://stackoverflow.com/questions/18250848/how-to-prevent-the-image-fromfile-method-to-lock-the-file
Dim img As Bitmap = Nothing
fsIMG = New FileStream(FilePath, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fsIMG)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
fsIMG.Close()
Return img
End Function
After searching the internet for long time I found out I can use this code without any error.
Private fsIMG As FileStream
Private Function SafeImageFromFile(FilePath As String) As Image
'Ref: http://stackoverflow.com/questions/18250848/how-to-prevent-the-image-fromfile-method-to-lock-the-file
Dim img As Bitmap = Nothing
fsIMG = New FileStream(FilePath, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fsIMG)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
fsIMG.Close()
Return img
End Function
I encountered the same situation and used this code:
' Create memory stream from file
Dim ms As New MemoryStream()
' Open image file
Using fs As New FileStream(.FileName, FileMode.Open, FileAccess.Read)
' Save to memory stream
fs.CopyTo(ms)
End Using
' Create image from the file's copy in memory
Dim img = Image.FromStream(ms)
I didn't dispose the memory stream because it allows to save the image later using exactly the same encoding as the original file, using this code:
img.Save(someOtherStream, img.RawFormat)

how to load a file from folder to memory stream buffer

I am working on vb.net win form. My task is display the file names from a folder onto gridview control. when user clicks process button in my UI, all the file names present in gridview, the corresponding file has to be loaded onto memory stream buffer one after another and append the titles to the content of the file and save it in hard drive with _ed as a suffix to the file name.
I am very basic programmer. I have done the following attempt and succeeded in displaying filenames onto gridview. But no idea of later part. Any suggestions please?
'Displaying files from a folder onto a gridview
Dim inqueuePath As String = "C:\Users\Desktop\INQUEUE"
Dim fileInfo() As String
Dim rowint As Integer = 0
Dim name As String
Dim directoryInfo As New System.IO.DirectoryInfo(inqueuePath)
fileInfo = System.IO.Directory.GetFiles(inqueuePath)
With Gridview1
.Columns.Add("Column 0", "FileName")
.AutoResizeColumns()
End With
For Each name In fileInfo
Gridview1.Rows.Add()
Dim filename As String = System.IO.Path.GetFileName(name)
Gridview1.Item(0, rowint).Value = filename
rowint = rowint + 1
Next
Thank you very much for spending your valuable time to read this post.
to read a file into a memorystream is quite easy, just have a look at the following example and you should be able to convert it to suite your needs:
Dim bData As Byte()
Dim br As BinaryReader = New BinaryReader(System.IO.File.OpenRead(Path))
bData = br.ReadBytes(br.BaseStream.Length)
Dim ms As MemoryStream = New MemoryStream(bData, 0, bData.Length)
ms.Write(bData, 0, bData.Length)
then just use the MemoryStream ms as you please. Just to clearify Path holds the full path and filename you want to read into your memorystream.