Vb.net image mask making smooth along the edges - vb.net

Hey all i am trying to get my images to look nice and smooth (antialiasing) from using a mask in order to make the round image as you see below:
The original image looks like this:
The mask for the image above looks like this (the red being the mask color to take out):
It works but it gives me those not-so-nice jagged edges around it. The mask is an .png and also the image itself is a .png.
The code i use to make the mask is this:
picNextTopic1.Image = Image.FromStream(wc.OpenRead(anAPI.wallOrgPostImage(keying).Replace("{width}", "50").Replace("{height}", "50"))) 'Download the image from the website.
picNextTopic1.Image = ApplyMask(New Bitmap(picNextTopic1.Image), New Bitmap(My.Resources.mask), Color.Red) 'Apply mask to the downloaded image above.
The ApplyMask function is this:
Public Function ApplyMask(ByVal bImg As Bitmap, ByVal bMask As Bitmap, ByVal maskColor As Color) As Image
Dim wImg As Integer = bImg.Width
Dim hImg As Integer = bImg.Height
Dim wMask As Integer = bMask.Width
Dim hMask As Integer = bMask.Height
Dim intMask As Integer = maskColor.ToArgb
Dim intTransparent As Integer = Color.Transparent.ToArgb
Using fpImg As New FastPix(bImg)
Using fpMask As New FastPix(bMask)
Dim pixelsImg = fpImg.PixelArray
Dim pixelsMask = fpMask.PixelArray
For y As Integer = 0 To Math.Min(hImg, hMask) - 1
For x As Integer = 0 To Math.Min(wImg, wMask) - 1
Dim iImg As Integer = (y * wImg) + x
Dim iMask As Integer = (y * wMask) + x
If pixelsMask(iMask) = intMask Then
pixelsImg(iImg) = intTransparent
End If
Next
Next
End Using
End Using
Return bImg
End Function
Which uses FastPix found here.
Any help to smooth this out would be great! Thanks!
UPDATE
code for transparent form that i have:
Public Sub InitializeMyForm()
BackColor = Color.Plum
TransparencyKey = BackColor
End Sub

Playing around with this, I did manage to make a smooth image this way using a TextureBrush:
Dim profile As Image = Image.FromFile("c:\...\profile.png")
Protected Overrides Sub OnPaint(e As PaintEventArgs)
e.Graphics.Clear(Color.SteelBlue)
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
Using tb As New TextureBrush(profile)
tb.TranslateTransform(120, 64)
Using p As New GraphicsPath
p.AddEllipse(120, 64, profile.Width, profile.Width)
e.Graphics.FillPath(tb, p)
End Using
End Using
MyBase.OnPaint(e)
End Sub
The TranslateTransform and the AddEllipse location use the same point information in order to "center" the texture brush appropriately.
The result:

Related

The cropping area has an x and y offset in the new bitmap, but only if the original has been scaled

a strange phenomenon occurs.
With my edge detection program, I can transfer the inside of the GraphicsPath to a new image.
It always works great – except when I scale the original image with GIMP and Word (aspect ratio remains, only the dimensions are changed). Then the area is shifted. To the left and up. See attachement. In line 68, I looked what is in rectCutout. Everything OK.
Does this have anything to do with GIMP? The dots per inch are the same (72). The compression quality of the JPEG also (100%).
I just realized: if I scale an image larger, the result is completely black.
The strange thing is: I'm not saying: the picture that is drawn on is larger than the picture that is saved. Then it would be logical that the path is not in the same position. It's about the fact that the loaded image is just smaller.
I would be happy if someone could tell me why. 😄
this is the scaled image which is loaded
Here you see the GUI, ready to save
cropped image, area has x and y offset
#Disable Warning CA1707 ' Bezeichner dürfen keine Unterstriche enthalten
Imports System.Drawing.Drawing2D
Imports Microsoft.WindowsAPICodePack.Dialogs
Public NotInheritable Class AllesGrafische
Public Shared Sub Paint_the_Rectangle(ByVal g As Graphics, ByVal recta As Rectangle)
If g IsNot Nothing Then
g.SmoothingMode = SmoothingMode.AntiAlias
g.CompositingQuality = CompositingQuality.HighQuality
g.PixelOffsetMode = PixelOffsetMode.HighQuality
g.InterpolationMode = InterpolationMode.HighQualityBilinear
Using Pen_Hellblau As Pen = New Pen(Color.FromArgb(0, 200, 255), 1.0F)
g.DrawRectangle(Pen_Hellblau, recta)
End Using
End If
End Sub
Public Shared Sub Draw_Curve(ByVal g As Graphics, ByVal theList As List(Of Point))
If theList IsNot Nothing AndAlso theList.Count > 0 AndAlso g IsNot Nothing Then
g.SmoothingMode = SmoothingMode.AntiAlias
g.CompositingQuality = CompositingQuality.HighQuality
g.PixelOffsetMode = PixelOffsetMode.HighQuality
g.InterpolationMode = InterpolationMode.HighQualityBilinear
Dim theList_neu As New List(Of Point)
Using gp As New GraphicsPath
For i As Integer = 1 To theList.Count - 1 Step 1
Dim a As Integer = theList(i).X
Dim b As Integer = theList(i).Y
Dim c As Integer = theList(i - 1).X
Dim d As Integer = theList(i - 1).Y
Dim Entfernungsbetrag As Double = Math.Sqrt(Math.Pow(a, 2) + Math.Pow(b, 2) + Math.Pow(c, 2) + Math.Pow(d, 2) - 2 * a * c - 2 * b * d)
If Entfernungsbetrag < Form1.erlaubte_Entfernung Then
theList_neu.Add(theList(i))
End If
Next
If theList_neu.Count = 0 Then Return
gp.AddLines(theList_neu.ToArray())
Using Pen_hellrosa As Pen = New Pen(Color.FromArgb(255, 64, 239), 1.0F)
g.DrawPath(Pen_hellrosa, gp)
End Using
If Form1.ClosePath Then
gp.CloseFigure()
End If
If Form1.CheckBox1.Checked Then
Dim Speicherpfad As String
Using SFD1 As New CommonSaveFileDialog
SFD1.Title = "Wo soll das Bild gespeichert werden?"
SFD1.Filters.Add(New CommonFileDialogFilter("PNG", ".png"))
If System.IO.Directory.Exists("C:\Users\...\source\repos\VB.NET\Get mouse position and draw rectangle on screen") Then
SFD1.InitialDirectory = "C:\Users\...\source\repos\VB.NET\Get mouse position and draw rectangle on screen"
Else
SFD1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
End If
If SFD1.ShowDialog = CommonFileDialogResult.Ok Then
Speicherpfad = SFD1.FileName & ".png"
Else
Return
End If
End Using
Using bmpSource As Bitmap = New Bitmap(Form1.Pfad_Bild)
Dim rectCutout As RectangleF = gp.GetBounds()
Using m As Matrix = New Matrix()
m.Translate(-rectCutout.Left, -rectCutout.Top)
gp.Transform(m)
End Using
Using bmpCutout As Bitmap = New Bitmap(CInt(Math.Round(rectCutout.Width, 0)), CInt(Math.Round(rectCutout.Height, 0)))
Using graphicsCutout As Graphics = Graphics.FromImage(bmpCutout)
graphicsCutout.Clip = New Region(gp)
graphicsCutout.DrawImage(bmpSource, CInt(-rectCutout.Left), CInt(-rectCutout.Top))
bmpCutout.Save(Speicherpfad, Imaging.ImageFormat.Png)
Form1.CheckBox1.Checked = False
End Using
End Using
End Using
End If
End Using
End If
End Sub
End Class
#Enable Warning CA1707 ' Bezeichner dürfen keine Unterstriche enthalten
The solution is to use .SetResolution()
Using Original As Bitmap = New Bitmap(Form1.Pfad_Bild)
Dim rectCutout As RectangleF = gp.GetBounds()
Using m As System.Drawing.Drawing2D.Matrix = New System.Drawing.Drawing2D.Matrix()
m.Translate(-rectCutout.Left, -rectCutout.Top)
gp.Transform(m)
End Using
Using bmpCutout As Bitmap = New Bitmap(CInt(Math.Round(rectCutout.Width, 0)), CInt(Math.Round(rectCutout.Height, 0)))
bmpCutout.SetResolution(Original.HorizontalResolution, Original.VerticalResolution)
.
.
.
.
.

Problems with generating labels

The Problem: I'm programmatically generating labels but am having trouble referencing them in code because they don't exist at runtime.
The Context: For a game, I've generated a 10x10 grid of labels with the following:
Public lbl As Label()
Dim tilefont As New Font("Sans Serif", 8, FontStyle.Regular)
Private Sub Lucror_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim i As Integer = 0
Dim a As Integer = 0
Dim height As Integer
Dim width As Integer
height = 30
width = 30
ReDim lbl(99)
For i = 0 To 99
lbl(i) = New Label
lbl(i).Name = i
lbl(i).Size = New System.Drawing.Size(30, 30)
lbl(i).Location = New System.Drawing.Point((width), height)
lbl(i).Text = i
lbl(i).Font = tilefont
Me.Controls.Add(lbl(i))
width = width + 30
a = a + 1 'starting new line if required
If (a = 10) Then
height = height + 30
width = 30
a = 0
End If
Next
End Subenter code here
This worked fine but the labels function as tiles in the game and game tiles need to store 2-3 integers each as well as be able to be referenced through event handlers. I figured a possible way to store integers would be to generate 100 arrays, each named after a label and each holding the 2-3 integers, but that seems very redundant.
What I need:
On click and on hover event handlers for every label
An array (or dictionary?) to store 2-3 integers for every label
Labels have to reference each others names ie. do something to label with name (your name + 1).
The Question: Is there a simple way to achieve these three things with the current way I generate labels (and if so, how?) and if not, how else can I generate the 100 labels to make achieving these things possible?
Any help is much appreciated.
Your labels do exist at runtime, but not at compile time. Attaching events is a little different at runtime, you must use AddHandler.
Below is some sample code that should illustrate everything you're asking for. I've introduced inheritance as a way of saving data that is pertinent to each tile. The GameTile type behaves exactly as a label, and we've added some functionality for storing integers and naming the control.
Public Class Form1
Dim tilefont As New Font("Sans Serif", 8, FontStyle.Regular)
Public Property GameTiles As List(Of GameTile)
Private Sub Lucror_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim a As Integer = 0
Dim xPosition As Integer = 30
Dim yPosition As Integer = 30
GameTiles = New List(Of GameTile)
For i = 0 To 99
Dim gt As New GameTile(i.ToString)
gt.Size = New System.Drawing.Size(30, 30)
gt.Location = New System.Drawing.Point((yPosition), xPosition)
gt.Font = tilefont
gt.Integer1 = i + 1000
gt.Integer2 = i + 2000
gt.Integer3 = i + 3000
Me.Controls.Add(gt)
AddHandler gt.Click, AddressOf TileClickHandler
GameTiles.Add(gt)
yPosition = yPosition + 30
a = a + 1 'starting new line if required
If (a = 10) Then
xPosition = xPosition + 30
yPosition = 30
a = 0
End If
Next
End Sub
Private Sub TileClickHandler(sender As Object, e As EventArgs)
Dim gt = CType(sender, GameTile)
MsgBox("This tile was clicked: " & gt.Text &
Environment.NewLine & gt.Integer1 &
Environment.NewLine & gt.Integer2 &
Environment.NewLine & gt.Integer3)
End Sub
End Class
Public Class GameTile
Inherits Label
'this class should be in a separate file, but it's all together for the sake of clarity
Public Property Integer1 As Integer
Public Property Integer2 As Integer
Public Property Integer3 As Integer
Public Sub New(NameText As String)
MyBase.New()
Name = NameText
Text = NameText
End Sub
End Class

VB Downscaling coordinates

I have the need to know when a gdi+ drawn line is clicked on by the mouse.
I have fashioned this function which is used in a loop on all the existing lines and what the function does is:
It makes a buffer of the line's container's size
It makes the whole thing black
It draws the line in green
It gets the pixel at the mouse location
If the pixel is different from black a.k.a green, the line has successfully been clicked and the function should then return true.
This works great, there's no misinterpretations, but I'm afraid that there's a tiny delay (not really noticeable) when my form is in full screen (due to the large buffer).
I'm looking for a way to optimize this, and my first thought is to downscale everything. So what I mean by that is make the buffer like 20x20 and then draw the line in a scaled down version using math. Problem is, I suck at math, so I'm basically asking you how to do this and preferably with an explanation for dummies.
This is the function:
Public Function Contains(ByVal e As Point) As Boolean
Dim Width As Integer = Container.Size.Width
Dim Height As Integer = Container.Size.Height
Dim Buffer As Bitmap = New Bitmap(Width, Height)
Using G As Graphics = Graphics.FromImage(Buffer)
G.Clear(Color.Black)
Dim Start As Point = New Point(ParentNode.Location.X + ParentNode.Size.Width / 2, ParentNode.Location.Y + ParentNode.Size.Height / 2)
Dim [End] As Point = New Point(ChildNode.Location.X + ChildNode.Size.Width / 2, ChildNode.Location.Y + ChildNode.Size.Height / 2)
Dim Control1 As Point
Dim Control2 As Point
Control1.X = Start.X + GetAngle(ChildNode.Location, ParentNode.Location, ChildNode.Location.X - ParentNode.Location.X, ChildNode.Location.Y - ParentNode.Location.Y)
Control1.Y = Start.Y
Control2.X = [End].X
Control2.Y = Start.Y
G.DrawBezier(New Pen(Color.Green, 4), Start, Control1, Control2, [End])
End Using
If Buffer.GetPixel(e.X, e.Y).ToArgb() <> Color.Black.ToArgb() Then
Return True
End If
Return False
End Function
This is one of my attempts to make the function use the idea above:
Public Function Contains(ByVal e As Point) As Boolean
Dim Width As Integer = 20
Dim Height As Integer = 20
Dim Buffer As Bitmap = New Bitmap(Width, Height)
Using G As Graphics = Graphics.FromImage(Buffer)
G.Clear(Color.Black)
Dim Start As Point = New Point(ParentNode.Location.X + ParentNode.Size.Width / 2, ParentNode.Location.Y + ParentNode.Size.Height / 2)
Dim [End] As Point = New Point(ChildNode.Location.X + ChildNode.Size.Width / 2, ChildNode.Location.Y + ChildNode.Size.Height / 2)
Dim Control1 As Point
Dim Control2 As Point
Control1.X = Start.X + GetAngle(ChildNode.Location, ParentNode.Location, ChildNode.Location.X - ParentNode.Location.X, ChildNode.Location.Y - ParentNode.Location.Y)
Control1.Y = Start.Y
Control2.X = [End].X
Control2.Y = Start.Y
G.DrawBezier(New Pen(Color.Green, 4), New Point(Start.X / Width, Start.Y / Height), New Point(Control1.X / Width, Control1.Height / Height), New Point(Control2.X / Width, Control2.Y / Height), New Point([End].X / Width, [End].Y / Height))
End Using
If Buffer.GetPixel(Width, Height).ToArgb() <> Color.Black.ToArgb() Then
Return True
End If
Return False
End Function
Try using a GraphicsPath for drawing and testing with the built-in IsOutlineVisible function:
Public Function Contains(ByVal e As Point) As Boolean
Dim result as Boolean = False
Using gp As New GraphicsPath
gp.AddBezier(your four points)
Using p As New Pen(Color.Empty, 4)
result = gp.IsOutlineVisible(e, p)
End Using
End Using
Return result
End Function
Side note: Bitmaps and Graphic objects need to be disposed when you create them.

Improve Image Comparison Function

I'm working on Visual Inspection System. One of my key function is to compare an image captured with an image from a data base. The comparison would reveal missing parts or damaged part. I have tried using pixel comparison, but this method is not reliable as it needs exactly similar image captured every time. Is there a way to improve this function to be more versatile. In a way it has to detect the difference in image even if the image captured is slightly offset or rotated. Please guide me using VB.Net. Below is my current code.
Private Sub btnGo_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnGo.Click
Me.Cursor = Cursors.WaitCursor
Application.DoEvents()
' Load the images.
Dim bm1 As Bitmap = Image.FromFile("C:\Users\pnasguna\Desktop\A56.jpg")
Dim bm2 As Bitmap = Image.FromFile("C:\Users\pnasguna\Desktop\A54.jpg")
' Make a difference image.
Dim wid As Integer = Math.Min(bm1.Width, bm2.Width)
Dim hgt As Integer = Math.Min(bm1.Height, bm2.Height)
Dim bm3 As New Bitmap(wid, hgt)
' Create the difference image.
Dim are_identical As Boolean = True
Dim eq_color As Color = Color.White
Dim ne_color As Color = Color.Red
For x As Integer = 0 To wid - 1
For y As Integer = 0 To hgt - 1
If bm1.GetPixel(x, y).Equals(bm2.GetPixel(x,y)) Then
bm3.SetPixel(x, y, eq_color)
Else
bm3.SetPixel(x, y, ne_color)
are_identical = False
End If
Next y
Next x
' Display the result.
PictureBox1.Image = bm3
Me.Cursor = Cursors.Default
If (bm1.Width <> bm2.Width) OrElse (bm1.Height <> bm2.Height) Then are_identical =False
If are_identical Then
MessageBox.Show("The images are identical")
Else
MessageBox.Show("The images are different")
End If
bm1.Dispose()
bm2.Dispose()
End Sub
You could take the XnaFan ImageComparison library to inspectionate the sourcecode as an example for your needs
It reveals the difference between pixels and can compare images with a Similarity coefficient, I've write a basic example of both:
Imports XnaFan.ImageComparison
' ===================================================
' Get percentage difference value between two images:
' ===================================================
Dim img1 As Image = Image.FromFile("C:\Image1.jpg")
Dim img2 As Image = Image.FromFile("C:\Image2.jpg")
Dim PercentageDifference As Single =
ImageTool.PercentageDifference(img1:=img1, img2:=img2, threshold:=3)
MessageBox.Show(String.Format("Percentage Difference: {0}%",
CSng(PercentageDifference * 100I).ToString("n1")))
' ========================================
' Get difference image between two images:
' ========================================
Dim img1 As Image = Image.FromFile("C:\Image1.jpg")
Dim img2 As Image = Image.FromFile("C:\Image2.jpg")
Dim DifferenceBitmap As Bitmap =
ImageTool.GetDifferenceImage(img1:=img1,
img2:=img2,
adjustColorSchemeToMaxDifferenceFound:=True,
absoluteText:=False)
PictureBox1.Image = DifferenceBitmap
If you want something more complex you could use AForge (Imaging) library to do the similarity comparison

Creating listview icons on the fly on VB.NET?

Is that possible? I want to pass my custom color as a parameter and I want to receive an Image (rectangle for example).
Public Function createIcon(ByVal c As Color) As Bitmap
Dim g As Graphics
Dim Brush As New SolidBrush(c)
g.FillRectangle(Brush, New Rectangle(0, 0, 20, 20))
Dim bmp As New Bitmap(20, 20, g)
Return bmp
End Function
I tried this way and couldn't success.
Bitmap: A canvas (in memory) that contains an image.
Graphics: A tool set that allows you to draw on an associated canvas.
With this in mind, here is the solution:
Public Function CreateIcon(ByVal c As Color, ByVal x As Integer, ByVal y As Integer) As Bitmap
Dim icon As New Bitmap(x, y)
Using g = Graphics.FromImage(icon)
Using b As New SolidBrush(c)
g.FillRectangle(b, New Rectangle(0, 0, 20, 20))
End Using
End Using
Return icon
End Function
The Using blocks here merely serve the purpose of disposing the graphics resources properly (by automatically calling their Dispose method at the end of the block). You need to do this, otherwise you will leak graphics resources.
Okay, got it. I am going to share what I did just in case.
Public Function createIcon(ByVal c As Color, ByVal x As Integer, ByVal y As Integer) As Bitmap
createIcon = New Bitmap(x, y)
For i = 0 To x - 1
For j = 0 To y - 1
If i = 0 Or j = 0 Or i = x - 1 Or j = y - 1 Then
createIcon.SetPixel(i, j, Color.Black)
Else
createIcon.SetPixel(i, j, c)
End If
Next
Next
Return createIcon
End Function
This function will give you a colored rectangle with black border.