Weird VB.net bitmap-to-picturebox scaling - vb.net

As part of a larger tool I'm working displaying pieces of a bitmap in a picturebox. In my current test case, I want to take a 200x200 section of the bitmap and display it in a 200x200 picturebox (clientsize area). The code runs, but I find I'm actually getting 600x600 pixels from the bitmap in the picturebox. This is all running on a 4K monitor, and I suspect some dpi-based scaling is the culprit. I will want to use that scaling to allow zoom, but I can't see why it's happening at all right now.
Here's the code - the src_rect and dest_rect are identical, both 200x200, and this variant of DrawImage shouldn't be scaling on its own:
Public Overrides Sub Draw_Data(Pbox As PictureBox, Src_Rect As Rectangle, Dst_Rect As Rectangle)
''Pbox is the destination picturbox:
Dim bm As New Bitmap(Pbox.ClientSize.Width, Pbox.ClientSize.Height)
Dim gr As Graphics = Graphics.FromImage(bm) 'So GR relates to drawing destination.
gr.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
gr.SmoothingMode = Drawing2D.SmoothingMode.None
gr.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
'' Bitmap is the underlying data, a subset of which should be shown:
gr.DrawImage(Bitmap, Dst_Rect, Src_Rect, GraphicsUnit.Pixel)
Pbox.Image = bm
End Sub
I've played with the various gr.* settings, as well as the picturebox properties, trying pretty much anything reasonable, but I always get the same result. If it's an issue of bitmap "resolution", that's odd because the originating bitmap is (at least to me, conceptually) just an array of pixels. It would seem that a mapping of a 200x200 subset of that to a 200x200 bitmap used in the picturebox would be one-to-one. Does anyone see what I might be missing? If there's some scaling I have to apply, I can certainly do it, but I'd have to have some way of at least measuring the kind of weird scaling that's going on before I can compensate for it.

Related

Accessing pixel values from destination image of CopyMakeBorder function in Emgu CV

A little bit new to EmguCV here
Just want to ask quick question, about CopyMakeBorder function
Are the pixel values of the destination image accessible?
I want to process further the destination image, but when I tried to access pixel values from the image, it only returns me 0 (even in the location that are not supposed to be 0, for example the central pixel). When I used Imshow, it shows that the image borders are perfectly processed, but problem only persist when I try to access the pixel values, only getting 0 wherever the pixel location is.
This is not a problem when I tried to use destination images from other EmguCV functions, such as Threshold Function
Can anyone clarify? Thanks A lot!!
I am using VB.net, here is the code (I am away from my workstation for the weekend so I am just gonna try to remember the code, probably some capital letters here and there are mistyped, but I hope you get the gist.)
First I initialize the source images and destination image
Dim img As Image(Of Gray,Byte) = New Image (Of Gray, Byte)("myimage.jpg")
Dim img1 As Image(Of Gray,Byte) = New Image (Of Gray, Byte)(img.size)
CopyMakeBorder Function, extend 1 pixel to top, bottom, left and right. Border type constant 0 values
Cvinvoke.CopyMakeBorder(img,img1,1,1,1,1,BorderType.Constant, New MCvscalar(0))
Accessing pixel values from destination image, take example pixel in x = 100, y = 100, and channel 0 (as it is a grayscale image)
Console.writeline(img1.data(100,100,0))
This will make debug output to 0, and no matter where I try to take the pixel values, it is still 0, even though when I try to show the image that specific pixel should not be 0 (it is not black)
Cvinvoke.Imshow("test",img1)
You are trying to access the data through Image.Data, however, this doesn't include the added border(s); just the original bitmap.
The added border is in the Mat property, however. Through it the individual pixels can be accessed
' returns data from original bitmap
Console.WriteLine(img1.Data(100, 100, 0))
' returns data from modified bitmap
Console.WriteLine(img1.Mat.GetData(100, 100)(0))

2D Graphics with matrix transformation too small for integer mouseevents?

I'm designing a simple app to display some Cartesian type graphics using DrawLine() and DrawEllipse() functions into a PictureBox control. To make the coordinate system more "real-world" instead of the picture box I am using a matrix to flip the Y axis, scale everything down and reposition it so that (0,0) is at the center of the screen and (+2,+2) is at the upper right corner. All works well for drawing of graphics. However, in trying to read mouse events it appears that the MouseEventArgs variable (returned by most Mouse events) returns the mouse position X and Y as integers. I am properly using an inverted matrix to retrieve the coordinates at the scaled values, but at the scale I am using, this won't work as integers as I require screen positions in fractional values (1.5, 1.6, etc).
Is there no way to retrieve the mouse values as a floating point or double/decimal value that will give the "resolution" I require?
Some code fragements:
--Globally
Private MyTransform As Matrix
--Within the picturebox
Paint() event
Dim G As Graphics = e.Graphics
Dim mx As New Matrix(1, 0, 0, -1, 0, 0) 'Y-axis orientation flipped to match Cartesian plane
mx.Translate(PictureBox.Width / 2, -PictureBox.Height / 2) 'Move 0,0 to lower left corner
mx.Scale(100, 100)
G.Transform = mx
MyTransform = G.Transform
'All drawing is performed at this point and works fine.
--Within the MouseDown event
MyTransform.Invert()
'Here is the issue--the Mouse points returned, being integers, cannot properly
'show the mouse point if the transformation matrix has scaled up the drawing space at all.
e.Location.x 'is an integer, so it cannot show .01 as the proper mouse location within the transformed viewspace.
e.Location.y 'same issue.
MyTransform.Invert()
I've looked for a cartesian coordinate-based picturebox alternative to no avail, and Charting components won't work because they require the points being drawn be contained in their own proprietary containers/sets. I'm doing all the drawing myself with GDI-type methods. The only alternative seems to be to avoid doing the transformations with vb and doing all the translation/untranslation myself, unless someone has an alternative or example to suggest....?
Store the Scale as a property. Then as you change the scale you apply a function to the mouse coordinates difference. Example will use a 10/1 plane so the Scale will be 10.
Private Property Scale As Single
Function:
Private Function CorrectForScale(coord As Integer) As Single
Return (coord / Scale)
End Function
Now if the difference in distance between mouse position = 3 then the result after the function would be 0.3.

Low quality image into a label

I'm trying to pass an image showing to the user a countdown. For this, I'm using a separate thread where I'm checking when the countdown timer should be started, and when so, I draw an image for every 6 seconds passed.
What's annoying is when I pass the drawn image to the UI, the quality of the image is changed and it looks bad to the user.
This is my little script that handles the drawings:
Try
remainingTime = (#12:04:00 AM# - (DateTime.Now - local_DateTimeclick)).ToString("HH:mm:ss")
remainingTimeInSeconds = Convert.ToDateTime(remainingTime).Minute * 60 + Convert.ToDateTime(remainingTime).Second
If remainingTimeInSeconds Mod 6 = 0 Then
g.ResetTransform()
g.TranslateTransform(52, 52)
g.RotateTransform(230 - remainingTimeInSeconds / 6 * 9)
'g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
'g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
'g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
'g.CompositingMode = Drawing2D.CompositingMode.SourceCopy
'g.PixelOffsetMode = Drawing2D.PixelOffsetMode.
g.DrawImage(Tick, 10, 10)
End If
Catch
remainingTime = "Times Up"
End Try
In the above section,
- *local_DateTimeClick* is the variable that is set when the countdown should start
- Tick is a Bitmap that represents the image i have to draw for every 6 elipsed seconds
- g is a Graphics object from image that i return into the main window.
Also tried with changing the properties of g, but there was no positive effect.
Anyone have any idea what can i do to make this properly work without changing the quality of the returned image? Any tip/advice is welcomed.
Because Drawing2D.SmoothingMode only applies for 2D vector drawing methods such as Graphics.DrawEllipse and Graphics.DrawLine.
It doesn't affect drawing bitmaps with Graphics.DrawImage.
From the .NET documentation:
The smoothing mode specifies whether lines, curves, and the edges of filled areas use smoothing (also called antialiasing).
You have two options at this point:
Pre-render every possible orientation of the image, and then just display different Image objects at each tick
use the .NET vector drawing methods to render your image instead of using a pre-rendered bitmap.
Without knowing the actual image you're transforming, I can't say which would be more appropriate.
from a graphic designer perspective followed at my company, i think your should crop your images to fit in the label:
1. it enhance performance by saving processing power.
2. it look better ?!.
regards

Comparing two images - Detect egg in a nest

I have a webcam directly over a chicken nest. This camera takes images and uploads them to a folder on a server. I'd like to detect if an egg has been laid from this image.
I'm thinking the best method would be to compare the contrast as the egg will be much more reflective than the straw nest. (The camera has Infrared so the image is partly grey scale)
I'd like to do this in .NET if possible.
Try to resize your image to a smaller size, maybe 10 x 10 pixel. This averages out any small disturbing details.
Const N As Integer = 10
Dim newImage As New Bitmap(N, N)
Dim fromCamera As Image = Nothing ' Get image from camera here
Using gr As Graphics = Graphics.FromImage(newImage)
gr.SmoothingMode = SmoothingMode.HighSpeed
gr.InterpolationMode = InterpolationMode.Bilinear
gr.PixelOffsetMode = PixelOffsetMode.HighSpeed
gr.DrawImage(fromCamera, New Rectangle(0, 0, N, N))
End Using
Note: you do not need a high quality, but you need a good averaging. Maybe you will have to test different quality settings.
Since now, a pixel covers a large area of your original image, a bright pixel is very likely part of an egg. It might also be a good idea to compare the brightness of the brightest pixel to the average image brightness, since that would reduce problems due to global illumination changes.
EDIT (in response to comment):
Your code is well structured and makes sense. Here some thoughts:
Calculate the gray value from the color value with:
Dim grayValue = c.R * 0.3 + c.G * 0.59 + c.B * 0.11
... instead of comparing the three color components separately. The different weights are due to the fact, that we perceive green stronger than red and red stronger than blue. Again, we do not want a beautiful thumbnail we want a good contrast. Therefore, you might want to do some experiments here as well. May be it is sufficient to use only the red component. Dependent on lighting conditions one color component might yield a better contrast than others. I would recommend, to make the gray conversion part of the thumbnail creation and to write the thumbnails to a file or to the screen. This would allow you to play with the different settings (size of the thumbnail, resizing parameters, color to gray conversion, etc.) and to compare the (intermediate) results visually. Creating a bitmap (bmp) with the (end-)result is a very good idea.
The Using statement does the Dispose() for you. It does it even if an exception should occur before End Using (There is a hidden Try Finally involved).

Get resolution of bitmap from file vb .net

I have a filename that leads to a picture. It is not an embedded resource. My bitmap object always tells me the resolution is 96x96 no matter what, how can I get the actual resolution. Thanks
96 sounds pretty accurate to me. I think you're confusing pixel dimension with resolution.
Resolution is the number of dots per inch* (DPI), and 96 is a common number for graphics targeted at monitor display.
As mentioned, the Height and Width properties are probably what you're looking for.
*Note: technically, I should have said PPI, as dots and pixels aren't necessarily interchangeable.
The methods you are looking for are those :
Dim bmp as Bitmap = new Bitmap(IMAGE_NAME_LOCATION)
bmp.HorizontalResolution ' --> Horizontal PPI (points per inch)
bmp.VerticalResolution ' --> Vertical PPI
bmp.SetResolution ' --> Define both Horizontal and Vertical PPI
try this (its in C#):
Bitmap b = new Bitmap(IMAGE_NAME_LOCATION);
Size s = b.Size;
s.Height;
s.Width;
Height & width are in pixels. The Height & width are the original pic's size.
If you're loading a file using Bitmap.FromFile("C:\whatever.jpg"), and the resulting Bitmap has a .Width of 96 and a .Height of 96, then that is the actual resolution of that image.
If what you're doing is loading a file into a PictureBox control by setting its Image property in the designer (and browsing for the file), then it may be that your PictureBox just happens to be 96x96 and the SizeMode is set to Stretch, which would make any file you load appear to be 96x96.
It's simple:
Bitmaps don't contain resolution information. They are only an ordered collection of pixels. They're device-independent. You can show the same bitmap at different resolutions (pixels per inch) on two different devices.
The fact that your bitmap object has a resolution property is misleading.