从 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::Rectcv::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::memcpycv::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 中删除行或列的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

Java 插入隐藏/显示删除Excel行或列

c_cpp 计算行或列的意思

在Excel中查找一个值,返回该值所在行或列的某个特定标签。

Numpy - 从数组中切片二维行或列向量

【pandas笔记】删除DataFrame中特定所在的行或列

R语言 -- 删除 dataFrame/matrix 中含有NA或全为NA的行或列