How to convert Bgr pixel to Hsv pixel in EmguCV? - c#

I'd like to convert a Bgr value (one pixel) to an Hsv value. How can I do that (without writing the conversion code from scratch) in EmguCV?
Please note that I am not interested in converting whole image's color space but only one pixel, therefore CvInvoke.cvCvtColor() does not work for me.

If you want to do this within EmguCV just read the image into a Image the get the value of the pixel and stick it in a Hsv structure.
For example:
static void Main(string[] args)
{
bool haveOpenCL = CvInvoke.HaveOpenCL;
bool haveOpenClGpu = CvInvoke.HaveOpenCLCompatibleGpuDevice;
CvInvoke.UseOpenCL = true;
Emgu.CV.Image<Bgr, Byte> lenaBgr = new Image<Bgr, Byte>(#"D:\OpenCV\opencv-3.2.0\samples\data\Lena.jpg");
CvInvoke.Imshow("Lena BSG", lenaBgr);
Bgr color = lenaBgr[100, 100];
Console.WriteLine("Bgr: {0}", color.ToString());
Emgu.CV.Image<Hsv, Byte> lenaHsv = new Image<Hsv, Byte>(#"D:\OpenCV\opencv-3.2.0\samples\data\Lena.jpg");
CvInvoke.Imshow("Lena HSV", lenaHsv);
Hsv colorHsv = lenaHsv[100, 100];
Console.WriteLine("HSV: {0}", colorHsv.ToString());
CvInvoke.WaitKey(0);
}
}
The result:
Bgr: [87,74,182]
HSV: [176,151,182]
Lena BGR Lena HSV

Okay I already found a way using some help from .NET framework
given a Bgr pixel ;
1- Converting the color to System.Drawing.Color :
System.Drawing.Color intermediate = System.Drawing.Color.FromArgb((int)pixel.Red, (int)pixel.Green, (int)pixel.Blue);
2- Constructing Hsv from the intermediate.
Hsv hsvPixel = new Hsv(intermediate.GetHue(), intermediate.GetSaturation(), intermediate.GetBrightness());
Cheers.

Related

How To Threshold Raw Byte Image with EmguCV

i am trying to Threshold and also applying the Gaussian Blur on Raw byte image in EmguCV. I have raw array bytes, which are bytes from Dicomfile, i read those bytes and store them in a empty image, once i wanna apply threshold or gaussian blur i get these Exception:
OpenCv: src.type() == CV_8UC1.
Thank you.
var pixelData_ = dataset.GetValues<byte>(DicomTag.PixelData);
var splitPixelSpacing = pixelSpacing.Split('\\');
var IPP = dataset.GetString(DicomTag.ImagePositionPatient);
var iimg = new Image<Gray, ushort>(cols, rows);
var parse = float.Parse(splitPixelSpacing[0], CultureInfo.InvariantCulture.NumberFormat);
var newPixelSpacing = parse* (cols / 512);
iimg.Bytes = pixelData_;
iimg = iimg.Resize(512, 512, Inter.Area);
var imgBin = new Image<Gray, byte>(512, 512, new Gray(10));
var blr = iimg.ThresholdAdaptive(new Gray(100),
AdaptiveThresholdType.GaussianC,
ThresholdType.ToZero,
5,
new Gray(2));
//I set here a breakpoint then i get these Exception:src.type() == CV_8UC1.
Unfortenatly the documentation of EmguCV lacks any information about this error, and also lack any prerequisites. But emguCV is just a wrapper around OpenCV, and if we check that documentation we can find:
src Source 8-bit single-channel image.
I.e. ThresholdAdaptive is limited to 8 bit grayscale and will not work for 16 bit grayscale images.
If you just want a simple threshold you might want to use ThresholdBinary, or one of the other simple thresholding methods.

Negative values when comparing two histograms

I am using EmguCV(c#) histogram to compare two HSV images. But sometimes I get negative values. I assumed that when I compare 2 histogram values, the value will be in the interval <0 and 1>. However, some of the values of hue or saturation are sometimes negative numbers like -0.145.
Firstly, I get byte image array, which I convert into Image<Hsv, Byte> - img1.
Image<Hsv, Byte> img1 = null;
Mat byteImageMat = new Mat();
Mat hsvMat = new Mat();
CvInvoke.Imdecode(request.ByteImage, Emgu.CV.CvEnum.ImreadModes.AnyColor, byteImageMat);
CvInvoke.CvtColor(byteImageMat, hsvMat, ColorConversion.Bgr2Hsv);
img1 = hsvMat.ToImage<Hsv, Byte>();
Then I create DenseHistogram and spliting individual channels.
DenseHistogram ComparedHistoHue = new DenseHistogram(180, new RangeF(0, 180));
DenseHistogram ComparedHistoSaturation = new DenseHistogram(256, new RangeF(0, 256));
DenseHistogram ComparedHistoBrightness = new DenseHistogram(256, new RangeF(0, 256));
Image<Gray, Byte> hueChannel = img1[0];
Image<Gray, Byte> saturationChannel = img1[1];
Image<Gray, Byte> brightnessChannel = img1[2];
After that I calculate histograms
ComparedHistoHue.Calculate(new Image<Gray, Byte>[] { hueChannel }, false, null);
ComparedHistoSaturation.Calculate(new Image<Gray, Byte>[] { saturationChannel }, false, null);
ComparedHistoBrightness.Calculate(new Image<Gray, Byte>[] { brightnessChannel }, false, null);
At this point, I loaded histogram from file which I created before and assign it into Mat (loadedMatHue, loadedMatSaturation and loadedMatBrightness).
double hue = CvInvoke.CompareHist(loadedMatHue, ComparedHistoHue, Emgu.CV.CvEnum.HistogramCompMethod.Correl);
double satuation = CvInvoke.CompareHist(loadedMatSaturation, ComparedHistoSaturation, Emgu.CV.CvEnum.HistogramCompMethod.Correl);
double brightnes = CvInvoke.CompareHist(loadedMatBrightness, ComparedHistoBrightness, Emgu.CV.CvEnum.HistogramCompMethod.Correl);
Can somebody tell me, why is in hue or saturation variable negative value? In my opinion and tests, there is always only one negative value at one momemnt across the double variables.
For HSV, the idea that the numbers would be between 0 and 1 is incorrect. If you want your image to have values between 0 and 1, then that image would have to be in grayscale.
In HSV, you split it up into three definitions, Hue, Saturation, and Value.
Hue is stored from 0 to 360 degrees, but can become negative if you rotate the hue past 0.
Saturation is considered from 0 to 1, i.e grayscale values. If you have negative values in this channel, disregard them, as the lowest that this value should be is 0. The same can be said for the highest value, which will be 1 since the highest value of a grayscale channel can only be one. Like I said before, its best to think of this channel in terms of grayscale from 0 to 1.
Value is very similar to saturation, the only difference being that value is considered the "lightness of the color, by the given S[saturation]" This value also can only be between 0 and 1, and any values outside of this space should be clipped.
If you want a more in depth explanation, you can check out this Stack post, which is very detailed and I thought it should be credited in this post.
If you do have to clip these values, you can always access the pixel values for each channel using some sample code below.
Image<Hsv,Byte> sampleImage = new Image<Hsv,Byte>("path\to\image");
//X and y are the pixel coordinates on an image
//Hue channel
byte hue = sampleImage.Data[y,x,0];
//Saturation channel
byte sat = sampleImage.Data[y,x,1];
//Value channel
byte val = sampleImage.Data[y,x,2];
You can throw these values inside of a loop and check if a pixel is outside the boundaries, and if it is replace it with the high or low value respectively.

How to desaturate an image using Emgu c#

I know that we desaturate an image by decreasing the values in the Saturation channel. I want to acomplish this using c# with emgu
For instance here is c++ code using opencv to do so:
Mat Image = imread("images/any.jpg");
// Specify scaling factor
float saturationScale = 0.01;
Mat hsvImage;
// Convert to HSV color space
cv::cvtColor(Image,hsvImage,COLOR_BGR2HSV);
// Convert to float32
hsvImage.convertTo(hsvImage,CV_32F);
vector<Mat>channels(3);
// Split the channels
split(hsvImage,channels);
// Multiply S channel by scaling factor
channels[1] = channels[1] * saturationScale;
// Clipping operation performed to limit pixel values
// between 0 and 255
min(channels[1],255,channels[1]);
max(channels[1],0,channels[1]);
// Merge the channels
merge(channels,hsvImage);
// Convert back from float32
hsvImage.convertTo(hsvImage,CV_8UC3);
Mat imSat;
// Convert to BGR color space
cv::cvtColor(hsvImage,imSat,COLOR_HSV2BGR);
// Display the images
Mat combined;
cv::hconcat(Image, imSat, combined);
namedWindow("Original Image -- Desaturated Image", CV_WINDOW_AUTOSIZE);
In c# I have:
var img = new Image<Gray, byte>("images/any.jpg");
var imhHsv = img.Convert<Hsv, byte>();
var channels = imhHsv.Split();
// Multiply S channel by scaling factor and clip (limit)
channels[1] = (channels[1] * saturationScale);
I am not sure how to merge modified saturation channel with imHsv, if I do this:
CvInvoke.Merge(channels, imhHsv);
there is error:
cannot convert 'Emgu.CV.Image[]' to
'Emgu.CV.IInputArrayOfArrays'
I put a VectorOfMat into the CvInvoke.Merge and it works.
Mat[] m = new Mat[3];
m[0] = CvInvoke.CvArrToMat(channels[0]);
m[1] = CvInvoke.CvArrToMat(channels[1]);
m[2] = CvInvoke.CvArrToMat(channels[2]);
VectorOfMat vm = new VectorOfMat(m);
CvInvoke.Merge(vm, imhHsv);

Opencv: Working with 16bit image, Uint16

I have a 12bit image that I want to work with in Opencv, such as detecting blobs etc.
The image is now a Uint16 array. And i want to convert it to Opencv Mat or Iplimage. I need to do that inorder to threshold and detect blobs.
What im doing is to convert the ushort array to bitmap, and then to Mat using opencv extensions, see below.
ushort[,] red = new ushort [480,640];
//Grab image in to ushort array
//..
bmpred = U16ArrayToBitmap(red);
Mat redMat = new Mat();
redMat = OpenCvSharp.Extensions.BitmapConverter.ToMat(bmpred);
Now redMat, my Mat image is U8C4, as I understand its 4 Chanel Unsigned 8 bit.
This wont work for me, because I want to use all of the 12 bits!
Is it possible to convert a ushort array or a bitmap into a 16bit grayscale Mat or iplimage?
Thanks!
You can use Mat constructor directly:
var mat = new Mat(red.GetLength(0), red.GetLength(1), MatType.CV_16UC1, red);
Or use a MatOfUShort:
var mat = new MatOfUShort(red.GetLength(0), red.GetLength(1), red);
Note that mat is using a pointer to red array. Clone mat if you want to use a copy:
var clone = mat.Clone();
For more information check Mat - The Basic Image Container OpenCV documentation page and OpenCvSharp Wiki.

get the lowest and highest most popular hue color from an image

I have an image and I want to find the most dominant lowest hue colour and the most dominant highest hue colour from an image.
It is possible that there are several colours/hues that are close to each other in populace to be dominant and if that is the case I would need to take an average of the most popular.
I am using emgu framework here.
I load an image into the HSV colour space.
I split the hue channel away from the main image.
I then use the DenseHistogram to return my ranges of 'buckets'.
Now, I could enumerate through the bin collection to get what I want but I am mindful of conserving memory when and wherever I can.
So, is there a way of getting what I need at all from the DenseHistogram 'object'?
i have tried MinMax (as shown below) and I have consider using linq but not sure if that is expensive to use and/or how to use it with just using a float array.
This is my code so far:
float[] GrayHist;
Image<Hsv, Byte> hsvSample = new Image<Hsv, byte>("An image file somewhere");
DenseHistogram Histo = new DenseHistogram(255, new RangeF(0, 255));
Histo.Calculate(new Image<Gray, Byte>[] { hsvSample[0] }, true, null);
GrayHist = new float[256];
Histo.MatND.ManagedArray.CopyTo(GrayHist, 0);
float mins;
float maxs;
int[] minVals;
int[] maxVals;
Histo.MinMax(out mins, out maxs, out minVals, out maxVals); //only gets lowest and highest and not most popular
List<float> ranges= GrayHist.ToList().OrderBy( //not sure what to put here..

Categories

Resources