如何通过OpenCV合并大量的方形图像

Fro*_*art 5 opencv opencv3.0

如何使用OpenCV将下面的图像合并为单个图像(水平和垂直可以有任意数量的图像)?有没有内置的解决方案呢?

在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述 在此输入图像描述

其他部分:

在此输入图像描述 在此输入图像描述 在此输入图像描述

Mik*_*iki 9

好吧,似乎我完成了这个难题:

在此输入图像描述

主要步骤:

  1. 比较每对图像(拼图)以了解相对位置(findRelativePositionsgetPosition).
  2. 知道碎片的相对位置(buildPuzzlebuilfForPiece)
  3. 创建最终拼贴,将每个图像放在正确的位置(最后一部分buildPuzzle).

在步骤1中的片A和B之间的比较是在检查相似性(绝对差的总和)之间进行的:

  • B从北到A:第一行,B最后一行;
  • B是南到A:最后一行和B第一行;
  • B是A到WEST:最后一列和B第一列;
  • B是EAST到A:第一列和B最后一列.

由于图像不重叠,但我们可以假设限制行(列)非常相似,关键方面是使用(ad-hoc)阈值来区分限制部分.这是在函数中处理的getPosition,具有阈值参数threshold.

这里是完整的代码.如果不清楚,请告诉我.

#include <opencv2\opencv.hpp>
#include <algorithm>
#include <set>

using namespace std;
using namespace cv;

enum Direction
{
    NORTH = 0,
    SOUTH,
    WEST,
    EAST
};


int getPosition(const Mat3b& A, const Mat3b& B, double& cost)
{
    Mat hsvA, hsvB;
    cvtColor(A, hsvA, COLOR_BGR2HSV);
    cvtColor(B, hsvB, COLOR_BGR2HSV);

    int threshold = 1000;

    // Check NORTH
    Mat3b AN = hsvA(Range(0, 1), Range::all());
    Mat3b BS = hsvB(Range(B.rows - 1, B.rows), Range::all());

    Mat3b AN_BS;
    absdiff(AN, BS, AN_BS);
    Scalar scoreN = sum(AN_BS);

    // Check SOUTH
    Mat3b AS = hsvA(Range(A.rows - 1, A.rows), Range::all());
    Mat3b BN = hsvB(Range(0, 1), Range::all());

    Mat3b AS_BN;
    absdiff(AS, BN, AS_BN);
    Scalar scoreS = sum(AS_BN);

    // Check WEST
    Mat3b AW = hsvA(Range::all(), Range(A.cols - 1, A.cols));
    Mat3b BE = hsvB(Range::all(), Range(0, 1));

    Mat3b AW_BE;
    absdiff(AW, BE, AW_BE);
    Scalar scoreW = sum(AW_BE);

    // Check EAST
    Mat3b AE = hsvA(Range::all(), Range(0, 1));
    Mat3b BW = hsvB(Range::all(), Range(B.cols - 1, B.cols));

    Mat3b AE_BW;
    absdiff(AE, BW, AE_BW);
    Scalar scoreE = sum(AE_BW);

    vector<double> scores{ scoreN[0], scoreS[0], scoreW[0], scoreE[0] };
    int idx_min = distance(scores.begin(), min_element(scores.begin(), scores.end()));
    int direction = (scores[idx_min] < threshold) ? idx_min : -1;
    cost = scores[idx_min];
    return direction;
}

void resolveConflicts(Mat1i& positions, Mat1d& costs)
{
    for (int c = 0; c < 4; ++c)
    {
        // Search for duplicate pieces in each column

        set<int> pieces;
        set<int> dups;

        for (int r = 0; r < positions.rows; ++r)
        {
            int label = positions(r, c);
            if (label >= 0)
            {
                if (pieces.count(label) == 1)
                {
                    dups.insert(label);
                }
                else
                {
                    pieces.insert(label);
                }
            }
        }

        if (dups.size() > 0)
        {
            int min_idx = -1;
            for (int duplicate : dups)
            {
                // Find minimum cost position
                Mat1d column = costs.col(c);
                min_idx = distance(column.begin(), min_element(column.begin(), column.end()));

                // Keep only minimum cost position
                for (int ir = 0; ir < positions.rows; ++ir)
                {
                    int label = positions(ir, c);
                    if ((label == duplicate) && (ir != min_idx))
                    {
                        positions(ir, c) = -1;
                    }
                }
            }
        }
    }
}

void findRelativePositions(const vector<Mat3b>& pieces, Mat1i& positions)
{
    positions = Mat1i(pieces.size(), 4, -1);
    Mat1d costs(pieces.size(), 4, DBL_MAX);

    for (int i = 0; i < pieces.size(); ++i)
    {
        for (int j = i + 1; j < pieces.size(); ++j)
        {
            double cost;
            int pos = getPosition(pieces[i], pieces[j], cost);

            if (pos >= 0)
            {
                if (costs(i, pos) > cost)
                {
                    positions(i, pos) = j;
                    costs(i, pos) = cost;
                    switch (pos)
                    {
                    case NORTH:
                        positions(j, SOUTH) = i;
                        costs(j, SOUTH) = cost;
                        break;
                    case SOUTH:
                        positions(j, NORTH) = i;
                        costs(j, NORTH) = cost;
                        break;
                    case WEST:
                        positions(j, EAST) = i;
                        costs(j, EAST) = cost;
                        break;
                    case EAST:
                        positions(j, WEST) = i;
                        costs(j, WEST) = cost;
                        break;
                    }
                }
            }
        }
    }

    resolveConflicts(positions, costs);
}


void builfForPiece(int idx_piece, set<int>& posed, Mat1i& labels, const Mat1i& positions)
{
    Point pos(-1, -1);

    // Find idx_piece on grid;
    for (int r = 0; r < labels.rows; ++r)
    {
        for (int c = 0; c < labels.cols; ++c)
        {
            if (labels(r, c) == idx_piece)
            {
                pos = Point(c, r);
                break;
            }
        }
        if (pos.x >= 0) break;
    }

    if (pos.x < 0) return;


    // Put connected pieces
    for (int c = 0; c < 4; ++c)
    {
        int next = positions(idx_piece, c);
        if (next > 0)
        {
            switch (c)
            {
            case NORTH:
                labels(Point(pos.x, pos.y - 1)) = next;
                posed.insert(next);
                break;
            case SOUTH:
                labels(Point(pos.x, pos.y + 1)) = next;
                posed.insert(next);
                break;
            case WEST:
                labels(Point(pos.x + 1, pos.y)) = next;
                posed.insert(next);
                break;
            case EAST:
                labels(Point(pos.x - 1, pos.y)) = next;
                posed.insert(next);
                break;
            }
        }
    }
}


Mat3b buildPuzzle(const vector<Mat3b>& pieces, const Mat1i& positions, Size sz)
{
    int n_pieces = pieces.size();
    set<int> posed;
    set<int> todo;
    for (int i = 0; i < n_pieces; ++i) todo.insert(i);

    Mat1i labels(n_pieces * 2 + 1, n_pieces * 2 + 1, -1);

    // Place first element in the center
    todo.erase(0);
    labels(Point(n_pieces, n_pieces)) = 0;
    posed.insert(0);
    builfForPiece(0, posed, labels, positions);

    // Build puzzle starting from the already placed elements
    while (todo.size() > 0)
    {
        auto it = todo.begin();
        int next = -1;
        do
        {
            next = *it;
            ++it;
        } while (posed.count(next) == 0 && it != todo.end());


        todo.erase(next);
        builfForPiece(next, posed, labels, positions);
    }

    // Posed all pieces, now collage!

    vector<Point> pieces_position;
    Mat1b mask = labels >= 0;
    findNonZero(mask, pieces_position);
    Rect roi = boundingRect(pieces_position);

    Mat1i lbls = labels(roi);
    Mat3b collage(roi.height * sz.height, roi.width * sz.width, Vec3b(0, 0, 0));

    for (int r = 0; r < lbls.rows; ++r)
    {
        for (int c = 0; c < lbls.cols; ++c)
        {
            if (lbls(r, c) >= 0)
            {
                Rect rect(c*sz.width, r*sz.height, sz.width, sz.height);
                pieces[lbls(r, c)].copyTo(collage(rect));
            }
        }
    }

    return collage;
}


int main()
{
    // Load images

    vector<String> filenames;
    glob("D:\\SO\\img\\puzzle*", filenames);

    vector<Mat3b> pieces(filenames.size());

    for (int i = 0; i < filenames.size(); ++i)
    {
        pieces[i] = imread(filenames[i], IMREAD_COLOR);
    }

    // Find Relative positions
    Mat1i positions;
    findRelativePositions(pieces, positions);

    // Build the puzzle
    Mat3b puzzle = buildPuzzle(pieces, positions, pieces[0].size());

    imshow("Puzzle", puzzle);
    waitKey();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

注意

  1. 不,没有内置的解决方案来执行此操作.由于图像不重叠,因此图像拼接无效.
  2. 我无法保证这适用于每个谜题,但应该最有效.
  3. 我可能应该在这几个小时工作,但这很有趣:D

编辑

添加更多拼图会在之前的代码版本中生成错误的结果.这是因为(错误的)假设最多一件作品足以与一件作品相连.

现在我添加了一个成本矩阵,只有最小成本块被保存为给定块的邻居.我还添加了一个resolveConflicts函数,可以避免一个部分与多个部分合并(在非冲突位置).

这是添加更多部分的结果:

在此输入图像描述


UPDATE

增加拼图数量后的注意事项:

  • 这个解决方案依赖于碎片的输入顺序,因为事实证明它有一种贪婪的方法来寻找邻居.
  • 在搜索邻居时,最好比较HSV空间中的H通道.我用这个改进更新了上面的代码.
  • 最终解决方案可能需要某种全球最小化全球成本矩阵.这将使该方法独立于输入顺序.我会尽快回来.


小智 0

将这些图像加载为 OpenCV Mat 后,您可以使用以下方法垂直或水平连接这些 Mat:

Mat A, B; // Images that will be concatenated
Mat H; // Here we will concatenate A and B horizontally
Mat V; // Here we will concatenate A and B vertically
hconcat(A, B, H);
vconcat(A, B, V);
Run Code Online (Sandbox Code Playgroud)

如果需要连接两个以上的图像,可以递归地使用这些方法。

顺便说一句,我认为这些方法没有包含在 OpenCV 文档中,但我过去使用过它们。