Image Straightening in iOS using Open CV framework - ios

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.

Related

How to use cv::decode (access image) correct?

I need help with the following problem:
Task script:
read in the message sensor_msgs/PointCloud2, display Bird Eye View image and save (png or jpg).
Desired new function:
Send out the displayed images directly as an image message.
Problem:
cv::Mat *bgr is the matrix that contains the image and gives it to a map (for visualisation only).
Solutions by others/so far:
opencv read jpeg image from buffer //
How to use cv::imdecode, if the contents of an image file are in a char array?
Using different member functions, but unsuccessful.
Code reduced to necessary snippets
(complete version here: https://drive.google.com/file/d/1HI3E4nM9mQ--oNh1Q7zfwRFGJB5JRiGD/view?usp=sharing)
// Global Publishers/Subscribers
ros::Subscriber subPointCloud;
ros::Publisher pubPointCloud;
image_transport::Publisher pubImage;
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_grid (new pcl::PointCloud<pcl::PointXYZ>);
sensor_msgs::PointCloud2 output;
// create Matrix to store pointcloud data
cv::Mat *heightmap, *hsv, *bgr;
std::vector<int> compression_params;
std::vector<String> fn; //filename
cv::Mat image;
// main generation function
void DEM(const sensor_msgs::PointCloud2ConstPtr& pointCloudMsg)
{
ROS_DEBUG("Point Cloud Received");
// clear cloud and height map array
lowest = FLT_MAX;
for(int i = 0; i < IMAGE_HEIGHT; ++i){
for(int j = 0; j < IMAGE_WIDTH; ++j){
heightArray[i][j] = (double)(-FLT_MAX);
}
}
// Convert from ROS message to PCL point cloud
pcl::fromROSMsg(*pointCloudMsg, *cloud);
// Populate the DEM grid by looping through every point
int row, column;
for(size_t j = 0; j < cloud->points.size(); ++j){
// If the point is within the image size bounds
if(map_pc2rc(cloud->points[j].x, cloud->points[j].y, &row, &column) == 1 && row >= 0 && row < IMAGE_HEIGHT && column >=0 && column < IMAGE_WIDTH){
if(cloud->points[j].z > heightArray[row][column]){
heightArray[row][column] = cloud->points[j].z;
}
// Keep track of lowest point in cloud for flood fill
else if(cloud->points[j].z < lowest){
lowest = cloud->points[j].z;
}
}
}
// Create "point cloud" and opencv image to be published for visualization
int index = 0;
double x, y;
for(int i = 0; i < IMAGE_HEIGHT; ++i){
for(int j = 0; j < IMAGE_WIDTH; ++j){
// Add point to cloud
(void)map_rc2pc(&x, &y, i, j);
cloud_grid->points[index].x = x;
cloud_grid->points[index].y = y;
cloud_grid->points[index].z = heightArray[i][j];
++index;
// Add point to image
cv::Vec3b &pixel_hsv = hsv->at<cv::Vec3b>(i,j); // access pixels vector HSV
cv::Vec3b &pixel_bgr = heightmap->at<cv::Vec3b>(i,j); // access pixels vector BGR
if(heightArray[i][j] > -FLT_MAX){
//Coloured Pixel Pointcloud
pixel_hsv[0] = map_m2i(heightArray[i][j]); // H - color value (hue)
pixel_hsv[1] = 255; // S -color saturation
pixel_hsv[2] = 255; // V - brightness
// White Pixel PointCloud
pixel_bgr[0] = map_m2i(heightArray[i][j]); // B
pixel_bgr[1] = map_m2i(heightArray[i][j]); // G
pixel_bgr[2] = map_m2i(heightArray[i][j]); // R
}
else{
// Coloured Pixel Pointcloud
pixel_hsv[0] = 0;
pixel_hsv[1] = 0;
pixel_hsv[2] = 0;
// White Pixel Pointcloud
pixel_bgr[0] = 0;
pixel_bgr[1] = 0;
pixel_bgr[2] = 0; //map_m2i(lowest);
}
}
}
// Display image
cv::cvtColor(*hsv, *bgr, cv::COLOR_HSV2BGR); // HSV matrix (src) to BGR matrix (dst)
// Image denoising (filter strength, pixel size template patch, pixel size window)
//cv::fastNlMeansDenoising(*hsv,*bgr,30 , 7, 11);
// Image denoising (filter strength luminance, same colored, pixel size template patch, pixel size window)
//cv::fastNlMeansDenoisingColored(*hsv,*bgr,30 ,1, 7, 11);
// Plot HSV(colored) and BGR (b/w)
cv::imshow(WIN_NAME, *bgr); // show new HSV matrix
cv::imshow(WIN_NAME2, *heightmap); // show old BGR matrix
// Save image to disk
char filename[100];
// FLAG enable/disable saving function
if (save_to_disk == true)
{
// save JPG format
snprintf(filename, 100, "/home/pkatsoulakos/catkin_ws/images/image_%d.jpg", fnameCounter);
std::cout << filename << std::endl;
// JPG image writing
cv::imwrite(filename, *bgr, compression_params);
/* // generate pathnames matching a pattern
glob("/home/pkatsoulakos/catkin_ws/images/*.jpg",fn); // directory, filter pattern
// range based for loop
for (auto f:fn) // range declaration:range_expression
{
image = cv::imread(f, IMREAD_COLOR);
if (image.empty())
{
std::cout << "!!! Failed imread(): image not found" << std::endl;
}
}*/
// Approach 2
//cv::Mat rawdata(1, bgr,CV_8UC1,(void*)bgr);
image = cv::imdecode(cv::Mat(*bgr, CV_8UC3, CV_AUTO_STEP), IMREAD_COLOR);
//image = cv::imdecode(cv::Mat(*bgr, CV_8UC1), IMREAD_UNCHANGED);
if (image.data == NULL)
{
std::cout << "!!! Failed imread(): image not found" << std::endl;
}
/* // save PNG format
snprintf(filename, 100, "/home/pkatsoulakos/catkin_ws/images/image_%d.png", fnameCounter);
std::cout << filename << std::endl;
// PNG image writing
// cv::imwrite(filename, *heightmap, compression_params);*/
}
++fnameCounter;
// Output height map to point cloud for python node to parse to PNG
pcl::toROSMsg(*cloud_grid, output);
output.header.stamp = ros::Time::now();
output.header.frame_id = "yrl_cloud_id"; // fixed frame (oblique alignment) from LiDAR
pubPointCloud.publish(output);
// Publish bird_view img
cv_bridge::CvImage cv_bird_view;
cv_bird_view.header.stamp = ros::Time::now();
cv_bird_view.header.frame_id = "out_bev_image";
cv_bird_view.encoding = "bgr8";
cv_bird_view.image = image;
pubImage.publish(cv_bird_view.toImageMsg());
// Output Image
//sensor_msgs::ImagePtr msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", image).toImageMsg();
//pubImage.publish(msg);pubPoin
}
int main(int argc, char** argv)
{
ROS_INFO("Starting LIDAR Node");
ros::init(argc, argv, "lidar_node");
ros::NodeHandle nh;
image_transport::ImageTransport it(nh);
// Setup image
cv::Mat map(IMAGE_HEIGHT, IMAGE_WIDTH, CV_8UC3, cv::Scalar(0, 0, 0));
cv::Mat map2(IMAGE_HEIGHT, IMAGE_WIDTH, CV_8UC3, cv::Scalar(0, 0, 0));
cv::Mat map3(IMAGE_HEIGHT, IMAGE_WIDTH, CV_8UC3, cv::Scalar(0, 0, 0));
// H S V
// image container
heightmap = &map; // default source code (mcshiggings)
hsv = &map2; // added for hSV visualization
bgr = &map3; // for displaying colored Pc
cv::namedWindow(WIN_NAME, WINDOW_AUTOSIZE);
cv::namedWindow(WIN_NAME2, WINDOW_AUTOSIZE);
cv::startWindowThread();
cv::imshow(WIN_NAME, *bgr); // BGR visualization of HSV
cv::imshow(WIN_NAME2, *heightmap); // default visualization
// Setup Image Output Parameters
fnameCounter = 0;
lowest = FLT_MAX;
/* PNG compression param
compression_params.push_back(IMWRITE_PNG_COMPRESSION);
A higher value means a smaller size and longer compression time. Default value is 3.
compression_params.push_back(9); */
// JPG compression param
compression_params.push_back(IMWRITE_JPEG_QUALITY);
// from 0 to 100 (the higher is the better). Default value is 95.
compression_params.push_back(95);
// Setup indicies in point clouds
/*
int index = 0;
double x, y;
for(int i = 0; i < IMAGE_HEIGHT; ++i){
for(int j = 0; j < IMAGE_WIDTH; ++j){
index = i * j;
(void)map_rc2pc(&x, &y, i, j);
cloud_grid->points[index].x = x;
cloud_grid->points[index].y = y;
cloud_grid->points[index].z = (-FLT_MAX)master.log
}
*/
// subscriber and publisher
subPointCloud = nh.subscribe<sensor_msgs::PointCloud2>("/pointcloud", 2, DEM);
pubPointCloud = nh.advertise<sensor_msgs::PointCloud2> ("/heightmap/pointcloud", 1);
pubImage = it.advertise("/out_bev_image",1);
ros::spin();
return 0;
}
Thank you for any advice and suggested solutions.
You can't simply pass the char array to opencv functions to create an image because of how the data is formatted. PointCloud2 data fields are strictly containing information about where a point lives in 3d space(think [x,y,z]); this means nothing in terms of an actual image. Instead you have to first convert the pointcloud into something that better represents an image. Luckily, this already exists. Check out the CloudToImage ROS package.

How tu put B, G and R component value straight into a pixel of cv::Mat? [duplicate]

I have searched internet and stackoverflow thoroughly, but I haven't found answer to my question:
How can I get/set (both) RGB value of certain (given by x,y coordinates) pixel in OpenCV? What's important-I'm writing in C++, the image is stored in cv::Mat variable. I know there is an IplImage() operator, but IplImage is not very comfortable in use-as far as I know it comes from C API.
Yes, I'm aware that there was already this Pixel access in OpenCV 2.2 thread, but it was only about black and white bitmaps.
EDIT:
Thank you very much for all your answers. I see there are many ways to get/set RGB value of pixel. I got one more idea from my close friend-thanks Benny! It's very simple and effective. I think it's a matter of taste which one you choose.
Mat image;
(...)
Point3_<uchar>* p = image.ptr<Point3_<uchar> >(y,x);
And then you can read/write RGB values with:
p->x //B
p->y //G
p->z //R
Try the following:
cv::Mat image = ...do some stuff...;
image.at<cv::Vec3b>(y,x); gives you the RGB (it might be ordered as BGR) vector of type cv::Vec3b
image.at<cv::Vec3b>(y,x)[0] = newval[0];
image.at<cv::Vec3b>(y,x)[1] = newval[1];
image.at<cv::Vec3b>(y,x)[2] = newval[2];
The low-level way would be to access the matrix data directly. In an RGB image (which I believe OpenCV typically stores as BGR), and assuming your cv::Mat variable is called frame, you could get the blue value at location (x, y) (from the top left) this way:
frame.data[frame.channels()*(frame.cols*y + x)];
Likewise, to get B, G, and R:
uchar b = frame.data[frame.channels()*(frame.cols*y + x) + 0];
uchar g = frame.data[frame.channels()*(frame.cols*y + x) + 1];
uchar r = frame.data[frame.channels()*(frame.cols*y + x) + 2];
Note that this code assumes the stride is equal to the width of the image.
A piece of code is easier for people who have such problem. I share my code and you can use it directly. Please note that OpenCV store pixels as BGR.
cv::Mat vImage_;
if(src_)
{
cv::Vec3f vec_;
for(int i = 0; i < vHeight_; i++)
for(int j = 0; j < vWidth_; j++)
{
vec_ = cv::Vec3f((*src_)[0]/255.0, (*src_)[1]/255.0, (*src_)[2]/255.0);//Please note that OpenCV store pixels as BGR.
vImage_.at<cv::Vec3f>(vHeight_-1-i, j) = vec_;
++src_;
}
}
if(! vImage_.data ) // Check for invalid input
printf("failed to read image by OpenCV.");
else
{
cv::namedWindow( windowName_, CV_WINDOW_AUTOSIZE);
cv::imshow( windowName_, vImage_); // Show the image.
}
The current version allows the cv::Mat::at function to handle 3 dimensions. So for a Mat object m, m.at<uchar>(0,0,0) should work.
uchar * value = img2.data; //Pointer to the first pixel data ,it's return array in all values
int r = 2;
for (size_t i = 0; i < img2.cols* (img2.rows * img2.channels()); i++)
{
if (r > 2) r = 0;
if (r == 0) value[i] = 0;
if (r == 1)value[i] = 0;
if (r == 2)value[i] = 255;
r++;
}
const double pi = boost::math::constants::pi<double>();
cv::Mat distance2ellipse(cv::Mat image, cv::RotatedRect ellipse){
float distance = 2.0f;
float angle = ellipse.angle;
cv::Point ellipse_center = ellipse.center;
float major_axis = ellipse.size.width/2;
float minor_axis = ellipse.size.height/2;
cv::Point pixel;
float a,b,c,d;
for(int x = 0; x < image.cols; x++)
{
for(int y = 0; y < image.rows; y++)
{
auto u = cos(angle*pi/180)*(x-ellipse_center.x) + sin(angle*pi/180)*(y-ellipse_center.y);
auto v = -sin(angle*pi/180)*(x-ellipse_center.x) + cos(angle*pi/180)*(y-ellipse_center.y);
distance = (u/major_axis)*(u/major_axis) + (v/minor_axis)*(v/minor_axis);
if(distance<=1)
{
image.at<cv::Vec3b>(y,x)[1] = 255;
}
}
}
return image;
}

How to efficiently merge two overlapping contours into one big contour?

I have a huge image ( about 63000 x 63000 pixels = 3969 Megapixels )
what i have done so far is i decided to make "tiles" of (1024 x 1024) and do my calculations based on these tiles, resulting in an 62 x 62 image tile grid!
(this works out very well and has the advantage of making the image viewable with zoom-in and zoom out, only viewn tiles are downsized for example)
But what i need now are the contours from the huge image!
i use the OpenCV function "findContours" to detect contours on each
one of the tiles.
i have added some overlap in the tiles so i get
overlapping contours ( 1 pixel overlap )
i used the offset parameter
of "findContours" to shift the contours to the right position
into the "virtual total image"
Here are some screenshot's i made from a demo application
What I want is this:
Now my questions:
is it possible to stitch the contours, my worst case is a contour which covers the total image... is there some library that can do this?
is there a library which works on a compressed version of the total image ( like rle for example )
is there a way to make opencv findcontours work on 1 bit binary images ?
Here's the code used by findcontours:
// Surf2DTiledData ...a gobject based class used for 2d tile management and viewing..
Surf2DTiledData* td = (Surf2DTiledData*)in_td;
int nr_hor_tiles = surf2_d_tiled_data_get_nr_hor_tiles(td);
int nr_ver_tiles = surf2_d_tiled_data_get_nr_ver_tiles(td);
int tile_size_x = surf2_d_tiled_data_get_tile_width(td);
int tile_size_y = surf2_d_tiled_data_get_tile_height(td);
contouring_data_obj = surf2_d_tiled_data_get_ContouringData(td);
p_contours = contouring_data_obj->p_contours;
p_border_contours = contouring_data_obj->p_border_contours;
g_return_if_fail(p_border_contours != NULL);
g_return_if_fail(p_contours != NULL);
for (y = 0; y < nr_ver_tiles; y++){
int x;
for (x = 0; x < nr_hor_tiles; x++){
int idx = x + y*nr_hor_tiles;
CvMemStorage *mem = contouring_data_obj->contour_storage[idx];
CvMat _src;
CvSeq *contours = NULL;
uchar* dataBuffer = (uchar*)p_data[x][y];
// the idea is to have some extra space available for the overlap
// detection of contours!
// the extra space is needed for the algorithm to check for
// overlaps of contours later on!
#define VIRT_BORDER_EXTEND 2
int virtual_x = x * tile_size_x - VIRT_BORDER_EXTEND;
int virtual_y = y * tile_size_y - VIRT_BORDER_EXTEND;
int virtual_width = tile_size_x + VIRT_BORDER_EXTEND * 2;
int virtual_height = tile_size_y + VIRT_BORDER_EXTEND * 2;
int x_off = -VIRT_BORDER_EXTEND;
int y_off = -VIRT_BORDER_EXTEND;
if (virtual_x < 0) {
virtual_width += virtual_x;
virtual_x = 0;
x_off = 0;
}
if (virtual_y < 0) {
virtual_height += virtual_y;
virtual_y = 0;
y_off = 0;
}
if ((virtual_x + virtual_width) > (nr_hor_tiles*tile_size_x)) {
virtual_width = nr_hor_tiles*tile_size_x - virtual_x;
}
if ((virtual_y + virtual_height) > (nr_ver_tiles*tile_size_y)) {
virtual_height = nr_ver_tiles*tile_size_y - virtual_y;
}
CvMat* _roi_mat = get_roi_mat(td,
virtual_x, virtual_y,
virtual_width, virtual_height);
// Use either this:
//mem = cvCreateMemStorage(0);
if (_roi_mat){
// CV_LINK_RUNS => different algorithm!!!!
int tile_off_x = tile_size_x * x;
int tile_off_y = tile_size_y * y;
CvPoint contour_shift = cvPoint(x_off + tile_off_x, y_off + tile_off_y);
int n = cvFindContours(_roi_mat, mem, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, contour_shift);
cvReleaseMat(&_roi_mat);
p_contours[x][y] = contours;
}
//cvReleaseMemStorage(&mem);
}
}
later i used opengl to make textures out of the tiles and for every tile there is a quad !
the opencv contours are not drawn as this could be too slow for now, but i draw their bounding boxes... which are drawn in opengl too..

Drawing spectrum of an image in C++ (fftw, OpenCV)

I'm trying to create a program that will draw a 2d greyscale spectrum of a given image. I'm using OpenCV and FFTW libraries. By using tips and codes from the internet and modifying them I've managed to load an image, calculate fft of this image and recreate the image from the fft (it's the same). What I'm unable to do is to draw the fourier spectrum itself. Could you please help me?
Here's the code (less important lines removed):
/* Copy input image */
/* Create output image */
/* Allocate input data for FFTW */
in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
dft = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
/* Create plans */
plan_f = fftw_plan_dft_2d(w, h, in, dft, FFTW_FORWARD, FFTW_ESTIMATE);
/* Populate input data in row-major order */
for (i = 0, k = 0; i < h; i++)
{
for (j = 0; j < w; j++, k++)
{
in[k][0] = ((uchar*)(img1->imageData + i * img1->widthStep))[j];
in[k][1] = 0.;
}
}
/* forward DFT */
fftw_execute(plan_f);
/* spectrum */
for (i = 0, k = 0; i < h; i++)
{
for (j = 0; j < w; j++, k++)
((uchar*)(img2->imageData + i * img2->widthStep))[j] = sqrt(pow(dft[k][0],2) + pow(dft[k][1],2));
}
cvShowImage("iplimage_dft(): original", img1);
cvShowImage("iplimage_dft(): result", img2);
cvWaitKey(0);
/* Free memory */
}
The problem is in the "Spectrum" section. Instead of a spectrum I get some noise. What am I doing wrong? I would be grateful for your help.
You need to draw magnitude of spectrum. here is the code.
void ForwardFFT(Mat &Src, Mat *FImg)
{
int M = getOptimalDFTSize( Src.rows );
int N = getOptimalDFTSize( Src.cols );
Mat padded;
copyMakeBorder(Src, padded, 0, M - Src.rows, 0, N - Src.cols, BORDER_CONSTANT, Scalar::all(0));
// Создаем комплексное представление изображения
// planes[0] содержит само изображение, planes[1] его мнимую часть (заполнено нулями)
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexImg;
merge(planes, 2, complexImg);
dft(complexImg, complexImg);
// После преобразования результат так-же состоит из действительной и мнимой части
split(complexImg, planes);
// обрежем спектр, если у него нечетное количество строк или столбцов
planes[0] = planes[0](Rect(0, 0, planes[0].cols & -2, planes[0].rows & -2));
planes[1] = planes[1](Rect(0, 0, planes[1].cols & -2, planes[1].rows & -2));
Recomb(planes[0],planes[0]);
Recomb(planes[1],planes[1]);
// Нормализуем спектр
planes[0]/=float(M*N);
planes[1]/=float(M*N);
FImg[0]=planes[0].clone();
FImg[1]=planes[1].clone();
}
void ForwardFFT_Mag_Phase(Mat &src, Mat &Mag,Mat &Phase)
{
Mat planes[2];
ForwardFFT(src,planes);
Mag.zeros(planes[0].rows,planes[0].cols,CV_32F);
Phase.zeros(planes[0].rows,planes[0].cols,CV_32F);
cv::cartToPolar(planes[0],planes[1],Mag,Phase);
}
Mat LogMag;
LogMag.zeros(Mag.rows,Mag.cols,CV_32F);
LogMag=(Mag+1);
cv::log(LogMag,LogMag);
//---------------------------------------------------
imshow("Логарифм амплитуды", LogMag);
imshow("Фаза", Phase);
imshow("Результат фильтрации", img);
Can you try to do the IFFT step and see if you recover the original image ? then , you can check step by step where is your problem. Another solution to find the problem is to do this process with a small matrix predefined by you ,and calculate it FFT in MATLAB, and check step by step, it worked for me!

Masking frequencies in a Fourier Transform

I'm messing around with OpenCV, and am trying to do some of the same stuff signal processing stuff I've done in MatLab. I'm looking to mask out some frequencies, so I have constructed a matrix which will do this. The problem is that there seem to be a few more steps in OpenCV than in Matlab to accomplish this.
In Matlab, it's simple enough:
F = fft2(image);
smoothF = F .* mask; // multiply FT by mask
smooth = ifft2(smoothF); // do inverse FT
But I'm having trouble doing the same in OpenCV. The DFT leaves me with a 2 channel image, so I've split the image, multiplied by the mask, merged it back, and then perform the inverse DFT. However, I got a weird result in my final image. I'm pretty sure I'm missing something...
CvMat* maskImage(CvMat* im, int maskWidth, int maskHeight)
{
CvMat* mask = cvCreateMat(im->rows, im->cols, CV_64FC1);
cvZero(mask);
int cx, cy;
cx = mask->cols/2;
cy = mask->rows/2;
int left_x = cx - maskWidth;
int right_x = cx + maskWidth;
int top_y = cy + maskHeight;
int bottom_y = cy - maskHeight;
//create mask
for(int i = bottom_y; i < top_y; i++)
{
for(int j = left_x; j < right_x; j++)
{
cvmSet(mask,i,j,1.0f); // Set M(i,j)
}
}
cvShiftDFT(mask, mask);
IplImage* maskImage, stub;
maskImage = cvGetImage(mask, &stub);
cvNamedWindow("mask", 0);
cvShowImage("mask", maskImage);
CvMat* real = cvCreateMat(im->rows, im->cols, CV_64FC1);
CvMat* imag = cvCreateMat(im->rows, im->cols, CV_64FC1);
cvSplit(im, imag, real, NULL, NULL);
cvMul(real, mask, real);
cvMul(imag, mask, imag);
cvMerge(real, imag, NULL, NULL, im);
IplImage* maskedImage;
maskedImage = cvGetImage(imag, &stub);
cvNamedWindow("masked", 0);
cvShowImage("masked", maskedImage);
return im;
}
Any reason you are merging the real and imaginary components in the reverse order?

Resources