I have a C# desktop application.
I am using emgu framework for image processing.
What I am trying to do is improve 'washed' out images and make them more vibrant.
Using myImage._EqualHist() works OK for when the picture has a rich array of colours (like during the day) but at night-time the white-balancing will distort after applying that emgu filter and I get a very bright/busy night-time image. If I could just adjust the contrast that would be good - I think.
I have found some code that will adjust the contrast for me. I am now trying to find what value should I use to adjust the contrast by. Obviously, this value will vary during a 24hr cycle.
I have made a 1st stab at what relationship/formula I can use to auto-contrast an image.
Obviously, I am guessing and trying things out. Not very scientific//mathematical and I am searching the net for some logic I can apply.
Should I also consider the gamma of an image?
This is my code so far:
Image<Bgr, Byte> imgColor = new Image<Bgr, byte>(#"d:\20140320022038047.jpg");
Image<Hsv,Byte> hsv = new Image<Hsv,byte>(imgColor.ToBitmap());
double averageHue = hsv[0].GetAverage().Intensity;
double averageSat = hsv[1].GetAverage().Intensity;
double averageLum = hsv[2].GetAverage().Intensity;
//I am guessing here and playing around with the constants
float adjustContrastBy =(float)( averageLum / averageHue);
byte[, ,] data = imgColor.Data;
//this part of the code enumertaes through the Image byte array
for (int y = 0; y < imgColor.Height ; y++)
{
for (int x = 0; x < imgColor.Width; x++)
{
byte B = data[y, x, 0];
byte G = data[y, x, 1];
byte R = data[y, x, 2];
float Red = R / 255.0f;
float Green = G / 255.0f;
float Blue = B / 255.0f;
Red = (((Red - 0.5f) * adjustContrastBy) + 0.5f) * 255.0f;
Green = (((Green - 0.5f) * adjustContrastBy) + 0.5f) * 255.0f;
Blue = (((Blue - 0.5f) * adjustContrastBy) + 0.5f) * 255.0f;
int iR = (int)Red;
iR = iR > 255 ? 255 : iR;
iR = iR < 0 ? 0 : iR;
int iG = (int)Green;
iG = iG > 255 ? 255 : iG;
iG = iG < 0 ? 0 : iG;
int iB = (int)Blue;
iB = iB > 255 ? 255 : iB;
iB = iB < 0 ? 0 : iB;
data[y, x, 0] = (byte)iB;
data[y, x, 1] = (byte)iG;
data[y, x, 2] = (byte)iR;
}
}
pictureBox1.Image = imgColor.ToBitmap();
I also adjust the gamma using this method:
double intensity = grayCurrent.GetAverage().Intensity;
double g = Math.Log(Shared.Optimum / 255) / Math.Log(intensity / 255);
grayCurrent._GammaCorrect(g);
This certainly helps me with the motion detection but want I am now focusing on is improving what the User 'sees' as opposed to what computer detects..
Related
So, I was trying to come up with my own simple implementation of Perlin/Fractal noise(I'm just adding the noise to itself resampled multiple times).
When it comes to the resampling part of the algorithm, to achieve lower frequency noise I'm using bilinear interpolation on a smaller part of the array to effectively "stretch out" that smaller part (i.e: look at a 16x16 part of a 256x256 sheet of noise, but treat it as if it was a 256x256 array of pixels)
The site I use (well, one of them) for reference is http: //lodev.org/cgtutor/randomnoise.html
From the site the first two pictures is the result I'm looking for but instead i get something like this (I used a 64x64 array):
Original
Resampled using x8 Zoom
All of the code I'm using is in Unity (I know there is a built-in Perlin-generator for it, but I wanted my own):
using UnityEngine;
using System.Collections;
public class NoiseGenerator : MonoBehaviour {
//Dimensions of the array
public int width;
public int height;
//RNG Stuff for reproducability
public bool useTimeAsSeed;
public string seed;
//The array the nopise gets stored in
int[,] noise;
//These are used in the editor to control if we zoon in or not, original image gets resized to 1/ratio
public bool zoom;
public int ratio=1;
//Function used to fill up the array with white noise (random numbers with a uniform distribution between 0-255)
void InitNoise() {
noise = new int[width, height];
if (useTimeAsSeed) {
seed = Time.time.ToString();
}
System.Random r = new System.Random(seed.GetHashCode());
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
noise[x, y] = r.Next(0,256);
}
}
}
//Function to smooth the noise when it's being resampled
/*
AB
CD
|when resampled
v
A--R---B
| X |
| |
| |
C--J---D
Uses bilinear Interpolation to get the wighted average of A B C and D, which are all neighbouring pixels in the original picture
Returns an int value between 0-255
*/
int SmoothNoise(float x, float y) {
float fractX = x - (int)x; //Fractional part of the X-coord, used as the weight for Lerping in the x-direction
float fractY = y - (int)y; //Fractional part of the Y-coord, used as the weight for Lerping in the y-direction
//Calculating the neigbouring pixels
//For edge values the respective "missing pixel" is filled in by the other end of the noise sheet
int x1 = ((int)x + width) % width;
int x2 = (x1 + width - 1) % width;
int y1 = ((int)y + height) % height;
int y2 = (y1 + height - 1) % height;
//Calculating R and J by Lerping the respective pixels
float R = Mathf.Lerp(noise[x1, y1], noise[x2, y1], fractX);
float J = Mathf.Lerp(noise[x2, y1], noise[x2, y2], fractX);
//Finally Lerp R and J to get the value for X
return (int)(Mathf.Lerp(R,J,fractY));
}
void Start() {
InitNoise();
}
//Easy way to display the array as a temporary solution until actual terrain display is implemented
void OnDrawGizmos() {
if (noise != null) {
for (int x = 0; x < width; x++){
for (int y = 0; y < height; y++){
float component = (float)noise[x, y] / 255;
if (zoom) {
//Using the result of SmoothNoise normalised to be between 0-1 we construct a color component to be used when displaying
component = (float)SmoothNoise((float)x/ratio, (float)y/ratio) / 255;
}
Gizmos.color = new Color(component,component,component);
Vector3 pos = new Vector3(-width/2 + x + .5f, 0, -height/2 + y + .5f);
Gizmos.DrawCube(pos, Vector3.one);
}
}
}
}
}
C# has a very convenient getHue method, but I can't find a setHue method. Is there one?
If not, I think the best way to define a color after changing the hue would be to convert the HSL value to RGB, and then set the RGB value. I know there are formulas on the internet for doing this, but how would I best go about performing this conversion from HSL to RGB using C#?
Thank You
To set the Hue you create a new Color, maybe from a given one by using GetHue and GetSaturation. See below for the getBrightness function!
I'm using this:
Color SetHue(Color oldColor)
{
var temp = new HSV();
temp.h = oldColor.GetHue();
temp.s = oldColor.GetSaturation();
temp.v = getBrightness(oldColor);
return ColorFromHSL(temp);
}
// A common triple float struct for both HSL & HSV
// Actually this should be immutable and have a nice constructor!!
public struct HSV { public float h; public float s; public float v;}
// the Color Converter
static public Color ColorFromHSL(HSV hsl)
{
if (hsl.s == 0)
{ int L = (int)hsl.v; return Color.FromArgb(255, L, L, L); }
double min, max, h;
h = hsl.h / 360d;
max = hsl.v < 0.5d ? hsl.v * (1 + hsl.s) : (hsl.v + hsl.s) - (hsl.v * hsl.s);
min = (hsl.v * 2d) - max;
Color c = Color.FromArgb(255, (int)(255 * RGBChannelFromHue(min, max,h + 1 / 3d)),
(int)(255 * RGBChannelFromHue(min, max,h)),
(int)(255 * RGBChannelFromHue(min, max,h - 1 / 3d)));
return c;
}
static double RGBChannelFromHue(double m1, double m2, double h)
{
h = (h + 1d) % 1d;
if (h < 0) h += 1;
if (h * 6 < 1) return m1 + (m2 - m1) * 6 * h;
else if (h * 2 < 1) return m2;
else if (h * 3 < 2) return m1 + (m2 - m1) * 6 * (2d / 3d - h);
else return m1;
}
Do not use the built-in GetBrightness method! It returns the same value (0.5f) for red, magenta, cyan, blue and yellow (!). This is better:
// color brightness as perceived:
float getBrightness(Color c)
{ return (c.R * 0.299f + c.G * 0.587f + c.B *0.114f) / 256f; }
System.Drawing.Color is a value type, which are almost always made immutable, especially in frameworks. That's why you can't setHue on it, you can only construct a new value type with fields you need.
So, if you have a function that will give you RGB values for your HSB values, you can do it like this
Color oldColor = ...;
int red, green, blue;
FromHSB(oldColor.GetHue(), oldColor.GetSaturation(), oldColor.GetBrightness(), out red, out green out blue);
Color newColor = Color.FromArgb(oldColor.A, red, green, blue);
where FromHSB looks like this
void FromHSB(float hue, float saturation, float brightness, out int red, out int green, out int blue)
{
// ...
}
I have a .png with transparency that I need to desaturate. I read I need to take the average R,G and B value of the bitmap then use:
G*.59
R*.3
B*.11
I calculate the average color in this way:
private Color Average_Color(Bitmap bitmap) {
Color c = new Color();
int pixel_number = 0;
int r = 0;
int g = 0;
int b = 0;
for (int i = 0; i < bitmap.Width; i++) {
for (int j = 0; j < bitmap.Height; j++) {
c = bitmap.GetPixel(i, j);
r += c.R;
g += c.G;
b += c.B;
pixel_number++;
}
}
c = Color.FromArgb(1, r / pixel_number, g / pixel_number, b / pixel_number);
return c;
}
Then, first to paint my texture, I set the Color in this way:
rgb = Average(bitmap);
GL.Color3(rgb.R * 0.59, rgb.G * 0.3, rgb.B * 0.11);
//here I draw my texture
I don't know why but it doesn't work (I get the texture with his original colors). I guess it's something wrong in Average_Color. Maybe because it's not a total opaque bitmap?
OpenGL expects its colors to be floats normalized from 0 to 1, but I suspect that your bitmap is reporting colors from 0-255.
Therefore I assume you're passing RGB values much greater than 1 to glColor, which get clamped to 1, so your texture looks the same.
Try
GL.Color3(rgb.R*0.59/255.f, rgb.G*0.3/255.f, rgb.B*0.11/255.f);
Try casting your variables to floats before the divide
Hello I have heightData in memory and sometimes (when I edit it) I wanna save it to jpg. This is my code:
float multi = 0.2f;
float[,] heightData = quadTree.HeightData;
Color[] heightMapColors = new Color[heightData.Length];
for (int x = 0; x < heightData.GetLength(0); x++)
{
for (int y = 0; y < heightData.GetLength(1); y++)
{
byte colorData = (byte)(heightData[x, y] / multi);
heightMapColors[x + y * heightData.GetLength(0)].R = colorData;
heightMapColors[x + y * heightData.GetLength(0)].G = colorData;
heightMapColors[x + y * heightData.GetLength(0)].B = colorData;
}
}
Texture2D heightMap = new Texture2D(device, heightData.GetLength(0), heightData.GetLength(1), false, SurfaceFormat.Color);
heightMap.SetData<Color>(heightMapColors);
using (System.IO.Stream stream = System.IO.File.OpenWrite(#"D:\test.jpg"))
{
heightMap.SaveAsJpeg(stream, heightData.GetLength(0), heightData.GetLength(1));
}
I am 100% sure that I have data in heightMapColors, but saved jpg is only black. :/ Is it a good way how to do it or something is wrong?
Alpha should not be zero
heightMapColors[x + y * heightData.GetLength(0)].R = colorData;
heightMapColors[x + y * heightData.GetLength(0)].G = colorData;
heightMapColors[x + y * heightData.GetLength(0)].B = colorData;
heightMapColors[x + y * heightData.GetLength(0)].A = 255;
A JPG is probably not a good format to store a heightmap into because it is a lossy format. You should be putting it into a BMP pr PNG. That said, what is the range of your "height"? It looks like your height is a float which means it is probably not in the right range to be displayed or even converted to discrete values.
If your allowed height range is Xf to Yf, convert that to a 0 - 255 range using
byteValue = (byte)(((OldValue - OldMin) * (255 - 0)) / (OldMax - OldMin)) + 0
and then give it a shot.
I have two images and I want to multiply these two images together in C# as we multiply two layers in Photoshop.
I have found the method by which the layers are multiplied in photoshop or any other application.
Following is the formula that I have found on GIMP documentation. It says that
E=(M*I)/255
where M and I are the color component(R,G,B) values of the two layers. We have to apply this to every color component. E will be the resultant value for that color component.
If the color component values are >255 then it should be set to white i.e. 255 and if it is <0 then it should be set as Black i.e. 0
Here I have a suggestion - I didn't test it, so sorry for any errors - I'm also assuming that both images have the same size and are greylevel.
Basically I'm multiplying the image A for the relative pixel percentage of image B.
You can try different formulas like:
int result = ptrB[0] * ( (ptrA[0] / 255) + 1);
or
int result = (ptrB[0] * ptrA[0]) / 255;
Never forget to check for overflow (above 255)
public void Multiply(Bitmap srcA, Bitmap srcB, Rectangle roi)
{
BitmapData dataA = SetImageToProcess(srcA, roi);
BitmapData dataB = SetImageToProcess(srcB, roi);
int width = dataA.Width;
int height = dataA.Height;
int offset = dataA.Stride - width;
unsafe
{
byte* ptrA = (byte*)dataA.Scan0;
byte* ptrB = (byte*)dataB.Scan0;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x, ++ptrA, ++ptrB)
{
int result = ptrA[0] * ( (ptrB[0] / 255) + 1);
ptrA[0] = result > 255 ? 255 : (byte)result;
}
ptrA += offset;
ptrB += offset;
}
}
srcA.UnlockBits(dataA);
srcB.UnlockBits(dataB);
}
static public BitmapData SetImageToProcess(Bitmap image, Rectangle roi)
{
if (image != null)
return image.LockBits(
roi,
ImageLockMode.ReadWrite,
image.PixelFormat);
return null;
}