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.
Related
How to create a Gaussian kernel by only specifying its width w (3,5,7,9...), and without specifying its variance sigma?
In other word, how to adapt sigma so that the Gaussian distribution 'fits well' w?
I would be interested in a C++ implementation:
void create_gaussian_kernel(int w, std::vector<std::vector<float>>& kernel)
{
kernel = std::vector<std::vector<float>>(w, std::vector<float>(w, 0.f)); // 2D array of size w x w
const Scalar sigma = 1.0; // how to adapt sigma to w ???
const int hw = (w-1)/2; // half width
for(int di = -hw; di <= +hw; ++di)
{
const int i = hw + di;
for(int dj = -hw; dj <= +hw; ++dj)
{
const int j = hw + dj;
kernel[i][j] = gauss2D(di, dj, sigma);
}
}
}
Everything I see on the Internet use a fixed size w and a fixed variance sigma :
geeksforgeeks.org/gaussian-filter-generation-c/
tutorialspoint.com/gaussian-filter-generation-in-cplusplus
stackoverflow.com/a/8204880/5317819
stackoverflow.com/q/42186498/5317819
stackoverflow.com/a/54615770/5317819
I found a simple (arbitrary) relation between sigma and w.
I want the next value outside the kernel (along one axis) below a very small value epsilon:
exp( - (half_width + 1)^2 / (2 * sigma^2) ) < epsilon
with half_width the kernel 'half width'.
The result is
sigma^2 = - (half_width + 1)^2 / (2 * log(epsilon))
I use the following c++ code:
#include <vector>
#include <cmath>
#include <cassert>
using Matrix = std::vector<std::vector<float>>;
// compute sigma^2 that 'fit' the kernel half width
float compute_squared_variance(int half_width, float epsilon = 0.001)
{
assert(0 < epsilon && epsilon < 1); // small value required
return - (half_width + 1.0) * (half_width + 1.0) / 2.0 / std::log(epsilon);
}
float gaussian_exp(float y, float x, float sigma2)
{
assert(0 < sigma2);
return std::exp( - (x*x + y*y) / (2 * sigma2) );
}
// create a Gaussian kernel of size 2*half_width+1 x 2*half_width+1
Matrix make_gaussian_kernel(int half_width)
{
if(half_width <= 0)
{
// kernel of size 1 x 1
Matrix kernel(1, std::vector<float>(1, 1.0));
return kernel;
}
Matrix kernel(2*half_width+1, std::vector<float>(2*half_width+1, 0.0));
const float sigma2 = compute_squared_variance(half_width, 0.1);
float sum = 0;
for(int di = -half_width; di <= +half_width; ++di)
{
const int i = half_width + di;
for(int dj = -half_width; dj <= +half_width; ++dj)
{
const int j = half_width + dj;
kernel[i][j] = gaussian_exp(di, dj, sigma2);
sum += kernel[i][j];
}
}
assert(0 < sum);
// normalize
for(int i=0; i<2*half_width+1; ++i)
{
for(int j=0; j<2*half_width+1; ++j)
{
kernel[i][j] /= sum;
}
}
return kernel;
}
I would like to get average contrast value of an image and I found working code in processing .I tried to apply this in qt-creator as in c++, however when I try to run my code I get this error:
cannot convert ‘cv::Mat’ to ‘uint8_t* {aka unsigned char*}’ in initialization
uint8_t* rowPtr = foo.row(i);<
In these parts:
uint8_t* rowPtr = foo.row(i);>
Here is the code I am trying to run;
bool normalizeRange = true; // normalize results to range of 0-1
float contrast = 0;
float brightness =0;
Mat foo = imread("1.png");
int cn = foo.channels();
Scalar_<uint8_t> bgrPixel;
for(int i = 0; i < foo.rows; i++)
{
uint8_t* rowPtr = foo.row(i);
for(int j = 0; j < foo.cols; j++)
{
bgrPixel.val[0] = rowPtr[j*cn + 0]; // B
bgrPixel.val[1] = rowPtr[j*cn + 1]; // G
bgrPixel.val[2] = rowPtr[j*cn + 2]; // R
// do something with BGR values...
}
}
brightness += (0.2126 * bgrPixel.val[2]) + (0.7152 * bgrPixel.val[1]) + (0.0722 * bgrPixel.val[0]); // scales RGB to perceived brightness
if (normalizeRange) {
brightness /= 255.0; // normalize to 0-1
}
brightness /= sizeof(bgrPixel); // average result
qDebug() << "Average brightness:" << brightness;
// find contrast by comparing average brightness with current value
for(int i = 0; i < foo.rows; i++)
{
uint8_t* rowPtr = foo.row(i);
for(int j = 0; j < foo.cols; j++)
{
bgrPixel.val[0] = rowPtr[j*cn + 0]; // B
bgrPixel.val[1] = rowPtr[j*cn + 1]; // G
bgrPixel.val[2] = rowPtr[j*cn + 2]; // R
// do something with BGR values...
}
}
float pxIntensity = (0.2126 * bgrPixel.val[2]) + (0.7152 * bgrPixel.val[1]) + (0.0722 * bgrPixel.val[2]);
if (normalizeRange) {
pxIntensity /= 255.0; // normalizes to range 0-1
}
contrast += pow((brightness - pxIntensity), 2);
contrast /= sizeof(bgrPixel);
qDebug() << "Average cotrast:" << contrast;
}
Update: I have added the java code which I would like to run in C++
boolean normalizeRange = true; // normalize results to range of 0-1
PImage img;
float brightness = 0;
float contrast = 0;
void setup() {
img = loadImage(filename);
size(img.width, img.height);
image(img, 0,0);
loadPixels(); // load pixels into array, iterate!
// find average brightness across image
for (color c : pixels) {
float r = c >> 16 & 0xFF; // extract RGB values quickly (better than red(), etc)
float g = c >> 8 & 0xFF;
float b = c & 0xFF;
brightness += (0.2126 * r) + (0.7152 * g) + (0.0722 * b); // scales RGB to perceived brightness
if (normalizeRange) {
brightness /= 255.0; // normalize to 0-1
} }
brightness /= pixels.length; // average result
println("Average brightness: " + brightness);
// find contrast by comparing average brightness with current value
for (color c : pixels) {
float r = c >> 16 & 0xFF;
float g = c >> 8 & 0xFF;
float b = c & 0xFF;
float pxIntensity = (0.2126 * r) + (0.7152 * g) + (0.0722 * b);
if (normalizeRange) {
pxIntensity /= 255.0; // normalizes to range 0-1 }
contrast += pow((brightness - pxIntensity), 2);}
contrast /= pixels.length;
println("Average contrast: " + contrast);
}
Use cv::Mat::ptr() to obtain a pointer to the specified matrix row buffer.
cv::Mat::row() constructs a new cv::Mat header for submatrix that consists of that single row instead.
Iterating raw cv::Mat buffer may be more efficient but less readable way to iterate elements. Read about different ways and their comparison here.
I'm have this function taken from here:
bool interpolate(const Mat &im, float ofsx, float ofsy, float a11, float a12, float a21, float a22, Mat &res)
{
bool ret = false;
// input size (-1 for the safe bilinear interpolation)
const int width = im.cols-1;
const int height = im.rows-1;
// output size
const int halfWidth = res.cols >> 1;
const int halfHeight = res.rows >> 1;
int dim = res.rows * res.cols;
float *out = res.ptr<float>(0);
for (int j=-halfHeight; j<=halfHeight; ++j)
{
const float rx = ofsx + j * a12;
const float ry = ofsy + j * a22;
#pragma omp simd
for(int i=-halfWidth; i<=halfWidth; ++i)
{
float wx = rx + i * a11;
float wy = ry + i * a21;
const int x = (int) floor(wx);
const int y = (int) floor(wy);
if (x >= 0 && y >= 0 && x < width && y < height)
{
// compute weights
wx -= x; wy -= y;
// bilinear interpolation
*out++ =
(1.0f - wy) * ((1.0f - wx) * im.at<float>(y,x) + wx * im.at<float>(y,x+1)) +
( wy) * ((1.0f - wx) * im.at<float>(y+1,x) + wx * im.at<float>(y+1,x+1));
} else {
*out++ = 0;
ret = true; // touching boundary of the input
}
}
}
return ret;
}
I don't know what interpolation is in details, but looking at this opencv page, it seems that it's a bilinear interpolation using INTER_LINEAR. The point is that I don't know how to call an equivalent opencv function for the code above.
This function is called in two different points here.
You can't solely apply simple an interpolation using openCV,
it has to be part of image processing operation, e.g warp or resize operation.
I think you are trying to vectorize a warp affine, the easiest way and most efficient code on Intel platform would be to use IPP.
Otherwise, I'd let Opencv Warp affine do the job.
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.
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 );
}
}
}