I'm trying to warp colour image using sin function in OpenCV and I was successful in doing so. However, how can I make a 'diagonal' warping using sine wave?
My code is this:
Mat result = src.clone();
for (int i = 0; i < src.rows; i++) { // to y
for (int j = 0; j < src.cols; j++) { // to x
for (int ch = 0; ch < 3; ch++) { // each colour
int offset_x = 0;
int offset_y = (int)(25.0 * sin(3.14 * j / 150));
if (i + offset_y < src.rows) {
result.at<Vec3b>(i, j)[ch] = src.at<Vec3b>((i + offset_y) % src.rows, j)[ch];
}
else
result.at<Vec3b>(i, j)[ch] = 0.0;
}
}
}
imshow("result", result);
How can I do this? Not drawing a sine graph, but warping an image.
Solved this! Several times ago, I've received a message by someone who told me that the image is stolen. It was from Google, actually, but I've deleted it to fulfill not to cause any situations. Thx!
I think it should look like this:
void deform()
{
float alpha = 45 * CV_PI / 180.0; // wave direction
float ox = cos(alpha);
float oy = sin(alpha);
cv::Mat src = cv::imread("F:/ImagesForTest/lena.jpg");
for (int i = 0; i < src.rows; i+=8)
{
cv::line(src, cv::Point(i, 0), cv::Point(i, src.rows),cv::Scalar(255,255,255));
}
for (int j = 0; j < src.cols; j += 8)
{
cv::line(src, cv::Point(0,j), cv::Point(src.cols,j), cv::Scalar(255, 255, 255));
}
cv::Mat result = src.clone();
for (int i = 0; i < src.rows; i++)
{ // to y
for (int j = 0; j < src.cols; j++)
{ // to x
float t =(i * oy)+ (j * ox); // wave parameter
for (int ch = 0; ch < 3; ch++)
{ // each colour
int offset_x =ox* (int)(25.0 * (sin(3.14 * t/ 150)));
int offset_y =oy* (int)(25.0 * (sin(3.14 * t / 150)));
if (i + offset_y < src.rows && j + offset_x < src.rows && i + offset_y >=0 && j + offset_x>=0)
{
result.at<cv::Vec3b>(i, j)[ch] = src.at<cv::Vec3b>(i + offset_y, j + offset_x )[ch];
}
else
result.at<cv::Vec3b>(i, j)[ch] = 0.0;
}
}
}
cv:: imshow("result", result);
cv::imwrite("result.jpg", result);
cv::waitKey();
}
The result:
BTW, may be better to use cv::remap ?
Related
I want to create a dilation image using a kernel that runs through the entire image and checks if the kernel zone has 0, if so, it gives the new image a pixel of 255. My code is giving me an all black dst and I don't know why.
This is the code:
Mat vcpi_binary_dilate(Mat src)
{
if (src.empty())
{
cout << "Failed to load image.";
return src;
}
Mat dst(src.rows, src.cols, CV_8UC1, Scalar(0));
const int kernnel = 3;
int array[kernnel * kernnel] = {};
for (int y = kernnel / 2; y < src.cols - kernnel / 2; y++)
{
for (int x = kernnel / 2; x < src.rows - kernnel / 2; x++)
for (int yk = -kernnel / 2; yk <= kernnel / 2; yk++)
{
for (int xk = -kernnel / 2; xk <= kernnel / 2; xk++)
{
if (src.at<uchar>(y + yk, x + xk) == 0)
{
dst.at<uchar>(y + yk, x + xk) = 255;
}
}
}
}
imshow("Image ", src);
imshow("Image dilate", dst);
waitKey(0);
return dst;
}
I hope to have an output image of this type.
I am not sure about the algorithm you are trying to implement.
But there is one thing that is definately wrong:
The image dimensions are mixed up-
cols is the width and corresponds to the x axis.
rows is the height and corresponds to the y axis.
This is causing you to access the images using cv::Mat::at with invalid coordinates.
Therefore you need to change:
for (int y = kernnel / 2; y < src.cols - kernnel / 2; y++)
{
for (int x = kernnel / 2; x < src.rows - kernnel / 2; x++)
{
To:
//--------------------------------vvvv--------------------
for (int y = kernnel / 2; y < src.rows - kernnel / 2; y++)
{
//------------------------------------vvvv--------------------
for (int x = kernnel / 2; x < src.cols - kernnel / 2; x++)
{
Note that this is consistent with your calls ...at<uchar>(y + yk, x + xk), since cv::Mat::at expects the row (i.e. y coordinate) first.
A side note: Why is "using namespace std;" considered bad practice?.
Edit:
After having a look at the matlab code in your comment, which applies an algorithm different than what you described in your question, you'll need to do the following changes:
Call something equivalent to matlab's im2bw.
Update dst current pixel, not the one in the neighborhood.
Maybe something like:
cv::Mat vcpi_binary_dilate(cv::Mat src) {
if (src.empty()) {
std::cout << "Failed to load image.";
return src;
}
cv::threshold(src, src, 127, 255, CV_THRESH_BINARY);
cv::Mat dst(src.rows, src.cols, CV_8UC1, cv::Scalar(255)); // <-- Replacement for im2bw, might need tuning.
const int kernnel = 3;
int array[kernnel * kernnel] = {};
for (int y = kernnel / 2; y < src.rows - kernnel / 2; y++)
{
for (int x = kernnel / 2; x < src.cols - kernnel / 2; x++)
{
for (int yk = -kernnel / 2; yk <= kernnel / 2; yk++)
{
for (int xk = -kernnel / 2; xk <= kernnel / 2; xk++)
{
if (src.at<uchar>(y + yk, x + xk) == 0) {
dst.at<uchar>(y, x) = 0; // <-- Update the current pixel, not the one in the neighborhood.
}
}
}
}
}
cv::imshow("Image ", src);
cv::imshow("Image dilate", dst);
cv::waitKey(0);
return dst;
}
I need to normalize the histogram of an image f which mean to applicated an transformation of histogram from image in order to extend the range of value of f to all available values.
the norm(fmin) = Vmin ( minimal value we want to reach) and normal(fmin) = Vmax ( maximal value we want to reach)
I have this formula too
the goal is to have the same result that the function normalize which openCV gives.
Mat normalize(Mat image, float minValue, float maxValue)
{
Mat res = image.clone();
assert(minValue <= maxValue);
float Fmax = 0;
float Fmin = 0;
for(int i = 0; i < res.rows; i++)
{
for(int j = 0; j < res.cols; j++)
{
float x = res.at<float>(i,j);
if(i < minValue)
{
Fmin = i;
}
if( i > maxValue)
{
Fmax = i;
}
res.at<float>(i,j) = (x - Fmin) * ((maxValue - minValue) / (Fmax - Fmin)) + minValue;
}
}
return res;
}
I have this error : !!! Warning, saved image values not between 0 and 1.
!!! Warning, saved image values not between 0 and 1.
I think I didn't understand how to calculate fmin/ fmax
So, as I explained in my comment, there are some mistakes, here's the corrected version. You need to run the double loop twice, once to find the min-max, and a second time to apply the formula. There were also errors in the comparisons:
cv::Mat normalize(cv::Mat image, float minValue, float maxValue)
{
cv::Mat res = image.clone();
assert(minValue <= maxValue);
// 1) find min and max values
float Fmax = 0.0f;
float Fmin = 1.0f; // set it to 1, not 0
for (int i = 0; i < res.rows; i++)
{
float* pixels = res.ptr<float>(i); // this is quicker
for (int j = 0; j < res.cols; j++)
{
float x = pixels[j];
if (x < Fmin) // compare x and Fmin, not i and minValue
{
Fmin = x;
}
if (x > Fmax) // compare x and Fmax, not i and maxValue
{
Fmax = x;
}
}
}
// 1 color image => don't normalize + avoid crash
if (Fmin >= Fmax)
return res;
// 2) normalize using your formula
for (int i = 0; i < res.rows; i++)
{
float* pixels = res.ptr<float>(i);
for (int j = 0; j < res.cols; j++)
{
pixels[j] = (pixels[j] - Fmin) * ((maxValue - minValue) / (Fmax - Fmin)) + minValue;
}
}
return res;
}
If your source image is a grayscale image in 8 bit, you can convert it like that:
cv::Mat floatImage;
grayImage.convertTo(floatImage, CV_32F, 1.0 / 255, 0);
floatImage = normalize(floatImage, 0, 1.0f);
floatImage.convertTo(grayImage, CV_8UC1, 255.0, 0);
Also, if you use cv::minMaxLoc, your normalize function can be made shorter =>
cv::Mat normalize(cv::Mat image, float minValue, float maxValue)
{
cv::Mat res = image.clone();
assert(minValue <= maxValue);
// 1) find min and max values
double Fmax;
double Fmin;
cv::minMaxLoc(image, &Fmin, &Fmax);
if (Fmin >= Fmax)
return res;
// 2) normalize using your formula
for (int i = 0; i < res.rows; i++)
{
float* pixels = res.ptr<float>(i);
for (int j = 0; j < res.cols; j++)
{
pixels[j] = (pixels[j] - Fmin) * ((maxValue - minValue) / (Fmax - Fmin)) + minValue;
}
}
return res;
}
Here is my code for creating the hough accumulator for lines in image :
void hough_lines_acc(cv::Mat img_a_edges, std::vector<std::vector<int> > &hough_acc) {
for (size_t r = 0; r < img_a_edges.rows; r++) {
for (size_t c = 0; c < img_a_edges.cols; c++) {
int theta = static_cast<int> (std::atan2(r, c) * 180 / M_PI);
int rho = static_cast<int> ((c * cos(theta)) + (r * sin(theta)));
if (theta < -90) theta = -90;
if (theta > 89) theta = 89;
++hough_acc[abs(rho)][theta];
}
}
cv::Mat img_mat(hough_acc.size(), hough_acc[0].size(), CV_8U);
std::cout << hough_acc.size() << " " << hough_acc[0].size() << std::endl;
for (size_t i = 0; i < hough_acc.size(); i++) {
for (size_t j = 0; j < hough_acc[0].size(); j++) {
img_mat.at<int> (i,j) = hough_acc[i][j];
}
}
imwrite("../output/ps1-2-b-1.png", img_mat);
}
theta varies from -90 to 89. I am getting negative rho values. Right now I am just replacing the negative who with a positive one but am not getting a correct answer. What do I do to the negative rho? Please explain the answer.
theta = arctan (y / x)
rho = x * cos(theta) + y * sin(theta)
Edited code :
bool hough_lines_acc(cv::Mat img_a_edges, std::vector<std::vector<int> > &hough_acc,\
std::vector<double> thetas, std::vector<double> rhos, int rho_resolution, int theta_resolution) {
int img_w = img_a_edges.cols;
int img_h = img_a_edges.rows;
int max_votes = 0;
int min_votes = INT_MAX;
for (size_t r = 0; r < img_h; r++) {
for (size_t c = 0; c < img_w; c++) {
if(img_a_edges.at<int>(r, c) == 255) {
for (size_t i = 0; i < thetas.size(); i++) {
thetas[i] = (thetas[i] * M_PI / 180);
double rho = ( (c * cos(thetas[i])) + (r * sin(thetas[i])) );
int buff = ++hough_acc[static_cast<int>(abs(rho))][static_cast<int>(i)];
if (buff > max_votes) {
max_votes = buff;
}
if (buff < min_votes) {
min_votes = buff;
}
}
}
}
}
double div = static_cast<double>(max_votes) / 255;
int threshold = 10;
int possible_edge = round(static_cast<double>(max_votes) / div) - threshold;
props({
{"max votes", max_votes},
{"min votes", min_votes},
{"scale", div}
});
// needed for scaling intensity for contrast
// not sure if I am doing it correctly
for (size_t r = 0; r < hough_acc.size(); r++) {
for (size_t c = 0; c < hough_acc[0].size(); c++) {
double val = hough_acc[r][c] / div;
if (val < 0) {
val = 0;
}
hough_acc[r][c] = static_cast<int>(val);
}
}
cv::Mat img_mat = cv::Mat(hough_acc.size(), hough_acc[0].size(), CV_8UC1, cv::Scalar(0));
for (size_t i = 0; i < hough_acc.size(); i++) {
for (size_t j = 0; j < hough_acc[0].size(); j++) {
img_mat.at<uint8_t> (i,j) = static_cast<uint8_t>(hough_acc[i][j]);
}
}
imwrite("../output/ps1-2-b-1.png", img_mat);
return true;
}
Still not correct output. What is the error here?
atan2 of two positive numbers... should not be giving you negative angles, it should only be giving you a range of 0-90
also for the hough transform, I think you want everything relative to one point (ie 0,0 in this case). I think for that you would actually want to make theta=90-atan2(r,c)
Admittedly though, I am a bit confused as I thought you had to encode line direction, rather than just "edge pt". ie I thought at each edge point you had to provide a discrete array of guessed line trajectories and calculate rho and theta for each one and throw all of those into your accumulator. As is... I am not sure what you are calculating.
i want to transport the follow codes into c++:
gaussFilter = fspecial('gaussian', 2*neighSize+1, 0.5*neighSize);
pointFeature = imfilter(pointFeature, gaussFilter, 'symmetric');
where the pointFeature is a [height, width, 24] array.
i try to use filter2D, but it only support the 2D array.
so i want to know if there are functions in opencv that can filtering the multi-dimensional array?
You can use separable kernel filters for make anydimentional filter.
If you are using OpenCV, you could try this for a 3 Dimensional MatND:
void Smooth3DHist(cv::MatND &hist, const int& kernDimension)
{
assert(hist.dims == 3);
int x_size = hist.size[0];
int y_size = hist.size[1];
int z_size = hist.size[2];
int xy_size = x_size*y_size;
cv::Mat kernal = cv::getGaussianKernel(kernDimension, -1, CV_32F);
// Filter XY dimensions for every Z
for (int z = 0; z < z_size; z++)
{
float *ind = (float*)hist.data + z * xy_size; // sub-matrix pointer
cv::Mat subMatrix(2, hist.size, CV_32F, ind);
cv::sepFilter2D(subMatrix, subMatrix, CV_32F, kernal.t(), kernal, Point(-1,-1), 0.0, cv::BORDER_REPLICATE);
}
// Filter Z dimension
float* kernGauss = (float *)kernal.data;
unsigned kernSize = kernal.total();
int kernMargin = (kernSize - 1)/2;
float* lineBuffer = new float[z_size + 2*kernMargin];
for (int y = 0; y < y_size; y++)
{
for (int x = 0; x < x_size; x++)
{
// Copy along Z dimension into a line buffer
float* z_ptr = (float*)hist.data + y * x_size + x;//same as hist.ptr<float>(0, y, x)
for (int z = 0; z < z_size; z++, z_ptr += xy_size)
{
lineBuffer[z + kernMargin] = *z_ptr;
}
// Replicate borders
for (int m = 0; m < kernMargin; m++)
{
lineBuffer[m] = lineBuffer[kernMargin];// replicate left side
lineBuffer[z_size + 2*kernMargin - 1 - m] = lineBuffer[kernMargin + z_size - 1];//replicate right side
}
// Filter line buffer 1D - convolution
z_ptr = (float*)hist.data + y * x_size + x;
for (int z = 0; z < z_size; z++, z_ptr += xy_size)
{
*z_ptr = 0.0f;
for (unsigned k = 0; k < kernSize; k++)
{
*z_ptr += lineBuffer[z+k]*kernGauss[k];
}
}
}
}
delete [] lineBuffer;
}
For a project I'm writing some code to compute the HoG of some images, but I'm stuck with the fact that my orientations are only between 0 ~ 90 degrees, while using the atan2 function.
I'm guessing that this problem occurs due to the filter2D function of OpenCV but I'm not sure if this is the reason or that I'm doing something else wrong:
Vector<Vector<Mat_<float>>> HoG(Mat image) {
Mat img_x;
Mat img_y;
IplImage img = image;
Mat kern_x = (Mat_<char>(1, 3) << -1, 0, 1);
Mat kern_y = (Mat_<char>(3, 1) << -1, 0, 1);
filter2D(image, img_x, image.depth(), kern_x);
filter2D(image, img_y, image.depth(), kern_y);
Vector<Vector<Mat_<float>>> histograms;
for(int y = 0; y < image.rows - size; y += size) {
Vector<Mat_<float>> temp_hist;
for(int x = 0; x < image.cols - size; x += size) {
float total_mag = 0;
Mat hist = Mat::zeros(1, 8, CV_32FC1);
for(int i = y; i < y + size; ++i) {
for(int j = x; j < x + size; ++j) {
float grad_x = (float)img_x.at<uchar>(i, j);
float grad_y = (float)img_y.at<uchar>(i, j);
double ori = myatan2(grad_x, grad_y);
float mag = sqrt(pow(grad_x, 2) + pow(grad_y, 2));
int bin = round(ori/45);
hist.at<float>(0, (bin - 1 < 0 ? 7 : bin - 1)) += - (float)(ori - ((round(ori/45) - 1) * 45.0 + 22.5)) / 45.0f;
hist.at<float>(0, bin) += -(float)(ori - ((round(ori/45) - 1) * 45.0 + 22.5)) / 45.0f;
total_mag += mag;
}
}
// Normalize the histogram
for(int i = 0; i < 8; ++i) {
hist.at<float>(0, i) = hist.at<float>(0, i) / total_mag;
}
temp_hist.push_back(hist);
}
histograms.push_back(temp_hist);
}
return histograms;
}
If you have any other tips to increase a speed-up in my code or something else that is also welcome of course.
I notice this:
float grad_x = (float)img_x.at<uchar>(i, j);
float grad_y = (float)img_y.at<uchar>(i, j);
You seem to be using uchar. Should this not be char?