How to resize image according to original image ration? - vb.net

The white background after re-sizing the image using VB.net as shown below. I tried to change some values but failed.
Dim FileToResize As String = Server.MapPath("~/images/" & filename & FileUpload1.FileName)
Using originalBitmap As Bitmap = Bitmap.FromFile(FileToResize, True), newbmp As Bitmap = New Bitmap(200, 200)
Dim WidthVsHeightRatio = CDec(originalBitmap.Width) / CDec(originalBitmap.Height)
Using newg As Graphics = Graphics.FromImage(newbmp)
newg.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
newg.Clear(Color.White)
If WidthVsHeightRatio = 1D Then
newg.DrawImage(originalBitmap, 0, 0, 200, 200)
newg.Save()
ElseIf WidthVsHeightRatio < 1D Then 'Image is taller than wider
newg.DrawImage(originalBitmap, New RectangleF(New PointF((100 - (200 * WidthVsHeightRatio) / 2), 0), New SizeF(200 * WidthVsHeightRatio, 200.0F)))
newg.Save()
Else 'Image is wider than taller
Dim inverse As Double = Math.Pow(WidthVsHeightRatio, -1)
newg.DrawImage(originalBitmap, New RectangleF(New PointF(0, 100 - ((200 * inverse) / 2)), New SizeF(200.0F, 200 * inverse)))
newg.Save()
End If
End Using
newbmp.Save(Server.MapPath("~/images/" & "_th_" & filename & FileUpload1.FileName), System.Drawing.Imaging.ImageFormat.Jpeg)
NewsItem.ImageLink = filename & FileUpload1.FileName
NewsItem.smallimage = "_th_" & filename & FileUpload1.FileName
End Using

I did a bit of research recently for generating Image Thumbnails on the fly for an eCommerce site. I started off doing this myself generating a bitmap and then resizing etc. After problems with image size on disc and quality I looked in to http://imageresizing.net/ and I haven't looked back since. It can generate images from byte(), streams and physicals files all very quickly with one line of code:
ImageBuilder.Current.Build(New MemoryStream(bImage), sImageLocation + sFullFileName, New ResizeSettings("maxwidth=214&maxheight=238"))
I would definitely recommend this component rather than trying to reinvent the wheel...

Related

Merge large monochromatic (BW) images

I need to work with large monochromatic images. I basically need to add a banner (frame and formatted text) to a hi-res (like 30000x20000px) monochromatic images (A2+ plots at 400dpi).
The default PixelFormat is Format1bppIndexed, which makes even those large picture relatively small in size. However, using .NET GDI+ Graphics object requires unindexed bitmap.
When turning the image to lowest available unindexed PixelFormat.Format16bppGrayscale:
bmpResized = ImgResized.Clone(New System.Drawing.Rectangle(0, 0, ImgResized.Width, ImgResized.Height),
System.Drawing.Imaging.PixelFormat.Format16bppGrayScale)
...it becomes to large to be handled by Image and Bitmap (OutOfMemoryExeption: Not enough memory - on 32GB machine).
I tried to create the banner separately and join the pictures pixel-wise, however I ran into the very same limitations - SetPixel requires unindexed bitmap.
Is there any way to overcome those issues?
EDIT: A possible solution would be to create a byte array and edit the bytes as per [https://social.msdn.microsoft.com/Forums/vstudio/en-US/54a096ff-46f3-45ce-8560-bf5a0618ef75/how-to-set-pixel-into-bitmap-with-pixel-format-of-format8bppindexed-?forum=csharpgeneral][1]. However I'm not sure how exactly I can "shift" the 2nd image byte-wise.
Finally I had to do it using bit-wise manipulations. It worked out perfectly, even the performance is very good.
' Create byte array for the main ploted TIF image
Dim bmp As New Drawing.Bitmap(Img.Width, Img.Height, Drawing.Imaging.PixelFormat.Format1bppIndexed)
bmp = Img ' getting bitmap from the default image
Dim data As System.Drawing.Imaging.BitmapData = bmp.LockBits(New Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), Drawing.Imaging.ImageLockMode.[WriteOnly], Drawing.Imaging.PixelFormat.Format1bppIndexed)
Dim bytes As Byte() = New Byte(data.Height * data.Stride - 1) {}
System.Runtime.InteropServices.Marshal.Copy(data.Scan0, bytes, 0, bytes.Length)
' Create byte array for the banner
Dim bmpB As New Drawing.Bitmap(Img.Width, Img.Height, Drawing.Imaging.PixelFormat.Format1bppIndexed)
bmpB = Img.Clone() ' banner had to be generated in 16bppp unindexed gray scale image to be able to use graphics
Dim dataB As System.Drawing.Imaging.BitmapData = bmpB.LockBits(New Drawing.Rectangle(0, 0, BmpB.Width, BmpB.Height), Drawing.Imaging.ImageLockMode.[WriteOnly], Drawing.Imaging.PixelFormat.Format1bppIndexed)
Dim bytesB As Byte() = New Byte(dataB.Height * dataB.Stride - 1) {}
System.Runtime.InteropServices.Marshal.Copy(dataB.Scan0, bytesB, 0, bytesB.Length)
' Join byte arrays: Resulting bitmap = plotted TIF + banner
Dim bytesResult((bytes.Length + bytesB.Length) - 1) As Byte
bytesB.CopyTo(bytesResult, 0)
bytes.CopyTo(bytesResult, bytesB.Length)
' Revert resulting byte array back to bitmap and save resulting TIF image
BmpResized = New Bitmap(Img.Width, Img.Height * 2, Drawing.Imaging.PixelFormat.Format1bppIndexed)
Dim dataResult As System.Drawing.Imaging.BitmapData = BmpResized.LockBits(New Drawing.Rectangle(0, 0, BmpResized.Width, BmpResized.Height), Drawing.Imaging.ImageLockMode.[WriteOnly], Drawing.Imaging.PixelFormat.Format1bppIndexed)
System.Runtime.InteropServices.Marshal.Copy(bytesResult, 0, dataResult.Scan0, bytes.Length + bytesB.Length - 0)
Dim exportFileName As String = filenames1(0).Replace(".tif", "_mod.tif")
exportFileName = exportFileName.Replace(".png", "_mod.tif")
BmpResized.Save(exportFileName, Drawing.Imaging.ImageFormat.Tiff)
' Unlock locked bitmaps
bmp.UnlockBits(dataResult)
BmpResized.UnlockBits(dataResult)
' dispose anything not used later...

Notification When Screen is Flashing VB.Net

I would like a notification to be triggered when part the screen starts to flash. This notification can be a msgbox for now, but I will eventually evolve it into an audible sound.
The purpose of this is we have a dashboard that displays various cells throughout the company. When a cell needs assistance, its spot on the dashboard starts to flash. The cells are displayed in horizontally stackedboxes like this;
Cell 1
Cell 2
Cell 3
Ect...
I would like to build an application that scans the screen, lets say every second, and gets each cells pixel intensity.
The notification will be triggered if/when the cells pixel intensity changes each scan for three consecutive scans in a row (ie. the cell must be flashing).
I am hoping that you guys can help me find a way to scan the screen an return a regions average pixel intensity to which I can then replicate and do the comparison to find out if it is flashing.
Thank you in advance, I am using VB.Net.
I was able to accomplish what I was asking by using this:
Private Sub AvgColors(ByVal InBitmap As Bitmap)
Dim btPixels(InBitmap.Height * InBitmap.Width * 3 - 1) As Byte
Dim hPixels As GCHandle = GCHandle.Alloc(btPixels, GCHandleType.Pinned)
Dim bmp24Bpp As New Bitmap(InBitmap.Width, InBitmap.Height, InBitmap.Width * 3,
Imaging.PixelFormat.Format24bppRgb, hPixels.AddrOfPinnedObject)
Using gr As Graphics = Graphics.FromImage(bmp24Bpp)
gr.DrawImageUnscaledAndClipped(InBitmap, New Rectangle(0, 0,
bmp24Bpp.Width, bmp24Bpp.Height))
End Using
Dim sumRed As Int32
Dim sumGreen As Int32
Dim sumBlue As Int32
For i = 0 To btPixels.Length - 1 Step 3
sumRed += btPixels(i)
sumGreen += btPixels(i + 1)
sumBlue += btPixels(i + 2)
Next
hPixels.Free()
Dim avgRed As Byte = CByte(sumRed / (btPixels.Length / 3))
Dim avgGreen As Byte = CByte(sumGreen / (btPixels.Length / 3))
Dim avgBlue As Byte = CByte(sumBlue / (btPixels.Length / 3))
MsgBox(avgRed & vbCrLf & avgGreen & vbCrLf & avgBlue)
End Sub
Private Function Screenshot() As Bitmap
Dim b As Bitmap = New Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)
Using g As Graphics = Graphics.FromImage(b)
g.CopyFromScreen(0, 0, 0, 0, b.Size, CopyPixelOperation.SourceCopy)
g.Save()
End Using
Return b
End Function
and from here I can just adjust the range of bitmap to what I need, add a timer to tick every second, and keep a variable to compare average RGB's to.
Most of the code found from here:
http://www.vbforums.com/showthread.php?776021-RESOLVED-Getting-Average-RGB-Color-Value-of-Entire-Screen

PDFsharp: How to find the Size dimensions of all the pages in a PDF file?

I am using PDFsharp, a great tool for working with PDFs.
I am writing an application in VB.net to work with PDFs for the printing industry. I need to know how to find out the dimensions of each page of the PDF.
Loop through all pages in the PDF and query the page size for each page.
Use the Pages property of the PdfDocument object.
Function GetPDFMetaData(ByRef pSourceFile As OpenFileDialog)
Dim lpdfDocument As PdfDocument = PdfReader.Open(pSourceFile.FileName, PdfDocumentOpenMode.Import)
Dim lpdfpage As PdfPage
Dim Text As String = ""
Dim Width As Integer
Dim Height As Integer
For idx As Integer = 0 To lpdfDocument.PageCount - 1
lpdfpage = lpdfDocument.Pages(idx)
Width = lpdfpage.Width.Millimeter
Height = lpdfpage.Height.Millimeter
Text = Text & vbCrLf & "Page: (" & idx + 1 & "); Size =(" & Width & " X " & Height & ")"
Next
Return Text
End Function

Put images side by side

How do I combine two images in VB.net to create one big image. They are both 1920x1080 jpegs and I'd like it to come out in a 3840x1080 jpeg.
Like this: I get these images in:
image 1
And get this out: image 1 side by side with image 2
I found it. The code is pretty easy, basically two images in one image out.
Dim ImageOne As System.Drawing.Image = Image.FromFile("img1")
Dim ImageTwo As System.Drawing.Image = System.Drawing.Image.FromFile("img2")
'replace path of image two with Image2.ImageUrl
Dim NewImageHeight As Integer = If(ImageOne.Height > ImageTwo.Height, ImageOne.Height, ImageTwo.Height)
'To calculate height of new image
Dim NewImageWidth As Integer = ImageOne.Width + ImageTwo.Width
' width of new image
Dim NewImageBmp As New Bitmap(NewImageWidth, NewImageHeight, Imaging.PixelFormat.Format32bppArgb)
' you can change the bpp as per your requirment. Size of image directly propotionate to bpp of image
Dim NewImageGrx As Graphics = System.Drawing.Graphics.FromImage(NewImageBmp)
NewImageGrx.DrawImageUnscaled(ImageOne, 0, 0)
'draw first image at coordinate 0,0
NewImageGrx.DrawImageUnscaled(ImageTwo, ImageOne.Width, 0)
'draw second image at coordinate image1.width,0
Dim CombineImage As String = Guid.NewGuid().ToString() + ".jpg"
NewImageBmp.Save("output file", ImageFormat.Jpeg)
' saving combined image. You can specify the ImageFormat as per your requirment.
'disposing objects after use
ImageOne.Dispose()
ImageTwo.Dispose()
NewImageBmp.Dispose()
NewImageGrx.Dispose()

Improving performance when working on Images with ASP.NET in VB

I am trying to speed up various aspects of my website.
The site is a shopping site various search pages which are slow. There are several potential reasons for this, one being working with images.
The other night I created a function which generates a thumbnail for my Facebook OG tags. The function writes a square image to the server 400px x 400px created by tiling the first few (up to 14) product images generated by a search query.
Running this script in total isolation on a test.aspx file gives me reasonable load time, but nothing I can do can speed it up, even when I don't return anything in the browser, just doing this process is taking about 3 seconds, which obviously expanded across the whole live site slows things down.
The entire code is:
Function mergeImgs(idList, imgList, head, fileName) As String
Try
Dim maxRows = HttpContext.Current.Request.QueryString("r")
Dim imgDim = 400
Dim Image3 As New Bitmap(imgDim, imgDim)
Dim g As Graphics = Graphics.FromImage(Image3)
Dim i = 0
Dim left = 0
Dim rows = 0
For Each item In idList
Dim img = Common.getImageUrl(idList(i), imgList(i), "server", "-tb")
i += 1
If img <> "/Images/awaiting.png" Then
Dim imageData As System.Drawing.Image = System.Drawing.Image.FromFile(img)
Dim aspect As Double = imageData.Width / imageData.Height ' determine aspect and decide how to display image
Dim Image1
Dim fixedHeight = imgDim / maxRows
Dim objGraphic As System.Drawing.Image = System.Drawing.Image.FromFile(img)
Dim aspectRatio = objGraphic.Height / objGraphic.Width
Dim reduction = fixedHeight / objGraphic.Height
Dim newwidth = objGraphic.Width * reduction
Image1 = New Bitmap(objGraphic, objGraphic.Width * reduction, fixedHeight)
g.DrawImage(Image1, New Point(left, fixedHeight * rows))
If left >= imgDim Then
rows += 1
left = 0
ElseIf left < imgDim Then
left += newwidth
ElseIf left < imgDim AndAlso rows = maxRows Then
Exit For
End If
End If
Next
Image3.Save(HttpContext.Current.Server.MapPath("/" & fileName), System.Drawing.Imaging.ImageFormat.Jpeg)
Return rootUrl & fileName
g.Dispose()
g = Nothing
Catch ex As Exception
Return ("<P>" & ex.ToString)
End Try
End Function
There's an external function getImageUrl which is simply a series of logic which writes out a directory structure based on the item ID, this is fast so I doubt holding it up at all.
Variables passed into the function are:
idList = a generic List(of String) ' a list of item IDs
imgList = a generic List(of String) ' a list of image names (image names only stored in DB)
head = the Page.Header ' not actually needed in this prototype, but aimed to allow this to write the OG tag to the Page Header
fileName = simply the name to give the generated image file
I can't help thinking that the section from
Dim imageData As System.Drawing.Image = System.Drawing.Image.FromFile(img)
onwards could potentially be sped up. What this does is work out the aspect ratio of the images loaded, then recalculate the size so they're all the same height so that the tile can be neatly populated in tidy rows.
Dim img = Common.getImageUrl(idList(i), imgList(i), "server", "-tb")
This line loads a thumbnail version ("-tb" in last variable) of the images concerned, so all images being dealt with are approx 50x50px so very small to load and determine the aspect ratios from
There are 4 versions of each image stored on my server, in 4 different sizes for fast display on the website, having messed around with the different images loaded here, it seems to make little difference to the script load time.
Is there anything faster than System.Drawing.Image.FromFile(img) that I can use to load in the images and determine the width and height of it?
There are a couple of other situations on my website where I am required to determine the dimensions of an image and then do something with it and all seem a bit slow.
Any advice most welcome!
Just for info, here's an example of an image generated by the above code
http://www.hgtrs.com/recents.jpg
This is generated from the products returned by this page (which might load slowly!):
http://www.hgtrs.com/search.aspx?Type=recents
I should state the my test.aspx file also includes a call to a database, having tested the query used directly, I get a query time of 0.18 seconds, I dont know how this time expands into a real application. I have optimised this query and the tables (it uses one nested query and a join) as much as I know how.