I'm hoping someone can give me some guidance here. I have been gogleing for a while now and I can't come up with anything that suits my needs. I'm a bit of a programmer but not a pro and I have no graphics experience. I am trying to develop a program for my wife to more easily transfer images to her needlepoint drawings. I want to write a C# application that will let me load an image of almost any type and overlay a "grid" on top of it. I want to also be able to implement simple "paint" operations like change the color of a grid square, color selector from the base image, bucket fill, etc. Any suggestions and examples would be greatly appreciated.
Thanks,
Tom
I've implemented something similar for my wife. My basic approach:
1) Scale the image down to the number of necessary pixels. For example, if she's stitching the image on a 10x10 13-mesh canvas, that equates to an image of 130x130 pixels.
Here's some example code to start you off:
// use NearestNeighbor algorithm
public static unsafe Bitmap Reduce(Bitmap source, SizeF toSize, int threadCount)
{
Bitmap reduced = new Bitmap((int)(toSize.Width * threadCount), (int)(toSize.Height * threadCount));
using (Graphics g = Graphics.FromImage(reduced))
{
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.DrawImage(source, new Rectangle(Point.Empty, reduced.Size));
}
return reduced;
}
2) Display the pixelated image full screen. This will provide a grid-like effect.
3) Display a color palette from DMC's yarn code card, or taken from the image (after down-scaling). Then have the mouse cursor pick up a color by clicking on it, then applying it to the cell that it was subsequently clicked on.
Here's some code for picking up the mouse cursor:
public Point GetPicturePointAtClick()
{
Point p = Cursor.Position;
Point picturePoint = previewBox.PointToClient(p);
if (Zoom != 0)
{
picturePoint.X = (int)(picturePoint.X / Zoom);
picturePoint.Y = (int)(picturePoint.Y / Zoom);
}
return picturePoint;
}
The idea here is to map the clicked area to the correct pixel in the reduced image, math:
Point reducedPoint =
new Point(
(int)(picPoint.X * (_reduced.Width / (float)WorkingBitmap.Width)),
(int)(picPoint.Y * (_reduced.Height / (float)WorkingBitmap.Height)));
There's a lot of code ahead of you. Did you try an online custom needlepoint provider? Try one of these sites, they're both pretty good and customization is free:
http://www.needlepaint.com/
http://www.pepitaneedlepoint.com/
Related
I was working on some image editing using System.Drawing, and now ported everything to SkiaSharp in order to use it on Linux / .NET Core. Everything works fine, except I have not yet found a way to programmatically give images rounded corners.
I wrote some code that relies on drawing a path in the form of a circle and then tries to color the outside of the path transparent. This does not work though since it seems like there are multiple layers and making parts of the upper layer transparent does not make the whole region of the image (all layers) transparent. Here is my code:
public static SKBitmap MakeImageRound(SKBitmap image)
{
SKBitmap finishedImage = new SKBitmap(image.Width, image.Height);
using (SKCanvas canvas = new SKCanvas(finishedImage))
{
canvas.Clear(SKColors.Transparent);
canvas.DrawBitmap(image, new SKPoint(0, 0));
SKPath path = new SKPath();
path.AddCircle(image.Width / 2, image.Height / 2, image.Width / 2 - 1f);
path.FillType = SKPathFillType.InverseEvenOdd;
path.Close();
canvas.DrawPath(path, new SKPaint {Color = SKColors.Transparent, Style = SKPaintStyle.Fill });
canvas.ResetMatrix();
return finishedImage;
}
}
I am sorry if this is bad code, this is my first experience with image editing in C#, and therefore I also am an absolute beginner in SkiaSharp. I modified a System.Drawing code I got from here.
I also took a look at this Microsoft document. It shows clipping using paths, but I have not yet been able to get that to work either.
So in conclusion: I am searching for a way to make all layers of an image/the canvas transparent in certain regions.
Any help is greatly appreciated! :D
I think you can do this by setting the SPaint.BlendMode = SKPaintBlendMode.Src. That means that when the canvas is drawing, it just must use the source color, and replace the existing colors.
https://learn.microsoft.com/dotnet/api/skiasharp.skpaint.blendmode
What you are actually doing with
canvas.DrawPath(path, new SKPaint { Color = SKColors.Transparent});
is picking up a brush, dipping it in the transparent paint, and then drawing. So you see nothing. The paint is clear.
But, what you even more want to do is clip before drawing:
https://learn.microsoft.com/dotnet/api/skiasharp.skcanvas.clippath
canvas.Clear(SKColors.Transparent);
// create the circle for the picture
var path = new SKPath();
path.AddCircle(image.Width / 2, image.Height / 2, image.Width / 2 - 1f);
// tell the canvas not to draw outside the circle
canvas.ClipPath(path);
// draw the bitmap
canvas.DrawBitmap(image, new SKPoint(0, 0));
I have some image (.png) it has resolution 96 x 96 and size 100 x 200. It is myImageLoadedFromFile.
And I need to draw properly over it some other images (anotherImage).
I use Photoshop to detect coordinates of X and Y. Photoshop shows some strange coordinates like X = 1.5f and Y= 1,8f when I use mouse to plan where I have to draw that little image. But what I have got is that all images I draw are at the left top corner.
It seems like I have to use different way to know which coordinates I have to use to do it properly.
Any clue how to do it? Which Tool is possible to use to get proper coords. using mouse?
Here my C# code
var bitmap = new Bitmap(myImageLoadedFromFile);
using (var g = Graphics.FromImage(bitmap))
{
g.DrawImage(anotherImage, new PointF(1.5f, 1,8f));
g.Flush();
}
You can change Photoshop's default units from points to pixels. With any document open just right-click on the rules (Ctrl + R if they're not showing already).
You can also get to this through the menus at Edit -> Preferences -> Units & Rulers
I am using a C#, Winforms application with the Aforge library to pull video from a USB camera and manipulate that video.
I am doing it by using the new frame event handler and posting that image into a picturebox. So far, it has worked well and done what I want.
However, I recently found out I need to add software zoom to the application. My solution was to increase the size of the overall image and then grab a section of the new, larger image, that fit the measurements I needed. This is done on every new frame.
int imageTopY;
int imageTopX;
Rectangle rect;
bitmap = new Bitmap(bitmap, new Size(bitmap.Width * zoomTrackBar.Value,
bitmap.Height * zoomTrackBar.Value));
imageTopY = ((bitmap.Height - height) / 2);
imageTopX = ((bitmap.Width - width) / 2);
if (imageTopY != 0 && imageTopX != 0)
rect = new Rectangle(imageTopX, imageTopY, width, height);
else
rect = new Rectangle(0, 0, width, height);
bitmap = (Bitmap)bitmap.Clone(rect, bitmap.PixelFormat)
This also does what I need it to. HOWEVER, it is not at all efficient. When the zoome level hits 3, the video becomes exceptionally laggy. There is a latency of 1-2 seconds between the video feed and what is going on. I can get up and do a little dance, and by the time I sit down in my chair, the me in the video starts doing a little dance :-P
Any suggestions on a better way to achieve what I am trying to do? Maybe something more streamlined?
Instead of enlarging the overall image, cut out the section you wish you zoom in to and fill the window with that image. This way, you are always having a max image size of the video window and not enlarging the overall image beyond the window's constraints.
I have an idea and maybe you guys can give me a good start or an idea in which path might be correct.
I have a picturebox right now loading a specific bmp file. What I want to do is load this bmp file into the picturebox and then load another picture on top of it. The kicker to this all is the 2nd picture must be drawn. The 2nd picture is just a fill in black box. This black box must also overlay on the first image exactly right, the black box has cordinates from paint on it (yes we have the # of the cordaints).
Still think the picturebox is the way to go, or is there a way to load paint into this, and then paint on top of the paint image?
1) Need to load an image
2) Need to read a specific file that has cords
3) Need to draw a black rectangle that matches those coords (Those cords were created in paint).
What do you think the best way to approach this is? A Picture box with code to draw in the cords of the redacted image
Here's a code sample that should do what you're after:
//Load in an image
pbTest.Image = Image.FromFile("c:\\Chrysanthemum.jpg");
//Create the graphics surface to draw on
using (Graphics g = Graphics.FromImage(pbTest.Image))
{
using (SolidBrush brush = new SolidBrush(Color.Black))
{
//Draw a black rectangle at some coordinates
g.FillRectangle(brush, new Rectangle(0, 0, 20, 10));
//Or alternatively, given some points
//I'm manually creating the array here to prove the point, you'll want to create your array from your datasource.
Point[] somePoints = new Point[] { new Point(1,1), new Point(20,25), new Point(35, 50), new Point(90, 100) };
g.FillPolygon(brush, somePoints);
}
}
The finished article:
This answer is written to apply to both web and non-web uses of C# (why I did not give specific examples.)
GDI and other graphics libs all have functions that will paint a filled rectangle on top of an image. This is the way to go. If you use two images there is a good chance for a standard user and a great chance for a hacker they will be able to view just the original image, exposing the information you are trying to hide.
If you only send an image with the areas redacted, you will never have to worry about them being seen.
To zoom images in and out, there is a possible way to resize the pictureBox and showing image in strechmode. Although I can not use it efficiently becauce in general over 8x it gives storage error [think that a pictureBox has the Size(32k, 32k) it needs over 1GB memory !
Is there a special method, or should I zoom only the seen part of the image by using ImageClone ?
Update:
Here is the project at first try to zoom at the project [impossible, storage error] than delete the 41. line in form.cs :
pictureBox1.Image = youPicture;
After deleting this line, the program will work, please move the zoomed image.
Here is the link: http://rapidshare.com/files/265835370/zoomMatrix.rar.html
By using the matrix object and the transform property of your graphics object:
using(Graphics g = this.CreateGraphics())
{
using(Bitmap youPicture = new Bitmap(yourPictureFile))
{
g.DrawImage(youPicture, 0, 0, 300, 100); //set the desired size
//Now you need to create a matrix object to apply transformation on your graphic
Matrix mat = new Matrix();
mat.Scale(1.5f, 1.5f, MatrixOrder.Append); //zoom to 150%
g.Transform = mat;
g.DrawImage(youPicture, new Rectangle(...), 0, 0, youPicture.Width,
youPicture.Height, GraphicsUnit.Pixel) ;
}
}
I personally would just zoom the visible part as the rest is hidden anyway (and thus no use)
See this answer to an earlier question. You definitely don't want to zoom by making the image huge and showing only part of it - you'll run into the memory problem that you've already encountered. Also, the stretch mode of a picture box doesn't use high-quality interpolation, so the result will look pretty crappy.
In the answer I linked here, I included a link to a C# project that shows you how to do this kind of zooming.
Update: here is a direct link to the downloadable project.