从 cv mat 中删除行或列的最佳方法是啥
Posted
技术标签:
【中文标题】从 cv mat 中删除行或列的最佳方法是啥【英文标题】:what is the best way to remove a row or col from a cv mat从 cv mat 中删除行或列的最佳方法是什么 【发布时间】:2015-04-17 10:32:44 【问题描述】:假设我有一个 mat 对象,如下所示:
mat =
[75, 97, 66, 95, 15, 22;
24, 21, 71, 72, 34, 66;
21, 69, 88, 72, 64, 1;
26, 47, 26, 40, 95, 24;
70, 37, 9, 83, 16, 83];
我想从中删除一行,说第二行有这样的垫子:
[75, 97, 66, 95, 15, 22;
21, 69, 88, 72, 64, 1;
26, 47, 26, 40, 95, 24;
70, 37, 9, 83, 16, 83]
或删除一个 col 说 col 3:
[75, 97, 95, 15, 22;
24, 21, 72, 34, 66;
21, 69, 72, 64, 1;
26, 47, 40, 95, 24;
70, 37, 83, 16, 83]
最快的方法是什么?我可以将矩阵分解为 ROI,然后将它们相互合并,但是有没有更好的方法?
【问题讨论】:
【参考方案1】:我测试了两种方式:
使用cv::Rect
和cv::Mat::copyTo
:
// Removing a row
cv::Mat matIn; // Matrix of which a row will be deleted.
int row; // Row to delete.
int col; // Column to delete.
cv::Mat matOut; // Result: matIn less that one row.
if ( row > 0 ) // Copy everything above that one row.
cv::Rect rect( 0, 0, size.width, row );
matIn( rect ).copyTo( matOut( rect ) );
if ( row < size.height - 1 ) // Copy everything below that one row.
cv::Rect rect1( 0, row + 1, size.width, size.height - row - 1 );
cv::Rect rect2( 0, row, size.width, size.height - row - 1 );
matIn( rect1 ).copyTo( matOut( rect2 ) );
// Removing a column
if ( col > 0 ) // Copy everything left of that one column.
cv::Rect rect( 0, 0, col, size.height );
matIn( rect ).copyTo( matOut( rect ) );
if ( col < size.width - 1 ) // Copy everything right of that one column.
cv::Rect rect1( col + 1, 0, size.width - col - 1, size.height );
cv::Rect rect2( col, 0, size.width - col - 1, size.height );
matIn( rect1 ).copyTo( matOut( rect2 ) );
使用std::memcpy
和cv::Mat::data
:
// Removing a row
int rowSizeInBytes = size.width * sizeof( T );
if ( row > 0 )
int numRows = row;
int numBytes = rowSizeInBytes * numRows;
std::memcpy( matOut.data, matIn.data, numBytes );
if ( row < size.height - 1 )
int matOutOffset = rowSizeInBytes * row;
int matInOffset = matOutOffset + rowSizeInBytes;
int numRows = size.height - ( row + 1 );
int numBytes = rowSizeInBytes * numRows;
std::memcpy( matOut.data + matOutOffset , matIn.data + matInOffset, numBytes );
// Removing a column
int rowInInBytes = size.width * sizeof( T );
int rowOutInBytes = ( size.width - 1 ) * sizeof( T );
if ( col > 0 )
int matInOffset = 0;
int matOutOffset = 0;
int numCols = col;
int numBytes = numCols * sizeof( T );
for ( int y = 0; y < size.height; ++y )
std::memcpy( matOut.data + matOutOffset, matIn.data + matInOffset, numBytes );
matInOffset += rowInInBytes;
matOutOffset += rowOutInBytes;
if ( col < size.width - 1 )
int matInOffset = ( col + 1 ) * sizeof( T );
int matOutOffset = col * sizeof( T );
int numCols = size.width - ( col + 1 );
int numBytes = numCols * sizeof( T );
for ( int y = 0; y < size.height; ++y )
std::memcpy( matOut.data + matOutOffset, matIn.data + matInOffset, numBytes );
matInOffset += rowInInBytes;
matOutOffset += rowOutInBytes;
第一种方法的时序测试显示:
Removed: row
Method: cv::Rect + cv::Mat::copyTo()
Iterations: 10000
Size: [500 x 500]
Best time: 67ms
Worst time: 526ms
Average time: 70.9061ms
Median time: 70ms
Removed: column
Method: cv::Rect + cv::Mat::copyTo()
Iterations: 10000
Size: [500 x 500]
Best time: 64ms
Worst time: 284ms
Average time: 80.3893ms
Median time: 79ms
对于第二种方法:
Removed: row
Method: std::memcpy and/or for-loop
Iterations: 10000
Size: [500 x 500]
Best time: 31ms
Worst time: 444ms
Average time: 68.9445ms
Median time: 68ms
Removed: column
Method: std::memcpy and/or for-loop
Iterations: 10000
Size: [500 x 500]
Best time: 49ms
Worst time: 122ms
Average time: 79.3948ms
Median time: 78ms
因此,考虑到接近的时序结果和简短的实现,第一种方法似乎更合适。 我发布了minimal working example on github 以验证此测试的结果。
【讨论】:
【参考方案2】:删除第 N 行:
memmove(mat + N * x_size,
mat + (N + 1) * x_size,
x_size * sizeof(int) * (y_size - N - 1));
删除列 N:
for(int y = 0; y < y_size; y++)
memmove(mat + N + y * (x_size - 1),
mat + N + y * x_size + 1,
(x_size - 1) * sizeof(int));
ATTN:第二个代码(删除列)读取矩阵后面的额外行。在大多数情况下,这是可以接受的,并且算法保持简单。如果需要,修改代码以将正确的大小传递到最后一个 memmove。
【讨论】:
以上是关于从 cv mat 中删除行或列的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章
在Excel中查找一个值,返回该值所在行或列的某个特定标签。