I had posted before on Stack Overflow for the question how exactly to rotate a BMP image in a c++ program. Now, however, I have more to show in regards of my progress.
I was wondering how (or why) my program will not output the image after I do the image calculation:
void BMPImage::Rotate45Left(float point1, float point2, float point3)
{
float radians = (2 * 3.1416*45) / 360;
float cosine = (float)cos(radians);
float sine = (float)sin(radians);
float point1Xtreme = 0;
float point1Yearly = 0;
float point2Xtreme = 0;
float point2Yearly = 0;
float point3Xtreme = 0;
float point3Yearly = 0;
int SourceBitmapHeight = m_BIH.biHeight;
int SourceBitmapWidth = m_BIH.biWidth;
point1Xtreme = (-m_BIH.biHeight*sine);
point1Yearly = (m_BIH.biHeight*cosine);
point2Xtreme = (m_BIH.biWidth*cosine - m_BIH.biHeight*sine);
point2Yearly = (m_BIH.biHeight*cosine + m_BIH.biWidth*sine);
point3Xtreme = (m_BIH.biWidth*cosine);
point3Yearly = (m_BIH.biWidth*sine);
float Minx = min(0, min(point1Xtreme, min(point2Xtreme, point3Xtreme)));
float Miny = min(0, min(point1Yearly, min(point2Yearly, point3Yearly)));
float Maxx = max(point1Xtreme, max(point2Xtreme, point3Xtreme));
float Maxy = max(point1Yearly, max(point2Yearly, point3Yearly));
int FinalBitmapWidth = (int)ceil(fabs(Maxx) - Minx);
int FinalBitmapHeight = (int)ceil(fabs(Maxy) - Miny);
FinalBitmapHeight = m_BIH.biHeight;
FinalBitmapWidth = m_BIH.biWidth;
int finalBitmap;
If anyone has any helpful pointers, that would be great.
I should mention that:
I can't use other outside libraries for the purpose of this program
It is a small image processing program, which has a menu system
Image transformation is usually done by projecting a target pixel onto a source pixel then calculating the value for that target pixel. This way you can easily incorporate different interpolation methods.
template <typename T>
struct Image {
Image(T* data, size_t rows, size_t cols) :
data_(data), rows_(rows), cols_(cols) {}
T* data_;
size_t rows_;
size_t cols_;
T& operator()(size_t row, size_t col) {
return data_[col + row * cols_];
}
};
template <typename T>
T clamp(T value, T lower_bound, T upper_bound) {
value = std::min(std::max(value, lower_bound), upper_bound);
}
void rotate_image(Image const &src, Image &dst, float ang) {
// Affine transformation matrix
// H = [a, b, c]
// [d, e, f]
// Remember, we are transforming from destination to source,
// thus the negated angle.
float H[] = {cos(-ang), -sin(-ang), dst.cols_/2 - src.cols_*cos(-ang)/2,
sin(-ang), cos(-ang), dst.rows_/2 - src.rows_*cos(-ang)/2};
for (size_t row = 0; row < dst.rows_; ++row) {
for (size_t col = 0; col < dst.cols_; ++cols) {
int src_col = round(H[0] * col + H[1] * row + H[2]);
src_col = clamp(src_col, 0, src.cols_ - 1);
int src_row = round(H[3] * col + H[4] * row + H[5]);
src_row = clamp(src_row, 0, src.rows_ - 1);
dst(row, col) = src(src_row, src_col);
}
}
}
The above method rotates an image with an arbitrary angle and uses nearest-neighbour interpolation. I typed it directly into stackoverflow, so it is full of bugs; nonetheless, the concept is there.
Related
I need a little help with an appointment of mine.
My professor gave us this class (and a Color class that has RGB colors as float variables inside) now I have to implement the functions shown in the header.
#include "color.h"
#include <assert.h>
Color::Color()
{
R = 255;
G = 255;
B = 255;
}
Color::Color( float r, float g, float b)
{
R = r;
G = g;
B = b;
}
Color Color::operator*(const Color& c) const
{
return Color(R * c.R, G * c.G, B * c.B );
}
Color Color::operator*(const float Factor) const
{
return Color(R * Factor, G * Factor, B * Factor);
}
Color Color::operator+(const Color& c) const
{
return Color(R + c.R, G + c.G, B + c.B);
}
Color& Color::operator+=(const Color& c)
{
R += c.R;
G += c.G;
B += c.B;
return *this;
}
Header RGBImage
The Konstruktor should create a 2DImage memory to save width*height Pixel. (Dunno what the best solution here would be? Array of type Color or a Vector?)
My first guess was this:
RGBImage class (i just got empty methodes)
#include "rgbimage.h"
#include "color.h"
#include "assert.h"
using namespace std;
RGBImage::RGBImage( unsigned int Width, unsigned int Height)
{
m_Image = new Color[Width * Height]; // probably wrong?
m_Width = Width;
m_Height = Height;
}
RGBImage::~RGBImage()
{
}
void RGBImage::setPixelColor( unsigned int x, unsigned int y, const Color& c)
{
if (x < width() && y < height())
{
// get offset of pixel in 2D array.
const unsigned offset = (y * width()) + x;
m_Image[offset] = c;
}
}
const Color& RGBImage::getPixelColor( unsigned int x, unsigned int y) const
{
if (x < width() && y < height())
{
// get offset of pixel in 2D array.
const unsigned offset = (y * width()) + x;
return m_Image[offset];
}
}
unsigned int RGBImage::width() const
{
return this->m_Width;
}
unsigned int RGBImage::height() const
{
return this->m_Height;
}
unsigned char RGBImage::convertColorChannel( float v)
{
if (v < 0) {
v = 0;
}
else if (v > 1) {
v = 1;
}
int convertedColorChannel = v * 255;
return convertedColorChannel;
}
bool RGBImage::saveToDisk( const char* Filename)
{
// TODO: add your code
return false; // dummy (remove)
}
afterward, I realized Color arrays are no variable of the Class RGBImage per definition of his Header so how can I save the Pixel in an RGBImage, or is it a viable option to continue this approach. If so how can I set the Color in a setter? tryed it with this.bildspeicher[x] didnt work...
I'm fairly new to Programming, and this is my first question on this platform, so sorry if I stated my problem poorly.
RGB data is usually stored in a one-dimensional array, as is the case for RGBImage. The pixels are packed line-by-line, starting either from the bottom left, or the top-left of the image. The orientation should not affect the functions accessing individual rows of pixels, but will affect how the calling application handles the pixel data.
For accessing individual pixels, use this formula:
// ...
inline void setPixel(unsigned x, unsigned y, const Color& clr)
{
if (x < width() && y < height()) // ALWAYS crop!
{
// get offset of pixel in 2D array.
const unsigned offset = (y * width()) + x;
m_image[offset] = clr;
}
}
I've put the formula in its own line of code, but this is usually done as a one-liner.
The same formula can be used for reading pixels. Note that this formula assumes the lines of pixels have no alignment. Some bitmap fprmats do require 2 of 4 byte alignment of each line within the 2d array, in which case you'd multiply y by alignedWidth() instead of width()).
I want to implement 2D convolution function in C++ by myself, without using filter2D(). I'm trying to iterate all pixels of input image and kernel, then, assign new value to each pixel of dst.
However, I got this error.
Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
I found that this error tells I'm accessing nullptr, but I could not solve the problem. Here is my c++ code.
cv::Mat_<float> spatialConvolution(const cv::Mat_<float>& src, const cv::Mat_<float>& kernel)
{
// declare variables
Mat_<float> dst;
Mat_<float> flipped_kernel;
float tmp = 0.0;
// flip kernel
flip(kernel, flipped_kernel, -1);
// multiply and integrate
// input rows
for(int i=0;i<src.rows;i++){
// input columns
for(int j=0;j<src.cols;j++){
// kernel rows
for(int k=0;k<flipped_kernel.rows;k++){
// kernel columns
for(int l=0;l<flipped_kernel.cols;l++){
tmp += src.at<float>(i,j) * flipped_kernel.at<float>(k,l);
}
}
dst.at<float>(i,j) = tmp;
}
}
return dst.clone();
}
To simplify let's suppose you have kernel 3x3
k(0,0) k(0,1) k(0,2)
k(1,0) k(1,1) k(1,2)
k(2,0) k(2,1) k(2,2)
to calculate convolution you are scanning input image (marked as I) from left to fright, from top to bottom
and for every pixel of input image you assign one value calculated from the formula below:
newValue(y,x) = I(y-1,x-1) * k(0,0) + I(y-1,x) * k(0,1) + I(y-1,x+1) * k(0,2)
+ I(y,x-1) * k(1,0) + I(y,x) * k(1,1) + I(y,x+1) * k(1,2) +
+ I(y+1,x-1) * k(2,0) + I(y+1,x) * k(2,1) + I(y+1,x+1) * k(2,2)
------------------x------------>
|
|
| [k(0,0) k(0,1) k(0,2)]
y [k(1,0) k(1,1) k(1,2)]
| [k(2,0) k(2,1) k(2,2)]
|
(y,x) of input Image (I) is anchor point of kernel, to assign new value to I(y,x)
you need to multiply every k coefficient by corresponding point of I - your code doesn't do it.
First you need to create dst matrix with dimenstion as original image, and the same type of pixel.
Then you need to rewrite your loops to reflect formula described above:
cv::Mat_<float> spatialConvolution(const cv::Mat_<float>& src, const cv::Mat_<float>& kernel)
{
Mat dst(src.rows,src.cols,src.type());
Mat_<float> flipped_kernel;
flip(kernel, flipped_kernel, -1);
const int dx = kernel.cols / 2;
const int dy = kernel.rows / 2;
for (int i = 0; i<src.rows; i++)
{
for (int j = 0; j<src.cols; j++)
{
float tmp = 0.0f;
for (int k = 0; k<flipped_kernel.rows; k++)
{
for (int l = 0; l<flipped_kernel.cols; l++)
{
int x = j - dx + l;
int y = i - dy + k;
if (x >= 0 && x < src.cols && y >= 0 && y < src.rows)
tmp += src.at<float>(y, x) * flipped_kernel.at<float>(k, l);
}
}
dst.at<float>(i, j) = saturate_cast<float>(tmp);
}
}
return dst.clone();
}
Your memory access error is presumably happening due to the line:
dst.at<float>(i,j) = tmp;
because dst is not initialized. You can't assign something to that index of the matrix if it has no size/data. Instead, initialize the matrix first, as Mat_<float> is a declaration, not an initialization. Use one of the initializations where you can specify a cv::Size or the rows/columns from the different constructors for Mat (see the docs). For example, you can initialize dst with:
Mat dst{src.size(), src.type()};
I need to read pixels from two parts (with same width and height) of image ( e.g. squares ([0,0], [300, 300]) and ([400,0], [700,300])) and make difference for each pixel.
This is C (pseudo)code:
/**
* #param img Input image
* #param pos Integer position of top left corner of the second square (in this case 400)
*/
double getSum(Image& img, int pos)
{
const int width_of_cut = 300;
int right_bottom = pos + width;
Rgb first, second;
double ret_val = 0.0;
for(int i=0; i < width_of_cut; i++)
{
for(int j=0; j < width_of_cut; j++)
{
first = img.getPixel( i, j );
second = img.getPixel( i + pos, j );
ret_val += ( first.R - second.R ) +
( first.G - second.G ) +
( first.B - second.B );
}
}
return ret_val;
}
But my kernel (with same arguments and the __global float* output is set to 0.0 in host code) is giving me completely different values:
__constant sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |
CLK_ADDRESS_CLAMP_TO_EDGE |
CLK_FILTER_NEAREST;
__kernel void getSum( __read_only image2d_t input,
const int x_coord,
__global float* output )
{
int width = get_image_width( input );
int height = get_image_height( input );
int2 pixelcoord = (int2) (get_global_id(0), get_global_id(1)); // image coordinates
const int width_of_cut = 300;
const int right_bottom = x_coord + width_of_cut;
int a,b;
a = (int)(pixelcoord.x + x_coord);
b = pixelcoord.y;
if( a < right_bottom && b < width_of_cut )
{
float4 first = read_imagef(input, sampler, pixelcoord);
float4 second = read_imagef(input, sampler, (int2)(a,b));
output[get_global_id(0)] += ((first.x - second.x) +
(first.y - second.y) +
(first.z - second.z));
}
}
I am new to OpenCL and I have no idea what am I doing wrong.
Update (1d image):
I changed the kernel code. Now I'm reading an 1d image in one loop, but I'm still not getting the correct values. I'm not sure that I know, how to read pixels from 1d image correctly.
__kernel void getSum( __read_only image1d_t input,
const int x_coord,
__global float* output,
const int img_width )
{
const int width_of_cut = 300;
int i = (int)(get_global_id(0));
for(int j=0; j < width_of_cut; j++)
{
int f = ( img_width*i + j );
int s = f + x_coord;
float4 first = read_imagef( input, sampler, f ); //pixel from 1st sq.
float4 second = read_imagef( input, sampler, s ); //pixel from 2nd sq.
output[get_global_id(0)] += ((first.x - second.x) +
(first.y - second.y) +
(first.z - second.z));
}
}
Race condition.
All vertical work items are accessing the same output memory (output[get_global_id(0)] +=) and not atomically. Therefore the result are likely incorrect (e.g., two threads read the same value, add something to it, and write it back. Only one wins).
If your device supports it, you could make this an atomic operation, but it would be slow. You'd be better off running a 1D kernel that has a loop accumulating these vertically (so, the j loop from your C example).
I am making an application that uses OCR and I am using OpenCV to threshold the image to improve the OCR results, I have gotten pretty good results but I want to know if anyone has any suggestions for improvement.
Here is what I've done so far:
// Convert to grayscale.
cv::cvtColor(cvMat, cvMat, CV_RGB2GRAY);
// Apply adaptive threshold.
cv::adaptiveThreshold(cvMat, cvMat, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 3, 5);
// Attempt to sharpen the image.
cv::GaussianBlur(cvMat, cvMat, cv::Size(0, 0), 3);
cv::addWeighted(cvMat, 1.5, cvMat, -0.5, 0, cvMat);
Let me know if you have any suggestions to improve results, thanks.
Sample Images:
After:
One of the best algorithms for thresholding problem in the OCR field is sauvola method.You can use the below code.
#ifndef _THRESHOLDER
#define _THRESHOLDER
#include <cv.h>
#include "type.h"
using namespace cv;
enum class BhThresholdMethod{OTSU,NIBLACK,SAUVOLA,WOLFJOLION};
class BhThresholder
{
public :
void doThreshold(InputArray src ,OutputArray dst,const BhThresholdMethod &method);
private:
};
#endif //_THRESHOLDER
thresholder.cpp
#include "stdafx.h"
#define uget(x,y) at<unsigned char>(y,x)
#define uset(x,y,v) at<unsigned char>(y,x)=v;
#define fget(x,y) at<float>(y,x)
#define fset(x,y,v) at<float>(y,x)=v;
// *************************************************************
// glide a window across the image and
// create two maps: mean and standard deviation.
// *************************************************************
//#define BINARIZEWOLF_VERSION "2.3 (February 26th, 2013)"
double calcLocalStats (Mat &im, Mat &map_m, Mat &map_s, int win_x, int win_y) {
double m,s,max_s, sum, sum_sq, foo;
int wxh = win_x / 2;
int wyh = win_y / 2;
int x_firstth = wxh;
int y_lastth = im.rows-wyh-1;
int y_firstth= wyh;
double winarea = win_x*win_y;
max_s = 0;
for (int j = y_firstth ; j<=y_lastth; j++)
{
// Calculate the initial window at the beginning of the line
sum = sum_sq = 0;
for (int wy=0 ; wy<win_y; wy++)
for (int wx=0 ; wx<win_x; wx++) {
foo = im.uget(wx,j-wyh+wy);
sum += foo;
sum_sq += foo*foo;
}
m = sum / winarea;
s = sqrt ((sum_sq - (sum*sum)/winarea)/winarea);
if (s > max_s)
max_s = s;
map_m.fset(x_firstth, j, m);
map_s.fset(x_firstth, j, s);
// Shift the window, add and remove new/old values to the histogram
for (int i=1 ; i <= im.cols -win_x; i++) {
// Remove the left old column and add the right new column
for (int wy=0; wy<win_y; ++wy) {
foo = im.uget(i-1,j-wyh+wy);
sum -= foo;
sum_sq -= foo*foo;
foo = im.uget(i+win_x-1,j-wyh+wy);
sum += foo;
sum_sq += foo*foo;
}
m = sum / winarea;
s = sqrt ((sum_sq - (sum*sum)/winarea)/winarea);
if (s > max_s)
max_s = s;
map_m.fset(i+wxh, j, m);
map_s.fset(i+wxh, j, s);
}
}
return max_s;
}
void NiblackSauvolaWolfJolion (InputArray _src, OutputArray _dst,const BhThresholdMethod &version,int winx, int winy, double k, double dR) {
Mat src = _src.getMat();
Mat dst = _dst.getMat();
double m, s, max_s;
double th=0;
double min_I, max_I;
int wxh = winx/2;
int wyh = winy/2;
int x_firstth= wxh;
int x_lastth = src.cols-wxh-1;
int y_lastth = src.rows-wyh-1;
int y_firstth= wyh;
int mx, my;
// Create local statistics and store them in a double matrices
Mat map_m = Mat::zeros (src.size(), CV_32FC1);
Mat map_s = Mat::zeros (src.size(), CV_32FC1);
max_s = calcLocalStats (src, map_m, map_s, winx, winy);
minMaxLoc(src, &min_I, &max_I);
Mat thsurf (src.size(), CV_32FC1);
// Create the threshold surface, including border processing
// ----------------------------------------------------
for (int j = y_firstth ; j<=y_lastth; j++) {
// NORMAL, NON-BORDER AREA IN THE MIDDLE OF THE WINDOW:
for (int i=0 ; i <= src.cols-winx; i++) {
m = map_m.fget(i+wxh, j);
s = map_s.fget(i+wxh, j);
// Calculate the threshold
switch (version) {
case BhThresholdMethod::NIBLACK:
th = m + k*s;
break;
case BhThresholdMethod::SAUVOLA:
th = m * (1 + k*(s/dR-1));
break;
case BhThresholdMethod::WOLFJOLION:
th = m + k * (s/max_s-1) * (m-min_I);
break;
default:
cerr << "Unknown threshold type in ImageThresholder::surfaceNiblackImproved()\n";
exit (1);
}
thsurf.fset(i+wxh,j,th);
if (i==0) {
// LEFT BORDER
for (int i=0; i<=x_firstth; ++i)
thsurf.fset(i,j,th);
// LEFT-UPPER CORNER
if (j==y_firstth)
for (int u=0; u<y_firstth; ++u)
for (int i=0; i<=x_firstth; ++i)
thsurf.fset(i,u,th);
// LEFT-LOWER CORNER
if (j==y_lastth)
for (int u=y_lastth+1; u<src.rows; ++u)
for (int i=0; i<=x_firstth; ++i)
thsurf.fset(i,u,th);
}
// UPPER BORDER
if (j==y_firstth)
for (int u=0; u<y_firstth; ++u)
thsurf.fset(i+wxh,u,th);
// LOWER BORDER
if (j==y_lastth)
for (int u=y_lastth+1; u<src.rows; ++u)
thsurf.fset(i+wxh,u,th);
}
// RIGHT BORDER
for (int i=x_lastth; i<src.cols; ++i)
thsurf.fset(i,j,th);
// RIGHT-UPPER CORNER
if (j==y_firstth)
for (int u=0; u<y_firstth; ++u)
for (int i=x_lastth; i<src.cols; ++i)
thsurf.fset(i,u,th);
// RIGHT-LOWER CORNER
if (j==y_lastth)
for (int u=y_lastth+1; u<src.rows; ++u)
for (int i=x_lastth; i<src.cols; ++i)
thsurf.fset(i,u,th);
}
cerr << "surface created" << endl;
for (int y=0; y<src.rows; ++y)
for (int x=0; x<src.cols; ++x)
{
if (src.uget(x,y) >= thsurf.fget(x,y))
{
dst.uset(x,y,255);
}
else
{
dst.uset(x,y,0);
}
}
}
void BhThresholder::doThreshold(InputArray _src ,OutputArray _dst,const BhThresholdMethod &method)
{
Mat src = _src.getMat();
int winx = 0;
int winy = 0;
float optK=0.5;
if (winx==0 || winy==0) {
winy = (int) (2.0 * src.rows - 1)/3;
winx = (int) src.cols-1 < winy ? src.cols-1 : winy;
// if the window is too big, than we asume that the image
// is not a single text box, but a document page: set
// the window size to a fixed constant.
if (winx > 100)
winx = winy = 40;
}
// Threshold
_dst.create(src.size(), CV_8UC1);
Mat dst = _dst.getMat();
//medianBlur(src,dst,5);
GaussianBlur(src,dst,Size(5,5),0);
//#define _BH_SHOW_IMAGE
#ifdef _BH_DEBUG
#define _BH_SHOW_IMAGE
#endif
//medianBlur(src,dst,7);
switch (method)
{
case BhThresholdMethod::OTSU :
threshold(dst,dst,128,255,CV_THRESH_OTSU);
break;
case BhThresholdMethod::SAUVOLA :
case BhThresholdMethod::WOLFJOLION :
NiblackSauvolaWolfJolion (src, dst, method, winx, winy, optK, 128);
}
bitwise_not(dst,dst);
#ifdef _BH_SHOW_IMAGE
#undef _BH_SHOW_IMAGE
#endif
}
Here is comparsion table for thresholding methods: http://clweb.csa.iisc.ernet.in/rahulsharma/binarize/set1.php?id=set1%2Fimage00b
A few thoughts:
Since you're starting with a rectangular object that may be viewed at a non-normal angle, use an affine transform to warp the image so that it appears rectangular with right angle corners.
Before the affine transform, you should probably remove barrel distortion (the curviness of the card edges).
Consider using an adaptive threshold rather than a simple global binarization threshold.
If you can find a proper OCR algorithm that doesn't require binary images, use that. Although binarization will work well for black text on a white background, in general binarization presents a lot of problems if you want to achieve high accuracy (i.e., character recognition approaching 98%+ for arbitrary strings of characters)
Try to sample with better resolution.
I am using Eigen library for my project. I am searching how to remove a certain row or column from the given matrix in Eigen. I am not successful.
MatrixXd A = X1 X2 X3 X4
Y1 Y2 Y3 Y4
Z1 Z2 Z3 Z4
A1 A2 A3 A4
MatrixXd Atransform = X1 X2 X4
Y1 Y2 Y4
Z1 Z2 Z4
A1 A2 A4
enter code here
other than iterating through whole matrix or by using block operations on matrix A . Is there a method to do it simply.
Using the block functions is a bit cleaner:
void removeRow(Eigen::MatrixXd& matrix, unsigned int rowToRemove)
{
unsigned int numRows = matrix.rows()-1;
unsigned int numCols = matrix.cols();
if( rowToRemove < numRows )
matrix.block(rowToRemove,0,numRows-rowToRemove,numCols) = matrix.block(rowToRemove+1,0,numRows-rowToRemove,numCols);
matrix.conservativeResize(numRows,numCols);
}
void removeColumn(Eigen::MatrixXd& matrix, unsigned int colToRemove)
{
unsigned int numRows = matrix.rows();
unsigned int numCols = matrix.cols()-1;
if( colToRemove < numCols )
matrix.block(0,colToRemove,numRows,numCols-colToRemove) = matrix.block(0,colToRemove+1,numRows,numCols-colToRemove);
matrix.conservativeResize(numRows,numCols);
}
You can do it a lot easier and shorter by using Eigen 3.3.0+(released 2016.08):
vector<int> indicesToKeep = vector<int>{ 1, 2, 3 };
VectorXi indicesToKeepVector = VectorXi(indicesToKeep.data(), indicesToKeep.size());
MatrixXf matrix = MatrixXf(); // your data should be here!
matrix = matrix(Eigen::placeholders::all, indicesToKeepVector); // select columns you want to keep(indicesToKeep), discard others
matrix = matrix(indicesToKeepVector, Eigen::placeholders::all); // select rows you want to keep(indicesToKeep), discard others
matrix = matrix(Eigen::seq(5, 10), Eigen::placeholders::all); // keep rows from 5 to 10
matrix = matrix(Eigen::placeholders::all, Eigen::seq(5, 10)); // keep columns from 5 to 10
matrix = matrix(Eigen::seqN(5, 5), Eigen::placeholders::all); // keep rows from 5 to 10
matrix = matrix(Eigen::placeholders::all, Eigen::seqN(5, 5)); // keep columns from 5 to 10
To improve Andrew's answer, use bottomRows/rightCols.
void removeRow(Eigen::MatrixXd& matrix, unsigned int rowToRemove)
{
unsigned int numRows = matrix.rows()-1;
unsigned int numCols = matrix.cols();
if( rowToRemove < numRows )
matrix.block(rowToRemove,0,numRows-rowToRemove,numCols) = matrix.bottomRows(numRows-rowToRemove);
matrix.conservativeResize(numRows,numCols);
}
void removeColumn(Eigen::MatrixXd& matrix, unsigned int colToRemove)
{
unsigned int numRows = matrix.rows();
unsigned int numCols = matrix.cols()-1;
if( colToRemove < numCols )
matrix.block(0,colToRemove,numRows,numCols-colToRemove) = matrix.rightCols(numCols-colToRemove);
matrix.conservativeResize(numRows,numCols);
}
You may find the following static version better for certain uses (and more in-line with the spirit of Eigen's compile-time efficiency). In this case, you will be creating a new matrix without the row. A similar function can be constructed for columns using .leftCols() .rightCols()
template<typename T>
inline constexpr auto removeRow(const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>& matrix, const int& rowNum)
{
return (Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>(matrix.rows() - 1, matrix.cols())
<< static_cast<Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>>(matrix.topRows(rowNum - 1)),
static_cast<Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>>(matrix.bottomRows(matrix.rows() - rowNum))).finished();
}
Enjoy!
I know it is an old question but it seems that Eigen now support creating a submatrix defined by the rows and columns indexed:
http://eigen.tuxfamily.org/bz/show_bug.cgi?id=329
http://eigen.tuxfamily.org/dox-devel/classEigen_1_1DenseBase.html#a0b44220621cd59a75cd0f48cc499518f
It is just not in the documentation it seems...
inline Eigen::MatrixXd removeMatrixRow(const Eigen::MatrixXd original_matrix, const int row_to_remove)
{
// New matrix has one fewer rows
Eigen::MatrixXd new_matrix(original_matrix.rows()-1, original_matrix.cols());
// Track rows in new matrix. Skip one at row_to_remove.
int row_to_fill = 0;
for (int orig_matrix_row = 0; orig_matrix_row < original_matrix.rows(); ++orig_matrix_row)
{
if (orig_matrix_row != row_to_remove)
{
new_matrix.row(row_to_fill) = original_matrix.row(orig_matrix_row);
++row_to_fill;
}
}
return new_matrix;
}
I'm very new in c++ but this code works in may application.
It works only for full dynamic matrixs but can adapt it.
If anyone has a better way please show me i really want to learn.
template<typename ScalarType>
void MatrixXdRemoveCol(Eigen::Matrix<ScalarType,-1,-1,0,-1,-1> *mat, int colindex)
{
Eigen::Matrix<ScalarType,-1,-1,0,-1,-1> *auxmat = new Eigen::Matrix<ScalarType,-1,-1,0,-1,-1>;
*auxmat = *mat;
mat->resize(mat->rows(),mat->cols()-1);
int rightColsSize = auxmat->cols()-colindex-1;
mat->leftCols(colindex) = auxmat->leftCols(colindex);
mat->rightCols(rightColsSize) = auxmat->rightCols(rightColsSize);
}
template<typename ScalarType>
void MatrixXdRemoveCols(Eigen::Matrix<ScalarType,-1,-1,0,-1,-1> *mat, std::vector<int>* cols)
{
for(auto iter = cols->rbegin();iter != cols->rend();iter++)
MatrixXdRemoveCol<ScalarType>(mat,*iter);
}
template<typename ScalarType>
void MatrixXdRemoveRow(Eigen::Matrix<ScalarType,-1,-1,0,-1,-1> *mat, int rowindex)
{
Eigen::Matrix<ScalarType,-1,-1,0,-1,-1> *auxmat = new Eigen::Matrix<ScalarType,-1,-1,0,-1,-1>;
*auxmat = *mat;
mat->resize(mat->rows()-1,mat->cols());
int BottomRowsSize = auxmat->rows()-rowindex-1;
mat->topRows(rowindex) = auxmat->topRows(rowindex);
mat->bottomRows(BottomRowsSize) = auxmat->bottomRows(BottomRowsSize);
}