I know that it is possible to equalize the histogram of an image like:
equalizeHist(image, image);
and if I want I can define a roi and equalize just this roi in the image:
Mat aux3 = image.clone();
equalizeHist(image(Rect(0,100, 200,200)), aux3(Rect(0,100, 200,200)));
What I would like to do now (and I don't know if it is possible), is to define a Roi(contour) using a vector of points (cv::vector contour) and equalize this roi (this roi not always will be a rectangle)
So, The question here is:
Is it possible to equalize a portion of the image that is not a rectangle using openCV functions?
There is no builtin function in OpenCV to perform histogram equalization with a mask. Still you can write your custom one.
Get a grayscale image:
// Load image
Mat3b img = imread("path_to_image");
// Convert to grayscale
Mat1b gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
Form a mask from your points:
// Your vector of points
vector<Point> pts = { Point(300, 180), Point(450, 150), Point(600, 200), Point(650, 350), Point(300,300) };
// Create the mask
Mat1b mask(img.rows, img.cols, uchar(0));
vector<vector<Point>> ptsarray{pts};
fillPoly(mask, ptsarray, Scalar(255));
Call your custom function equalizeHistWithMask that equalize the image with a mask:
// Equalize with mask
Mat1b equalized;
equalizeHistWithMask(gray, equalized, mask);
Here the full code for reference, with the equalizeHistWithMask function:
#include <iostream>
#include <vector>
#include <algorithm>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
void equalizeHistWithMask(const Mat1b& src, Mat1b& dst, Mat1b mask = Mat1b())
{
int cnz = countNonZero(mask);
if (mask.empty() || ( cnz == src.rows*src.cols))
{
equalizeHist(src, dst);
return;
}
dst = src.clone();
// Histogram
vector<int> hist(256,0);
for (int r = 0; r < src.rows; ++r) {
for (int c = 0; c < src.cols; ++c) {
if (mask(r, c)) {
hist[src(r, c)]++;
}
}
}
// Cumulative histogram
float scale = 255.f / float(cnz);
vector<uchar> lut(256);
int sum = 0;
for (int i = 0; i < hist.size(); ++i) {
sum += hist[i];
lut[i] = saturate_cast<uchar>(sum * scale);
}
// Apply equalization
for (int r = 0; r < src.rows; ++r) {
for (int c = 0; c < src.cols; ++c) {
if (mask(r, c)) {
dst(r, c) = lut[src(r,c)];
}
}
}
}
int main()
{
// Load image
Mat3b img = imread("path_to_image");
// Convert to grayscale
Mat1b gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
// Your vector of points
vector<Point> pts = { Point(300, 180), Point(450, 150), Point(600, 200), Point(650, 350), Point(300,300) };
// Create the mask
Mat1b mask(img.rows, img.cols, uchar(0));
vector<vector<Point>> ptsarray{pts};
fillPoly(mask, ptsarray, Scalar(255));
// Equalize with mask
Mat1b equalized;
equalizeHistWithMask(gray, equalized, mask);
imshow("Gray", gray);
imshow("Mask", mask);
imshow("Equalized", equalized);
waitKey();
return 0;
}
Credits
The code is based on this question on answers.opencv.org
Related
I would use gamma correction to a image.
So, I have to pow every pixel intensity of my source image with a G = 0.6.
I have problem cause the destination image is completely wrong.
Maybe I have a casting problem when I take pixel from source image.
Here my code:
#include <opencv2/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
int main() {
Mat src = imread("spine1.jpeg");
Mat dst = Mat(src.rows, src.cols, CV_8UC1);
cvtColor(src, src, CV_8UC1);
dst = Scalar(0);
for (int x = 0; x < src.rows; x++) {
for (int y = 0; y < src.cols; y++) {
int pixelValue = (int)src.at<uchar>(x, y);
dst.at<uchar>(x, y) = pow(pixelValue, 0.6);
}
}
namedWindow("Input", CV_WINDOW_AUTOSIZE);
namedWindow("Output", CV_WINDOW_AUTOSIZE);
imshow("Input", src);
imshow("Output", dst);
waitKey(0);
return 0;
}
Edit: change cvtColor(src, src, CV_8UC1); in cvtColor(src, src, COLOR_BGR2GRAY);
The call to cvtColor is wrong. You should use:
cvtColor(src, src, COLOR_BGR2GRAY);
Also, you can make your code much simpler, and less error prone:
#include <opencv2/opencv.hpp>
int main()
{
// Load the image as grayscale
cv::Mat1b src = cv::imread("path_to_img", cv::IMREAD_GRAYSCALE);
// Convert to double for "pow"
cv::Mat1d dsrc;
src.convertTo(dsrc, CV_64F);
// Compute the "pow"
cv::Mat1d ddst;
cv::pow(dsrc, 0.6, ddst);
// Convert back to uchar
cv::Mat1b dst;
ddst.convertTo(dst, CV_8U);
// Show results
imshow("SRC", src);
imshow("DST", dst);
waitKey();
return 0;
}
I have an image and I obtained a binary image of it. I would expect a rectangular bounding box, but i didn't get it. This is my code:
vector<vector<Point>> contours;
Vec4i hierarchy;
findContours(binary, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
/*Mat drawing = Mat::zeros(binary.size(), CV_8UC3);
for (int i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(drawing, contours, i, color, 1, 8, hierarchy, 0, Point());
}
imshow("contours", drawing);*/
vector<Point> approx, approxRectangle;
Rect bounding_rect(0, 0, 0, 0);
double max_area = 0;
for (int i = 0; i < contours.size(); i++)// xet tung contour
{
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
if (approx.size() == 4 && isContourConvex(Mat(approx)))
{
Rect box = boundingRect(contours[i]);
if (bounding_rect.area() == 0){
bounding_rect = box;
approxRectangle = approx;
}
else{
if (bounding_rect.area() < box.area()){
bounding_rect = box;
approxRectangle = approx;
}
}
}
}`
This is my image:
You don't get the desired result, because you're looking for almost rectangular contours, but this won't work since the contours you're interested in is not rectanglar. You can see (in blue) the approximation of that contour (obtained on my binarized image):
This shows you that this is not a reliable constraint.
You can easily solve this, in this case, computing the bounding box for each contour, and keep the largest (in green):
Code:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <algorithm>
using namespace std;
using namespace cv;
int main()
{
// Load image
Mat3b img = imread("path_to_image");
// Convert to grayscale
Mat1b binary;
cvtColor(img, binary, COLOR_BGR2GRAY);
// Binarize (remove anti-aliasing artifacts)
binary = binary > 200;
// Find contours
vector<vector<Point>> contours;
findContours(binary.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
// Compute the bounding boxes
vector<Rect> boxes;
for (int i = 0; i < contours.size(); ++i)
{
boxes.push_back(boundingRect(contours[i]));
}
// Find index of largest contours
int idx_largest_box = distance(boxes.begin(), max_element(boxes.begin(), boxes.end(), [](const Rect& lhs, const Rect& rhs) {
return lhs.area() < rhs.area();
}));
// Draw largest box
rectangle(img, boxes[idx_largest_box], Scalar(0,255,0));
imshow("Result", img);
waitKey();
return 0;
}
User,
I want to crop that Triangle on the image and show it in another window with opencv c++. I know all three Coordinates.
Can anyone help me? I did not find any answer on the Internet about "triangle cropping". Thanks!
EDIT: The Problem here is that i cannot use ROI for cropping the Triangle. I have to copy just the triangle without any background or something around. Is it possible to create my own ROI by knowing the Coordinates of the triangle [p1(302,179), p2(329,178), p3(315,205)]?
cv::Mat inputImage = cv::imread("input.png");
if (inputImage.channels() > 1)
{
cv::cvtColor(inputImage, inputImage, CV_RGB2GRAY);
}
// replace these values with your actual coordinates
// I found these by first saving your provided image, then
// using Microsoft Paint
int x0 = 242;
int y0 = 164;
int x1 = 314;
int y1 = 38;
int x2 = 387;
int y2 = 164;
// then create a line masking using these three points
cv::Mat lineMask = cv::Mat::zeros(inputImage.size(), inputImage.type());
cv::line(lineMask, cv::Point(x0, y0), cv::Point(x1, y1), cv::Scalar(255, 255, 0), 1, 8, 0);
cv::line(lineMask, cv::Point(x0, y0), cv::Point(x2, y2), cv::Scalar(255, 255, 0), 1, 8, 0);
cv::line(lineMask, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(255, 255, 0), 1, 8, 0);
// perform contour detection on your line mask
cv::vector<cv::vector<cv::Point>> contours;
cv::vector<cv::Vec4i> hierarchy;
cv::findContours(lineMask, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
// calculate the distance to the contour
cv::Mat raw_dist(lineMask.size(), CV_32FC1);
for (int i = 0; i < lineMask.rows; i++)
{
for (int j = 0; j < lineMask.cols; j++)
{
raw_dist.at<float>(i, j) = cv::pointPolygonTest(contours[0], cv::Point2f(j, i), true);
}
}
double minVal; double maxVal;
cv::minMaxLoc(raw_dist, &minVal, &maxVal, 0, 0, cv::Mat());
minVal = std::abs(minVal);
maxVal = std::abs(maxVal);
// depicting the distances graphically
cv::Mat mask = cv::Mat::zeros(inputImage.size(), CV_8UC1);
for (int i = 0; i < mask.rows; i++)
{
for (int j = 0; j < mask.cols; j++)
{
if (raw_dist.at<float>(i, j) < 0)
{
mask.at<uchar>(i, j) = static_cast<uchar>(0);
continue;
}
mask.at<uchar>(i, j) = static_cast<uchar>(255);
}
}
// inverse the input image
cv::Mat invInput;
cv::bitwise_not(inputImage, invInput);
// then get only the region of your triangle
cv::Mat outputImage;
invInput.copyTo(outputImage, mask);
cv::bitwise_not(outputImage, outputImage);
// display for debugging purpose
cv::imshow("inputImage", inputImage);
cv::imshow("lineMask", lineMask);
cv::imshow("mask", mask);
cv::imshow("outputImage", outputImage);
cv::waitKey();
This is your inputImage:
This is your lineMask:
This is your created binary mask:
And this is your final outputImage:
References:
OpenCV draw line
OpenCV findContours
Point Polygon Test
you can do it by using mask as shown with the code below
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main( int, char** argv )
{
Mat src = imread( argv[1] );
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY );
gray = gray < 127;
vector<vector<Point> > contours;
findContours(gray, contours,
RETR_EXTERNAL,
CHAIN_APPROX_SIMPLE);
for( size_t i = 0; i< contours.size(); i++ )
{
Rect rect = boundingRect(contours[i]);
Mat mask = gray(rect);
Mat srcROI = src(rect);
srcROI.setTo(Scalar(0,0,255),mask);
imshow("srcROI",srcROI);
waitKey();
}
imshow( "result", src );
waitKey(0);
return(0);
}
EDIT: according the change on the question i suggest the test code below
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
int main( int, char** argv )
{
Mat src = imread("lena.jpg");
vector<Point> points;
points.push_back( Point(200,200));
points.push_back( Point(370,370));
points.push_back( Point(220,410));
Mat mask = Mat::zeros( src.size(), CV_8UC1 );
fillConvexPoly( mask, points, Scalar( 255 ));
Rect rect = boundingRect( points );
Mat roi = src( rect ).clone();
mask = mask( rect ).clone();
rect.x = rect.x - 180;
rect.y = rect.y - 180;
Mat srcROI = src( rect );
roi.copyTo( srcROI, mask );
imshow( "result", src );
waitKey(0);
return(0);
}
As you told that you know co-ordinates of the triangle, using below code you can find triangle.
Mat image = imread("imagePath");
bitwise_not(image, image);
Mat grayImage;
cv::cvtColor(image, grayImage, CV_RGB2GRAY);
cv::vector<cv::vector<cv::Point> > contours;
cv::vector<cv::Vec4i> hierarchy;
cv::findContours(grayImage, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
Mat contourMat(grayImage.size(), grayImage.type(), Scalar(255));
for(int i = 0; i < contours.size(); i++)
{
if(contours[i].data()->x == 314 && contours[i].data()->y == 37)
drawContours(contourMat, contours, i, Scalar(0), CV_FILLED, 8, hierarchy);
}
imshow("WindowName", contourMat);
Hope this will help.
I have used canny edge detection and I have found the contours on an image I am trying to process.
I want to find the five largest contours and then see whether or not there are contours within the five biggest contours in the image.
Is this possible? I am new to OpenCV.
You can find the N largest contours checking their length. You should take care to pass to findContours the parameter CHAIN_APPROX_NONE for this to work properly.
You can then check inside each mask if there are other contours.
Image:
N = 5 largest contours, with inner contours for each one.
Code:
#include <opencv2\opencv.hpp>
#include <vector>
#include <numeric>
using namespace cv;
using namespace std;
int main()
{
Mat3b img = imread("path_to_image");
Mat1b gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
Mat1b edges;
Canny(gray, edges, 200, 50);
vector<vector<Point>> contours;
findContours(edges.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
vector<int> indices(contours.size());
iota(indices.begin(), indices.end(), 0);
sort(indices.begin(), indices.end(), [&contours](int lhs, int rhs) {
return contours[lhs].size() > contours[rhs].size();
});
int N = 5; // set number of largest contours
N = min(N, int(contours.size()));
Mat3b res = img.clone();
// Draw N largest contours
for (int i = 0; i < N; ++i)
{
Scalar color(rand() & 255, rand() & 255, rand() & 255);
Vec3b otherColor(color[2], color[0], color[1]);
drawContours(res, contours, indices[i], color, CV_FILLED);
// Create a mask for the contour
Mat1b res_mask(img.rows, img.cols, uchar(0));
drawContours(res_mask, contours, indices[i], Scalar(255), CV_FILLED);
// AND with edges
res_mask &= edges;
// remove larger contours
drawContours(res_mask, contours, indices[i], Scalar(0), 2);
for (int r = 0; r < img.rows; ++r)
{
for (int c = 0; c < img.cols; ++c)
{
if (res_mask(r, c))
{
res(r,c) = otherColor;
}
}
}
}
imshow("Image", img);
imshow("N largest contours", res);
waitKey();
return 0;
}
I have Implemented a Canny Edge detector using “Church Canny Edge” image as below. now I want to extend/modify my code in order to colour every pixel with a gradient between 22.5 and 67.5 degrees in the same image.
can you please give me lines of codes need be added to my current implementation?
<< EDITION 1 >>
this is the version after editing it by help of Miki. when I build it , I get a black window which means somehow I am messing the image show part of code. any tips ?
#include <opencv2/opencv.hpp>
#include <vector>
#include <algorithm>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
using namespace std;
using namespace cv;
Mat image, im_gray;
int main( int argc, char** argv )
{
/// Load an image
if(argc > 1)
image = imread(argv[1]);
else
image = imread("Church Canny Edge.png");
/// Create a window
namedWindow( "window");
if(image.empty())
cout << "Output sentence";
// Mat3b img = imread("Church Canny Edge.png");
Mat1b im_gray;
cvtColor(image, im_gray, cv::COLOR_BGR2GRAY);
Mat1b detected_edges;
GaussianBlur(im_gray, detected_edges, Size(3, 3), 0, 0);
int lowThreshold = 200;
int ratio = 3;
int kernel_size = 3;
Canny(detected_edges, detected_edges, lowThreshold,
lowThreshold*ratio, kernel_size);
// Compute Sobel derivatives
Mat1f sx, sy;
Sobel(detected_edges, sx, CV_32F, 1, 0, kernel_size);
Sobel(detected_edges, sy, CV_32F, 0, 1, kernel_size);
// Compute edge angle
Mat1f angles(detected_edges.rows, detected_edges.cols, 0.f);
for (int r = 0; r < detected_edges.rows; ++r)
{
for (int c = 0; c < detected_edges.cols; ++c)
{
angles(r, c) = atan2(sy(r, c), sx(r,c));
}
}
// Convert radians to degrees
angles = (angles / CV_PI * 180.0);
// Get values in range
Mat1b maskPositive;
inRange(angles, Scalar(22.5), Scalar(67.5), maskPositive);
Mat1b maskNegative;
inRange(angles, Scalar(-67.5), Scalar(-22.5), maskNegative);
Mat1b mask = maskNegative | maskPositive;
// Show results
Mat3b result;
cvtColor(detected_edges, result, COLOR_GRAY2BGR);
for (int r = 0; r < detected_edges.rows; ++r)
{
for (int c = 0; c < detected_edges.cols; ++c)
{
if (mask(r, c) && detected_edges(r, c))
{
result(r, c) = Vec3b(0,255,0);
}
}
}
waitKey(0);
return 0;
}
Image:
You can compute the gradient angle using Sobel derivatives:
// Compute Sobel derivatives
Mat1f sx, sy;
Sobel(detected_edges, sx, CV_32F, 1, 0, kernel_size);
Sobel(detected_edges, sy, CV_32F, 0, 1, kernel_size);
// Compute edge angle
Mat1f angles(detected_edges.rows, detected_edges.cols, 0.f);
for (int r = 0; r < detected_edges.rows; ++r)
{
for (int c = 0; c < detected_edges.cols; ++c)
{
angles(r, c) = atan2(sy(r, c), sx(r,c));
}
}
// Convert radians to degrees
angles = (angles / CV_PI * 180.0);
Angles:
Then you can keep only values in a specified range:
// Get values in range
Mat1b maskPositive;
inRange(angles, Scalar(22.5), Scalar(67.5), maskPositive);
Mat1b maskNegative;
inRange(angles, Scalar(-67.5), Scalar(-22.5), maskNegative);
Mat1b mask = maskNegative | maskPositive;
You can then color pixels that are both in mask and in canny edges:
// Show results
Mat3b result;
cvtColor(detected_edges, result, COLOR_GRAY2BGR);
for (int r = 0; r < detected_edges.rows; ++r)
{
for (int c = 0; c < detected_edges.cols; ++c)
{
if (mask(r, c) && detected_edges(r, c))
{
result(r, c) = Vec3b(0,255,0); // green
}
}
}
Result:
NOTES
You probably don't want to draw pixels with a given gradient, but extract lines and draw lines with a given slope. You can do this using Hough Transform (available in OpenCV). I won't show this since it's not what was asked here.
You can basically copy this code into your CannyThreshold function
Full code for reference:
#include <opencv2/opencv.hpp>
#include <vector>
#include <algorithm>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
Mat1b im_gray;
cvtColor(img, im_gray, COLOR_BGR2GRAY);
Mat1b detected_edges;
GaussianBlur(im_gray, detected_edges, Size(3, 3), 0, 0);
int lowThreshold = 200;
int ratio = 3;
int kernel_size = 3;
Canny(detected_edges, detected_edges, lowThreshold,
lowThreshold*ratio, kernel_size);
// Compute Sobel derivatives
Mat1f sx, sy;
Sobel(detected_edges, sx, CV_32F, 1, 0, kernel_size);
Sobel(detected_edges, sy, CV_32F, 0, 1, kernel_size);
// Compute edge angle
Mat1f angles(detected_edges.rows, detected_edges.cols, 0.f);
for (int r = 0; r < detected_edges.rows; ++r)
{
for (int c = 0; c < detected_edges.cols; ++c)
{
angles(r, c) = atan2(sy(r, c), sx(r,c));
}
}
// Convert radians to degrees
angles = (angles / CV_PI * 180.0);
// Get values in range
Mat1b maskPositive;
inRange(angles, Scalar(22.5), Scalar(67.5), maskPositive);
Mat1b maskNegative;
inRange(angles, Scalar(-67.5), Scalar(-22.5), maskNegative);
Mat1b mask = maskNegative | maskPositive;
// Show results
Mat3b result;
cvtColor(detected_edges, result, COLOR_GRAY2BGR);
for (int r = 0; r < detected_edges.rows; ++r)
{
for (int c = 0; c < detected_edges.cols; ++c)
{
if (mask(r, c) && detected_edges(r, c))
{
result(r, c) = Vec3b(0,255,0);
}
}
}
return 0;
}