Convert gray image to binary image in OpenCV - opencv

I would like to know what is the problem in below code, since it only appears only part of the Gray image as Binary image!
cv::Mat gry = cv::imread("image_gray.jpg");
cv::Mat bin(gry.size(), gry.type());
for (int i=0; i<gry.rows ;i++)
{
for (int j=0; j<gry.cols ;j++)
{
if (gry.at<uchar>(i,j)>=100)
bin.at<uchar>(i,j)=255;
else
bin.at<uchar>(i,j)=0;
}
}
cv::namedWindow("After", cv::WINDOW_AUTOSIZE);
cv::imshow("After",bin);
waitKey(0);
cvDestroyWindow( "After" );
imwrite("binary_image.bmp", bin);

Your problem is in cv::imread.
The function assumes it should load the image as a color image, if you want to load it as a garyscale image, you should call the function as follows:
cv::imread(fileName, CV_LOAD_IMAGE_GRAYSCALE)
By the way, the reason you only see part of the image, is because the image is simply bigger than a uchar for each pixel. (and you end up iterating only over part of it).

it would be easier if you use use the OpenCV function:
cv::threshold(image_src, image_dst, 200, 255, cv::THRESH_BINARY);
This piece of code set as black value (255) all those pixels which have as original value 200.

Related

How to get similarties and differences between two images using Opencv

I want to compare two images and find same and different parts of images. I tired "cv::compare and cv::absdiff" methods but confused which one can good for my case. Both show me different results. So how i can achieve my desired task ?
Here's an example how you can use cv::absdiff to find image similarities:
int main()
{
cv::Mat input1 = cv::imread("../inputData/Similar1.png");
cv::Mat input2 = cv::imread("../inputData/Similar2.png");
cv::Mat diff;
cv::absdiff(input1, input2, diff);
cv::Mat diff1Channel;
// WARNING: this will weight channels differently! - instead you might want some different metric here. e.g. (R+B+G)/3 or MAX(R,G,B)
cv::cvtColor(diff, diff1Channel, CV_BGR2GRAY);
float threshold = 30; // pixel may differ only up to "threshold" to count as being "similar"
cv::Mat mask = diff1Channel < threshold;
cv::imshow("similar in both images" , mask);
// use similar regions in new image: Use black as background
cv::Mat similarRegions(input1.size(), input1.type(), cv::Scalar::all(0));
// copy masked area
input1.copyTo(similarRegions, mask);
cv::imshow("input1", input1);
cv::imshow("input2", input2);
cv::imshow("similar regions", similarRegions);
cv::imwrite("../outputData/Similar_result.png", similarRegions);
cv::waitKey(0);
return 0;
}
Using those 2 inputs:
You'll observe that output (black background):

OpenCV color extraction in iOS

I have a CvVideoCamera and I'm trying to detect the blue color in each frame, and the output frames should contain only the blue objects, like here. I'm doing this in the delegate method:
- (void)processImage:(cv::Mat&)image
{
cv::Mat bgrMat;
cvtColor(image, bgrMat, CV_BGRA2BGR);
// Covert color space to HSV
cv::Mat hsvMat;
cvtColor(bgrMat, hsvMat, CV_BGR2HSV);
// Threshold the HSV image
cv::Mat blueMask;
cv::Scalar lower_blue(110, 50, 50);
cv::Scalar upper_blue(130, 255, 255);
cv::inRange(hsvMat, lower_blue, upper_blue, blueMask);
bitwise_and(bgrMat, bgrMat, image, blueMask);
}
Original image:
Result:
The blue color detection seems to be working fine, but the final result is red instead of blue. Any ideas why? Am I using the bitwise_and correctly?
[Edit]
These lines do the trick:
cv::Mat output;
image.copyTo(output, blueMask);
output.copyTo(image);
instead of:
bitwise_and(bgrMat, bgrMat, image, blueMask);
Thanks to karlphillip for the suggestion. For some reason the bgrMat gets 'altered' along the way, so I'm using the original image instead.
I think what you are trying to accomplish is to copy the pixels from the input image using a blue mask, right? Adjust your code at the end to:
cv::inRange(hsvMat, lower_blue, upper_blue, blueMask);
cv::Mat output;
bgrMat.copyTo(output, blueMask);

How to obtain the floodfilled area?

Let me start by saying that I'm still a beginner using OpenCV. Some things might seem obvious and once I learn them hopefully they also become obvious to me.
My goal is to use the floodFill feature to generate a separate image containing only the filled area. I have looked into this post but I'm a bit lost on how to convert the filled mask into an actual BGRA image with the filled color. Besides that I also need to crop the newly filled image to contain only the filled area. I'm guessing OpenCV has some magical function that could do the trick.
Here is what I'm trying to achieve:
Original image:
Filled image:
Filled area only:
UPDATE 07/07/13
Was able to do a fill on a separate image using the following code. However, I still need to figure out the best approach to get only the filled area. Also, my floodfill solution has an issue with filling an image that contains alpha values...
static int floodFillImage (cv::Mat &image, int premultiplied, int x, int y, int color)
{
cv::Mat out;
// un-multiply color
unmultiplyRGBA2BGRA(image);
// convert to no alpha
cv::cvtColor(image, out, CV_BGRA2BGR);
// create our mask
cv::Mat mask = cv::Mat::zeros(image.rows + 2, image.cols + 2, CV_8U);
// floodfill the mask
cv::floodFill(
out,
mask,
cv::Point(x,y),
255,
0,
cv::Scalar(),
cv::Scalar(),
+ (255 << 8) + cv::FLOODFILL_MASK_ONLY);
// set new image color
cv::Mat newImage(image.size(), image.type());
cv::Mat maskedImage(image.size(), image.type());
// set the solid color we will mask out of
newImage = cv::Scalar(ARGB_BLUE(color), ARGB_GREEN(color), ARGB_RED(color), ARGB_ALPHA(color));
// crop the 2 extra pixels w and h that were given before
cv::Mat maskROI = mask(cv::Rect(1,1,image.cols,image.rows));
// mask the solid color we want into new image
newImage.copyTo(maskedImage, maskROI);
// pre multiply the colors
premultiplyBGRA2RGBA(maskedImage, image);
return 0;
}
you can get the difference of those two images to get the different pixels.
pixels with no difference will be zero and other are positive value.
cv::Mat A, B, C;
A = getImageA();
B = getImageB();
C = A - B;
handle negative values in the case.(i presume not in your case)

Image transparency darkened when saved using OpenCv

I created a drawing application where I allow the user to draw and save the image to later reload to continue drawing. Essentially, I'm passing the drawing as a bitmap to the JNI layer to be saved and the same to load a previous drawing.
I'm using OpenCv to write and read to png file.
I'm noticing something weird in terms of the transparencies of the image. It almost seems as the transparency is being calculated against a black color on OpenCv? Take a look a the images attached, the contain lines that have transparencies.
Correct transparency by passing int array to native code, no color conversion needed:
Darkened transparency by passing Bitmap object to native code, color conversion needed:
What could potentially be happening?
Saving image using native Bitmap get pixel methods:
if ((error = AndroidBitmap_getInfo(pEnv, jbitmap, &info)) < 0) {
LOGE("AndroidBitmap_getInfo() failed! error:%d",error);
}
if (0 == error)
{
if ((error = AndroidBitmap_lockPixels(pEnv, jbitmap, &pixels)) < 0) {
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", error);
}
}
if (0 == error)
{
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888)
{
LOGI("ANDROID_BITMAP_FORMAT_RGBA_8888");
}
else
{
LOGI("ANDROID_BITMAP_FORMAT %d",info.format);
}
Mat bgra(info.height, info.width, CV_8UC4, pixels);
Mat image;
//bgra.copyTo(image);
// fix pixel order RGBA -> BGRA
cvtColor(bgra, image, COLOR_RGBA2BGRA);
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(3);
// save image
if (!imwrite(filePath, image, compression_params))
{
LOGE("saveImage() -> Error saving image!");
error = -7;
}
// release locked pixels
AndroidBitmap_unlockPixels(pEnv, jbitmap);
}
Saving image using native int pixel array methods:
JNIEXPORT void JNICALL Java_com_vblast_smasher_Smasher_saveImageRaw
(JNIEnv *pEnv, jobject obj, jstring jFilePath, jintArray jbgra, jint options, jint compression)
{
jint* _bgra = pEnv->GetIntArrayElements(jbgra, 0);
const char *filePath = pEnv->GetStringUTFChars(jFilePath, 0);
if (NULL != filePath)
{
Mat image;
Mat bgra(outputHeight, outputWidth, CV_8UC4, (unsigned char *)_bgra);
bgra.copyTo(image);
if (0 == options)
{
// replace existing cache value
mpCache->insert(filePath, image);
}
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(compression);
// save image
if (!imwrite(filePath, image))
{
LOGE("saveImage() -> Error saving image!");
}
}
pEnv->ReleaseIntArrayElements(jbgra, _bgra, 0);
pEnv->ReleaseStringUTFChars(jFilePath, filePath);
}
Update 05/25/12:
After a little more research I'm finding out that this issue does not happen if I get the int array of pixels from the bitmap and pass that directly to the JNI as opposed to what I do currently which is pass the entire Bitmap to the JNI layer then get the pixels and use cvtColor to convert pixels properly. Am I using the right pixel conversion?
There are two ways representing alpha in an RGBA pixel, premultiplied or not. With premultiplication, the R, G, and B values are multiplied by the percentage of alpha: color = (color * alpha) / 255. This simplifies a lot of blending calculations and is often used internally in imaging libraries. Before saving out to a format that doesn't use premultiplied alpha, such as PNG, the color values must be "unmultiplied": color = (255 * color) / alpha. If it is not, the colors will look too dark; the more transparent the color, the darker it will be. That looks like the effect you're seeing here.
There is nothing called as transparent image in opencv. The foreground and the background images are mixed appropriately to give the illusion of transparency. Check this to see how its done.

OpenCV IplImage data to float

Is there a way to convert IplImage pointer to float pointer? Basically converting the imagedata to float.
Appreciate any help on this.
Use cvConvert(src,dst) where src is the source image and dst is the preallocated floating point image.
E.g.
dst = cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_32F,1);
cvConvert(src,dst);
// Original image gets loaded as IPL_DEPTH_8U
IplImage* colored = cvLoadImage("coins.jpg", CV_LOAD_IMAGE_UNCHANGED);
if (!colored)
{
printf("cvLoadImage failed!\n");
return;
}
// Allocate a new IPL_DEPTH_32F image with the same dimensions as the original
IplImage* img_32f = cvCreateImage(cvGetSize(colored),
IPL_DEPTH_32F,
colored->nChannels);
if (!img_32f)
{
printf("cvCreateImage failed!\n");
return;
}
cvConvertScale(colored, img_32f);
// quantization for 32bit. Without it, this img would not be displayed properly
cvScale(img_32f, img_32f, 1.0/255);
cvNamedWindow("test", CV_WINDOW_AUTOSIZE);
cvShowImage ("test", img_32f);
You can't convert the image to float by simply casting the pointer. You need to loop over every pixel and calculate the new value.
Note that most float image types assume a range of 0-1 so you need to divide each pixel by whatever you want the maximum to be.

Resources