--[[---------------------------------------------------------------------------- Interface for manipulating masks stored in RLE format. For an overview of RLE please see http://mscoco.org/dataset/#download. Additionally, more detailed information can be found in the Matlab MaskApi.m: https://github.com/pdollar/coco/blob/master/MatlabAPI/MaskApi.m The following API functions are defined: encode - Encode binary masks using RLE. decode - Decode binary masks encoded via RLE. merge - Compute union or intersection of encoded masks. iou - Compute intersection over union between masks. nms - Compute non-maximum suppression between ordered masks. area - Compute area of encoded masks. toBbox - Get bounding boxes surrounding encoded masks. frBbox - Convert bounding boxes to encoded masks. frPoly - Convert polygon to encoded mask. drawCirc - Draw circle into image (alters input). drawLine - Draw line into image (alters input). drawMasks - Draw masks into image (alters input). Usage: Rs = MaskApi.encode( masks ) masks = MaskApi.decode( Rs ) R = MaskApi.merge( Rs, [intersect=false] ) o = MaskApi.iou( dt, gt, [iscrowd=false] ) keep = MaskApi.nms( dt, thr ) a = MaskApi.area( Rs ) bbs = MaskApi.toBbox( Rs ) Rs = MaskApi.frBbox( bbs, h, w ) R = MaskApi.frPoly( poly, h, w ) MaskApi.drawCirc( img, x, y, rad, clr ) MaskApi.drawLine( img, x0, y0, x1, y1, rad, clr ) MaskApi.drawMasks( img, masks, [maxn=n], [alpha=.4], [clrs] ) For detailed usage information please see cocoDemo.lua. In the API the following formats are used: R,Rs - [table] Run-length encoding of binary mask(s) masks - [nxhxw] Binary mask(s) bbs - [nx4] Bounding box(es) stored as [x y w h] poly - Polygon stored as {[x1 y1 x2 y2...],[x1 y1 ...],...} dt,gt - May be either bounding boxes or encoded masks Both poly and bbs are 0-indexed (bbox=[0 0 1 1] encloses first pixel). Common Objects in COntext (COCO) Toolbox. version 3.0 Data, paper, and tutorials available at: http://mscoco.org/ Code written by Pedro O. Pinheiro and Piotr Dollar, 2016. Licensed under the Simplified BSD License [see coco/license.txt] ------------------------------------------------------------------------------]] local ffi = require 'ffi' local coco = require 'coco.env' coco.MaskApi = {} local MaskApi = coco.MaskApi coco.libmaskapi = ffi.load(package.searchpath('libmaskapi',package.cpath)) local libmaskapi = coco.libmaskapi -------------------------------------------------------------------------------- MaskApi.encode = function( masks ) local n, h, w = masks:size(1), masks:size(2), masks:size(3) masks = masks:type('torch.ByteTensor'):transpose(2,3) local data = masks:contiguous():data() local Qs = MaskApi._rlesInit(n) libmaskapi.rleEncode(Qs[0],data,h,w,n) return MaskApi._rlesToLua(Qs,n) end MaskApi.decode = function( Rs ) local Qs, n, h, w = MaskApi._rlesFrLua(Rs) local masks = torch.ByteTensor(n,w,h):zero():contiguous() libmaskapi.rleDecode(Qs,masks:data(),n) MaskApi._rlesFree(Qs,n) return masks:transpose(2,3) end MaskApi.merge = function( Rs, intersect ) intersect = intersect or 0 local Qs, n, h, w = MaskApi._rlesFrLua(Rs) local Q = MaskApi._rlesInit(1) libmaskapi.rleMerge(Qs,Q,n,intersect) MaskApi._rlesFree(Qs,n) return MaskApi._rlesToLua(Q,1)[1] end MaskApi.iou = function( dt, gt, iscrowd ) if not iscrowd then iscrowd = NULL else iscrowd = iscrowd:type('torch.ByteTensor'):contiguous():data() end if torch.isTensor(gt) and torch.isTensor(dt) then local nDt, k = dt:size(1), dt:size(2); assert(k==4) local nGt, k = gt:size(1), gt:size(2); assert(k==4) local dDt = dt:type('torch.DoubleTensor'):contiguous():data() local dGt = gt:type('torch.DoubleTensor'):contiguous():data() local o = torch.DoubleTensor(nGt,nDt):contiguous() libmaskapi.bbIou(dDt,dGt,nDt,nGt,iscrowd,o:data()) return o:transpose(1,2) else local qDt, nDt = MaskApi._rlesFrLua(dt) local qGt, nGt = MaskApi._rlesFrLua(gt) local o = torch.DoubleTensor(nGt,nDt):contiguous() libmaskapi.rleIou(qDt,qGt,nDt,nGt,iscrowd,o:data()) MaskApi._rlesFree(qDt,nDt); MaskApi._rlesFree(qGt,nGt) return o:transpose(1,2) end end MaskApi.nms = function( dt, thr ) if torch.isTensor(dt) then local n, k = dt:size(1), dt:size(2); assert(k==4) local Q = dt:type('torch.DoubleTensor'):contiguous():data() local kp = torch.IntTensor(n):contiguous() libmaskapi.bbNms(Q,n,kp:data(),thr) return kp else local Q, n = MaskApi._rlesFrLua(dt) local kp = torch.IntTensor(n):contiguous() libmaskapi.rleNms(Q,n,kp:data(),thr) MaskApi._rlesFree(Q,n) return kp end end MaskApi.area = function( Rs ) local Qs, n, h, w = MaskApi._rlesFrLua(Rs) local a = torch.IntTensor(n):contiguous() libmaskapi.rleArea(Qs,n,a:data()) MaskApi._rlesFree(Qs,n) return a end MaskApi.toBbox = function( Rs ) local Qs, n, h, w = MaskApi._rlesFrLua(Rs) local bb = torch.DoubleTensor(n,4):contiguous() libmaskapi.rleToBbox(Qs,bb:data(),n) MaskApi._rlesFree(Qs,n) return bb end MaskApi.frBbox = function( bbs, h, w ) if bbs:dim()==1 then bbs=bbs:view(1,bbs:size(1)) end local n, k = bbs:size(1), bbs:size(2); assert(k==4) local data = bbs:type('torch.DoubleTensor'):contiguous():data() local Qs = MaskApi._rlesInit(n) libmaskapi.rleFrBbox(Qs[0],data,h,w,n) return MaskApi._rlesToLua(Qs,n) end MaskApi.frPoly = function( poly, h, w ) local n = #poly local Qs, Q = MaskApi._rlesInit(n), MaskApi._rlesInit(1) for i,p in pairs(poly) do local xy = p:type('torch.DoubleTensor'):contiguous():data() libmaskapi.rleFrPoly(Qs[i-1],xy,p:size(1)/2,h,w) end libmaskapi.rleMerge(Qs,Q[0],n,0) MaskApi._rlesFree(Qs,n) return MaskApi._rlesToLua(Q,1)[1] end -------------------------------------------------------------------------------- MaskApi.drawCirc = function( img, x, y, rad, clr ) assert(img:isContiguous() and img:dim()==3) local k, h, w, data = img:size(1), img:size(2), img:size(3), img:data() for dx=-rad,rad do for dy=-rad,rad do local xi, yi = torch.round(x+dx), torch.round(y+dy) if dx*dx+dy*dy<=rad*rad and xi>=0 and yi>=0 and xi=0 and yi>=0 and xi