Halftone effect in with gdi+ - c#

How would I go about mimicking this halftone effect in GDI+?
It almost looks like Floyd–Steinberg dithered version of the image overlaying a full one but I'm not convinced.

I gave this a try and got this result:
It may be a place to start. I did it like this:
Draw the original picture with low saturation (using a color
matrix)
Draw the original image onto 1) with high saturation
using a pattern mask (ie the dots)
I created the pattern mask like this:
using (var g = Graphics.FromImage(bmpPattern))
{
g.Clear(Color.Black);
g.SmoothingMode = SmoothingMode.HighQuality;
for (var y = 0; y < bmp.Height; y += 10)
for (var x = 0; x < bmp.Width ; x += 6)
{
g.FillEllipse(Brushes.White, x, y, 4, 4);
g.FillEllipse(Brushes.White, x + 3, y + 5, 4, 4);
}
}
And then I imposed it over the oversaturated bitmap using this technique.
Update: Elaboration on how the images got merged. Let's talk even a little more general and say that we want to combine two different colorized versions of the same image using a pattern mask, resulting in a new image - we could do it like this:
Create THREE new bitmaps, all with the same size as the original image. Call them bmpA, bmpB and bmpMask.
Draw one colored/effect version into bmpA
Draw the other colored/effect version into bmpB
Create the mask in bmpMask (black and white)
Push one of the R/G/B channels of bmpMask into the alpha channel of bmpB using the
transferOneARGBChannelFromOneBitmapToAnother method.
Draw bmpB over bmpA (since bmpB now has transparent parts in it)
The result is now bmpA. bmpB and bmpMask can be disposed.
Done

Related

Plot values of a line from a grayscale image

I am trying to take a grayscale bitmap and extract a single line from it and then graph the gray values. I got something to work, but I'm not really happy with it. It just seems slow and tedious. I am sure someone has a better idea
WriteableBitmap someImg; //camera image
int imgWidth = someImg.PixelWidth;
int imgHeight = someImg.PixelHeight;
Int32Rect rectLine = new Int32Rect(0, imgHeight / 2, imgWidth, 1); //horizontal line half way down the image as a rectangle with height 1
//calculate stride and buffer size
int imgStride = (imgWidth * someImg.Format.BitsPerPixel + 7) / 8; // not sure I understand this part
byte[] buffer = new byte[imgStride * rectLine.Height];
//copy pixels to buffer
someImg.CopyPixels(rectLine, buffer, imgStride, 0);
const int xGraphHeight = 256;
WriteableBitmap xgraph = new WriteableBitmap(imgWidth, xGraphHeight, someImg.DpiX, someImg.DpiY, PixelFormats.Gray8, null);
//loop through pixels
for (int i = 0; i < imgWidth; i++)
{
Int32Rect dot = new Int32Rect(i, buffer[i], 1, 1); //1x1 rectangle
byte[] WhiteDotByte = { 255 }; //white
xgraph.WritePixels(dot, WhiteDotByte, imgStride, 0);//write pixel
}
You can see the image and the plot below the green line. I guess I am having some WPF issues that make it look funny but that's a problem for another post.
I assume the goal is to create a plot of the pixel value intensities of the selected line.
The first approach to consider it to use an actual plotting library. I have used oxyplot, it works fine, but is lacking in some aspects. Unless you have specific performance requirements this will likely be the most flexible approach to take.
If you actually want to render to an image you might be better of using unsafe code to access the pixel values directly. For example:
xgraph.Lock();
for (int y = 0; y < imgHeight; y++){
var rowPtr = (byte*)(xgraph.BackBuffer + y * xgraph.BackBufferStride);
for(int x = 0; x < imgWidth; x++){
rowPtr[x] = (byte)(y < buffer[i] ? 0 : 255);
}
}
self.Unlock(); // this should be placed in a finally statement
This should be faster than writing 1x1 rectangles. It should also write columns instead of single pixels, and that should help making the graph more visible. You might also consider allowing arbitrary image height and scale the comparison value.
If you want to plot the pixel values along an arbitrary line, and not just a horizontal one. You can take equidistant samples along the line, and use bilinear interpolation to sample the image.

How can I can the integer value (range 0-255) of a grayscale image pixel

Hi I want to get the integer values (0-255 range) of a gray scale image ....this code shows me the R,G,B values not one value..how can i get it?
Bitmap temp1 = image1;
for (int i = 0; i < temp1.Height; i++)
{
for (int j = 0; j < temp1.Width; j++)
{
Color cl = new Color();
cl = temp1.GetPixel(i, j);
}
}
just read the properties R or G or B, any of them will have the same value.
var intValue = cl.R;
If your source image is greyscale and you just want the level of greyness, just pick any of the three components. They will be equal.
If your source image is color but you want to get the grey equivalent, you can convert your color to a grey value in the range 0..255 by blending the red, green and blue color components together. The blending factors are different because the human eye has different sensitivity to the three primary colors. For fun, try varying the factors (e.g. use 0.3333 for each) and see what the result looks like.
Color cl = c.GetPixel(i, j); // No need to separately allocate a new Color()
int greyValue = (int)((cl.R * 0.3) + (cl.G * 0.59) + (cl.B * 0.11));
Color grey = Color.FromArgb(cl.A, greyValue, greyValue, greyValue);
Note that it is quite slow to loop through a larger Bitmap, using GetPixel() on each pixel. There are much faster techniques available.
UPDATE
Here's an example image with different scaling factors for R, G, and B applied. The image will always be greyscaled because the same numeric value is used for each RGB component in the modified image, but the relative lightness does change. The middle image uses scaling factors suitable for the human eye. Note how blue areas in the original image seem oversaturated in the rightmost version.
There are multiple ways to get grayscale from RGB.
A common way is to do (R+G+B)/3
Others are computing some luminance Luminance measure (Lab, YUV, HSV)

How to split an image into smaller pieces in C#?

How can I split an image into multiple sub images? Like, would I have to read the pixels and convert it to an image somehow?
For example:
If an image's dimension is 100px (width) and 180px (height) and I wanted to, say, split it as a 4x4, would I read the first 25px for the width and the first 45 px for the height and then just increment it correctly?
If so, what would I store the pixels to? More specifically, would it be saved as an array of bytes, images, etc?
You may try the following code sample (taken from https://stackoverflow.com/a/4118195/);
for (int i = 0; i < 4; i++)
{
for (int y = 0; y < 4; y++)
{
Rectangle r = new Rectangle(i*(pictureBox1.Image.Width / 4),
y*(pictureBox1.Image.Height / 4),
pictureBox1.Image.Width / 4,
pictureBox1.Image.Height / 4);
g.DrawRectangle(pen,r );
list.Add(cropImage(pictureBox1.Image, r));
}
}
The other alternative is using BitMap.Clone, you may find an example in the following link.
Use the Bitmap class to hold the image and its Clone method to cut out your rectangles of arbitrary size. As a Bitmap it comes with several convenience methods such as Save this overload will save to a stream another allows you to save it to a file.

Get the 2D coordinate outline of a region of an image (such as a country on a map)

How would I go about generating the 2D coordinates for an area of an image, so for example if one of the countries on this map was singled out and was the only one visible: but on a canvas the same size, how would I go about getting the 2D coordinates for it?
As I then want to create hover/click areas based on these coordinates using c#, I'm unable to find a tool which can detect for example a shape within a blank canvas and spit out its outline coordinates.
I mainly believe this to be a phrasing/terminology issue on my part, as I feel this whole process is already a "thing", and well documented.
There are many ways to achieve your task here are few:
Look at Generating Polygons from Image (Filled Shapes) which is Almost duplicate of yours but has a bit different start point.
In a nutshell:
extract all non white pixels which are neighboring white pixel
Just loop through whole image (except outer border pixels) if processed pixel is not white then look to its 4/8 neighbors of processed pixel. If any of them is different color then add the processed pixel color and coordinates to a list.
sort the point list by color
This will separate countries
apply closed loop / connectivity analysis
This is vectorisation/polygonize process. Just join not yet used neighboring pixels from list to form lines ...
There is also A* alternative for this that might be easier to implement:
extract all non white pixels which are neighboring white pixel
Just loop through whole image (except outer border pixels) if processed pixel is not white then look to its 4/8 neighbors of processed pixel. If none of them is different color then clear current pixel with some unused color (black).
recolor all white and the clear color to single color (black).
from this the recolor color will mean wall
Apply A* path finding
find first non wall pixel and apply A* like growth filling. When you done filling then just trace back remembering the order of points in a list as a polygon. Optionally joining straight line pixels to single line ...
Another option is adapt this Finding holes in 2d point sets
[notes]
If your image is filtered (Antialiasing,scaling,etc) then you need to do the color comparisons with some margin for error and may be even port to HSV (depends on the level of color distortion).
You can use opencv's findcontour() function. See documentation here: http://docs.opencv.org/2.4/doc/tutorials/imgproc/shapedescriptors/find_contours/find_contours.html.
I think you're going at this the wrong way. Outlines of continents are madness; they are often made up of several parts with lots of small islands. And, you don't need the coordinates of the continents on the image; looking up if your current coordinates are in a list would take far too long. Instead, you should do the opposite: make an index table of the whole image, on which is indicated for each pixel which continent it belongs to.
And that's much, much easier.
Since you obviously have to assign a colour to each continent to identify them, you can go over all of the image's pixels, match each pixel's colour to the closest match in the colours of your continents, and fill each byte in the array with the corresponding found continent index. This way, you get a byte array that directly references your continents array. Effectively, this means you create an indexed 8-bit image, just as a plain bytes array. (There are methods to actually combine this with the colours array and get an image you can use, mind you. It's not too hard.)
For the actual colour matching, the best practice is to use LockBits on the source image to get direct access to the underlying bytes array. In the code below, the call to GetImageData gets me the bytes and the data stride. Then you can iterate over the bytes per line, and build a colour from each block of data that represents one pixel. If you don't want to bother too much with supporting different pixel sizes (like 24bpp), a quick trick is to just paint the source image on a new 32bpp image of the same dimensions (the call to PaintOn32bpp), so you can always simply iterate per four bytes and take the byte values in the order 3,2,1,0 for ARGB. I ignored transparency here because it just complicates the concept of what is and isn't a colour.
private void InitContinents(Bitmap map, Int32 nearPixelLimit)
{
// Build hues map from colour palette. Since detection is done
// by hue value, any grey or white values on the image will be ignored.
// This does mean the process only works with actual colours.
// In this function it is assumed that index 0 in the palette is the white background.
Double[] hueMap = new Double[this.continentsPal.Length];
for (Int32 i = 0; i < this.continentsPal.Length; i++)
{
Color col = this.continentsPal[i];
if (col.GetSaturation() < .25)
hueMap[i] = -2;
else
hueMap[i] = col.GetHue();
}
Int32 w = map.Width;
Int32 h = map.Height;
Bitmap newMap = ImageUtils.PaintOn32bpp(map, continentsPal[0]);
// BUILD REDUCED COLOR MAP
Byte[] guideMap = new Byte[w * h];
Int32 stride;
Byte[] imageData = ImageUtils.GetImageData(newMap, out stride);
for (Int32 y = 0; y < h; y++)
{
Int32 sourceOffs = y * stride;
Int32 targetOffs = y * w;
for (Int32 x = 0; x < w; x++)
{
Color c = Color.FromArgb(255, imageData[sourceOffs + 2], imageData[sourceOffs + 1], imageData[sourceOffs + 0]);
Double hue;
// Detecting on hue. Values with < 25% saturation are ignored.
if (c.GetSaturation() < .25)
hue = -2;
else
hue = c.GetHue();
// Get the closest match
Double smallestHueDiff = Int32.MaxValue;
Int32 smallestHueIndex = -1;
for (Int32 i = 0; i < hueMap.Length; i++)
{
Double hueDiff = Math.Abs(hueMap[i] - hue);
if (hueDiff < smallestHueDiff)
{
smallestHueDiff = hueDiff;
smallestHueIndex = i;
}
}
guideMap[targetOffs] = (Byte)(smallestHueIndex < 0 ? 0 : smallestHueIndex);
// Increase read pointer with 4 bytes for next pixel
sourceOffs += 4;
// Increase write pointer with 1 byte for next index
targetOffs++;
}
}
// Remove random edge pixels, and save in global var.
this.continentGuide = RefineMap(guideMap, w, h, nearPixelLimit);
// Build image from the guide map.
this.overlay = ImageUtils.BuildImage(this.continentGuide, w, h, w, PixelFormat.Format8bppIndexed, this.continentsPal, null);
}
The GetImageData function:
/// <summary>
/// Gets the raw bytes from an image.
/// </summary>
/// <param name="sourceImage">The image to get the bytes from.</param>
/// <param name="stride">Stride of the retrieved image data.</param>
/// <returns>The raw bytes of the image</returns>
public static Byte[] GetImageData(Bitmap sourceImage, out Int32 stride)
{
BitmapData sourceData = sourceImage.LockBits(new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), ImageLockMode.ReadOnly, sourceImage.PixelFormat);
stride = sourceData.Stride;
Byte[] data = new Byte[stride * sourceImage.Height];
Marshal.Copy(sourceData.Scan0, data, 0, data.Length);
sourceImage.UnlockBits(sourceData);
return data;
}
Now, back to the process; once you have that reference table, all you need are the coordinates of your mouse and you can check the reference map at index (Y*Width + X) to see what area you're in. To do that, you can add a MouseMove listener on an ImageBox, like this:
private void picImage_MouseMove(object sender, MouseEventArgs e)
{
Int32 x = e.X - picImage.Padding.Top;
Int32 y = e.Y - picImage.Padding.Left;
Int32 coord = y * this.picWidth + x;
if (x < 0 || x > this.picWidth || y < 0 || y > this.picHeight || coord > this.continentGuide.Length)
return;
Int32 continent = this.continentGuide[coord];
if (continent == previousContinent)
return;
previousContinent = continent;
if (continent >= this.continents.Length)
return;
this.lblContinent.Text = this.continents[continent];
this.picImage.Image = GetHighlightPic(continent);
}
Note that a simple generated map produced by nearest colour matching may have errors; when I did automatic mapping of this world map's colours, the border between blue and red, and some small islands in Central America, ended up identifying as Antarctica's purple colour, and some other rogue pixels appeared around the edges of different continents too.
This can be avoided by clearing (I used 0 as default "none") all indices not bordered by the same index at the top, bottom, left and right. This removes some smaller islands, and creates a slight gap between any neighbouring continents, but for mouse coordinates detection it'll still very nicely match the areas. This is the RefineMap call in my InitContinents function. The argument it gets determines how many identical neighbouring values an index needs to allow it to survive the pruning.
A similar technique with checking neigbouring pixels can be used to get outlines, by making a map of pixels not surrounded at all sides by the same value.

c# .NET green screen background remove

I am working on a photo software for desktop PC that works on Windows 8. I would like to be able to remove the green background from the photo by means of chroma keying.
I'm a beginner in image manipulation, i found some cool links ( like http://www.quasimondo.com/archives/000615.php ), but I can't transale it in c# code.
I'm using a webcam (with aforge.net) to see a preview and take a picture.
I tried color filters but the green background isn't really uniform, so this doesn't work.
How to do that properly in C#?
It will work, even if the background isn't uniform, you'll just need the proper strategy that is generous enough to grab all of your greenscreen without replacing anything else.
Since at least some links on your linked page are dead, I tried my own approach:
The basics are simple: Compare the image pixel's color with some reference value or apply some other formula to determine whether it should be transparent/replaced.
The most basic formula would involve something as simple as "determine whether green is the biggest value". While this would work with very basic scenes, it can screw you up (e.g. white or gray will be filtered as well).
I've toyed around a bit using some simple sample code. While I used Windows Forms, it should be portable without problems and I'm pretty sure you'll be able to interpret the code. Just note that this isn't necessarily the most performant way to do this.
Bitmap input = new Bitmap(#"G:\Greenbox.jpg");
Bitmap output = new Bitmap(input.Width, input.Height);
// Iterate over all piels from top to bottom...
for (int y = 0; y < output.Height; y++)
{
// ...and from left to right
for (int x = 0; x < output.Width; x++)
{
// Determine the pixel color
Color camColor = input.GetPixel(x, y);
// Every component (red, green, and blue) can have a value from 0 to 255, so determine the extremes
byte max = Math.Max(Math.Max(camColor.R, camColor.G), camColor.B);
byte min = Math.Min(Math.Min(camColor.R, camColor.G), camColor.B);
// Should the pixel be masked/replaced?
bool replace =
camColor.G != min // green is not the smallest value
&& (camColor.G == max // green is the biggest value
|| max - camColor.G < 8) // or at least almost the biggest value
&& (max - min) > 96; // minimum difference between smallest/biggest value (avoid grays)
if (replace)
camColor = Color.Magenta;
// Set the output pixel
output.SetPixel(x, y, camColor);
}
}
I've used an example image from Wikipedia and got the following result:
Just note that you might need different thresholds (8 and 96 in my code above), you might even want to use a different term to determine whether some pixel should be replaced. You can also add smoothening between frames, blending (where there's less green difference), etc. to reduce the hard edges as well.
I've tried Mario solution and it worked perfectly but it's a bit slow for me.
I looked for a different solution and I found a project that uses a more efficient method here.
Github postworthy GreenScreen
That project takes a folder and process all files, I just need an image so I did this:
private Bitmap RemoveBackground(Bitmap input)
{
Bitmap clone = new Bitmap(input.Width, input.Height, PixelFormat.Format32bppArgb);
{
using (input)
using (Graphics gr = Graphics.FromImage(clone))
{
gr.DrawImage(input, new Rectangle(0, 0, clone.Width, clone.Height));
}
var data = clone.LockBits(new Rectangle(0, 0, clone.Width, clone.Height), ImageLockMode.ReadWrite, clone.PixelFormat);
var bytes = Math.Abs(data.Stride) * clone.Height;
byte[] rgba = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(data.Scan0, rgba, 0, bytes);
var pixels = Enumerable.Range(0, rgba.Length / 4).Select(x => new {
B = rgba[x * 4],
G = rgba[(x * 4) + 1],
R = rgba[(x * 4) + 2],
A = rgba[(x * 4) + 3],
MakeTransparent = new Action(() => rgba[(x * 4) + 3] = 0)
});
pixels
.AsParallel()
.ForAll(p =>
{
byte max = Math.Max(Math.Max(p.R, p.G), p.B);
byte min = Math.Min(Math.Min(p.R, p.G), p.B);
if (p.G != min && (p.G == max || max - p.G < 7) && (max - min) > 20)
p.MakeTransparent();
});
System.Runtime.InteropServices.Marshal.Copy(rgba, 0, data.Scan0, bytes);
clone.UnlockBits(data);
return clone;
}
}
Do not forget to dispose of your Input Bitmap and the return of this method.
If you need to save the image just use the Save instruction of Bitmap.
clone.Save(#"C:\your\folder\path", ImageFormat.Png);
Here you can find methods to process an image even faster.Fast Image Processing in C#
Chromakey on a photo should assume an analog input. In the real world, exact values are very rare.
How do you compensate for this? Provide a threshold around the green of your choice in both hue and tone. Any colour within this threshold (inclusive) should be replaced by your chosen background; transparent may be best. In the first link, the Mask In and Mask Out parameters achieve this. The pre and post blur parameters attempt to make the background more uniform to reduce encoding noise side effects so that you can use a narrower (preferred) threshold.
For performance, you may want to write a pixel shader to zap the 'green' to transparent but that is a consideration for after you get it working.

Categories