将opencv Mat创建为二维网格

Posted

技术标签:

【中文标题】将opencv Mat创建为二维网格【英文标题】:Creating opencv Mat to 2d grid 【发布时间】:2017-04-06 20:23:00 【问题描述】:

我正在使用 OpenCV Mat 构建搜索算法,我将 Mat 转换为灰色图像,然后检查像素以将其标记为可步行或不可步行及其坐标。我使用矢量>网格。当我尝试从网格中打印 nodeID 时,程序突然关闭(例如grid.grid[10][10]->NodeID)。

using namespace std;
int gridZise;
class location
public:
    int x;
    int y;

;

class Node
public:
    int gridX;
    int gridY;
    bool walkable;
    location worldPosition;
    int NodeID;


    int gCost;
    int hCost;
    Node *parent;

    Node(bool _walkable, int _gridX, int _gridY)
        
            walkable = _walkable;
            gridX = _gridX;
            gridY = _gridY;
            NodeID = gridY * gridZise + gridX;
        
    Node(int _gridX, int _gridY)
        gridX = _gridX;
        gridY = _gridY;
        NodeID = gridY * gridZise + gridX;
    

    int fCost()
        return gCost + hCost;
    

;

class Grid

public:
    cv::Mat map;
    vector<vector<Node*> > grid;
    int gridx;
    int gridy;


    Grid(cv::Mat _map)
        map = _map;
        gridx = map.cols;
        gridy = map.cols;
        gridZise = map.cols;
    

    void CreateGrid()
        // Set up sizes. (HEIGHT x WIDTH)
          grid.resize(gridy);
          for (int i = 0; i < gridy; ++i)
            grid[i].resize(gridx);

          // build up the grid
          for(int i=0; i <gridx;i++)
              for(int j=0; j < gridy;j++)
                  int pixel_val = map.at<int>(i,j);
                  bool _walkable = false;
                  if(pixel_val > 120)//if the value of the pixel is bigger than 120 is walkable
                       _walkable = true;
                  
                  grid[i][j]->walkable = _walkable;
                  grid[i][j]->gridX = i;
                  grid[i][j]->gridY = j;
              
          
    

    void PrintGrid()
        for(int i=0; i <gridx;i++)
            for(int j=0; j < gridy;j++)
                cout << grid[i][j]->NodeID <<endl;
            
        
    

    vector<Node> GetNeighbours(Node node)
        
            vector<Node> neighbours;

            for (int x = -1; x <=1; x++)
            
                for (int y = -1; y <= 1; y++)
                
                    if (x == 0 && y == 0)
                        continue;

                    int checkX = node.gridX + x;
                    int checkY = node.gridY + y;

                    if(checkX >=0 && checkX < gridx && checkY >=0 && checkY < gridy)
                    
                        Node neighbour(checkX,checkY);
                        neighbours.push_back(neighbour);
                    
                
            
            return neighbours;
        

    Node nodeFromLocation(location _node)
        Node currentNode = *grid[_node.x][_node.y];
        return currentNode;
    

;


using namespace cv;
int main()

    cv::Mat img;
    img = imread("C:\\Users\\abdulla\\Pictures\\maze.jpg");

    if(img.empty())
        cout<<"image not load "<<endl;
        return -1;
    
    cvtColor(img,img,CV_BGR2GRAY);
    imshow("image",img);
    waitKey();
    Grid grid(img);

    grid.PrintGrid();

    return 0;

谢谢。

【问题讨论】:

灰度图像是CV_8UC,所以需要用uchar读取每个像素,uchar pixel_val = map.at&lt;uchar&gt;(i,j);。您也可以使用img = imread(path, CV_LOAD_IMAGE_GRAYSCALE ) 加载图像,因此您不必再次将其转换为灰色。 嗨@Tony J,感谢您的回复。我确实使用了 uchar 而不是 int。当我尝试在网格中输入节点的值时,问题就开始了。 grid.grid[10][10].NodeID 【参考方案1】:

首先,摆脱using namespace std;。虽然它看起来很方便,但您正在为一些令人讨厌的惊喜做好准备(请参阅this 问题)。

Grid 的构造函数使用了Grid::grid 的默认构造函数,它创建了一个空向量。

PrintGrid 中,您会得到未定义的行为。

grid; // ok, grid is a member of Grid
grid[0]; // Returns a reference. No bounds checking is performed.
grid[0][0]; // undefined behaviour. grid[0] is outside of bounds.

有一个方法CreateGrid 在这里不相关,因为你从不调用它。但是让我们假设你已经调用了它。然后 PrintGrid 会像这样工作:

grid; // ok, grid is a member of Grid
grid[0]; // ok
grid[0][0]; // ok, return a pointer to a Node. Which you never initialized.
grid[0][0]->NodeID; // Undefined behaviour. You're trying to access a random memory location.

您真的需要将节点存储为指针吗?你也可以使用vector&lt;vector&lt;Node&gt;&gt;。这样,某人 (std::vector&lt;...) 将负责分配和删除节点。您仍然可以使用 use 指针指向父级,只要指针用作引用而不是所有权即可。

如果您确实需要存储指针,make use of smart pointers。这样,就会有人负责删除节点。

最后,Grid 类负责维护正确的状态。总是。所以构造函数应该已经做了CreateGrid 所做的事情。问题是你不能从构造函数中调用CreateGrid,因为那样你会调用一个生命周期尚未开始的对象的方法(如果我错了,有人纠正我)。 如果您想将其保留为单独的功能,您可以将CreateGrid 设为静态:

class Grid 
    Grid(cv::Mat _map):
           map(_map),
           gridx(_map.cols),
           gridy(_map.cols),
           gridZise(_map.cols),
    
        GreateGrid(grid, map, gridx, gridy);
    

//...
    static void CreateGrid(std::vector<std::vector<Node>> & grid, cv::Mat map, int gridx, int grid y) 
    //...
    
//...
;

【讨论】:

谢谢@mars,您的建议非常有用。正如你所说,我重新组织了我的代码。但是,我之所以使用 Node 作为点,是因为当我在没有指针的情况下使用它时,我会收到此错误 error: no matching function for call to 'Node::Node()' ::new(static_cast&lt;void*&gt;(__p)) _T1(std::forward&lt;_Args&gt;(__args)...); ,它甚至不在我的代码文件中。 这个问题可以解决。编译器抱怨它不能默认构造节点,但如果你调整向量的大小,它就必须这样做。每当您创建任何自定义构造函数时,c++ 都不会提供自动生成的构造函数。可能的解决方案是:1) 使用 reserve 和 push_back,2) 指定在调整大小期间使用的 Node 值(例如.resize(10, Node(false, 0, 0));),3) 证明Node 的默认构造函数。要么自己提供Node(): walkable(false) ,要么使用自动生成的Node() = default;。如果您还需要,复制构造函数 Node(const &amp; Node) 也是如此。 谢谢@mars,这解决了我的问题。我设法顺利运行而没有任何错误,并且我设法在没有指针的情况下运行它,这使得进度更容易。再次感谢您

以上是关于将opencv Mat创建为二维网格的主要内容,如果未能解决你的问题,请参考以下文章

如何将 C++ 数组转换为 opencv Mat

OpenCV中Mat与二维数组之间的转换

将vector<Mat>转换为矢量文件Opencv

Mat的单通道作为矩阵Opencv

OpenCV isContinuous()函数简析

OpenCV isContinuous()函数简析