下文的代碼可能展示不全,詳情請下載文件:用cpp遍歷ndarray.rar
問題背景:
現在我有一張二值圖test.npy,需要對其閉區域進行孔洞填充,如下圖所示:
文件下載鏈接:用cpp遍歷ndarray.rar
用python實現BFS:
def bfs1(im, vis, x, y, xb, yb): def legal(tx, ty): if tx < xb and tx >= 0 and ty < yb and ty >= 0: return True else: return False dx = [0, 0, 1, -1] dy = [1, -1, 0, 0] q = Queue() ls = [] q.put((x, y)) flag = True while not q.empty(): tmp = q.get() cx, cy = tmp vis[cx][cy] = 1 for i in range(0, 4): tx = cx + dx[i] ty = cy + dy[i] if (legal(tx, ty)): if im[tx][ty] == 0 and vis[tx][ty] == 0: q.put((tx, ty)) ls.append((tx, ty)) vis[tx][ty] = 1 else: flag = False if flag: for pt in ls: tx, ty = pt im[tx][ty] = 255 def fillHolePy(im): x, y = im.shape[:2] ans=im.copy() vis = np.zeros([x, y]) for i in range(0, x): for j in range(0, y): if vis[i][j] == 0: # and im[i][j]==0 bfs1(ans, vis, i, j, x, y) return ans
程序執行了0.914秒
用C++實現BFS:(因為python向cpp傳參只能用一維數組,這涉及到多維數組到一維數組的映射,詳見我的另一篇博客:numpy中多維數組的絕對索引)
int x_s,y_s; inline int MAP(int x,int y){ return y_s*x + y; } int dx[4]={0, 0, 1, -1}; int dy[4]={1, -1, 0, 0}; int vis[1000*1000]; typedef struct Pt { int x,y; Pt(int x,int y):x(x),y(y){ } }Pt; bool legal(int x,int y){ if(x<x_s && x>=0 && y<y_s && y>=0) return true; return false; } void bfs(int *img,int x,int y){ queue<Pt> q; vector<Pt> v; q.push(Pt(x,y)); bool flag=1; int i; while(!q.empty()){ Pt pt=q.front(); q.pop(); vis[MAP(x,y)]=1; int cx=pt.x,cy=pt.y; FF(i,4){ int tx=cx+dx[i]; int ty=cy+dy[i]; if(legal(tx,ty)){ if(img[MAP(tx,ty)]==0 && vis[MAP(tx,ty)]==0){ q.push(Pt(tx,ty)); v.push_back(Pt(tx,ty)); vis[MAP(tx,ty)]=1; } }else{ flag=0; } } if(flag){ int sz=v.size(); FF(i,sz){ int & tx=v[i].x; int & ty=v[i].y; img[MAP(tx,ty)]=255; } } } } void fillHole(int * img,int X,int Y){ x_s=X,y_s=Y; int i,j; FF(i,x_s)FF(j,x_s)if(!vis[MAP(i,j)]){ bfs(img,i,j); } }
下面我們看怎樣用python調用cpp。
在上文的cpp中,對想要執行的函數fillHole進行聲明:
extern "C" { __declspec(dllexport) void fillHole(int * img,int X,int Y) ; }
用g++(mingw64位)編譯為dll:
g++ bfs.cpp -shared -o bfs.dll -Wl,--out-implib,bfs.lib pause
在python中使用numpy的封裝加載DLL並且傳參調用:
import numpy.ctypeslib as npct def fillHoleCpp(im): array_2d_int32 = npct.ndpointer(dtype=np.int32, ndim=2, flags='CONTIGUOUS') dll = npct.load_library("bfs.dll",".") bfs=dll.fillHole bfs.restype = None bfs.argtypes = [array_2d_int32, c_int, c_int] im=im.astype(dtype=np.int32) if not im.flags['C_CONTIGUOUS']: im = np.ascontiguous(im, dtype=im.dtype) X, Y=im.shape bfs(im,X,Y) return im
查看測試結果:
程序執行了0.058秒
根據測試cpp比python快了15倍。
cpp完整代碼:

#include <stdio.h> #include <vector> #include <queue> #include <algorithm> using namespace std; #define FF(a,b) for(a=0;a<b;a++) extern "C" { __declspec(dllexport) void fillHole(int * img,int X,int Y) ; } int x_s,y_s; inline int MAP(int x,int y){ return y_s*x + y; } int dx[4]={0, 0, 1, -1}; int dy[4]={1, -1, 0, 0}; int vis[1000*1000]; typedef struct Pt { int x,y; Pt(int x,int y):x(x),y(y){ } }Pt; bool legal(int x,int y){ if(x<x_s && x>=0 && y<y_s && y>=0) return true; return false; } void bfs(int *img,int x,int y){ queue<Pt> q; vector<Pt> v; q.push(Pt(x,y)); bool flag=1; int i; while(!q.empty()){ Pt pt=q.front(); q.pop(); vis[MAP(x,y)]=1; int cx=pt.x,cy=pt.y; FF(i,4){ int tx=cx+dx[i]; int ty=cy+dy[i]; if(legal(tx,ty)){ if(img[MAP(tx,ty)]==0 && vis[MAP(tx,ty)]==0){ q.push(Pt(tx,ty)); v.push_back(Pt(tx,ty)); vis[MAP(tx,ty)]=1; } }else{ flag=0; } } if(flag){ int sz=v.size(); FF(i,sz){ int & tx=v[i].x; int & ty=v[i].y; img[MAP(tx,ty)]=255; } } } } void fillHole(int * img,int X,int Y){ x_s=X,y_s=Y; int i,j; FF(i,x_s)FF(j,x_s)if(!vis[MAP(i,j)]){ bfs(img,i,j); } } //int main() { // return 0; //}
python完整代碼:

import numpy as np import numpy.ctypeslib as npct import pylab as plt from queue import Queue import datetime from ctypes import * def bfs1(im, vis, x, y, xb, yb): def legal(tx, ty): if tx < xb and tx >= 0 and ty < yb and ty >= 0: return True else: return False dx = [0, 0, 1, -1] dy = [1, -1, 0, 0] q = Queue() ls = [] q.put((x, y)) flag = True while not q.empty(): tmp = q.get() cx, cy = tmp vis[cx][cy] = 1 for i in range(0, 4): tx = cx + dx[i] ty = cy + dy[i] if (legal(tx, ty)): if im[tx][ty] == 0 and vis[tx][ty] == 0: q.put((tx, ty)) ls.append((tx, ty)) vis[tx][ty] = 1 else: flag = False if flag: for pt in ls: tx, ty = pt im[tx][ty] = 255 def fillHolePy(im): x, y = im.shape[:2] ans=im.copy() vis = np.zeros([x, y]) for i in range(0, x): for j in range(0, y): if vis[i][j] == 0: # and im[i][j]==0 bfs1(ans, vis, i, j, x, y) return ans import numpy.ctypeslib as npct def fillHoleCpp(im): array_2d_int32 = npct.ndpointer(dtype=np.int32, ndim=2, flags='CONTIGUOUS') dll = npct.load_library("bfs.dll",".") bfs=dll.fillHole bfs.restype = None bfs.argtypes = [array_2d_int32, c_int, c_int] im=im.astype(dtype=np.int32) if not im.flags['C_CONTIGUOUS']: im = np.ascontiguous(im, dtype=im.dtype) X, Y=im.shape bfs(im,X,Y) return im if __name__ == '__main__': img=np.load('test.npy') plt.subplot(121) plt.title('before fill') plt.imshow(img) starttime = datetime.datetime.now() img=fillHoleCpp(img) #使用BFS(廣度優先搜索算法)對原圖像進行處理 endtime = datetime.datetime.now() print("程序執行了%.03f秒" % ((endtime - starttime).microseconds / 1000000)) # exit(0) plt.subplot(122) plt.title('after fill') plt.imshow(img) plt.show()
參考資料:
https://segmentfault.com/a/1190000000479951