I am playing around with the Microsoft Vision API and learning C# as I go, and one of the properties of a Vision object is an "Accent Color" of the image.
From a series of images analysed, I want to show those colors ordered in a Linear Gradient -- because that will be able to show the user that most pictures are (for example) blue, because Blue colors take up half of the gradient etc.
I have this working, in that I am ordering the Colors by Hue, and able to produce a Linear Gradient I am filling into a Bitmap.
BUT, the gradient by default is Horizontal, and I need Vertical -- so I've used LinearGradientBrush.RotateTransform(90) which rotates that actual gradient fine, but doesn't seem to fill the entire Rectangle, and it repeats. This is what I'm getting as a result:
How do I create a Vertical LinearGradient that fills up the entire Height of the Rectangle object for my Bitmap?
Here is my code:
private Bitmap CreateColorGradient(System.Drawing.Rectangle rect, System.Drawing.Color[] colors)
{
Bitmap gradient = new Bitmap(rect.Width, rect.Height);
LinearGradientBrush br = new LinearGradientBrush(rect, System.Drawing.Color.White, System.Drawing.Color.White, 0, false);
ColorBlend cb = new ColorBlend();
// Positions
List<float> positions = new List<float>();
for (int i = 0; i < colors.Length; i++) positions.Add((float)i / (colors.Length - 1));
cb.Positions = positions.ToArray();
cb.Colors = colors;
br.InterpolationColors = cb;
br.RotateTransform(90);
using (Graphics g = Graphics.FromImage(gradient))
g.FillRectangle(br, rect);
return gradient;
}
Thanks for reading and any help -- also if you see something in my code that could be done better please point it out it helps me learn :)
You are ignoring the angle parameter in the constructor. And as you instead do a rotation on the Grahics object your brush rectangle is no longer correctly fits the target bitmap and the gradient can't fill it; so it repeats.
To correct
simply set the angle to 90 and
remove the br.RotateTransform(90); call.
Here this changes the result from the left to the middle version:
While we're looking at it, do take note of the WrapMode property of LinearGradientBrush. What you see in the first image is the default WrapMode.Clamp. Often a changing to one of the Flip mode helps.. So lets have a look at the impact of it one the first version at the right position.
It looks like WrapMode.TileFlipY but since I have brought back the rotation it actually takes a value WrapMode.TileFlipX or WrapMode.TileFlipXY:
br.WrapMode = WrapMode.TileFlipX;
Related
I tried looking through a few questions on here already but none seemed to fit.
This is what i've tried:
listBox1.BackColor = Color.FromArgb(85, 200, 200, 200);
But at runtime, there's an error. It states that the component doesn't support transparency. I'm asking on here because there could be a workaround. If anyone could help, that'd be great. Thanks in advance!
I suggest going for a ListView in Details View instead.
This is a more modern control, much more powerful and also more supportive when it comes to adding some extra styling..
ListView has a BackgroundImage which alone may be good enough. It doesn't support transparency, though.
But with a few tricks you can make it fake it by copying the background that would shine through..:
void setLVBack(ListView lv)
{
int alpha = 64;
Point p1 = lv.Parent.PointToScreen(lv.Location);
Point p2 = lv.PointToScreen(Point.Empty);
p2.Offset(-p1.X, -p1.Y );
if (lv.BackgroundImage != null) lv.BackgroundImage.Dispose();
lv.Hide();
Bitmap bmp = new Bitmap(lv.Parent.Width, lv.Parent.Height);
lv.Parent.DrawToBitmap(bmp, lv.Parent.ClientRectangle);
Rectangle r = lv.Bounds;
r.Offset(p2.X, p2.Y);
bmp = bmp.Clone(r, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(bmp))
using (SolidBrush br = new SolidBrush(Color.FromArgb(alpha, lv.BackColor)))
{
g.FillRectangle(br, lv.ClientRectangle);
}
lv.BackgroundImage = bmp;
lv.Show();
}
A few notes:
I hide the listview for a short moment while getting the background pixels
I calculate an offset to allow borders; one could (and maybe should?) also use SystemInformation.Border3DSize.Height etc..
I crop the right area using a bitmap.Clone overload
finally I paint over the image with the background color of the LV, green in my case
you can set the alpha to control how much I paint over the image
Also note that I dispose of any previous image, so you can repeat the call when necessary, e.g. when sizes or positions change or the background etc..
The ListView overlaps a PictureBox (left) but sits on a TabPage with an image of its own.
Result:
I need to remove the black background color of a bitmap in C# VS2013.
It is just like that I draw some points on a canvas. The canvas is black and I just need to change the canvas to be transparent meanwhile keeping colorful points on it without any changes.
I got the solution at:
How to remove white background color from Bitmap
Bitmap capcha = new Bitmap(#"C:/image.png");
capcha.MakeTransparent(Color.Black);
But, the background still have a gray color like a fog covering the points on the image.
How to remove it ?
UPDATE
I used the code:
ImageAttribute imageAttribute = new ImageAttribute();
imageAttribute.SetGamma(0.5f, ColorAdjustType.Bitmap);
gr.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height),
0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imageAttribute );
I got same thing.
More update of C# code to draw an image :
System.Drawing.Bitmap canvasImage = new System.Drawing.Bitmap(xSize, ySize, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
canvasImage.MakeTransparent(Color.Black);
Graphics g = Graphics.FromImage(canvasImage);
System.Drawing.Bitmap tempImage = myDrawImageFunction(myPoints);
g.Clear(Color.Transparent); // this only give me an empty image without any points on it. But, if I uncomment it, I will have an image with black background.
// my understanding about g.DrawImage() is to draw points on tempImage
// after clearing the background. But, the image still have a foggy covering on the image.
g.DrawImage(tempImage, new System.Drawing.PointF(x_position, y_position));
I want to have a transparent background for "tempImage" before any points are drawn on it.
The example image has a back ground that needs to be removed but the colorful points on the image need to be kept without any changes.
This will do the job:
public Color MakeTransparent(Color c, int threshold)
{ // calculate the weighed brightness:
byte val = (byte)((c.R * 0.299f + c.G * 0.587f + c.B * 0.114f));
return val < threshold ? Color.FromArgb(0, c.R, c.G, c.B) : c;
}
You could use it in a double loop over the pixels, but for fast results you should call it from the code in this post (second part!) which uses LockBits.
Change this
ModifyHue hueChanger = new ModifyHue(MaxChannel);
to the new function:
ModifyHue hueChanger = new ModifyHue(MakeTransparent);
and call it with a suitable threshold, maybe 10 or 20..:
c = hueChanger(c, 20);
The function skips the call to the system's MakeTransparent function and directly sets the alpha channel of each pixel to 0.
If you want to create a uniform color instead of a transparent one it should be easy to modify (e.g. by returning Color.FromArgb(255, 0, 0, 0) for solid black)
Do note that, while the code in the linked post takes both 24 and 32 bbp formats you should definitely not save as JPG, as this will re-introduce artifacts and the result will not work well with e.g. a TransparencyKey color..
Instead do save it as PNG, as Hans suggests!
I hope you can modify the button code to a function :-)
I'm trying to detect rectangles on this image:
with this code:
static void Main(string[] args)
{
// Open your image
string path = "test.png";
Bitmap image = (Bitmap)Bitmap.FromFile(path);
// locating objects
BlobCounter blobCounter = new BlobCounter();
blobCounter.FilterBlobs = true;
blobCounter.MinHeight = 5;
blobCounter.MinWidth = 5;
blobCounter.ProcessImage(image);
Blob[] blobs = blobCounter.GetObjectsInformation();
// check for rectangles
SimpleShapeChecker shapeChecker = new SimpleShapeChecker();
foreach (var blob in blobs)
{
List<IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints(blob);
List<IntPoint> cornerPoints;
// use the shape checker to extract the corner points
if (shapeChecker.IsQuadrilateral(edgePoints, out cornerPoints))
{
// only do things if the corners form a rectangle
if (shapeChecker.CheckPolygonSubType(cornerPoints) == PolygonSubType.Rectangle)
{
// here i use the graphics class to draw an overlay, but you
// could also just use the cornerPoints list to calculate your
// x, y, width, height values.
List<Point> Points = new List<Point>();
foreach (var point in cornerPoints)
{
Points.Add(new Point(point.X, point.Y));
}
Graphics g = Graphics.FromImage(image);
g.DrawPolygon(new Pen(Color.Red, 5.0f), Points.ToArray());
image.Save("result.png");
}
}
}
}
but it dont recognize the rectangles (walls). It just recognize the big square, and when I reduce the minHeight and minWidth, it recognize trapezoids on the writing..
I propose a different algorithm approach, after working almost a year with image processing algorithms what I can tell is that to create an efficient algorithm, you have to "reflect" how you, as a human would do that, here is the proposed approach:
We don't really care about the textures, we care about the edges (rectangles are edges), therefore we will apply an Edge-detection>Difference (http://www.aforgenet.com/framework/docs/html/d0eb5827-33e6-c8bb-8a62-d6dd3634b0c9.htm), this gives us:
We want to exaggerate the walls, as humans we know that we are looking for the walls, but the computer does not know this, therefore, apply two rounds of Morphology>Dilatation (http://www.aforgenet.com/framework/docs/html/88f713d4-a469-30d2-dc57-5ceb33210723.htm), this gives us:
We care only about the what is wall and what is not, apply a Binarization>Threshold (http://www.aforgenet.com/framework/docs/html/503a43b9-d98b-a19f-b74e-44767916ad65.htm), we get:
(Optional) We can apply a blob extraction to erase the labels ("QUARTO, BANHEIRO", etc)
We apply a Color>Invert, this is just done because the next step detects the white color not black.
Apply a Blob>Processing>Connected Components Labeling (http://www.aforgenet.com/framework/docs/html/240525ea-c114-8b0a-f294-508aae3e95eb.htm), this will give us all the rectangles, like this:
Note that for each colored box you have its coordinates, center, width and height. So you can extract a snip from the real image with that coordinates.
PS: Using the program AForge Image Processing Lab is highly recommended to test your algos.
Each time a rectangle is found, the polygon is drawn on Graphics and the file is saved only for THAT rectangle. This means that result.png will only contain a single rectangle at a time.
Try first saving all the rectangles in a List<List<Points>> and then going over it and add ALL the rectangles to the image. Something like this (Pseudo):
var image..
var rectangles..
var blobs..
foreach (blob in blobs)
{
if (blob is rectangle)
{
rectangles.add(blob);
}
}
foreach (r in rectangles)
{
image.draw(r.points);
}
image.save("result.png");
If your problem now is to avoid noise due to writings on the image, use FillHoles with widht and height of holes smaller than the smallest rectangle but larger than any of the writings.
If the quality of image is good and no text is touching the border of the image, Invert the image and FillHoles will remove most of the stuff.
Hope I understood your problem correctly.
We are trying to detect rectangles in so many rectangles (considering gray rectangles of grid). Almost all algorithms will get confused here. You're not eliminating externals from input image. Why not replace grid line color with background color or use threshold above to eliminate all grids first.
Then grow all pixels equal to the width of wall, Find all horizontal and vertical lines thereafter use maths to find rectangles using detected lines. Uncontrolled filling will be risky as when boundries are not closed fill will make two rooms as one rectangle.
What I want to do is basically cropping a rectangle from an image. However, it should satisfy some special cases:
I want to crop an angled rectangle on image.
I don't want to rotate the image and crop a rectangle :)
If cropping exceeds the image size, I don't want to crop an empty background color.
I want to crop from back of the starting point, that will end at starting point when rectangle size completed. I know I couldn't explain well so if I show what I want visually:
The blue dot is the starting point there, and the arrow shows cropping direction. When cropping exceeds image borders, it will go back to the back of the starting point as much as, when the rectangle width and height finished the end of the rectangle will be at starting point.
Besides this is the previous question I asked:
How to crop a cross rectangle from an image using c#?
In this question, I couldn't predict that a problem can occur about image dimensions so I didn't ask for it. But now there is case 3. Except case three, this is exactly same question. How can I do this, any suggestions?
What needs to be done is to add offsets to the matrix alignment. In this case I am taking one extra length of the rectangle from each side (total 9 rectangles) and offsetting the matrix each time.
Notice that it is necessary to place offset 0 (the original crop) last, otherwise you will get the wrong result.
Also note that if you specify a rectangle that is bigger than the rotated picture you will still get empty areas.
public static Bitmap CropRotatedRect(Bitmap source, Rectangle rect, float angle, bool HighQuality)
{
int[] offsets = { -1, 1, 0 }; //place 0 last!
Bitmap result = new Bitmap(rect.Width, rect.Height);
using (Graphics g = Graphics.FromImage(result))
{
g.InterpolationMode = HighQuality ? InterpolationMode.HighQualityBicubic : InterpolationMode.Default;
foreach (int x in offsets)
{
foreach (int y in offsets)
{
using (Matrix mat = new Matrix())
{
//create the appropriate filler offset according to x,y
//resulting in offsets (-1,-1), (-1, 0), (-1,1) ... (0,0)
mat.Translate(-rect.Location.X - rect.Width * x, -rect.Location.Y - rect.Height * y);
mat.RotateAt(angle, rect.Location);
g.Transform = mat;
g.DrawImage(source, new Point(0, 0));
}
}
}
}
return result;
}
To recreate your example:
Bitmap source = new Bitmap("C:\\mjexample.jpg");
Bitmap dest = CropRotatedRect(source, new Rectangle(86, 182, 87, 228), -45, true);
I am currently painting a light blue, partly transparent overlay over owner-drawn objects to indicate certain state. It's OK but I thought that it would be even nicer if I could at some sort of glass effect to further establish the idea that the particular object has "something" overlaid over the top of it.
I thought that some glass streaks, for example, in addition to the blue transparency would lend a nice effect.
I've Googled around for GDI+ (and others) algorithms to do simple things painting like this but have come up empty. Links to any (fairly simple) algorithms in any language would be appreciated. I prefer .NET but can figure out the painting from pseudo-code on up.
Sorry, shoul've also specified that I need to target WinXP and using .NET version 2.0 - So unable to use WPF or Vista/Win7 goodies.
I've not done this myself but, have used codeproject source to render a sample...Try this:
http://www.codeproject.com/KB/GDI-plus/Image-Glass-Reflection.aspx
public static Image DrawReflection(Image _Image, Color _BackgroundColor, int _Reflectivity)
{
// Calculate the size of the new image
int height = (int)(_Image.Height + (_Image.Height * ((float)_Reflectivity / 255)));
Bitmap newImage = new Bitmap(_Image.Width, height, PixelFormat.Format24bppRgb);
newImage.SetResolution(_Image.HorizontalResolution, _Image.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(newImage))
{
// Initialize main graphics buffer
graphics.Clear(_BackgroundColor);
graphics.DrawImage(_Image, new Point(0, 0));
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle destinationRectangle = new Rectangle(0, _Image.Size.Height,
_Image.Size.Width, _Image.Size.Height);
// Prepare the reflected image
int reflectionHeight = (_Image.Height * _Reflectivity) / 255;
Image reflectedImage = new Bitmap(_Image.Width, reflectionHeight);
// Draw just the reflection on a second graphics buffer
using (Graphics gReflection = Graphics.FromImage(reflectedImage))
{
gReflection.DrawImage(_Image,
new Rectangle(0, 0, reflectedImage.Width, reflectedImage.Height),
0, _Image.Height - reflectedImage.Height, reflectedImage.Width,
reflectedImage.Height, GraphicsUnit.Pixel);
}
reflectedImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
Rectangle imageRectangle =
new Rectangle(destinationRectangle.X, destinationRectangle.Y,
destinationRectangle.Width,
(destinationRectangle.Height * _Reflectivity) / 255);
// Draw the image on the original graphics
graphics.DrawImage(reflectedImage, imageRectangle);
// Finish the reflection using a gradiend brush
LinearGradientBrush brush = new LinearGradientBrush(imageRectangle,
Color.FromArgb(255 - _Reflectivity, _BackgroundColor),
_BackgroundColor, 90, false);
graphics.FillRectangle(brush, imageRectangle);
}
return newImage;
}
I was actually able to achieve a basic glass effect by overlaying my image with a rectangle about one third the size of the image below that contains a gradient fill of white that starts at 25% opacity and goes to 75% opacity. This is single bit of painting produces a glassy "streak" that I was happy with. The same idea could be repeated a number of times with a variety of rect widths to produce several "streaks" that will give the illusion of a glass overlay.
You could try the Aero Glass function, if you are using Vista or Windows 7.
These might be helpful:
http://msdn.microsoft.com/en-us/library/aa969537%28VS.85%29.aspx#blurbehind
http://msdn.microsoft.com/en-us/library/ms748975.aspx