I don't know if this is possible with Monotouch so I thought I'd ask the experts. Let's say I want to be able to take a picture of a painted wall and recognize the general color from it - how would I go about doing that in C#/Monotouch?
I know I need to capture the image and do some image processing but I'm more curious about the dynamics of it. Would I need to worry about lighting conditions? I assume the flash would "wash out" my image, right?
Also, I dont need to know exact colors, I just need to know the general color family. I dont need to know a wall is royal blue, I just need it to return "blue". I dont need to know hunter green, I just need it to return "green". I've never done that with image processing.
The code below relies on the .NET System.Drawing.Bitmap class and the System.Drawing.Color class, but I believe these are both supported in MonoTouch (at least based on my reading of the Mono Documentation).
So assuming you have an image in a System.Drawing.Bitmap object named bmp. You can obtain the average hue of that image with code like this:
float hue = 0;
int w = bmp.Width;
int h = bmp.Height;
for (int y = 0; y < bmp.Height; y++) {
for (int x = 0; x < bmp.Width; x++) {
Color c = bmp.GetPixel(x, y);
hue += c.GetHue();
}
}
hue /= (bmp.Width*bmp.Height);
That's iterating over the entire image which may be quite slow for a large image. If performance is an issue, you may want to limit the pixels evaluated to a smaller subsection of the image (as suggested by juhan_h), or just use a smaller image to start with.
Then given the average hue, which is in the range 0 to 360 degrees, you can map that number to a color name with something like this:
String[] hueNames = new String[] {
"red","orange","yellow","green","cyan","blue","purple","pink"
};
float[] hueValues = new float[] {
18, 54, 72, 150, 204, 264, 294, 336
};
String hueName = hueNames[0];
for (int i = 0; i < hueNames.Length; i++) {
if (hue < hueValues[i]) {
hueName = hueNames[i];
break;
}
}
I've just estimated some values for the hueValues and hueNames tables, so you may want to adjust those tables to suit your requirements. The values are the point at which the color appears to change to the next name (e.g. the dividing line between red and orange occurs at around 18 degrees).
To get an idea of the range of colors represent by the hue values, look at the color wheel below. Starting at the top it goes from red/orange (around 0° - north) to yellow/green (around 90° - east), to cyan (around 180° - south), to blue/purple (around 270° - west).
You should note, however, that we are ignoring the saturation and brightness levels, so the results of this calculation will be less than ideal on faded colors and under low light conditions. However, if all you are interested in is the general color of the wall, I think it might be adequate for your needs.
I recently dealt with shifting white balance on iOS (original question here: iOS White point/white balance adjustment examples/suggestions) which included a similar problem.
I can not give you code samples in C# but here are the steps that I would take:
Capture the image
Decide what point/part of the image is of interest (the smaller the better)
Calculate the "color" of that point of the image
Convert the "color" to human readable form (I guess that is what you need?)
To accomplish step #2 I would either let the user choose the point or take the point to be in the center of the image, because that is usually the place to which the camera is actually pointed.
How to accomplys step #3 depends on how big is the area chosen in step #2. If the area is 1x1 pixels then you render it in RGB and get the component (ie red green and blue) values from that rendered pixel. If the area is larger then you would need to get the RGB values of each of the pixels contained in that area and average them.
If you only need a general color this would be mostly it. But if you need to compensate for lighting conditions the problem gets very much more complicated. To compensate for lighting (ie White Balancing) you need to do some transformations and some guesses in which conditions the photo was taken. I will not go into details (I wrote my Bachelors thesis on those details) but Wikipedias article on White Balance is a good starting point.
It is also worth to note that the solution to White Balancing problem will always be subjective and dependent on the guesses made in which light the photo was taken (at least as far as I know).
To accomplish step #4 you should search for tables that map RGB values to human-readable colors. I have not had the need for these kinds of tables, but I am sure they exist somwhere on the Internet.
Related
I have a database that holds data of eyetracking on some videos.
I export those data to an int[,] input matrix for this issue. And then try to create a heatmap.
What I get so far is something like this:
But this is not actually what I want it to be. I want something like the heatmaps that you see when you google it, e.g.:
Treat each spot as an artificial circle with the center at that spot. A circle that has, say, 50 pixel radius. Now, go over all pixels of the image and for each one count all circles that cover that pixels. This is your score for that pixel. Translate it into color, i.e 0: black/transparent, 10: light green, 20: yelow, and so on. After analyzing all pixels you will get a color for each pixel. Write a bitmap, look at it. It should be something close to what you want.
Of course, circle radius, color mappings, etc, need adjusting to your needs. Also, that's probably not the best/simplest/fastest algorithm.
Different approach would be to store the "heat" in the pixels greyvalue.
Just create a second image with the same size as the original one and count up the greyvalue of a pixel everytime it was looked at.
Later you can use that value to calculate a size and color for the circle you want to draw.
You can then lay the heatmap image on top of the original one and you are done (dont forget to set transparency).
I'm trying to get an average color from a specific 4x4 area in a video texture on every update. It can either be black or white, but because I'm streaming the video and the resolution differs from time to time it might also be grayish. I need to find out more precisely if it's "almost black" or more on the white/light gray side of the spectrum.
I'm very new to c# and unity. I found Texture2D.GetPixel or Texture2D.GetPixels32 might be better, but I'm really not sure how I can do that from a video texture.
Thanks to anyone who can help.
Take a look at this post. It looks like you are doing the-same thing as that person which is to get Texture from an image and average the pixel.
The reason I don't consider this a duplicate is because you want to get a pixels from specific(4x4) area not all pixels from the texture.
Texture2D.GetPixel or Texture2D.GetPixels32 might be better,
Because you want to do this on speficic pixels, you can't do it with Texture2D.GetPixel or Texture2D.GetPixels32. You don't even need the UniversalMediaPlayer plugin you are using.
You can do this with the GetPixels function. Notice the 's' at the end.
This is the function prototype:
Color[] GetPixels(int x, int y, int blockWidth, int blockHeight);
Depending on the position of the pixels you want to read from the Texture it should look something like this:
Color[] texColors = tex.GetPixels(0, 0, 4, 4);
You need to use that instead of Color32[] texColors = tex.GetPixels32(); from this answer.
Note:
You can still use your UniversalMediaPlayer video plugin if you prefer it over Unity's new VideoPlayer. The GetPixels solution should still work.
I am trying to draw a diagonal shadow.
First I make all pixel to black:
Next with a simple for cicle this is are the result
Now I want to stretch this image diagonally to simulate a shadow.
I have tried:
Bitmap b = new Bitmap(tImage.Width + 100, tImage.Height);
Graphics p = Graphics.FromImage(b);
p.RotateTransform(30f);
p.TranslateTransform(100f, -200f);
p.DrawImage(tImage, new Rectangle(0, -20, b.Width+20, b.Height));
but the images are rotated and translated.
Please anyone have a solution for me?
I need it to look like this (created in Photoshop):
Creating a nice dropshadow is quite a task using Winforms and GDI+.
It features neither polygon scaling nor blurring; and let's not even think about 3D..! - But we can at least do a few things without too much work and get a nice result for many images..
Let's assume you already have an image that is cut out from its background.
The next step would be to turn all colors into black.
Then we most likely would want to add some level of transparency, so that the background the shadow falls on, still shines through.
Both task are done quite effectively by using a suitable ColorMatrix.
With a very transparent version we can also create simple blurring by drawing the image with offsets. For best results I would draw it nine times with 3 differents weights/alpha values..
High quality blurring is an art as you can see by even just looking at the filters and adjustments in pro software like Adobe Photoshop or Affinity Photo. Here is a nice set of interesting links..
But since we are only dealing with a b/w bitmap a simplitstic appraoch is good enough.. I use 3 alpha values of 5%, 10% and 20% for the 4 corner, the 4 edge and the 1 center drawings.
The final step is drawing the shadow with some skewing.
This is explained here; but while this is seemingly very simple it is also somewhat impractical. The three points the DrawImage overlay expects need to be calculated.
So here is a method that does just that; do note that is is a strongly simplified method:
The overlay takes three points, that is 6 floats. We only use 3 numbers:
one for the amount of skewing; 0.5 means the top is shifted to the right by half the width of the bitmap.
the other two are the scaling of the resulting bounding box. 1 and 0.5 mean that the width is unchanged and the height is reduced to 50%.
Here is the function:
public Bitmap SkewBitmap(Bitmap inMap, float skewX, float ratioX, float ratioY )
{
int nWidth = (int)(inMap.Width * (skewX + ratioX));
int nHeight = (int)(Math.Max(inMap.Height, inMap.Height * ratioY));
int yOffset = inMap.Height - nHeight;
Bitmap outMap = new Bitmap(nWidth, nHeight);
Point[] destinationPoints = {
new Point((int)(inMap.Width * skewX), (int)(inMap.Height * ratioY) + yOffset),
new Point((int)(inMap.Width * skewX + inMap.Width * ratioX),
(int)(inMap.Height * ratioY) + yOffset),
new Point(0, inMap.Height + yOffset ) };
using (Graphics g = Graphics.FromImage(outMap))
g.DrawImage(inMap, destinationPoints);
return outMap;
}
Note a few simplifications:
If you want to drop the shadow to the left you will need to not just move the first two points to the left but also to adapt the calculation of the width and also the way you overlay the object over the shadow.
If you study the MSDN example you will see that the DrawImage overlay also allows to do a rotation. I didn't add this to our function, as it is a good deal more complicated to calculate and even to just to write a signature.
If you wonder where the info of the six numbers go, here is the full layout:
3 go into our parameters
1 would be the angle of the rotation we don't do
2 could be either the rotation center point or a point (deltaX&Y) the by which the result is translated
If you look closely you can see the shadow of the left foot is a little below the foot. This is because the feet are not at the same level and with the vertical compression the base lines drift apart. To correct that we would either modify the image or add a tiny rotation after all.
Looking at your example imag it is clear that you will nee to take it apart and treat 'house' and 'tree' separately!
The signature is kept simple; this always a balance between ease of use and effort in coding. On could wish for a paramter the takes angle to control the skewing. Feel free to work out the necessary calculations..
Note that adding the functions behind the other buttons would go beyond the scope of the question. Suffice it to say that most are just one line to do the drawing and a dozen or so to set up the colormatrix..
Here is the code in the 'Skew' button:
Bitmap bmp = SkewBitmap((Bitmap)pictureBox4.Image, 0.5f, 1f, 0.5f);
pictureBox5.Image = pictureBox1.Image;
pictureBox5.BackgroundImage = bmp;
pictureBox5.ClientSize = new Size(bmp.Width, bmp.Height);
Instead of drawing the object over the shadow I make use of the extra layer of the PictureBox. You would of course combine the two Bitmaps..
I'm making a program and I need to know how to see if a pixel in a bitmap image is very dark or black. Big thanks if you can help.
//need help
if (_Bitmap.GetPixel(c, d, Color.FromArgb(255) == 255)) mouse_event((int)Test.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
//need help
In order to get color of a pixel you should use GetPixel method.
You can use GetBrightness method of Color class to get a brightness and check "how dark" is the color.
Brightness equals to 0 for pure black, and maximum value is 1.
So you can compare a brightness with some threshold for example 0.02.
if (_Bitmap.GetPixel(c,d).GetBrightness() < 0.02) { ... }
Your question is either very simple or deceptively complex.
Whether a pixel is black or not depends on the colour model the pixel is expressed in. In RGB a black pixel is expressed by the integer 0. Other colour models have other representations for black. In each case it is very straight forward to figure out whether a pixel is pure black.
However you also ask whether a pixel is very, very dark, and that may be tricky.
If you have a clear threshold for darkness (say the intensity/luminescence is less than 10%) then the answer is also straight forward.
This is easier in colour models with a separate intensity component but in RGB you might make due with the following approximation redComponent < 25 && greenComponent < 25 && blueComponent < 25
However if you ask whether a pixel is perceived as dark, then we thread into more complex territory. To determine whether a pixel is perceived as dark you'll need to take into consideration the values of adjacent pixels and figure out whether a darkening gradient exists. Some pixel shades will look bright if they are near the local intensity maximum, but will look dark if they are near the local intensity minimum (see checker shadow illusion for a well known example). Likewise a very dark vary narrow line adjacent a bright object may look as the edge of object and not as a dark element in the image.
How do I generate basic 3D shapes (red and blue) that can be seen as 3D with cellophane 3D glasses, using C# in a desktop app? (Note that this question is not limited to any particular language. If I can get a head start in any language, then that's great. I can always learn from that and eventually know enough to attempt to implement this in my desired language.)
I've seen so many questions about this, but the answers seem very complicated and don't lead me to anywhere in the end. I can't even find any docs or articles about this.
To generate Anaglyph 3D images, you first have to render the scene from two slightly different viewports, one for each eye. The further apart they are, the smaller the scene will look, and the higher the 3D-sense will be.
The easiest method would be to use some existing library to render the images. Using a "camera", position it slightly to the left (and right) of the center of view. Render two images, and get the pixels.
The second step is to combine the two images into an Anaglyph 3D image. One way to do this, is to combine the red channel from one image with the green and blue channels from the other.
(Pseduo-C#:)
Color Combine(Color left, Color right)
{
return new Color(left.Red, right.Green, right.Blue);
}
Image Combine(Image left, Image right)
{
Image result = new Image(left.Width, left.Height);
for (int y = 0; y < left.Height; y++)
for (int x = 0; x < left.Width; x++)
{
result.SetPixel(x, y, Combine(left.GetPixel(x, y), right.GetPixel(x, y)));
}
}