Change RGB value of pixel based on threshold OpenCV C++ - c++

I have an image in which pixels above a certain value I'd like to turn red, and pixels below a certain value I'd like to turn blue.
So far, I can get a matrix of red pixels, and a matrix of blue pixels by using thresholding, and bitwise operators to set the pixel value:
cvtColor(displayImage, displayImage, COLOR_GRAY2BGR);
threshold(displayImage, highThresh, highThreshVal, 255, 0);
highThresh = highThresh & Scalar(0, 0, 255); // Turn it red
threshold(displayImage, lowThresh, lowThreshVal, 255, 1);
lowThresh = lowThresh & Scalar(255, 0, 0); // Turn it blue
displayImage = lowThresh + highThresh;
When I display the displayImage, I see almost exactly what I want. It's an image where all the pixels below lowThreshVal are blue, and all pixels above highThreshVal are red. However, the pixels that are in between these values are all set to 0. Whereas, I would like to show the original image overlayed with the blue and red images. I'm not sure how to do this, or if I'm taking the best approach.
I know I can't add the images because I want to make sure every pixel above the threshold is pure red, not a mix of red and the original image, this yields pink-ish pixels instead of bright red pixels, which defeats the purpose of what I'm trying to build. But as of right now, I'm kind of stuck on what to do.

This worked.
cvtColor(displayImage, displayImage, COLOR_GRAY2BGR);
origImage = displayImage.clone();
threshold(origImage, highThresh, highThreshVal, 255, 0); // Binary thresholding
cvtColor(highThresh, highThresh, CV_BGR2GRAY);
displayImage.setTo(Scalar(0, 0, 255), highThresh);
threshold(origImage, lowThresh, lowThreshVal, 255, 1); // Binary thresholding
cvtColor(lowThresh, lowThresh, CV_BGR2GRAY);
displayImage.setTo(Scalar(255, 0, 0), lowThresh);

Related

Make all transparent outside the mask

I have jpg image and polygon which I want to use as mask in this way: image inside this polygon should be displayed, and all outside this polygon should be 100% transparent. Now I achieved only first goal - I can display image inside polygon, but all outside of it is black:
cv::Mat image;
//load image
image.convertTo(image, CV_8UC3, 255.0);
std::vector<cv::Point> contour;
//load polygon
const cv::Point* elementPoints[1] = { &contour[0] };
int numberOfPoints = (int)contour.size();
cv::Mat mask = cv::Mat::zeros(image.size(), image.type());
cv::fillPoly(mask, elementPoints, &numberOfPoints, 1, cv::Scalar( 255, 255, 255), 8);
cv::Mat dstImage = cv::Mat::zeros(image.size(), image.type());
cv::bitwise_and(image, mask, dstImage);
imwrite("test.jpg", dstImage);
I know that I need to use alpha channel, but it's unclear what I need to do next and how to implement this.
How can I get transparent background outside the mask?
First, create your image with four channels as described in this answer. Use negative source for fourth channel to get it zeroed out already. You now have a totally transparent image.
Create your mask just as you did before, just using different RGBA values (be aware that Scalar has a fourth constructor parameter for alpha values):
cv::fillPoly(mask, elementPoints, &numberOfPoints, 1, cv::Scalar(0, 0, 0, 255), 8);
Finally, apply the mask to set the region in question to totally opaque:
cv::bitwise_or(image, mask, dstImage);
You might want to retain the RGB values of the original image (so you can operate on later) or you might clear them out (which will result in higher compression and thus smaller file size). If the latter, use an inverted mask with RGBA set to 0, 0, 0, 0 and apply that with bitwise_and...

Remove black Background from an image.

I'm new to image processing and development. I have used opencv, There I need to extract circle from a given image. That circle given x, y coordinates are (radius) in Oder to do that I used following code. But my problem is I have to take black rectangle. So the image patch having unwanted black pixels. How do I save just only circle?
my code
double save_key_points(Mat3b img, double x, double y, double radius, string
filename, string foldername)
{
// print image height and width first and check.
Vec3f circ(x, y, radius);
// Draw the mask: white circle on black background
Mat1b mask(img.size(), uchar(0));
circle(mask, Point(circ[0], circ[1]), circ[2], Scalar(255), CV_FILLED);
// Compute the bounding box
Rect bbox(circ[0] - circ[2], circ[1] - circ[2], 2 * circ[2], 2 * circ[2]);
// Create a black image
Mat3b res(img.size(), Vec3b(0, 0, 0));
// Copy only the image under the white circle to black image
img.copyTo(res, mask);
// Crop according to the roi
res = res(bbox);
//remove black but doesn't work.
Mat tmp, alpha;
threshold(res, alpha, 100, 255, THRESH_BINARY);
// Save the image
string path = "C:\\Users\\bb\\Desktop\\test_results\\test_case8\\" + foldername + filename + ".png";
imwrite(path, res);
Mat keypointimg = imread(path, CV_LOAD_IMAGE_GRAYSCALE);
//print the cordinate of one patch.
cordinate_print(keypointimg, radius);
}
(Here i want without black background)
If I understand what you are asking correctly you could remove the black from an image you can use a mask. The mask can highlight anything that is of a certain colour or in your case the shade of black. Check out the link for this implementation and see if it is what you are looknig for. It is in python but can be easily adapted.
Image Filtering

How to create a semi transparent shape?

I would like to know how to draw semi-transparent shapes in OpenCV, similar to those in the image below (from http://tellthattomycamera.wordpress.com/)
I don't need those fancy circles, but I would like to be able to draw a rectangle, e.g, on a 3 channel color image and specify the transparency of the rectangle, something like
rectangle (img, Point (100,100), Point (300,300), Scalar (0,125,125,0.4), CV_FILLED);
where 0,125,125 is the color of the rectangle and 0.4 specifies the transparency.
However OpenCV doesn't have this functionality built into its drawing functions. How can I draw shapes in OpenCV so that the original image being drawn on is partially visible through the shape?
The image below illustrates transparency using OpenCV. You need to do an alpha blend between the image and the rectangle. Below is the code for one way to do this.
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
int main( int argc, char** argv )
{
cv::Mat image = cv::imread("IMG_2083s.png");
cv::Mat roi = image(cv::Rect(100, 100, 300, 300));
cv::Mat color(roi.size(), CV_8UC3, cv::Scalar(0, 125, 125));
double alpha = 0.3;
cv::addWeighted(color, alpha, roi, 1.0 - alpha , 0.0, roi);
cv::imshow("image",image);
cv::waitKey(0);
}
In OpenCV 3 this code worked for me:
cv::Mat source = cv::imread("IMG_2083s.png");
cv::Mat overlay;
double alpha = 0.3;
// copy the source image to an overlay
source.copyTo(overlay);
// draw a filled, yellow rectangle on the overlay copy
cv::rectangle(overlay, cv::Rect(100, 100, 300, 300), cv::Scalar(0, 125, 125), -1);
// blend the overlay with the source image
cv::addWeighted(overlay, alpha, source, 1 - alpha, 0, source);
Source/Inspired by: http://bistr-o-mathik.org/2012/06/13/simple-transparency-in-opencv/
Adding to Alexander Taubenkorb's answer, you can draw random (semi-transparent) shapes by replacing the cv::rectangle line with the shape you want to draw.
For example, if you want to draw a series of semi-transparent circles, you can do it as follows:
cv::Mat source = cv::imread("IMG_2083s.png"); // loading the source image
cv::Mat overlay; // declaring overlay matrix, we'll copy source image to this matrix
double alpha = 0.3; // defining opacity value, 0 means fully transparent, 1 means fully opaque
source.copyTo(overlay); // copying the source image to overlay matrix, we'll be drawing shapes on overlay matrix and we'll blend it with original image
// change this section to draw the shapes you want to draw
vector<Point>::const_iterator points_it; // declaring points iterator
for( points_it = circles.begin(); points_it != circles.end(); ++points_it ) // circles is a vector of points, containing center of each circle
circle(overlay, *points_it, 1, (0, 255, 255), -1); // drawing circles on overlay image
cv::addWeighted(overlay, alpha, source, 1 - alpha, 0, source); // blending the overlay (with alpha opacity) with the source image (with 1-alpha opacity)
For C++, I personally like the readability of overloaded operators for scalar multiplication and matrix addition:
... same initial lines as other answers above ...
// blend the overlay with the source image
source = source * (1.0 - alpha) + overlay * alpha;

How to draw square with HSV color in OpenFrameworks / OpenCV

I have a OpenFrameworks project that is using OpenCv. I have histogram of the hue and saturation values of my image. To show the histogram I am drawing a grid of gray square's based on the values from the histogram. To make it easier to understand I want to draw a box under each grid column the hue that it represents. But given the HSV values how do I specify the color to draw the box?
Normally you would use ofSetColor or ofSetHexColor but those are RGB based, and I dont know the RGB values.
Perhaps this only came in with a later version but you can now do the following using ofColor
ofColor c = ofColor::fromHsb( 0, 255, 255 ); // bright red
c.setHue( 128 ); // now bright cyan
ofColor c( 0, 0, 255 ); // vibrant blue
c.setSaturation( 128 ); // pale blue
ofColor c = ofColor_::setHsb(float hue, float saturation, float brightness)
You can then use ofSetColor(c);
source

Animate my Detected objects in OpenCV

I was wondering how it is possible to create effects like a glowing ball or a glowing line in my video frames in OpenCV. Any tips on where I can start or what can I use so I can create simple animations in my output?
Thanks in advance!
These effects are simple to accomplish with primitive OpenCV pixel operations. Let's say you have your ball identified as a white region in a separate mask image mask. Blur this mask with GaussianBlur and then combine the result with your source image img. For a glow effect, you probably want something like Photoshop's Screen blending mode, which will only brighten the image:
Result Color = 255 - [((255 - Top Color)*(255 - Bottom Color))/255]
The real key to the "glow" effect is using the pixels in the underlying layer as the screen layer. This translates to OpenCV:
cv::Mat mask, img;
...
mask = mask * img; //fill the mask region with pixels from the original image
cv::GaussianBlur(mask, mask, cv::Size(0,0), 4); //blur the mask, 4 pixels radius
mask = mask * 0.50; //a 50% opacity glow
img = 255 - ((255 - mask).mul(255 - img) / 255); //mul for per-element multiply
I did not test this code, so I might have something wrong here. Color Dodge is also a useful blending mode for glows.
More here: How does photoshop blend two images together?
I wrote a version of the effect that can run both on the CPU and on HW acceleration devices (e.g. GPU). If src is a cv::UMat and you have OpenCL support it will run using OpenCL otherwise if src is a cv::Mat it will run good old CPU code.
template<typename Tmat>
void glow_effect(Tmat& src, int ksize = 100) {
static Tmat resize;
static Tmat blur;
static Tmat src16;
cv::bitwise_not(src, src);
//Resize for some extra performance
cv::resize(src, resize, cv::Size(), 0.5, 0.5);
//Cheap blur
cv::boxFilter(resize, resize, -1, cv::Size(ksize, ksize), cv::Point(-1, -1), true, cv::BORDER_REPLICATE);
//Back to original size
cv::resize(resize, blur, cv::Size(VIDEO_WIDTH, VIDEO_HEIGHT));
//Multiply the src image with a blurred version of itself
cv::multiply(src, blur, src16, 1, CV_16U);
//Normalize and convert back to CV_8U
cv::divide(src16, cv::Scalar::all(255.0), src, 1, CV_8U);
cv::bitwise_not(src, src);
}