I am writing a function that gets a given percentile of an image (gray).
For that I wanted to use calcHist() with UMat in order to accelerate my code.
But in all the ways I've tried to do that - it took much more time when I used UMat (instead of Mat).
I am new here - any help would be highly appreciated.
Here is my code:
int CalcPercentile(UMat gray, float fPercent)
{
int hSize = 256;
UMat hist;
Mat histMat;
calcHist(vector<UMat>{gray}, vector<int>{0}, UMat(), hist, vector<int>{hSize}, vector<float>{0,255});
int iNumPixels = gray.rows * gray.cols;
float fSumFreqNeeded = (float)iNumPixels * fPercent;
histMat = hist.getMat(ACCESS_READ); // or: hist.copyTo(histMat);
int iSumFreq = 0, iVal;
for (iVal = 0; iVal < iHistSize; iVal++)
{
int iCurrFreq = (int)(histMat.at<float>(iVal));
iSumFreq += iCurrFreq;
if (iSumFreq >= fSumFreqNeeded)
break;
}
return iVal;
}
a corresponding function with Mat instead of UMat was much faster.
(But my code uses UMat gray image as input - and converting to Mat again takes too much time).
Related
As far as I know the built-in split will split one 3-channel Mat into three 1-channel Mat. As a result, those three Mat are just gray scale with some different intensities.
My intent is to get three 3-channel Mat as follows.
void splitTo8UC3(const Mat& input, vector<Mat>& output)
{
Mat blue = input.clone();
Mat green = input.clone();
Mat red = input.clone();
const uint N = input.rows * input.step;
for (uint i = 0; i < N; i += 3)
{
// blue.data[i]
green.data[i] = 0;
red.data[i] = 0;
blue.data[i + 1] = 0;
//green.data[i+1]
red.data[i + 1] = 0;
blue.data[i + 2] = 0;
green.data[i + 2] = 0;
//red.data[i+2]
}
output.push_back(blue);
output.push_back(green);
output.push_back(red);
}
It works but instead of reinventing the wheel, I am looking for the built-in if any.
Edit
The proposed solution must be faster than mine.
EDIT: I incorporated Dan's suggested improvements from his comment.
I can't think of a built-in function exactly doing this, and I also couldn't find one. But while doing some research, I came across the mixChannels function, which might improve your solution. At least, it avoids implementing a loop.
Here are my modifications to your code:
void splitTo8UC3(const cv::Mat& input, std::vector<cv::Mat>& output)
{
// Allocate outputs
cv::Mat b(cv::Mat::zeros(input.size(), input.type()));
cv::Mat g(cv::Mat::zeros(input.size(), input.type()));
cv::Mat r(cv::Mat::zeros(input.size(), input.type()));
// Collect outputs
cv::Mat out[] = { b, g, r };
// Set up index pairs
int from_to[] = { 0,0, 1,4, 2,8 };
cv::mixChannels(&input, 1, out, 3, from_to, 3);
output.assign(std::begin(out), std::end(out));
}
Let's have this test image colors.png:
And, let's have this test code:
cv::Mat img = cv::imread("images/colors.png");
std::vector<cv::Mat> bgr;
splitTo8UC3(img, bgr);
cv::imwrite("images/b.png", bgr[0]);
cv::imwrite("images/g.png", bgr[1]);
cv::imwrite("images/r.png", bgr[2]);
Then, we get the following outputs b.png, g.png, and r.png, which hopefully are the them as for your initial solution:
Hope that helps!
int sizeOfChannel = (_width / 2) * (_height / 2);
double* channel_gr = new double[sizeOfChannel];
// filling the data into channel_gr....
cv::Mat my( _width/2, _height/2, CV_32F,channel_gr);
cv::Mat src(_width/2, _height/2, CV_32F);
for (int i = 0; i < (_width/2) * (_height/2); ++i)
{
src.at<float>(i) = channel_gr[i];
}
cv::imshow("src",src);
cv::imshow("my",my);
cv::waitKey(0);
I'm wondering why i'm not getting the same image in my and src imshow
update:
I have changed my array into double* still same result;
I think it is something to do with steps?
my image output
src image output
this one works for me:
int halfWidth = _width/2;
int halfHeight = _height/2;
int sizeOfChannel = halfHeight*halfWidth;
// ******************************* //
// you use CV_321FC1 later so it is single precision float
float* channel_gr = new float[sizeOfChannel];
// filling the data into channel_gr....
for(int i=0; i<sizeOfChannel; ++i) channel_gr[i] = i/(float)sizeOfChannel;
// ******************************* //
// changed row/col ordering, but this shouldnt be important
cv::Mat my( halfHeight , halfWidth , CV_32FC1,channel_gr);
cv::Mat src(halfHeight , halfWidth, CV_32FC1);
// ******************************* //
// changed from 1D indexing to 2D indexing
for(int y=0; y<src.rows; ++y)
for(int x=0; x<src.cols; ++x)
{
int arrayPos = y*halfWidth + x;
// you have a 2D mat so access it in 2D
src.at<float>(y,x) = channel_gr[arrayPos ];
}
cv::imshow("src",src);
cv::imshow("my",my);
// check for differences
cv::imshow("diff1 > 0",src-my > 0);
cv::imshow("diff2 > 0",my-src > 0);
cv::waitKey(0);
'my' is array of floats but you give it pointer to arrays of double. There no way it can get data from this array properly.
It seems that the constructor version that you are using is
Mat::Mat(int rows, int cols, int type, const Scalar& s)
This is from OpenCV docs. Seems like you are using float for src and assigning from channel_gr (declared as double). Isn't that some form of precision loss?
Code :
cv::Point2f src_vertices[4];
src_vertices[0] = c1[0];
src_vertices[1] = c1[1];
src_vertices[2] = c1[2];
src_vertices[3] = c1[3];
cv::Point2f dst_vertices[4];
dst_vertices[0] = c2[0];
dst_vertices[1] = c2[1];
dst_vertices[2] = c2[2];
dst_vertices[3] = c2[3];
cv::Mat warpMatrix = getPerspectiveTransform(src_vertices,dst_vertices);
cv::Mat output = cv::Mat::zeros(original.cols,original.rows , CV_32FC3);
cv::warpPerspective(original, output, warpMatrix,cv::Size(606,606));
UIImage *_adjustedImage = [MAOpenCV UIImageFromCVMat:output];
Below is the original image
After apply straightening, output is below image
Issue
The output of the image that we are getting after straightening is getting cropped a bit from the corner and the output comes from the Open CV framework itself.
How to resolved this issue. Please let me know if anybody has found the solution. Thank you.
Since this question is asked quite often, I've written a few lines of code which save some time for many others.
try this:
cv::Rect computeWarpedContourRegion(const std::vector<cv::Point> & points, const cv::Mat & homography)
{
std::vector<cv::Point2f> transformed_points(points.size());
for(unsigned int i=0; i<points.size(); ++i)
{
// warp the points
transformed_points[i].x = points[i].x * homography.at<double>(0,0) + points[i].y * homography.at<double>(0,1) + homography.at<double>(0,2) ;
transformed_points[i].y = points[i].x * homography.at<double>(1,0) + points[i].y * homography.at<double>(1,1) + homography.at<double>(1,2) ;
}
// dehomogenization necessary?
if(homography.rows == 3)
{
float homog_comp;
for(unsigned int i=0; i<transformed_points.size(); ++i)
{
homog_comp = points[i].x * homography.at<double>(2,0) + points[i].y * homography.at<double>(2,1) + homography.at<double>(2,2) ;
transformed_points[i].x /= homog_comp;
transformed_points[i].y /= homog_comp;
}
}
// now find the bounding box for these points:
cv::Rect boundingBox = cv::boundingRect(transformed_points);
return boundingBox;
}
cv::Rect computeWarpedImageRegion(const cv::Mat & image, const cv::Mat & homography)
{
std::vector<cv::Point> imageBorder;
imageBorder.push_back(cv::Point(0,0));
imageBorder.push_back(cv::Point(image.cols,0));
imageBorder.push_back(cv::Point(image.cols,image.rows));
imageBorder.push_back(cv::Point(0,image.rows));
return computeWarpedContourRegion(imageBorder, homography);
}
cv::Mat adjustHomography(const cv::Rect & transformedRegion, const cv::Mat & homography)
{
if(homography.rows == 2) throw("homography adjustement for affine matrix not implemented yet");
// unit matrix
cv::Mat correctionHomography = cv::Mat::eye(3,3,CV_64F);
// correction translation
correctionHomography.at<double>(0,2) = -transformedRegion.x;
correctionHomography.at<double>(1,2) = -transformedRegion.y;
return correctionHomography * homography;
}
int main()
{
// straightening algorithm without cropping:
cv::Mat original = cv::imread("straightening_src.png");
cv::Mat output;
cv::Point2f src_vertices[4];
cv::Point2f dst_vertices[4];
// I have to add them manually, you can just use your old code here.
// my result will look different, since I don't use your original point correspondences, but system is the same...
src_vertices[0] = cv::Point2f(108,190);
src_vertices[1] = cv::Point2f(273,178);
src_vertices[2] = cv::Point2f(389,322);
src_vertices[3] = cv::Point2f(183,355);
dst_vertices[0] = cv::Point2f(172,190);
dst_vertices[1] = cv::Point2f(374,193);
dst_vertices[2] = cv::Point2f(380,362);
dst_vertices[3] = cv::Point2f(171,366);
// compute homography
cv::Mat warpMatrix = getPerspectiveTransform(src_vertices,dst_vertices);
// now you have to find out, whether the warped image will fit to the output image or whether it will be cropped.
// if it will be cropped you will most probably have to
// 1. find out how big your output image must be and the coordinates it will be warped to.
// 2. modify your transformation (by a translation) so that the output image will be placed properly inside the output image
// part 1: find the region that will hold the new image.
cv::Rect warpedImageRegion = computeWarpedImageRegion(original, warpMatrix);
// part 2: modify the transformation.
cv::Mat adjustedHomography = adjustHomography(warpedImageRegion, warpMatrix);
cv::Size transformedImageSize = cv::Size(warpedImageRegion.width,warpedImageRegion.height);
cv::warpPerspective(original, output, adjustedHomography, transformedImageSize);
cv::imshow("output", output);
cv::imwrite("straightening_result.png", output);
cv::waitKey(-1);
}
for this input (1) and the given transformation correspondences you will get that result (2)
(1)
(2)
After the image is skewed, it should be possible to remove the black extra part of the image.
I am using older version of C because the book I am using is outdated :( Currently, I am working on a project to detect an object in an image. First I do Gaussian smoothing on the gray scale image, then erode it. After that, I apply threshold. Now I am trying to obtain how many black pixels there are for every width so that I can compare it with other row to determine the center. I am trying this in 'for' loop, however, I am keep getting the error:
term does not evaluate to a function taking 1 arguments
#include <highgui.h>
#include <cv.h>
#include <cxcore.h>
int main()
{
int total,
zero,
width,
blackpixel;
IplImage* in = cvLoadImage("Wallet.jpg", CV_LOAD_IMAGE_GRAYSCALE);
IplImage* gsmooth = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
IplImage* erode = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
IplImage* Iat = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
IplImage* bpixel = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
cvSmooth(in, gsmooth, CV_GAUSSIAN, 3, 0, 0, 0);
cvErode(gsmooth, erode, NULL, 2);
cvThreshold(erode, Iat, 100, 255, CV_THRESH_BINARY);
total = (Iat->height)*(Iat->width);
zero = total - cvCountNonZero(Iat);
printf("Total pixels: %d\nWhite pixels: %d\nBlack pixels: %d\n", total, cvCountNonZero(Iat), zero);
for(int i = 0; i < Iat->width; i++)
{
blackpixel = Iat->width(i);
}
cvNamedWindow("Original", 1);
cvNamedWindow("Gaussian Smoothing", 1);
cvNamedWindow("Erode", 1);
cvNamedWindow("Adaptive Threshold", 1);
cvShowImage("Original", in);
cvShowImage("Gaussian Smoothing", gsmooth);
cvShowImage("Erode", erode);
cvShowImage("Adaptive Threshold", Iat);
cvWaitKey(0);
cvReleaseImage(&in);
cvReleaseImage(&gsmooth);
cvReleaseImage(&erode);
cvReleaseImage(&Iat);
cvDestroyWindow("Original");
cvDestroyWindow("Gaussian Smoothing");
cvDestroyWindow("Erode");
cvDestroyWindow("Adaptive Threshold");
}
First of all, don't be afraid to use C++ API when using an outdated book like "Learining OpenCV", because the concepts are still relevant. Translating to C++ API is not hard if You understand the idea, and is a great exercise because You can't just copy-paste the code. I learned OpenCV this way, and I think it worked :).
With C++ API it would be as simple as
cv::Mat zeros = cv::Mat::zeros(Iat.size());
cv::Mat blackPixels = (Iat == zeros);
int blackPixelsCount = blackPixels.total();
The problem in the line
blackpixel = Iat->width(i);
is the wrong syntax.
Iat->width will give you the width of the image, an integer property.
I don't thing that the loop
for(int i = 0; i < Iat->height; i++)
{
blackpixel = Iat->width(i);
}
can calculate the number of black pixels in a given row. You might need something like
for(int i = 0; i < Iat->height; i++) // // every row
{
for(int j = 0; j < Iat->width; j++) // pixels in each row
{
// get count pixels here
}
// do things with the count for the current row
}
If you are using a cvMat data structure instead of IplImage, this should be faster.
I am new in Match faces , I am trying to learn how to use SVM with HOG descriptors.
I wrote a simple face recognizer with SVM, but when i activate it , code always returns 1
float *getHOG(const cv::Mat &image, int* count)//Compute HOG
{
cv::HOGDescriptor hog;
std::vector<float> res;
cv::Mat img2;
cv::resize(image, img2, cv::Size(64, 128));
hog.compute(img2, res, cv::Size(8, 8), cv::Size(0, 0));
*count = res.size();
float* result = new float[*count];
for(int i = 0; i < res.size(); i++)
{
result[i] = res[i];
}
return result;
}
const int dataSetLength = 10;
float **getTraininigData(int* setlen, int* veclen)//Load some samples of data
{
char *names[dataSetLength] = {
"../faces/s1/1.pgm",
"../faces/s1/2.pgm",
"../faces/s1/3.pgm",
"../faces/s1/4.pgm",
"../faces/s1/5.pgm",
"../faces/cars/1.jpg",
"../faces/cars/2.jpg",
"../faces/cars/3.jpg",
"../faces/cars/4.jpg",
"../faces/cars/5.jpg",
};
float **res = new float* [dataSetLength];
for(int i = 0; i < dataSetLength; i++)
{
std::cout<<names[i]<<"\n";
cv::Mat img = cv::imread(names[i], 0);
res[i] = getHOG(img, veclen);
}
*setlen = dataSetLength;
return res;
}
void test()//Training and activate SVM
{
int setlen, veclen;
float **trainingData = getTraininigData(&setlen, &veclen);
float *labels = new float[dataSetLength];
for(int i = 0; i < dataSetLength; i++)
{
labels[i] = (i < dataSetLength/2)? 0.0 : 1.0;
}
cv::Mat labelsMat(setlen, 1, CV_32FC1, labels);
cv::Mat trainingDataMat(setlen, veclen, CV_32FC1, trainingData);
cv::SVMParams params;
params.svm_type = cv::SVM::C_SVC;
params.kernel_type = cv::SVM::LINEAR;
params.term_crit = cv::TermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
std::cout<<labelsMat<<"\n";
cv::SVM SVM;
SVM.train(trainingDataMat, labelsMat, cv::Mat(), cv::Mat(), params);
cv::Mat img = cv::imread("../faces/s1/2.pgm", 0);//sample from train data, but ansewer is 1 for every sample
auto desc = getHOG(img, &veclen);
cv::Mat sampleMat(1, veclen, CV_32FC1, desc);
float response = SVM.predict(sampleMat);
std::cout<<"resp "<< response<<"\n";
}
What wrong with my code ?
PS sorry for my writing mistakes. English in not my native language
You don't have much training data. Note how Dalal and Triggs in their original paper on HOG (http://lear.inrialpes.fr/people/triggs/pubs/Dalal-cvpr05.pdf) used thousands of examples to train the SVM, you have just 5 negative and 5 positive.
You haven't set the C parameter (you need to find a good value via cross validation) - you will need more data.
Possibly HOG descriptors for faces and cars are not separable with a linear kernel, try RBF.
But this is unlikely to be an issue since D&L use a linear SVM in their paper.
Read this: http://www.csie.ntu.edu.tw/~cjlin/papers/guide/guide.pdf
If you haven't done this yet, get the SVM working for a simpler case (e.g. just use image patches instead of HOG).