Shaping image pixel by pixel in OpenCV - c++

So the idea is that I have and image recorded from cylindrical camera through a rectangular window in it. Image we get is a rectangular picture though it must be circular. I'm using OpenCV to move image pixel by pixel, line by line into a circle from a given rectangular picture. Problem is that pixel distribution is uneven depending on a radius. What algorithm you'd suggest to make distribution more even? Here's the code:
int main( int argc, char** argv ) {
Mat src = imread( "srcImg.jpg", 1 );
Mat dst = imread( "dstImg.jpg", 1 );
int srcH = src.rows; int srcW = src.cols;
int dstH = dst.rows; int dstW = src.cols;
//convert chamber radius to pixels
double alpha, alpha_double, alpha_triple;
int r = 1500;
double k = 210 / (500 * PI);
int l = 1;
//take pixels from source and arrange them into circles
for (int i = srcH - 1; i > 0; i--) {
for (int j = 1; j <= srcW ; j++) {
alpha = (double) (2 * PI * (r * k + i)) / j;
alpha_double = (double) (2 * PI * ((r + 15) * k + i)) / j;
alpha_triple = alpha_double = (double) (2 * PI * ((r + 30) * k + i)) / j;
int x_new = abs((int) (dstW / 2 - (r * k + i) * cos(alpha)) - 200);
int y_new = abs((int) (dstH / 2 - (3.5*(r * k + i) * sin(alpha))) + 1000);
int x_new_double = abs((int) (dstW / 2 - (r * k + i) * cos(alpha_double)) - 200);
int y_new_double = abs((int) (dstH / 2 - (3.5*(r * k + i) * sin(alpha_double))) + 1000);
int x_new_triple = abs((int) (dstW / 2 - (r * k + i) * cos(alpha_triple)) - 200);
int y_new_triple = abs((int) (dstH / 2 - (3.5*(r * k + i) * sin(alpha_triple))) + 1000);
dst.at<uchar>(x_new, y_new) = src.at<uchar>(srcH - i, srcW - j);
dst.at<uchar>(x_new_double, y_new_double) = src.at<uchar>(srcH - i, srcW - j);
dst.at<uchar>(x_new_triple, y_new_triple) = src.at<uchar>(srcH - i, srcW - j);
}
}
//make dst image grey and show all images
Mat dstGray;
cvtColor(dst, dstGray, CV_RGB2GRAY);
imshow("Source", src);
imshow("Result", dstGray);
waitKey();
return 0;
And the

This is hardly a full answer, but I would try some kind of projection mapping instead of manually accessing every pixel. There must be an openCV way to create a destination shape then say : "Take my original image and make it fit the destination shape"
This is rather trivial for rectangles as you can see here, but your hole in the middle makes it harder.

Related

Subtracting two CV_64FC3 matrices and keeping the negative values

As far as i know, subtracting two non-float matrices yields expected results but subtracting two CV_64FC3 (and CV_32F for that matter) matrices clips the negative values to 0.0 and normalizes the result between 0.0 and 1.0. I know these limitations and read related documentation but still couldn't subtract two matrices.
Mat lowpass1, lowpass2, mask;
mask.convertTo(mask, CV_32F);
lowpass1.convertTo(lowpass1, CV_32F);
lowpass2.convertTo(lowpass2, CV_32F);
// all other variables (high_b1, high_a0 etc.) are double
// and in the range of -1.0 to 1.0 but never 0.0
// frame and prev_frame are CV_32F matrices
lowpass1 = (-high_b1 * lowpass1 + high_a0 * frame + high_a1 * prev_frame) / high_b0;
lowpass2 = (-low_b1 * lowpass2 + low_a0 * frame + low_a1 * prev_frame) / low_b0;
mask = lowpass1 - lowpass2
Altough the lowpass1 and lowpass2 contains non zero positive values, the subtraction always gives 0. I'm assuming it's clipping and/or rounding up to the nearest integer.
mask = lowpass2 - lowpass1
This also gives the exact same result as mentioned above.
Working minimal example;
#include <iostream>
#include <vector>
#include <list>
#include <opencv2/opencv.hpp>
using std::cout;
using std::string;
using std::list;
using std::vector;
using cv::Mat;
int main() {
cv::VideoCapture cap("small.mp4");
const int level = 2;
vector<Mat> *data = new vector<Mat>;
vector<vector<Mat>> pyramid;
pyramid.resize(level);
Mat frame;
while (true) {
cap >> frame;
if (frame.empty())
break;
frame.convertTo(frame, CV_32F, 1.0 / 255.0f);
data->push_back(frame.clone());
Mat current = frame.clone();
for (int i = 0; i < level; i++) {
Mat down, up;
if (i == (level - 1)) {
pyramid[i].push_back(current);
break;
}
cv::pyrDown(current, down);
cv::pyrUp(down, up, current.size());
pyramid[i].push_back(current - up);
current = down;
}
}
double low_a0 = 0.04979798;
double low_a1 = 0.04979798;
double low_b0 = 1;
double low_b1 = -0.90040404;
double high_a0 = 0.13672874;
double high_a1 = 0.13672874;
double high_b0 = 1;
double high_b1 = -0.72654253;
vector<vector<Mat>> filtered;
filtered.resize(level);
for (int i = 1; i < pyramid.size(); i++) {
Mat lowpass1 = pyramid[i][0], lowpass2 = pyramid[i][0];
for (int j = 1; j < pyramid[i].size(); j++) {
lowpass1 = (-high_b1 * lowpass1 + high_a0 * pyramid[i][j] + high_a1 * pyramid[i][j-1]) / high_b0;
lowpass2 = (-low_b1 * lowpass2 + low_a0 * pyramid[i][j] + low_a1 * pyramid[i][j - 1]) / low_b0;
filtered[i].push_back(lowpass1 - lowpass2);
}
}
}
The source video; http://techslides.com/demos/sample-videos/small.mp4
The problem was in this part;
lowpass1 = (-high_b1 * lowpass1 + high_a0 * pyramid[i][j] + high_a1 * pyramid[i][j-1]) / high_b0;
lowpass2 = (-low_b1 * lowpass2 + low_a0 * pyramid[i][j] + low_a1 * pyramid[i][j - 1]) / low_b0;
Since lowpass1 and lowpass2 are used in the formula and the left hand side of the code, it messed up something but I'm not sure what it did exactly. After changing it to this;
Mat lowpass1_, lowpass2_;
lowpass1_ = ( high_b1 * lowpass1 + high_a0 * pyramid[i][j] + high_a1 * pyramid[i][j - 1]);
lowpass2_ = ( low_b1 * lowpass2 + low_a0 * pyramid[i][j] + low_a1 * pyramid[i][j - 1]) ;
the calculations were correct and didn't clip to zero etc. Thanks for all the replies.

Edge detection for color images CannyAlgorithm

This is how I managed to use a Sobel Kernel on a GRAYSCALE image.However,I dont actually get how to modify it for a color image.
void Soble()
{
Mat img;
int w = 3;
int k = w / 2;
char fname[MAX_PATH];
openFileDlg(fname);
img = imread(fname, CV_LOAD_IMAGE_GRAYSCALE);
gaussianFiltering(img);
Mat destinationImg = img.clone();
float sobelY[3][3] = { 1, 2, 1, 0, 0, 0, -1, -2, -1 };
float sobelX[3][3] = { -1, 0, 1, -2, 0, 2, -1, 0, 1 };
for (int i = k; i < img.rows - k; i++)
{
for (int j = k; j < img.cols - k; j++)
{
float Gx = 0, Gy = 0;
for (int l = 0; l < w; l++)
{
for (int p = 0; p < w; p++)
{
Gx += img.at<uchar>(i + l - k, j + p - k)*sobelX[l][p];
Gy += img.at<uchar>(i + l - k, j + p - k)*sobelY[l][p];
}
}
destinationImg.at<uchar>(i, j) = sqrt(Gx*Gx + Gy * Gy) / (4 * sqrt(2));
}
}
imshow("Intermediar",destinationImg);
imshow("Initial", img);
waitKey(0);
}
I thought of using each RGB chanel but it does not work and even give some errors.
float GxR = 0, GyR = 0;
float GxG = 0, GyG = 0;
float GxB = 0, GyB = 0;
for (int l = 0; l < w; l++)
{
for (int p = 0; p < w; p++)
{
GxR += img.at<Vec3b>[0](i + l - k, j + p - k)*sobelX[l][p];
GxG += img.at<Vec3b>[1](i + l - k, j + p - k)*sobelX[l][p];
GxB += img.at<Vec3b>[2](i + l - k, j + p - k)*sobelX[l][p];
GyR += img.at<Vec3b>[0](i + l - k, j + p - k)*sobelY[l][p];
GyG += img.at<Vec3b>[1](i + l - k, j + p - k)*sobelY[l][p];
GyB += img.at<Vec3b>[2](i + l - k, j + p - k)*sobelY[l][p];
}
}
destinationImg.at<Vec3b>[0](i, j) = sqrt(GxR*GxR + GyR * GyR) / (4 * sqrt(2));
destinationImg.at<Vec3b>[1](i, j) = sqrt(GxG*GxG + GyB * GyB) / (4 * sqrt(2));
destinationImg.at<Vec3b>[2](i, j) = sqrt(GxG*GxG + GyG * GyG) / (4 * sqrt(2));
Can you please explain how can this code must be rewritten?
You access the image data the wrong way.
destinationImg.at<Vec3b>[0](i, j)
destinationImg is a Mat of type Vec3b. That means it's a 2d array of three dimensional vectors.
You'r [ ] operator is in the wrong place...
The subscript error message tells you that you're using that operator on something that is neither a pointer nor an array which is not possible.
You get the other error message because you have that operator where the (i,j) is expected.
First you have to get one of these vectors, then you can get its elements.
destinationImg.at<Vec3b>(i,j) will give you the vector at i,j.
destinationImg.at<Vec3b>(i,j)[0] will give you the first element of that vector.
Example from the OpenCV documentation:
Vec3b intensity = img.at<Vec3b>(y, x);
uchar blue = intensity.val[0];
uchar green = intensity.val[1];
uchar red = intensity.val[2];
http://docs.opencv.org/2.4.13.2/doc/user_guide/ug_mat.html

cvtColor too Slow

I'm making a project where i need to change the lightness, and contrast of an image, it's lightness not brightness.
So my code at the start was
for (int y = 0; y < dst.rows; y++) {
for (int x = 0; x < dst.cols; x++) {
int b = dst.data[dst.channels() * (dst.cols * y + x) + 0];
int g = dst.data[dst.channels() * (dst.cols * y + x) + 1];
int r = dst.data[dst.channels() * (dst.cols * y + x) + 2];
... other processing stuff i'm doing
and it's good, doing it really fast, but when i try to make the hsv to hsl conversion to set the l value that i need it gets reaaaaaaally slow;
my hsl to hsl lines of code are
cvtColor(dst, dst, CV_BGR2HSV);
Vec3b pixel = dst.at<cv::Vec3b>(y, x); // read pixel (0,0)
double H = pixel.val[0];
double S = pixel.val[1];
double V = pixel.val[2];
h = H;
l = (2 - S) * V;
s = s * V;
s /= (l <= 1) ? (l) : 2 - (l);
l /= 2;
/* i will further make here the calcs to set the l i want */
H = h;
l *= 2;
s *= (l <= 1) ? l : 2 - l;
V = (l + s) / 2;
S = (2 * s) / (l + s);
pixel.val[0] = H;
pixel.val[1] = S;
pixel.val[2] = V;
cvtColor(dst, dst, CV_HSV2BGR);
and i ran it and was slow, so i was take of the lines to see which one was making it slow and i figure out it was cvtColor(dst, dst, CV_BGR2HSV);
So there's a way to make it faster than using cvtCOlor, or its time issue is something that can be handled?
I think (I haven't opened the text editor, but it seems) that you need to generate the entire image in HSV and then call cvtColor once for the entire image. Meaning that you should call cvtColor once instead of once for every pixel. That should give you a significant boost in speed.
You would do this:
cvtColor(dst, dst, CV_BGR2HSV);
for (int y = 0; y < dst.rows; y++) {
for (int x = 0; x < dst.cols; x++) {
Vec3b pixel = dst.at<cv::Vec3b>(y, x); // read current pixel
double H = pixel.val[0];
double S = pixel.val[1];
double V = pixel.val[2];
h = H;
l = (2 - S) * V;
s = s * V;
s /= (l <= 1) ? (l) : 2 - (l);
l /= 2;
H = h;
l *= 2;
s *= (l <= 1) ? l : 2 - l;
V = (l + s) / 2;
S = (2 * s) / (l + s);
pixel.val[0] = H;
pixel.val[1] = S;
pixel.val[2] = V;
}
}
cvtColor(dst, dst, CV_HSV2BGR);

How to optimize YUV to RGB color conversion code

I have written a function to convert an image in YUV420P to RGB but it is taking 30 millisecond to convert an image (size: 1280 x 720) into RGB, but when I am using ffmpeg function ( as this) to convert YUV image into RGB its taking only 2 millisecond for the same image. What is the problem with my code ? How can I optimize the code that I have written ??
My code is given below
int step = origImage->widthStep;
uchar *data = (uchar *)origImage->imageData;
int size = origImage->width * origImage->height;
IplImage* img1 = cvCreateImage(cvGetSize(origImage), IPL_DEPTH_8U, 3);
for (int i = 0; i<origImage->height; i++)
{
for (int j=0; j<origImage->width; j++)
{
float Y = data[i*step + j];
float U = data[ (int)(size + (i/2)*(step/2) + j/2) ];
float V = data[ (int)(size*1.25 + (i/2)*(step/2) + j/2)];
float R = Y + 1.402 * (V - 128);
float G = Y - 0.344 * (U - 128) - 0.714 * (V - 128);
float B = Y + 1.772 * (U - 128);
if (R < 0){ R = 0; } if (G < 0){ G = 0; } if (B < 0){ B = 0; }
if (R > 255 ){ R = 255; } if (G > 255) { G = 255; } if (B > 255) { B = 255; }
cvSet2D(img1, i, j,cvScalar(B,G,R));
}
}
Here, try this(should reduce to 25 milliseconds):
int step = origImage->widthStep;
uchar *data = (uchar *)origImage->imageData;
int size = origImage->width * origImage->height;
IplImage* img1 = cvCreateImage(cvGetSize(origImage), IPL_DEPTH_8U, 3);
int stepDb2=step /2;
float sizeMb1d25=size*1.25 ;
int origImagePTheight=origImage->height;
int origImagePTwidth=origImage->width;
for (int i = 0; i<origImagePTheight; i++)
{
float idb2=i/2;
int iStep=i*step;
for (int j=0; j<origImagePTwidth; j++)
{
float variable=idb2*stepDb2 + j/2;
float Y = data[iStep + j];
float U = -128 + data[ (int)(size + variable) ];
float V = -128 + data[ (int)(sizeMb1d25 + variable)];
float R = Y + 1.402 * V ;
float G = Y - 0.344 * U - 0.714 * V;
float B = Y + 1.772 * U;
R= R * !(R<0);
G= G * !(G<0);
B= B * !(B<0);
R=R*(!(R>255)) + 255 * (R>255);
G=G*(!(G>255)) + 255 * (G>255);
B=B*(!(B>255)) + 255 * (B>255);
cvSet2D(img1, i, j,cvScalar(B,G,R));
}
}

Robust image segmentation in OpenCV

I'm trying to write an OpenCV program that counts fish eggs for someone else. It currently takes their uploaded image, normalizes, blurs, thresholds, dilates, distance transforms, thresholds again, and then finds contours (like in a typical watershed tutorial).
The problem I'm having is that the lighting conditions can vary quite a bit, so even with my adaptive threshold values, the accuracy of the algorithm also varies wildly. If there's a gradient brightness across the image it seems to do especially poorly. Sometimes the objects are very bright against the background and other times they're almost the same luminosity. Are there any particularly effective ways to find objects in varying light conditions?
Sample images:
Because anything larger than 100 pixels isn't relevant to your image, I would construct a fourier band pass filter to remove these structures.
Here is an implementation I use, based off the one in ImageJ. In this implementation the input image is mirror padded to reduce edge artifacts.
static void GenerateBandFilter(thrust::host_vector<float>& filter, const BandPassSettings& band, const FrameSize& frame)
{
//From https://imagej.nih.gov/ij/plugins/fft-filter.html
if (band.do_band_pass == false)
{
return;
}
if (frame.width != frame.height)
{
throw std::runtime_error("Frame height and width should be the same");
}
auto maxN = static_cast<int>(std::max(frame.width, frame.height));//todo make sure they are the same
auto filterLargeC = 2.0f*band.max_dx / maxN;
auto filterSmallC = 2.0f*band.min_dx / maxN;
auto scaleLargeC = filterLargeC*filterLargeC;
auto scaleSmallC = filterSmallC*filterSmallC;
auto filterLargeR = 2.0f*band.max_dy / maxN;
auto filterSmallR = 2.0f*band.min_dy / maxN;
auto scaleLargeR = filterLargeR*filterLargeR;
auto scaleSmallR = filterSmallR*filterSmallR;
// loop over rows
for (auto j = 1; j < maxN / 2; j++)
{
auto row = j * maxN;
auto backrow = (maxN - j)*maxN;
auto rowFactLarge = exp(-(j*j) * scaleLargeR);
auto rowFactSmall = exp(-(j*j) * scaleSmallR);
// loop over columns
for (auto col = 1; col < maxN / 2; col++)
{
auto backcol = maxN - col;
auto colFactLarge = exp(-(col*col) * scaleLargeC);
auto colFactSmall = exp(-(col*col) * scaleSmallC);
auto factor = (((1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall));
filter[col + row] *= factor;
filter[col + backrow] *= factor;
filter[backcol + row] *= factor;
filter[backcol + backrow] *= factor;
}
}
auto fixy = [&](float t){return isinf(t) ? 0 : t; };
auto rowmid = maxN * (maxN / 2);
auto rowFactLarge = fixy(exp(-(maxN / 2)*(maxN / 2) * scaleLargeR));
auto rowFactSmall = fixy(exp(-(maxN / 2)*(maxN / 2) *scaleSmallR));
filter[maxN / 2] *= ((1 - rowFactLarge) * rowFactSmall);
filter[rowmid] *= ((1 - rowFactLarge) * rowFactSmall);
filter[maxN / 2 + rowmid] *= ((1 - rowFactLarge*rowFactLarge) * rowFactSmall*rowFactSmall); //
rowFactLarge = fixy(exp(-(maxN / 2)*(maxN / 2) *scaleLargeR));
rowFactSmall = fixy(exp(-(maxN / 2)*(maxN / 2) *scaleSmallR));
for (auto col = 1; col < maxN / 2; col++){
auto backcol = maxN - col;
auto colFactLarge = exp(-(col*col) * scaleLargeC);
auto colFactSmall = exp(-(col*col) * scaleSmallC);
filter[col] *= ((1 - colFactLarge) * colFactSmall);
filter[backcol] *= ((1 - colFactLarge) * colFactSmall);
filter[col + rowmid] *= ((1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall);
filter[backcol + rowmid] *= ((1 - colFactLarge*rowFactLarge) * colFactSmall*rowFactSmall);
}
// loop along column 0 and expanded_width/2
auto colFactLarge = fixy(exp(-(maxN / 2)*(maxN / 2) * scaleLargeC));
auto colFactSmall = fixy(exp(-(maxN / 2)*(maxN / 2) * scaleSmallC));
for (auto j = 1; j < maxN / 2; j++) {
auto row = j * maxN;
auto backrow = (maxN - j)*maxN;
rowFactLarge = exp(-(j*j) * scaleLargeC);
rowFactSmall = exp(-(j*j) * scaleSmallC);
filter[row] *= ((1 - rowFactLarge) * rowFactSmall);
filter[backrow] *= ((1 - rowFactLarge) * rowFactSmall);
filter[row + maxN / 2] *= ((1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall);
filter[backrow + maxN / 2] *= ((1 - rowFactLarge*colFactLarge) * rowFactSmall*colFactSmall);
}
filter[0] = (band.remove_dc) ? 0 : filter[0];
}
You can poke around my code that uses it here: https://github.com/kandel3/DPM_PhaseRetrieval
Calculate alpha and beta values of image
image = cv::imread("F:\Dilated.jpg");
int x,y;
int a=0; // variables to be used in loop
int count=0; // variables to be used in loop
for( int y = 0; y < image.rows; y++ )
{ for( int x = 0; x < image.cols; x++ )
{ for( int c = 0; c < 3; c++ )
{
image.at<Vec3b>(y,x)[c] =
saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
}
}
}