diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2019, Jiasen Wu
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/cbits/maskApi.c b/cbits/maskApi.c
new file mode 100644
--- /dev/null
+++ b/cbits/maskApi.c
@@ -0,0 +1,231 @@
+/**************************************************************************
+* Microsoft COCO Toolbox.      version 2.0
+* Data, paper, and tutorials available at:  http://mscoco.org/
+* Code written by Piotr Dollar and Tsung-Yi Lin, 2015.
+* Licensed under the Simplified BSD License [see coco/license.txt]
+**************************************************************************/
+#include "maskApi.h"
+#include <math.h>
+#include <stdlib.h>
+
+uint umin( uint a, uint b ) { return (a<b) ? a : b; }
+uint umax( uint a, uint b ) { return (a>b) ? a : b; }
+
+void rleInit( RLE *R, siz h, siz w, siz m, uint *cnts ) {
+  R->h=h; R->w=w; R->m=m; R->cnts=(m==0)?0:malloc(sizeof(uint)*m);
+  siz j; if(cnts) for(j=0; j<m; j++) R->cnts[j]=cnts[j];
+}
+
+void rleFree( RLE *R ) {
+  free(R->cnts); R->cnts=0;
+}
+
+void rlesInit( RLE **R, siz n ) {
+  siz i; *R = (RLE*) malloc(sizeof(RLE)*n);
+  for(i=0; i<n; i++) rleInit((*R)+i,0,0,0,0);
+}
+
+void rlesFree( RLE **R, siz n ) {
+  siz i; for(i=0; i<n; i++) rleFree((*R)+i); free(*R); *R=0;
+}
+
+void rleEncode( RLE *R, const byte *M, siz h, siz w, siz n ) {
+  siz i, j, k, a=w*h; uint c, *cnts; byte p;
+  cnts = malloc(sizeof(uint)*(a+1));
+  for(i=0; i<n; i++) {
+    const byte *T=M+a*i; k=0; p=0; c=0;
+    for(j=0; j<a; j++) { if(T[j]!=p) { cnts[k++]=c; c=0; p=T[j]; } c++; }
+    cnts[k++]=c; rleInit(R+i,h,w,k,cnts);
+  }
+  free(cnts);
+}
+
+void rleDecode( const RLE *R, byte *M, siz n ) {
+  siz i, j, k; for( i=0; i<n; i++ ) {
+    byte v=0; for( j=0; j<R[i].m; j++ ) {
+      for( k=0; k<R[i].cnts[j]; k++ ) *(M++)=v; v=!v; }}
+}
+
+void rleMerge( const RLE *R, RLE *M, siz n, int intersect ) {
+  uint *cnts, c, ca, cb, cc, ct; int v, va, vb, vp;
+  siz i, a, b, h=R[0].h, w=R[0].w, m=R[0].m; RLE A, B;
+  if(n==0) { rleInit(M,0,0,0,0); return; }
+  if(n==1) { rleInit(M,h,w,m,R[0].cnts); return; }
+  cnts = malloc(sizeof(uint)*(h*w+1));
+  for( a=0; a<m; a++ ) cnts[a]=R[0].cnts[a];
+  for( i=1; i<n; i++ ) {
+    B=R[i]; if(B.h!=h||B.w!=w) { h=w=m=0; break; }
+    rleInit(&A,h,w,m,cnts); ca=A.cnts[0]; cb=B.cnts[0];
+    v=va=vb=0; m=0; a=b=1; cc=0; ct=1;
+    while( ct>0 ) {
+      c=umin(ca,cb); cc+=c; ct=0;
+      ca-=c; if(!ca && a<A.m) { ca=A.cnts[a++]; va=!va; } ct+=ca;
+      cb-=c; if(!cb && b<B.m) { cb=B.cnts[b++]; vb=!vb; } ct+=cb;
+      vp=v; if(intersect) v=va&&vb; else v=va||vb;
+      if( v!=vp||ct==0 ) { cnts[m++]=cc; cc=0; }
+    }
+    rleFree(&A);
+  }
+  rleInit(M,h,w,m,cnts); free(cnts);
+}
+
+void rleArea( const RLE *R, siz n, uint *a ) {
+  siz i, j; for( i=0; i<n; i++ ) {
+    a[i]=0; for( j=1; j<R[i].m; j+=2 ) a[i]+=R[i].cnts[j]; }
+}
+
+void rleIou( RLE *dt, RLE *gt, siz m, siz n, byte *iscrowd, double *o ) {
+  siz g, d; BB db, gb; int crowd;
+  db=malloc(sizeof(double)*m*4); rleToBbox(dt,db,m);
+  gb=malloc(sizeof(double)*n*4); rleToBbox(gt,gb,n);
+  bbIou(db,gb,m,n,iscrowd,o); free(db); free(gb);
+  for( g=0; g<n; g++ ) for( d=0; d<m; d++ ) if(o[g*m+d]>0) {
+    crowd=iscrowd!=NULL && iscrowd[g];
+    if(dt[d].h!=gt[g].h || dt[d].w!=gt[g].w) { o[g*m+d]=-1; continue; }
+    siz ka, kb, a, b; uint c, ca, cb, ct, i, u; int va, vb;
+    ca=dt[d].cnts[0]; ka=dt[d].m; va=vb=0;
+    cb=gt[g].cnts[0]; kb=gt[g].m; a=b=1; i=u=0; ct=1;
+    while( ct>0 ) {
+      c=umin(ca,cb); if(va||vb) { u+=c; if(va&&vb) i+=c; } ct=0;
+      ca-=c; if(!ca && a<ka) { ca=dt[d].cnts[a++]; va=!va; } ct+=ca;
+      cb-=c; if(!cb && b<kb) { cb=gt[g].cnts[b++]; vb=!vb; } ct+=cb;
+    }
+    if(i==0) u=1; else if(crowd) rleArea(dt+d,1,&u);
+    o[g*m+d] = (double)i/(double)u;
+  }
+}
+
+void rleNms( RLE *dt, siz n, uint *keep, double thr ) {
+  siz i, j; double u;
+  for( i=0; i<n; i++ ) keep[i]=1;
+  for( i=0; i<n; i++ ) if(keep[i]) {
+    for( j=i+1; j<n; j++ ) if(keep[j]) {
+      rleIou(dt+i,dt+j,1,1,0,&u);
+      if(u>thr) keep[j]=0;
+    }
+  }
+}
+
+void bbIou( BB dt, BB gt, siz m, siz n, byte *iscrowd, double *o ) {
+  double h, w, i, u, ga, da; siz g, d; int crowd;
+  for( g=0; g<n; g++ ) {
+    BB G=gt+g*4; ga=G[2]*G[3]; crowd=iscrowd!=NULL && iscrowd[g];
+    for( d=0; d<m; d++ ) {
+      BB D=dt+d*4; da=D[2]*D[3]; o[g*m+d]=0;
+      w=fmin(D[2]+D[0],G[2]+G[0])-fmax(D[0],G[0]); if(w<=0) continue;
+      h=fmin(D[3]+D[1],G[3]+G[1])-fmax(D[1],G[1]); if(h<=0) continue;
+      i=w*h; u = crowd ? da : da+ga-i; o[g*m+d]=i/u;
+    }
+  }
+}
+
+void bbNms( BB dt, siz n, uint *keep, double thr ) {
+  siz i, j; double u;
+  for( i=0; i<n; i++ ) keep[i]=1;
+  for( i=0; i<n; i++ ) if(keep[i]) {
+    for( j=i+1; j<n; j++ ) if(keep[j]) {
+      bbIou(dt+i*4,dt+j*4,1,1,0,&u);
+      if(u>thr) keep[j]=0;
+    }
+  }
+}
+
+void rleToBbox( const RLE *R, BB bb, siz n ) {
+  siz i; for( i=0; i<n; i++ ) {
+    uint h, w, x, y, xs, ys, xe, ye, xp, cc, t; siz j, m;
+    h=(uint)R[i].h; w=(uint)R[i].w; m=R[i].m;
+    m=((siz)(m/2))*2; xs=w; ys=h; xe=ye=0; cc=0;
+    if(m==0) { bb[4*i+0]=bb[4*i+1]=bb[4*i+2]=bb[4*i+3]=0; continue; }
+    for( j=0; j<m; j++ ) {
+      cc+=R[i].cnts[j]; t=cc-j%2; y=t%h; x=(t-y)/h;
+      if(j%2==0) xp=x; else if(xp<x) { ys=0; ye=h-1; }
+      xs=umin(xs,x); xe=umax(xe,x); ys=umin(ys,y); ye=umax(ye,y);
+    }
+    bb[4*i+0]=xs; bb[4*i+2]=xe-xs+1;
+    bb[4*i+1]=ys; bb[4*i+3]=ye-ys+1;
+  }
+}
+
+void rleFrBbox( RLE *R, const BB bb, siz h, siz w, siz n ) {
+  siz i; for( i=0; i<n; i++ ) {
+    double xs=bb[4*i+0], xe=xs+bb[4*i+2];
+    double ys=bb[4*i+1], ye=ys+bb[4*i+3];
+    double xy[8] = {xs,ys,xs,ye,xe,ye,xe,ys};
+    rleFrPoly( R+i, xy, 4, h, w );
+  }
+}
+
+int uintCompare(const void *a, const void *b) {
+  uint c=*((uint*)a), d=*((uint*)b); return c>d?1:c<d?-1:0;
+}
+
+void rleFrPoly( RLE *R, const double *xy, siz k, siz h, siz w ) {
+  /* upsample and get discrete points densely along entire boundary */
+  siz j, m=0; double scale=5; int *x, *y, *u, *v; uint *a, *b;
+  x=malloc(sizeof(int)*(k+1)); y=malloc(sizeof(int)*(k+1));
+  for(j=0; j<k; j++) x[j]=(int)(scale*xy[j*2+0]+.5); x[k]=x[0];
+  for(j=0; j<k; j++) y[j]=(int)(scale*xy[j*2+1]+.5); y[k]=y[0];
+  for(j=0; j<k; j++) m+=umax(abs(x[j]-x[j+1]),abs(y[j]-y[j+1]))+1;
+  u=malloc(sizeof(int)*m); v=malloc(sizeof(int)*m); m=0;
+  for( j=0; j<k; j++ ) {
+    int xs=x[j], xe=x[j+1], ys=y[j], ye=y[j+1], dx, dy, t, d;
+    int flip; double s; dx=abs(xe-xs); dy=abs(ys-ye);
+    flip = (dx>=dy && xs>xe) || (dx<dy && ys>ye);
+    if(flip) { t=xs; xs=xe; xe=t; t=ys; ys=ye; ye=t; }
+    s = dx>=dy ? (double)(ye-ys)/dx : (double)(xe-xs)/dy;
+    if(dx>=dy) for( d=0; d<=dx; d++ ) {
+      t=flip?dx-d:d; u[m]=t+xs; v[m]=(int)(ys+s*t+.5); m++;
+    } else for( d=0; d<=dy; d++ ) {
+      t=flip?dy-d:d; v[m]=t+ys; u[m]=(int)(xs+s*t+.5); m++;
+    }
+  }
+  /* get points along y-boundary and downsample */
+  free(x); free(y); k=m; m=0; double xd, yd;
+  x=malloc(sizeof(int)*k); y=malloc(sizeof(int)*k);
+  for( j=1; j<k; j++ ) if(u[j]!=u[j-1]) {
+    xd=(double)(u[j]<u[j-1]?u[j]:u[j]-1); xd=(xd+.5)/scale-.5;
+    if( floor(xd)!=xd || xd<0 || xd>w-1 ) continue;
+    yd=(double)(v[j]<v[j-1]?v[j]:v[j-1]); yd=(yd+.5)/scale-.5;
+    if(yd<0) yd=0; else if(yd>h) yd=h; yd=ceil(yd);
+    x[m]=(int) xd; y[m]=(int) yd; m++;
+  }
+  /* compute rle encoding given y-boundary points */
+  k=m; a=malloc(sizeof(uint)*(k+1));
+  for( j=0; j<k; j++ ) a[j]=(uint)(x[j]*(int)(h)+y[j]);
+  a[k++]=(uint)(h*w); free(u); free(v); free(x); free(y);
+  qsort(a,k,sizeof(uint),uintCompare); uint p=0;
+  for( j=0; j<k; j++ ) { uint t=a[j]; a[j]-=p; p=t; }
+  b=malloc(sizeof(uint)*k); j=m=0; b[m++]=a[j++];
+  while(j<k) if(a[j]>0) b[m++]=a[j++]; else {
+    j++; if(j<k) b[m-1]+=a[j++]; }
+  rleInit(R,h,w,m,b); free(a); free(b);
+}
+
+char* rleToString( const RLE *R ) {
+  /* Similar to LEB128 but using 6 bits/char and ascii chars 48-111. */
+  siz i, m=R->m, p=0; long x; int more;
+  char *s=malloc(sizeof(char)*m*6);
+  for( i=0; i<m; i++ ) {
+    x=(long) R->cnts[i]; if(i>2) x-=(long) R->cnts[i-2]; more=1;
+    while( more ) {
+      char c=x & 0x1f; x >>= 5; more=(c & 0x10) ? x!=-1 : x!=0;
+      if(more) c |= 0x20; c+=48; s[p++]=c;
+    }
+  }
+  s[p]=0; return s;
+}
+
+void rleFrString( RLE *R, char *s, siz h, siz w ) {
+  siz m=0, p=0, k; long x; int more; uint *cnts;
+  while( s[m] ) m++; cnts=malloc(sizeof(uint)*m); m=0;
+  while( s[p] ) {
+    x=0; k=0; more=1;
+    while( more ) {
+      char c=s[p]-48; x |= (c & 0x1f) << 5*k;
+      more = c & 0x20; p++; k++;
+      if(!more && (c & 0x10)) x |= -1 << 5*k;
+    }
+    if(m>2) x+=(long) cnts[m-2]; cnts[m++]=(uint) x;
+  }
+  rleInit(R,h,w,m,cnts); free(cnts);
+}
diff --git a/cbits/maskApi.h b/cbits/maskApi.h
new file mode 100644
--- /dev/null
+++ b/cbits/maskApi.h
@@ -0,0 +1,60 @@
+/**************************************************************************
+* Microsoft COCO Toolbox.      version 2.0
+* Data, paper, and tutorials available at:  http://mscoco.org/
+* Code written by Piotr Dollar and Tsung-Yi Lin, 2015.
+* Licensed under the Simplified BSD License [see coco/license.txt]
+**************************************************************************/
+#pragma once
+
+typedef unsigned int uint;
+typedef unsigned long siz;
+typedef unsigned char byte;
+typedef double* BB;
+typedef struct { siz h, w, m; uint *cnts; } RLE;
+
+/* Initialize/destroy RLE. */
+void rleInit( RLE *R, siz h, siz w, siz m, uint *cnts );
+void rleFree( RLE *R );
+
+/* Initialize/destroy RLE array. */
+void rlesInit( RLE **R, siz n );
+void rlesFree( RLE **R, siz n );
+
+/* Encode binary masks using RLE. */
+void rleEncode( RLE *R, const byte *mask, siz h, siz w, siz n );
+
+/* Decode binary masks encoded via RLE. */
+void rleDecode( const RLE *R, byte *mask, siz n );
+
+/* Compute union or intersection of encoded masks. */
+void rleMerge( const RLE *R, RLE *M, siz n, int intersect );
+
+/* Compute area of encoded masks. */
+void rleArea( const RLE *R, siz n, uint *a );
+
+/* Compute intersection over union between masks. */
+void rleIou( RLE *dt, RLE *gt, siz m, siz n, byte *iscrowd, double *o );
+
+/* Compute non-maximum suppression between bounding masks */
+void rleNms( RLE *dt, siz n, uint *keep, double thr );
+
+/* Compute intersection over union between bounding boxes. */
+void bbIou( BB dt, BB gt, siz m, siz n, byte *iscrowd, double *o );
+
+/* Compute non-maximum suppression between bounding boxes */
+void bbNms( BB dt, siz n, uint *keep, double thr );
+
+/* Get bounding boxes surrounding encoded masks. */
+void rleToBbox( const RLE *R, BB bb, siz n );
+
+/* Convert bounding boxes to encoded masks. */
+void rleFrBbox( RLE *R, const BB bb, siz h, siz w, siz n );
+
+/* Convert polygon to encoded mask. */
+void rleFrPoly( RLE *R, const double *xy, siz k, siz h, siz w );
+
+/* Get compressed string representation of encoded mask. */
+char* rleToString( const RLE *R );
+
+/* Convert from compressed string representation of encoded mask. */
+void rleFrString( RLE *R, char *s, siz h, siz w );
diff --git a/examples/Mask.hs b/examples/Mask.hs
new file mode 100644
--- /dev/null
+++ b/examples/Mask.hs
@@ -0,0 +1,87 @@
+module Main where
+
+import qualified Data.ByteString.Lazy as BS
+import qualified Data.ByteString as SBS
+import Control.Lens ((^.), (^?), ix)
+import qualified Data.Vector as V
+import qualified Data.Vector.Storable as SV
+import qualified Data.Vector.Unboxed as UV
+import qualified Data.Aeson as Aeson
+import qualified Data.Array.Repa as RP
+import Data.Array.Repa ((:.)(..), Z(..))
+import qualified Data.Array.Repa.Repr.ForeignPtr as RF
+import Codec.Picture as JP
+import Codec.Picture.Repa
+import qualified Data.Store as Store
+import Control.Exception.Base
+
+import MXNet.Coco.Mask
+import MXNet.Coco.Types
+import MXNet.Coco.Index
+
+data Y8
+
+class ToDynamicImage a where
+    toDynamicImage :: Img a -> DynamicImage
+
+instance ToDynamicImage Y8 where
+    toDynamicImage (Img arr0) = ImageY8 $ JP.Image w h (SV.unsafeFromForeignPtr0 (RF.toForeignPtr arr) (h*w*z) )
+      where 
+        (Z :. h :. w :. z) = RP.extent arr
+        arr = RP.computeS arr0    
+ 
+readFromCache path = do
+    bs <- SBS.readFile path
+    Store.decodeIO bs
+
+readFromJson path = do
+    bs <- BS.readFile path
+    case Aeson.decode' bs of
+        Nothing -> error $ "cannot parse annotation file: " ++ path
+        Just inst -> return inst
+
+store path obj = do 
+    SBS.writeFile path (Store.encode obj)
+    return obj
+
+readAnnotations path =
+    readFromCache cache_file `catch` (\ e -> do
+        let _ = e :: IOException
+        readFromJson path >>= 
+            store cache_file)
+  where
+    cache_file = "./instance.store"
+
+annotatino_file = "/home/jiasen/dschungel/coco/annotations/instances_train2017.json"
+
+main = do
+    inst <- readAnnotations annotatino_file
+    mapM_ (\cat -> putStrLn $ cat ^. odc_name) $ allCats inst 
+    let anno = V.head $ allAnns inst
+        imgId = anno ^. ann_image_id
+        img   = V.head $ V.filter (\img -> img ^. img_id == imgId) (inst ^. images)
+        height = img ^. img_height
+        width  = img ^. img_width
+    
+    store "./imgs.store" $ inst ^. images
+    store "./anns.store" $ inst ^. annotations
+    store "./cats.store" $ inst ^. categories
+    
+
+    -- putStrLn $ show imgId
+
+    -- crle <- case anno ^. ann_segmentation of 
+    --     SegRLE cnts _ -> frUncompressedRLE cnts height width
+    --     SegPolygon polys -> frPoly (map SV.fromList polys) height width
+
+    -- mask <- decode crle
+
+    -- let Z :. c :. w :. h = RP.extent mask
+    --     maskHW = RP.backpermute (Z :. h :. w :. c) (\ (Z :. c :. w :. h) -> Z :. h :. w :. c) mask
+    --     maskImg = toDynamicImage $ (Img $ RP.map (*255) maskHW :: Img Y8)
+
+    -- savePngImage "a.png" maskImg
+
+    -- putStrLn $ img ^. img_file_name
+    -- putStrLn $ img ^. img_flickr_url
+    -- putStrLn $ img ^. img_coco_url
diff --git a/examples/Profiling.hs b/examples/Profiling.hs
new file mode 100644
--- /dev/null
+++ b/examples/Profiling.hs
@@ -0,0 +1,23 @@
+import Criterion.Main
+import Criterion.Main.Options
+import Data.Store
+import qualified Data.IntSet as Set
+import qualified Data.ByteString as BS
+import qualified Data.Vector as V
+import qualified Data.Vector.Unboxed as UV
+import Data.Array.Repa ((:.)(..), Z (..), fromUnboxed, computeUnboxedP, computeUnboxedS)
+
+import MXNet.NN.DataIter.Anchor
+
+main = do
+    goodIndices <- BS.readFile "examples/goodIndices.bin" >>= decodeIO :: IO (V.Vector Int)
+    gtBoxes     <- BS.readFile "examples/gtBoxes.bin"     >>= decodeIO :: IO (V.Vector (UV.Vector Float))
+    anchors     <- BS.readFile "examples/anchors.bin"     >>= decodeIO :: IO (V.Vector (UV.Vector Float))
+    goodIndices <- return $ Set.fromList $ V.toList goodIndices   :: IO Set.IntSet
+    gtBoxes <- return $ V.map (fromUnboxed (Z:.(5::Int))) gtBoxes
+    anchors <- return $ V.map (fromUnboxed (Z:.(4::Int))) anchors
+
+    defaultMain
+        [ bench "computeUnboxedP" $ whnfIO $ computeUnboxedP $ overlapMatrix goodIndices gtBoxes anchors
+        , bench "computeUnboxedS" $ whnf computeUnboxedS $ overlapMatrix goodIndices gtBoxes anchors
+        ]
diff --git a/fei-cocoapi.cabal b/fei-cocoapi.cabal
new file mode 100644
--- /dev/null
+++ b/fei-cocoapi.cabal
@@ -0,0 +1,89 @@
+name:                       fei-cocoapi
+version:                    0.2.0
+synopsis:                   Cocodataset with cocoapi
+description:                Haskell binding for the cocoapi in c
+homepage:                   http://github.com/pierric/fei-cocoapi
+license:                    BSD3
+license-file:               LICENSE
+author:                     Jiasen Wu
+maintainer:                 jiasenwu@hotmail.com
+copyright:                  Copyright: (c) 2019 Jiasen Wu
+category:                   Machine Learning, AI
+build-type:                 Simple
+cabal-version:              1.24
+extra-source-files:         cbits/*.h, cbits/*.c
+
+Library
+    exposed-modules:        MXNet.Coco.Types
+                            MXNet.Coco.Mask
+                            MXNet.Coco.Index
+                            MXNet.NN.DataIter.Coco
+                            MXNet.NN.DataIter.Anchor
+    other-modules:          MXNet.Coco.Raw
+    hs-source-dirs:         src
+    ghc-options:            -Wall
+    default-language:       Haskell2010
+    default-extensions:     GADTs,
+                            TypeFamilies,
+                            OverloadedLabels,
+                            FlexibleContexts,
+                            StandaloneDeriving,
+                            DeriveGeneric,
+                            TypeOperators
+    build-depends:          base >= 4.7 && < 5.0
+                          , storable-tuple
+                          , vector >= 0.12
+                          , mtl >= 2.2
+                          , lens >= 4.12
+                          , transformers-base >= 0.4.4
+                          , aeson >= 1.2
+                          , containers >= 0.5
+                          , bytestring >= 0.10
+                          , exceptions >= 0.8.3
+                          , time < 2.0
+                          , repa >= 3.4
+                          , JuicyPixels
+                          , JuicyPixels-repa
+                          , JuicyPixels-extra
+                          , aeson >= 1.0 && <1.5
+                          , attoparsec (>=0.13.2.2 && <0.14)
+                          , lens >= 4.12
+                          , conduit >= 1.2 && < 1.4
+                          , store
+                          , filepath
+                          , directory
+                          , random-fu
+                          , fei-base
+                          , fei-dataiter
+    Build-tools:         c2hs
+    c-sources:           cbits/maskApi.c
+    include-dirs:        cbits/
+    includes:            maskApi.h
+
+Executable mask
+    hs-source-dirs:         examples
+    main-is:                Mask.hs
+    default-language:       Haskell2010
+    build-depends:          base >= 4.7 && < 5.0,
+                            fei-cocoapi,
+                            bytestring,
+                            lens,
+                            aeson,
+                            vector,
+                            JuicyPixels,
+                            JuicyPixels-repa,
+                            repa,
+                            store
+
+Executable profiling
+    hs-source-dirs:         examples
+    main-is:                Profiling.hs
+    default-language:       Haskell2010
+    build-depends:          base >= 4.7 && < 5.0,
+                            fei-cocoapi,
+                            criterion,
+                            store,
+                            repa,
+                            bytestring,
+                            vector,
+                            containers
diff --git a/src/MXNet/Coco/Index.hs b/src/MXNet/Coco/Index.hs
new file mode 100644
--- /dev/null
+++ b/src/MXNet/Coco/Index.hs
@@ -0,0 +1,28 @@
+module MXNet.Coco.Index where
+
+import Control.Lens ((^.))
+import qualified Data.Vector as V (Vector, filter, null, head)
+
+import MXNet.Coco.Types
+
+allCats :: Instance -> V.Vector Category
+allCats = (^. categories)
+
+allAnns :: Instance -> V.Vector Annotation
+allAnns = (^. annotations)
+
+catByName :: String -> V.Vector Category -> Maybe Category
+catByName name = vecToMaybe . V.filter (\cat -> cat ^. odc_name == name)
+
+annsByCat :: Category -> V.Vector Annotation -> V.Vector Annotation
+annsByCat cat = V.filter (\ann -> ann ^. ann_category_id == cat ^. odc_id )
+
+annsByImg :: Image -> V.Vector Annotation -> V.Vector Annotation
+annsByImg img = V.filter (\ann -> ann ^. ann_image_id == img ^. img_id )
+
+annByCatImg :: Image -> Category -> V.Vector Annotation -> Maybe Annotation
+annByCatImg img cat = vecToMaybe . annsByCat cat . annsByImg img
+
+vecToMaybe :: V.Vector a -> Maybe a
+vecToMaybe vec | V.null vec = Nothing
+               | otherwise = Just $ V.head vec
diff --git a/src/MXNet/Coco/Mask.hs b/src/MXNet/Coco/Mask.hs
new file mode 100644
--- /dev/null
+++ b/src/MXNet/Coco/Mask.hs
@@ -0,0 +1,99 @@
+{-# LANGUAGE TypeOperators #-}
+module MXNet.Coco.Mask where
+
+import Data.Word
+import qualified Data.ByteString as BS
+import Data.Array.Repa (Array, Z(..), (:.)(..), DIM1, DIM2, DIM3, extent)
+import Data.Array.Repa.Repr.Unboxed
+import qualified Data.Vector.Unboxed as UV (convert, length)
+import qualified Data.Vector.Storable as SV (Vector, map, unsafeCast)
+
+import MXNet.Coco.Raw
+
+-- mask should be of 3 dimension and in CWH order
+type Mask = Array U DIM3 Word8
+type Area = Array U DIM1 Word32
+type Iou  = Array U DIM2 Double
+type BBox = Array U DIM2 Double
+type Poly = SV.Vector Double
+
+data CompactRLE = CompactRLE Int Int [BS.ByteString]
+
+encode :: Mask -> IO CompactRLE
+encode mask = do
+    let Z :. n :. w :. h = extent mask
+    -- assuming Word8 are identical with CUChar
+    rles <- rleEncode (SV.map fromIntegral $ UV.convert $ toUnboxed mask) h w n
+    CompactRLE h w <$> mapM rleToString rles
+
+decode :: CompactRLE -> IO Mask
+decode im@(CompactRLE h w bss) = do
+    let n = length bss
+    rles <- frString im
+    raw <- rleDecode rles h w
+    return $ 
+        fromUnboxed (Z :. n :. w :. h) $
+        UV.convert $
+        SV.map fromIntegral raw
+
+merge :: CompactRLE -> Bool -> IO CompactRLE
+merge im intersect = do
+    let CompactRLE h w bss = im
+        n = length bss
+    if n > 1 then do
+        rles <- frString im
+        orle <- rleMerge rles intersect
+        bs <- rleToString orle
+        return $ CompactRLE h w [bs]
+    else 
+        return im
+
+area :: CompactRLE -> IO Area
+area im@(CompactRLE _ _ bss) = do
+    let num = length bss
+    rles <- frString im
+    as <- rleArea rles num
+    -- assuming the area can be represented as Word32
+    return $ 
+        fromListUnboxed (Z :. num) $
+        map fromIntegral $ as
+
+iouRLEs :: CompactRLE -> CompactRLE -> [Bool] -> IO Iou
+iouRLEs dt gt iscrowd = do
+    dt_rles <- frString dt
+    gt_rles <- frString gt
+    ((m, n), arr) <- rleIou dt_rles gt_rles iscrowd
+    return $ fromListUnboxed (Z :. n :. m) arr
+
+iouBBs :: BBox -> BBox -> [Bool] -> IO Iou
+iouBBs bb1 bb2 iscrowd = do
+    let bb1' = BB $ SV.unsafeCast $ UV.convert $ toUnboxed bb1
+        bb2' = BB $ SV.unsafeCast $ UV.convert $ toUnboxed bb2
+    ((m, n), arr) <- bbIou bb1' bb2' iscrowd
+    return $ fromListUnboxed (Z :. n :. m) arr
+
+toBBox :: CompactRLE -> IO BBox
+toBBox im = do
+    rles <- frString im
+    BB bb <- rleToBbox rles
+    let bb' = UV.convert $ SV.unsafeCast bb
+    return $ fromUnboxed (Z :. UV.length bb' :. 4) bb'
+
+frBBox :: BBox -> Int -> Int -> IO CompactRLE
+frBBox bb h w = do
+    rles <- rleFrBbox (BB $ SV.unsafeCast $ UV.convert $ toUnboxed bb) h w
+    CompactRLE h w <$> mapM rleToString rles
+
+frPoly :: [Poly] -> Int -> Int -> IO CompactRLE
+frPoly polys h w = do
+    rles <- mapM (\poly -> rleFrPoly (SV.unsafeCast poly) h w) polys
+    CompactRLE h w <$> mapM rleToString rles
+
+frUncompressedRLE :: [Int] -> Int -> Int -> IO CompactRLE
+frUncompressedRLE raw h w = do
+    orle <- rleInit h w (map fromIntegral raw)
+    crle <- rleToString orle
+    return $ CompactRLE h w [crle]
+
+frString :: CompactRLE -> IO [RLE]
+frString (CompactRLE h w bss) = mapM (\bs -> rleFrString bs h w) bss
diff --git a/src/MXNet/Coco/Raw.chs b/src/MXNet/Coco/Raw.chs
new file mode 100644
--- /dev/null
+++ b/src/MXNet/Coco/Raw.chs
@@ -0,0 +1,315 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module MXNet.Coco.Raw where
+
+import Foreign.Storable
+import Foreign.Ptr
+import Foreign.ForeignPtr
+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
+import Foreign.C.Types
+import Foreign.C.String (CString)
+import Foreign.Marshal.Array
+import Foreign.Marshal.Alloc
+import Foreign.Storable.Tuple ()
+import qualified Data.Vector.Storable as SV
+import qualified Data.Vector.Storable.Mutable as SVM
+import qualified Data.ByteString as BS
+import Control.Exception 
+
+#include "maskApi.h"
+
+data RLE = RLE {
+    _rle_h :: Int,
+    _rle_w :: Int,
+    _rle_m :: Int,
+    _rle_cnts :: ForeignPtr CUInt
+}
+
+makeRLE :: (Ptr () -> IO ()) -> IO RLE
+makeRLE a = makeRLEs 1 a >>= return . head
+
+makeRLEs :: Int -> (Ptr () -> IO ()) -> IO [RLE]
+makeRLEs num a = allocaBytesAligned (num * {#sizeof RLE #}) {#alignof RLE#} (\prle -> do
+    a prle
+    go num prle [])
+  where
+    go 0 _ rles = return $ reverse rles
+    go n prle rles = do
+        rle <- peekRLE prle
+        go (n-1) (prle `plusPtr` {#sizeof RLE#}) (rle : rles)
+
+    peekRLE prle = do
+        h <- fromIntegral <$> {#get RLE->h #} prle
+        w <- fromIntegral <$> {#get RLE->w #} prle
+        m <- fromIntegral <$> {#get RLE->m #} prle
+        raw_c <- {#get RLE->cnts #} prle
+        mgr_c <- newForeignPtr finalizerFree raw_c
+        return $ RLE h w m mgr_c
+
+withRLE :: RLE -> (Ptr () -> IO a) -> IO a
+withRLE rle = withRLEs [rle]
+
+withRLEs :: [RLE] -> (Ptr () -> IO a) -> IO a
+withRLEs rles = withRLEsLen (length rles) rles
+
+withRLEsLen :: Int -> [RLE] -> (Ptr () -> IO a) -> IO a
+withRLEsLen num rles a = do
+    allocaBytesAligned (num * {#sizeof RLE#}) {#alignof RLE#} $ \prles -> do
+        go prles rles
+        ret <- a prles
+        mapM_ (touchForeignPtr . _rle_cnts) rles
+        return ret
+  where 
+    go _ [] = return ()
+    go prles (rle : nrles) = do
+        pokeRLE prles rle
+        go (prles `plusPtr` {#sizeof RLE#}) nrles
+
+    -- must touch _rle_cnts after using the prle
+    pokeRLE prle (RLE h w m c) = do
+        {#set RLE.h #} prle (fromIntegral h)
+        {#set RLE.w #} prle (fromIntegral w)
+        {#set RLE.m #} prle (fromIntegral m)
+        {#set RLE.cnts #} prle (unsafeForeignPtrToPtr c)
+
+svUnsafeWith :: Storable a => SV.Vector a -> (Ptr a -> IO b) -> IO b
+svUnsafeWith = SV.unsafeWith
+
+newtype BB = BB (SV.Vector (CDouble, CDouble, CDouble, CDouble))
+
+{#pointer BB as PtrBB #}
+
+{#fun rleInit as rleInit_
+    {
+        `Ptr ()',
+        `Int',
+        `Int',
+        `Int',
+        id `Ptr CUInt'
+    } -> `()'
+#}
+
+rleInit :: Int -> Int -> [CUInt] -> IO RLE
+rleInit h w cnts = do
+    makeRLE (\pr -> withArrayLen cnts (\m pc -> rleInit_ pr h w m pc))
+
+-- cause the storage owned by rle to be freed immediately,
+-- without not calling the c-api rleFree
+rleFree :: RLE -> IO ()
+rleFree rle = finalizeForeignPtr (_rle_cnts rle)
+
+{#fun rleEncode as rleEncode_
+    {
+        `Ptr ()',
+        id `Ptr CUChar',
+        `Int',
+        `Int',
+        `Int'
+    } -> `()'
+#}
+  
+rleEncode :: SV.Vector CUChar -> Int -> Int -> Int -> IO [RLE]
+rleEncode m h w n = do
+    makeRLEs n (\ prle ->
+        svUnsafeWith m (\pm -> do 
+            rleEncode_ prle (castPtr pm) h w n))
+
+{#fun rleDecode as rleDecode_
+    {
+        `Ptr ()',
+        id `Ptr CUChar',
+        `Int'
+    } -> `()'
+#}
+
+rleDecode :: [RLE] -> Int -> Int -> IO (SV.Vector CUChar)
+rleDecode rles h w = do
+    let n = length rles 
+        size = n * h * w
+    mv <- SVM.new size
+    SVM.unsafeWith mv $ (\ptr -> do
+        withRLEsLen n rles $ \prles -> do
+            rleDecode_ prles ptr n)
+    SV.unsafeFreeze mv
+
+{#fun rleMerge as rleMerge_
+    {
+        `Ptr ()',
+        `Ptr ()',
+        `Int',
+        `Bool'
+    } -> `()'
+#}
+
+rleMerge :: [RLE] -> Bool -> IO RLE
+rleMerge rles intersect = do
+    let num = length rles
+    withRLEsLen num rles $ \prles -> 
+        makeRLE $ \porle ->
+            rleMerge_ prles porle num intersect
+
+{#fun rleArea as rleArea_
+    {
+        withRLEs* `[RLE]',
+        `Int',
+        id `Ptr CUInt'
+    } -> `()'
+#}
+
+rleArea :: [RLE] -> Int -> IO [CUInt]
+rleArea r n = do
+    allocaArray n (\pa -> do
+        rleArea_ r n pa
+        peekArray n pa)
+    
+{#fun rleIou as rleIou_
+    {
+        `Ptr ()',
+        `Ptr ()',
+        `Int',
+        `Int',
+        svUnsafeWith* `SV.Vector CUChar',
+        id `Ptr CDouble'
+    } -> `()'
+#}
+
+rleIou :: [RLE] -> [RLE] -> [Bool] -> IO ((Int,Int), [Double])
+rleIou dt gt iscrowd = do
+    let m = length dt
+        n = length gt
+        c = length iscrowd
+    assert (n == c) $ allocaArray (m*n) $ \po -> 
+        withRLEsLen m dt $ \pdt -> 
+        withRLEsLen n gt $ \pgt -> do 
+            rleIou_ pdt pgt m n (SV.fromList $ map (toEnum . fromEnum) iscrowd) po
+            raw <- peekArray (m * n) po
+            return $ ((m,n), map realToFrac raw)
+
+{#fun rleNms as rleNms_
+    {
+        withRLEs* `[RLE]',
+        `Int',
+        id `Ptr CUInt',
+        `CDouble'
+    } -> `()'
+#}
+
+rleNms :: [RLE] -> Double -> IO [Bool]
+rleNms dt thr = do
+    let n = length dt
+    allocaArray n $ \keep -> do
+        rleNms_ dt n keep (realToFrac thr)
+        map (>0) <$> peekArray n keep
+
+{#fun bbIou as bbIou_
+    {
+        `PtrBB',
+        `PtrBB',
+        `Int',
+        `Int',
+        svUnsafeWith* `SV.Vector CUChar',
+        id `Ptr CDouble'
+    } -> `()'
+#}
+
+bbIou :: BB -> BB -> [Bool] -> IO ((Int,Int), [Double])
+bbIou (BB dt) (BB gt) iscrowd = do
+    let m = SV.length dt
+        n = SV.length gt
+        c = length iscrowd
+    assert (n == c) $ allocaArray (m*n) $ \po ->
+        svUnsafeWith dt $ \pdt -> svUnsafeWith gt $ \pgt -> do
+            bbIou_ (castPtr pdt) (castPtr pgt) m n (SV.fromList $ map (toEnum . fromEnum) iscrowd) po
+            raw <- peekArray (m * n) po
+            return $ ((m,n), map realToFrac raw)
+
+{#fun bbNms as bbNms_
+    {
+        `PtrBB',
+        `Int',
+        id `Ptr CUInt',
+        `CDouble'
+    } -> `()'
+#}
+
+bbNms :: BB -> Double -> IO [Bool]
+bbNms (BB dt) thr = do
+    let n = SV.length dt
+    svUnsafeWith dt $ \pbb -> 
+        allocaArray n $ \keep -> do
+            bbNms_ (castPtr pbb) n keep (realToFrac thr)
+            map (>0) <$> peekArray n keep
+
+{#fun rleToBbox as rleToBbox_
+    {
+        withRLEs* `[RLE]',
+        `PtrBB',
+        `Int'
+    } -> `()'
+#}
+
+rleToBbox :: [RLE] -> IO BB
+rleToBbox r = do
+    let n = length r
+    mbb <- SVM.new n
+    SVM.unsafeWith mbb $ \pbb -> rleToBbox_ r (castPtr pbb) n
+    BB <$> SV.unsafeFreeze mbb
+
+{#fun rleFrBbox as rleFrBbox_
+    {
+        `Ptr ()',
+        `PtrBB',
+        `Int',
+        `Int',
+        `Int'
+    } -> `()'
+#}
+
+rleFrBbox :: BB -> Int -> Int -> IO [RLE]
+rleFrBbox (BB bb) h w = do
+    let n = SV.length bb
+    makeRLEs n $ \prles -> svUnsafeWith bb $ \pbb -> do
+        rleFrBbox_ prles (castPtr pbb) h w n
+
+{#fun rleFrPoly as rleFrPoly_
+    {
+        `Ptr ()',
+        id `Ptr CDouble',
+        `Int',
+        `Int',
+        `Int'
+    } -> `()'
+#}
+
+rleFrPoly :: SV.Vector (CDouble, CDouble) -> Int -> Int -> IO RLE
+rleFrPoly xy h w = do
+    let k = SV.length xy
+    makeRLE $ \prle -> svUnsafeWith xy $ \pxy -> do
+        rleFrPoly_ prle (castPtr pxy) k h w
+
+{#fun rleToString as ^
+    {
+        withRLE* `RLE'
+    } -> `BS.ByteString' peekAndFreeCString*
+#}
+
+peekAndFreeCString :: Ptr CChar -> IO BS.ByteString
+peekAndFreeCString cstr = do
+    hstr <- BS.packCString cstr
+    free cstr
+    return hstr
+
+{#fun rleFrString as rleFrString_
+    {
+        `Ptr ()',
+        withByteString* `BS.ByteString',
+        `Int',
+        `Int'
+    } -> `()'
+#}
+
+rleFrString :: BS.ByteString -> Int -> Int -> IO RLE
+rleFrString bs h w = do
+    makeRLE $ (\pr -> rleFrString_ pr bs h w)
+
+withByteString :: BS.ByteString -> (CString -> IO a) -> IO a
+withByteString = BS.useAsCString
diff --git a/src/MXNet/Coco/Types.hs b/src/MXNet/Coco/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/MXNet/Coco/Types.hs
@@ -0,0 +1,166 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
+module MXNet.Coco.Types where
+
+import Control.Applicative
+import Data.Aeson
+import qualified Data.Attoparsec.Text as A
+import Data.Time.Calendar (Day, fromGregorianValid)
+import Data.Time (LocalTime(..), TimeOfDay(..))
+import Data.Bits ((.&.))
+import Data.Char (ord)
+import Data.Vector (Vector)
+import Control.Lens (makeLenses)
+import GHC.Generics (Generic)
+import Data.Store (Store)
+
+data Instance = Instance {
+    _info :: Info,
+    _images :: Vector Image,
+    _annotations :: Vector Annotation,
+    _licenses :: Vector License,
+    _categories :: Vector Category
+} deriving Generic
+
+instance Store Instance
+
+instance FromJSON Instance where
+    parseJSON = withObject "Instance" $ \v -> Instance
+        <$> v .: "info"
+        <*> v .: "images"
+        <*> v .: "annotations"
+        <*> v .: "licenses"
+        <*> v .: "categories"
+
+data Info = Info {
+    _info_year :: Int,
+    _info_version :: String,
+    _info_description :: String,
+    _info_contributor :: String,
+    _info_url :: String,
+    _info_date_created :: CocoDay
+} deriving Generic
+
+instance Store Info
+
+instance FromJSON Info where
+    parseJSON = withObject "Info" $ \v -> Info
+        <$> v .: "year"
+        <*> v .: "version"
+        <*> v .: "description"
+        <*> v .: "contributor"
+        <*> v .: "url"
+        <*> v .: "date_created"
+
+data License = License {
+    _lic_id :: Int,
+    _lic_name :: String,
+    _lic_url :: String
+} deriving Generic
+
+instance Store License
+
+instance FromJSON License where
+    parseJSON = withObject "License" $ \v -> License
+        <$> v .: "id"
+        <*> v .: "name"
+        <*> v .: "url"
+
+data Image = Image {
+    _img_id :: !Int, 
+    _img_width :: !Int, 
+    _img_height :: !Int, 
+    _img_file_name :: !String, 
+    _img_license :: !Int, 
+    _img_flickr_url :: !String, 
+    _img_coco_url :: !String,
+    _img_date_captured :: !LocalTime
+} deriving (Generic, Show)
+
+deriving instance Generic TimeOfDay
+deriving instance Generic LocalTime
+instance Store TimeOfDay
+instance Store LocalTime
+instance Store Image
+
+instance FromJSON Image where
+    parseJSON = withObject "Image" $ \v -> Image
+        <$> v .: "id"
+        <*> v .: "width"
+        <*> v .: "height"
+        <*> v .: "file_name"
+        <*> v .: "license"
+        <*> v .: "flickr_url"
+        <*> v .: "coco_url"
+        <*> v .: "date_captured"
+
+data Annotation = AnnObjectDetection {
+    _ann_id :: !Int,
+    _ann_image_id :: !Int,
+    _ann_category_id :: !Int,
+    _ann_segmentation :: !Segmentation,
+    _ann_area :: !Float,
+    _ann_bbox :: !(Float, Float, Float, Float)
+} deriving Generic
+
+instance Store Annotation
+
+instance FromJSON Annotation where
+    parseJSON = withObject "Annotation" $ \v -> AnnObjectDetection
+        <$> v .: "id"
+        <*> v .: "image_id"
+        <*> v .: "category_id"
+        <*> v .: "segmentation"
+        <*> v .: "area"
+        <*> v .: "bbox"
+
+data Segmentation = SegRLE { _seg_counts :: [Int], _seg_size :: (Int, Int)} | SegPolygon [[Double]]
+  deriving Generic
+
+instance Store Segmentation
+
+instance FromJSON Segmentation where
+    parseJSON value = (withObject "RLE" (\v -> SegRLE <$> v .: "counts" <*> v .: "size") value) <|>
+                      (withArray "Polygon" (\v -> SegPolygon <$> parseJSONList (Array v)) value)
+
+data Category = CatObjectDetection {
+    _odc_id :: Int,
+    _odc_name :: String,
+    _odc_supercategory :: String
+} deriving Generic
+
+instance Store Category
+
+instance FromJSON Category where
+    parseJSON = withObject "Category" $ \v -> CatObjectDetection
+        <$> v .: "id"
+        <*> v .: "name"
+        <*> v .: "supercategory"
+
+newtype CocoDay = CocoDay Day deriving Generic
+
+instance Store CocoDay
+
+instance FromJSON CocoDay where
+    parseJSON = withText "Day" $ \t -> case A.parseOnly (day <* A.endOfInput) t of
+        Left err -> fail $ "could not parse date: " ++ err
+        Right r  -> return r
+      where
+        day = do
+            y <- (A.decimal <* A.char '/') <|> fail "date must be of form YYYY/MM/DD"
+            m <- (twoDigits <* A.char '/') <|> fail "date must be of form YYYY/MM/DD"
+            d <- twoDigits <|> fail "date must be of form YYYY/MM/DD"
+            maybe (fail "invalid date") return (CocoDay <$> fromGregorianValid y m d)
+        twoDigits = do
+              a <- A.digit
+              b <- A.digit
+              let c2d c = ord c .&. 15
+              return $! c2d a * 10 + c2d b
+
+makeLenses ''Instance
+makeLenses ''Info
+makeLenses ''License
+makeLenses ''Image
+makeLenses ''Annotation
+makeLenses ''Segmentation
+makeLenses ''Category
diff --git a/src/MXNet/NN/DataIter/Anchor.hs b/src/MXNet/NN/DataIter/Anchor.hs
new file mode 100644
--- /dev/null
+++ b/src/MXNet/NN/DataIter/Anchor.hs
@@ -0,0 +1,236 @@
+{-# LANGUAGE TemplateHaskell #-}
+module MXNet.NN.DataIter.Anchor where
+
+import qualified Data.IntSet as Set
+import Control.Exception
+import qualified Data.Vector as V
+import qualified Data.Vector.Unboxed as UV
+import qualified Data.Vector.Unboxed.Mutable as UVM
+import Control.Lens (view, makeLenses)
+import Control.Monad.Reader
+import Data.Random (shuffleN, runRVar, StdRandom(..))
+import Data.Array.Repa (Array, DIM1, DIM2, D, U, (:.)(..), Z (..), All(..), (+^), fromListUnboxed)
+import qualified Data.Array.Repa as Repa
+
+-- import Debug.Trace
+
+type Anchor r = Array r DIM1 Float
+type GTBox r = Array r DIM1 Float
+
+data Configuration = Configuration {
+    _conf_anchor_scales :: [Int],
+    _conf_anchor_ratios :: [Float],
+    _conf_allowed_border :: Int,
+    _conf_fg_num :: Int, 
+    _conf_batch_num :: Int,
+    _conf_bg_overlap :: Float,
+    _conf_fg_overlap :: Float
+} deriving Show
+makeLenses ''Configuration
+
+anchors :: MonadReader Configuration m => 
+    Int -> Int -> Int -> m (V.Vector (Anchor U))
+anchors stride width height = do
+    scales <- view conf_anchor_scales
+    ratios <- view conf_anchor_ratios
+    base   <- baseAnchors stride
+    return $ V.fromList 
+        [ Repa.computeS $ anch +^ offs
+        | offY <- grid height
+        , offX <- grid width
+        , anch <- base 
+        , let offs = fromListUnboxed (Z :. 4) [offX, offY, offX, offY]]
+  where
+    grid size = map fromIntegral [0, stride .. size * stride-1]
+
+baseAnchors :: MonadReader Configuration m => 
+    Int -> m ([Anchor U])
+baseAnchors size = do
+    scales <- view conf_anchor_scales
+    ratios <- view conf_anchor_ratios
+    return [makeBase s r | r <- ratios, s <- scales]
+  where
+    makeBase :: Int -> Float -> Anchor U
+    makeBase scale ratio = 
+        let sizeF = fromIntegral size - 1
+            (w, h, x, y) = whctr (0, 0, sizeF, sizeF)
+            ws = round $ sqrt (w * h / ratio) :: Int
+            hs = round $ (fromIntegral ws) * ratio :: Int
+        in mkanchor x y (fromIntegral $ ws * scale) (fromIntegral $ hs * scale)
+
+whctr :: (Float, Float, Float, Float) -> (Float, Float, Float, Float)
+whctr (x0, y0, x1, y1) = (w, h, x, y)
+  where
+    w = x1 - x0 + 1
+    h = y1 - y0 + 1
+    x = x0 + 0.5 * (w - 1)
+    y = y0 + 0.5 * (h - 1)
+
+mkanchor :: Float -> Float -> Float -> Float -> Anchor U
+mkanchor x y w h = fromListUnboxed (Z :. 4) [x - hW, y - hH, x + hW, y + hH]
+  where
+    hW = 0.5 * (w - 1)
+    hH = 0.5 * (h - 1)
+
+(#!) :: Array U DIM1 Float -> Int -> Float
+(#!) = Repa.unsafeLinearIndex
+
+(%!) :: V.Vector a -> Int -> a
+(%!) = (V.!)
+
+overlapMatrix :: Set.IntSet -> V.Vector (GTBox U) -> V.Vector (Anchor U) -> Array D DIM2 Float
+overlapMatrix goodIndices gtBoxes anBoxes = Repa.fromFunction (Z :. width :. height) calcOvp
+  where
+    width = V.length gtBoxes
+    height = V.length anBoxes
+
+    calcArea box = (box #! 2 - box #! 0 + 1) * (box #! 3 - box #! 1 + 1)
+    areaA = V.map calcArea anBoxes
+    areaG = V.map calcArea gtBoxes
+
+    calcOvp (Z :. ig :. ia) = 
+        let gt = gtBoxes %! ig
+            anchor = anBoxes %! ia
+            iw = min (gt #! 2) (anchor #! 2) - max (gt #! 0) (anchor #! 0)
+            ih = min (gt #! 3) (anchor #! 3) - max (gt #! 1) (anchor #! 1)
+            areaI = iw * ih
+            areaU = areaA %! ia + areaG %! ig - areaI
+        in if Set.member ia goodIndices && iw > 0 && ih > 0 then areaI / areaU else 0
+
+type Labels  = Repa.Array U DIM1 Float -- UV.Vector Int
+type Targets = Repa.Array U DIM2 Float -- UV.Vector (Float, Float, Float, Float)
+type Weights = Repa.Array U DIM2 Float -- UV.Vector (Float, Float, Float, Float)
+
+assign :: (MonadReader Configuration m, MonadIO m) => 
+    V.Vector (GTBox U) -> Int -> Int -> V.Vector (Anchor U) -> m (Labels, Targets, Weights)
+assign gtBoxes imWidth imHeight anBoxes 
+    | numGT == 0 = do
+        goodIndices <- filterGoodIndices
+        liftIO $ do
+            indices <- runRVar (shuffleN (Set.size goodIndices) (Set.toList goodIndices)) StdRandom
+            labels <- UVM.replicate numLabels (-1)
+            forM_ indices $ flip (UVM.write labels) 0
+            let targets = UV.replicate (numLabels * 4) 0
+                weights = UV.replicate (numLabels * 4) 0
+            labels <- UV.unsafeFreeze labels
+            let labelsRepa  = Repa.fromUnboxed (Z:.numLabels) labels
+                targetsRepa = Repa.fromUnboxed (Z:.numLabels:.4) targets
+                weightsRepa = Repa.fromUnboxed (Z:.numLabels:.4) weights
+            return (labelsRepa, targetsRepa, weightsRepa)
+
+    | otherwise = do
+        _fg_overlap <- view conf_fg_overlap
+        _bg_overlap <- view conf_bg_overlap
+        _batch_num  <- view conf_batch_num
+        _fg_num     <- view conf_fg_num
+    
+        goodIndices <- filterGoodIndices
+
+        -- traceShowM ("#Good Anchors:", V.length goodIndices)
+
+        liftIO $ do
+            -- TODO filter valid anchor boxes
+            -- TODO case when gtBoxes is empty.
+            labels <- UVM.replicate numLabels (-1)
+
+            overlaps <- return $ Repa.computeUnboxedS $ overlapMatrix goodIndices gtBoxes anBoxes
+            -- for each GT, the hightest overlapping anchor is FG.
+            forM_ [0..numGT-1] $ \i -> do
+                -- let j = UV.maxIndex $ Repa.toUnboxed $ Repa.computeS $ Repa.slice overlaps (Z :. i :. All)
+                let j = argMax overlaps 0 i
+                -- traceShowM $ ("GT -> ", j)
+                UVM.write labels j 1
+            
+            -- FG anchors that have overlapping with any GT >= thresh
+            -- BG anchors that have overlapping with all GT < thresh
+            UV.forM_ (UV.indexed $ Repa.toUnboxed $ Repa.foldS max 0 $ Repa.transpose overlaps) $ \(i, m) -> do
+                when (Set.member i goodIndices) $ do
+                    when (m >= _fg_overlap) $ do
+                        -- traceShowM ("FG enable ", m, i)
+                        (UVM.write labels i 1)
+                    when (m < _bg_overlap) $ do
+                        -- s <- UVM.read labels i
+                        -- when (s == 1) $ traceShowM ("FG disable ", m, i)
+                        (UVM.write labels i 0)
+
+            -- subsample FG anchors if there are too many
+            fgs <- UV.findIndices (==1) <$> UV.unsafeFreeze labels
+            let numFG = UV.length fgs
+            when (numFG > _fg_num) $ do
+                indices <- runRVar (shuffleN numFG $ UV.toList fgs) StdRandom
+                -- traceShowM ("Disable A", take (numFG - _fg_num) indices)
+                forM_ (take (numFG - _fg_num) indices) $
+                    flip (UVM.write labels) (-1)
+
+            -- subsample BG anchors if there are too many
+            bgs <- UV.findIndices (==0) <$> UV.unsafeFreeze labels
+            let numBG = UV.length bgs
+                maxBG = _batch_num - min numFG _fg_num
+            when (numBG > maxBG) $ do
+                indices <- runRVar (shuffleN numBG $ UV.toList bgs) StdRandom
+                -- traceShowM ("Disable B", take (numBG - maxBG) indices)
+                forM_ (take (numBG - maxBG) indices) $ 
+                    flip (UVM.write labels) (-1)
+
+            -- compute the regression from each FG anchor to its gt
+            -- let gts = UV.map (\i -> UV.maxIndex $ Repa.toUnboxed $ Repa.computeS $ Repa.slice overlaps (Z :. i :. All)) fgs
+            let gts = UV.map (argMax overlaps 1) fgs
+                gtDiffs = UV.zipWith makeTarget fgs gts
+            targets <- UVM.replicate numLabels (0, 0, 0, 0)
+            UV.zipWithM_ (UVM.write targets) fgs gtDiffs
+            
+            -- indicates which anchors have a regression 
+            weights <- UVM.replicate numLabels (0, 0, 0, 0)
+            UV.forM_ fgs $ flip (UVM.write weights) (1, 1, 1, 1)
+
+            labels  <- UV.unsafeFreeze labels
+            targets <- UV.unsafeFreeze targets
+            weights <- UV.unsafeFreeze weights 
+            let labelsRepa  = Repa.fromUnboxed (Z:.numLabels) labels
+                targetsRepa = Repa.fromUnboxed (Z:.numLabels:.4) (flattenT targets)
+                weightsRepa = Repa.fromUnboxed (Z:.numLabels:.4) (flattenT weights)
+            return (labelsRepa, targetsRepa, weightsRepa)
+  where
+    numGT = V.length gtBoxes
+    numLabels = V.length anBoxes
+
+    argMax :: Array U DIM2 Float -> Int -> Int -> Int
+    argMax mat axis ind = 
+        let series = case axis of 
+                       0 -> Repa.slice mat $ Z :. ind :. All
+                       1 -> Repa.slice mat $ Z :. All :. ind
+                       _ -> throw BadDimension
+        in UV.maxIndex $ Repa.toUnboxed $ Repa.computeS series
+
+    asTuple :: Array U DIM1 Float -> (Float, Float, Float, Float)
+    asTuple box = (box #! 0, box #! 1, box #! 2, box #! 3)
+
+    filterGoodIndices :: MonadReader Configuration m => m Set.IntSet
+    filterGoodIndices = do
+        _allowed_border <- fromIntegral <$> view conf_allowed_border
+        let goodAnchor (x0, y0, x1, y1) =
+                x0 >= -_allowed_border &&
+                y0 >= -_allowed_border &&
+                x1 < fromIntegral imWidth + _allowed_border &&
+                y1 < fromIntegral imHeight + _allowed_border    
+        return $ Set.fromList $ V.toList $ V.findIndices (goodAnchor . asTuple) anBoxes
+
+    makeTarget :: Int -> Int -> (Float, Float, Float, Float)
+    makeTarget fgi gti = 
+        let fgBox = anBoxes %! fgi
+            gtBox = gtBoxes %! gti
+            (w1, h1, cx1, cy1) = whctr $ asTuple fgBox
+            (w2, h2, cx2, cy2) = whctr $ asTuple gtBox
+            dx = (cx2 - cx1) / (w1 + 1e-14)
+            dy = (cy2 - cy1) / (h1 + 1e-14)
+            dw = log (w2 / w1)
+            dh = log (h2 / h1)
+        in (dx, dy, dw, dh)
+
+    -- TODO: make it without any copy
+    flattenT :: UV.Vector (Float, Float, Float, Float) -> UV.Vector Float
+    flattenT = UV.concatMap (\(a,b,c,d) -> UV.fromList [a,b,c,d])
+
+data AnchorError = BadDimension
+  deriving Show
+instance Exception AnchorError
diff --git a/src/MXNet/NN/DataIter/Coco.hs b/src/MXNet/NN/DataIter/Coco.hs
new file mode 100644
--- /dev/null
+++ b/src/MXNet/NN/DataIter/Coco.hs
@@ -0,0 +1,352 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module MXNet.NN.DataIter.Coco (
+    cocoImages,
+    cocoImagesWithAnchors,
+    cocoImagesWithAnchors',
+    Coco(..),
+    coco,
+) where
+
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.List (unzip6)
+import System.FilePath
+import System.Directory
+import GHC.Generics (Generic)
+import qualified Data.ByteString as SBS
+import qualified Data.Store as Store
+import Control.Exception
+import Data.Array.Repa (Array, DIM1, DIM3, D, U, (:.)(..), Z (..), Any(..),
+    fromListUnboxed, extent, backpermute, extend, (-^), (+^), (*^), (/^))
+import qualified Data.Array.Repa as Repa
+import Data.Array.Repa.Repr.Unboxed (Unbox)
+import qualified Data.Vector as V
+import qualified Data.Vector.Unboxed as UV
+import qualified Codec.Picture.Repa as RPJ
+import Codec.Picture
+import Codec.Picture.Extra
+import qualified Data.Aeson as Aeson
+import Control.Lens ((^.), (%~) , view, makeLenses, _1, _2)
+import Data.Conduit
+import qualified Data.Conduit.Combinators as C (yieldMany)
+import qualified Data.Conduit.List as C
+import Control.Monad.Reader
+import qualified Data.IntMap.Strict as M
+import Data.Maybe (fromJust)
+import qualified Data.Random as RND (shuffleN, runRVar, StdRandom(..))
+
+import MXNet.Base (NDArray(..), Fullfilled, ArgsHMap, ParameterList, Attr(..), (!), (!?), (.&), HMap(..), ArgOf(..), fromVector,zeros)
+import MXNet.Base.Operators.NDArray (_Reshape)
+import MXNet.NN.DataIter.Conduit
+import qualified MXNet.NN.DataIter.Anchor as Anchor
+import MXNet.Coco.Types
+
+data Coco = Coco FilePath String Instance
+  deriving Generic
+instance Store.Store Coco
+
+raiseLeft :: Exception e => (a -> e) -> Either a b -> b
+raiseLeft exc = either (throw . exc) id
+
+data FileNotFound = FileNotFound String String
+  deriving Show
+instance Exception FileNotFound
+
+cached :: Store.Store a => String -> IO a -> IO a
+cached name action = do
+    createDirectoryIfMissing True "cache"
+    hitCache <- doesFileExist path
+    if hitCache then
+        SBS.readFile path >>= Store.decodeIO
+    else do
+        obj <- action
+        SBS.writeFile path (Store.encode obj)
+        return obj
+  where
+    path = "cache/" ++ name
+
+coco :: String -> String -> IO Coco
+coco base datasplit = cached (datasplit ++ ".store") $ do
+    let annotationFile = base </> "annotations" </> ("instances_" ++ datasplit ++ ".json")
+    inst <- raiseLeft (FileNotFound annotationFile) <$> Aeson.eitherDecodeFileStrict' annotationFile
+    return $ Coco base datasplit inst
+
+type ImageTensor = Array U DIM3 Float
+type ImageInfo = Array U DIM1 Float
+type GTBoxes = V.Vector (Array U DIM1 Float)
+
+data Configuration = Configuration {
+    _conf_short :: Int,
+    _conf_max_size :: Int,
+    _conf_mean :: (Float, Float, Float),
+    _conf_std :: (Float, Float, Float)
+}
+makeLenses ''Configuration
+
+cocoImages :: (MonadReader Configuration m, MonadIO m) => Coco -> Bool -> ConduitData m (ImageTensor, ImageInfo, GTBoxes)
+cocoImages (Coco base datasplit inst) shuffle = ConduitData (Just 1) $ do
+    let all = inst ^. images
+    all_images <- if shuffle then
+                    liftIO $ RND.runRVar (RND.shuffleN (length all) (V.toList all)) RND.StdRandom 
+                  else
+                    return $ V.toList all
+    C.yieldMany all_images {-- .| C.iterM (liftIO . print) --} .| C.mapM loadImg .| C.catMaybes
+  where
+    -- dropAlpha tensor =
+    --     let Z :. _ :. w :. h = extent tensor
+    --     in fromFunction (Z :. (3 :: Int) :. w :. h) (tensor Repa.!)
+    loadImg img = do
+        short <- view conf_short
+        maxSize <- view conf_max_size
+
+        let imgFilePath = base </> datasplit </> img ^. img_file_name
+        imgDyn <- raiseLeft (FileNotFound imgFilePath) <$> liftIO (readImage imgFilePath)
+
+        let imgRGB = convertRGB8 imgDyn
+            imgH = fromIntegral $ imageHeight imgRGB
+            imgW = fromIntegral $ imageWidth imgRGB
+
+            scale = calcScale imgW imgH short maxSize
+            imgH' = floor $ scale * imgH
+            imgW' = floor $ scale * imgW
+            imgInfo = fromListUnboxed (Z :. 3) [fromIntegral imgH', fromIntegral imgW', scale]
+
+            imgResized = scaleBilinear imgW' imgH' imgRGB
+            imgRGBRepa = Repa.computeUnboxedS $ RPJ.imgData (RPJ.convertImage imgResized :: RPJ.Img RPJ.RGB)
+
+            gt_boxes = get_gt_boxes scale img
+
+        if V.null gt_boxes 
+            then return Nothing 
+            else do
+                let imgRGBRepa' = Repa.computeUnboxedS $ Repa.map fromIntegral imgRGBRepa
+                imgEval <- transform imgRGBRepa'
+                return $ Just (imgEval, imgInfo, gt_boxes)
+
+    -- find a proper scale factor
+    calcScale imgW imgH short maxSize =
+      let imSizeMin = min imgH imgW
+          imSizeMax = max imgH imgW
+          imScale0 = fromIntegral short / imSizeMin  :: Float
+          imScale1 = fromIntegral maxSize / imSizeMax :: Float
+      in if round (imScale0 * imSizeMax) > maxSize then imScale1 else imScale0
+
+    -- map each category from id to its index in the cocoClassNames.
+    catTabl = M.fromList $ V.toList $ V.map (\cat -> (cat ^. odc_id, fromJust $ V.elemIndex (cat ^. odc_name) cocoClassNames)) (inst ^. categories)
+
+    -- get all the bbox and gt for the image
+    get_gt_boxes scale img = V.fromList $ catMaybes $ map makeGTBox $ V.toList imgAnns
+      where
+        imageId = img ^. img_id
+        width   = img ^. img_width
+        height  = img ^. img_height
+        imgAnns = V.filter (\ann -> ann ^. ann_image_id == imageId) (inst ^. annotations)
+
+        cleanBBox (x, y, w, h) =
+          let x0 = max 0 x
+              y0 = max 0 y
+              x1 = min (fromIntegral width - 1)  (x0 + max 0 (w-1))
+              y1 = min (fromIntegral height - 1) (y0 + max 0 (h-1))
+          in (x0, y0, x1, y1)
+
+        makeGTBox ann =
+          let (x0, y0, x1, y1) = cleanBBox (ann ^. ann_bbox)
+              classId = catTabl M.! (ann ^. ann_category_id)
+              
+          in
+          if ann ^. ann_area > 0 && x1 > x0 && y1 > y0
+            then Just $ fromListUnboxed (Z :. 5) [x0*scale, y0*scale, x1*scale, y1*scale, fromIntegral classId]
+            else Nothing
+
+
+-- transform HWC -> CHW
+transform :: MonadReader Configuration m =>
+    Array U DIM3 Float -> m (Array U DIM3 Float)
+transform img = do
+    mean <- view conf_mean
+    std <- view conf_std
+    let broadcast = Repa.computeUnboxedS . extend (Any :. height :. width)
+        mean' = broadcast $ fromTuple mean
+        std'  = broadcast $ fromTuple std
+        chnFirst = backpermute newShape (\ (Z :. c :. h :. w) -> Z :. h :. w :. c) img
+    return $ Repa.computeUnboxedS $ (chnFirst -^ mean') /^ std'
+  where
+    (Z :. height :. width :. chn) = extent img
+    newShape = Z:. chn :. height :. width
+
+-- transform CHW -> HWC
+transformInv :: (Repa.Source r Float, MonadReader Configuration m) =>
+    Array r DIM3 Float -> m (Array D DIM3 Float)
+transformInv img = do
+    mean <- view conf_mean
+    std <- view conf_std
+    let broadcast = extend (Any :. height :. width)
+        mean' = broadcast $ fromTuple mean
+        std'  = broadcast $ fromTuple std
+        addMean = img *^ std' +^ mean'
+    return $ backpermute newShape (\ (Z :. h :. w :. c) -> Z :. c :. h :. w) addMean
+  where
+    (Z :. chn :. height :. width) = extent img
+    newShape = Z :. height :. width :. chn
+
+fromTuple :: Unbox a => (a, a, a) -> Array U (Z :. Int) a
+fromTuple (a, b, c) = fromListUnboxed (Z :. (3 :: Int)) [a,b,c]
+
+cocoClassNames = V.fromList [
+    "__background__",  -- always index 0
+    "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train",
+    "truck", "boat", "traffic light", "fire hydrant", "stop sign",
+    "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep",
+    "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella",
+    "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard",
+    "sports ball", "kite", "baseball bat", "baseball glove", "skateboard",
+    "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork",
+    "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange",
+    "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair",
+    "couch", "potted plant", "bed", "dining table", "toilet", "tv",
+    "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave",
+    "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase",
+    "scissors", "teddy bear", "hair drier", "toothbrush"]
+
+type instance ParameterList "CocoImagesWithAnchors" =
+    '[ '("batch_size",     'AttrReq Int),
+       '("short_size",     'AttrReq Int),
+       '("long_size",      'AttrReq Int),
+       '("mean",           'AttrReq (Float, Float, Float)),
+       '("std",            'AttrReq (Float, Float, Float)),
+       '("feature_stride", 'AttrOpt Int),
+       '("anchor_scales",  'AttrOpt [Int]),
+       '("anchor_ratios",  'AttrOpt [Float]),
+       '("allowed_border", 'AttrOpt Int),
+       '("batch_rois",     'AttrOpt Int),
+       '("fg_fraction",    'AttrOpt Float),
+       '("fg_overlap",     'AttrOpt Float),
+       '("bg_overlap",     'AttrOpt Float),
+       '("shuffle",        'AttrOpt Bool)]
+
+
+cocoImagesWithAnchors' :: (Fullfilled "CocoImagesWithAnchors" args, MonadIO m) =>
+    ConduitData (ReaderT Configuration m) (ImageTensor, ImageInfo, GTBoxes) -> 
+    ((Int, Int) -> IO (Int, Int)) -> 
+    ArgsHMap "CocoImagesWithAnchors" args -> 
+    ConduitData m ((NDArray Float, NDArray Float, NDArray Float), (NDArray Float, NDArray Float, NDArray Float))
+cocoImagesWithAnchors' (ConduitData _ images) extractFeatureShape args = ConduitData (Just batchSize) $ 
+    morf images .| C.mapM (assignAnchors anchConf featureStride extractFeatureShape) .| C.chunksOf batchSize .| C.mapM toNDArray
+  where
+    batchSize = args ! #batch_size
+    batchRois     = fromMaybe 256 $ args !? #batch_rois
+    featureStride = fromMaybe 16 $ args !? #feature_stride
+    anchConf = Anchor.Configuration {
+        Anchor._conf_anchor_scales  = fromMaybe [8, 16, 32] $ args !? #anchor_scales,
+        Anchor._conf_anchor_ratios  = fromMaybe [0.5, 1, 2] $ args !? #anchor_ratios,
+        Anchor._conf_allowed_border = fromMaybe 0 $ args !? #allowed_border,
+        Anchor._conf_fg_num         = floor $ (fromMaybe 0.5 $ args !? #fg_fraction) * fromIntegral batchRois,
+        Anchor._conf_batch_num      = batchRois,
+        Anchor._conf_fg_overlap     = fromMaybe 0.7 $ args !? #fg_overlap,
+        Anchor._conf_bg_overlap     = fromMaybe 0.3 $ args !? #bg_overlap
+    }
+    cocoConf = Configuration {
+        _conf_short    = args ! #short_size,
+        _conf_max_size = args ! #long_size,
+        _conf_mean     = args ! #mean,
+        _conf_std      = args ! #std
+    }
+    morf = transPipe (flip runReaderT cocoConf)
+
+cocoImagesWithAnchors :: (Fullfilled "CocoImagesWithAnchors" args, MonadIO m) =>
+    Coco -> ((Int, Int) -> IO (Int, Int)) -> ArgsHMap "CocoImagesWithAnchors" args -> 
+    ConduitData m ((NDArray Float, NDArray Float, NDArray Float), (NDArray Float, NDArray Float, NDArray Float))
+cocoImagesWithAnchors cocoDef extractFeatureShape args = ConduitData (Just batchSize) $ 
+    morf imgs .| C.mapM (assignAnchors anchConf featureStride extractFeatureShape) .| C.chunksOf batchSize .| C.mapM toNDArray
+
+  where
+    ConduitData _ imgs = cocoImages cocoDef shuffle
+    cocoConf = Configuration {
+        _conf_short    = args ! #short_size,
+        _conf_max_size = args ! #long_size,
+        _conf_mean     = args ! #mean,
+        _conf_std      = args ! #std
+    }
+    shuffle       = fromMaybe True $ args !? #shuffle
+    batchSize     = args ! #batch_size
+    batchRois     = fromMaybe 256 $ args !? #batch_rois
+    featureStride = fromMaybe 16 $ args !? #feature_stride
+    anchConf = Anchor.Configuration {
+        Anchor._conf_anchor_scales  = fromMaybe [8, 16, 32] $ args !? #anchor_scales,
+        Anchor._conf_anchor_ratios  = fromMaybe [0.5, 1, 2] $ args !? #anchor_ratios,
+        Anchor._conf_allowed_border = fromMaybe 0 $ args !? #allowed_border,
+        Anchor._conf_fg_num         = floor $ (fromMaybe 0.5 $ args !? #fg_fraction) * fromIntegral batchRois,
+        Anchor._conf_batch_num      = batchRois,
+        Anchor._conf_fg_overlap     = fromMaybe 0.7 $ args !? #fg_overlap,
+        Anchor._conf_bg_overlap     = fromMaybe 0.3 $ args !? #bg_overlap
+    }
+
+    morf = transPipe (flip runReaderT cocoConf)
+
+assignAnchors :: MonadIO m => Anchor.Configuration -> Int -> ((Int, Int) -> IO (Int, Int)) -> (ImageTensor, ImageInfo, GTBoxes) ->
+    m (ImageTensor, ImageInfo, GTBoxes, Repa.Array U DIM1 Float, Repa.Array U DIM3 Float, Repa.Array U DIM3 Float) 
+assignAnchors conf featureStride extractFeatureShape (img, info, gt) = do
+    let imHeight = floor $ info Anchor.#! 0
+        imWidth  = floor $ info Anchor.#! 1
+    (featureWidth, featureHeight) <- liftIO $ extractFeatureShape (imWidth, imHeight)
+    anchors <- runReaderT (Anchor.anchors featureStride featureWidth featureHeight) conf
+
+    (lbls, targets, weights) <- runReaderT (Anchor.assign gt imWidth imHeight anchors) conf
+
+    -- reshape and transpose labls   from (feat_h * feat_w * #anch,  ) to (#anch,     feat_h, feat_w)
+    -- reshape and transpose targets from (feat_h * feat_w * #anch, 4) to (#anch * 4, feat_h, feat_w)
+    -- reshape and transpose weights from (feat_h * feat_w * #anch, 4) to (#anch * 4, feat_h, feat_w)
+    let numAnch = length (conf ^. Anchor.conf_anchor_scales) * length (conf ^. Anchor.conf_anchor_ratios)
+    lbls    <- return $ Repa.computeS $ 
+                Repa.reshape (Z :. numAnch * featureHeight * featureWidth) $ 
+                Repa.transpose $ 
+                Repa.reshape (Z :. featureHeight * featureWidth :. numAnch) lbls
+    targets <- return $ Repa.computeS $ 
+                Repa.reshape (Z :. numAnch * 4 :. featureHeight :. featureWidth) $
+                Repa.transpose $
+                Repa.reshape (Z :. featureHeight * featureWidth :. numAnch * 4) targets
+    weights <- return $ Repa.computeS $ 
+                Repa.reshape (Z :. numAnch * 4 :. featureHeight :. featureWidth) $
+                Repa.transpose $
+                Repa.reshape (Z :. featureHeight * featureWidth :. numAnch * 4) weights
+
+    -- let numAnch = length (anchConf ^. Anchor.conf_anchor_scales) * length (anchConf ^. Anchor.conf_anchor_ratios)
+    --     cvt1 (Z :. i :. h :. w) = Z :. ((h * featureWidth + w) * numAnch + i)
+    --     cvt2 (Z :. i :. h :. w) = let (m, n) = divMod i 4
+    --                               in Z :. ((h * featureWidth + w) * numAnch + m) :. n
+    -- lbls    <- Repa.computeP $ Repa.reshape (Z :. numAnch * featureHeight :. featureWidth) $ 
+    --                 Repa.backpermute (Z :. numAnch :. featureHeight :. featureWidth) cvt1 lbls
+    -- targets <- Repa.computeP $ Repa.backpermute (Z :. numAnch * 4 :. featureHeight :. featureWidth) cvt2 targets
+    -- weights <- Repa.computeP $ Repa.backpermute (Z :. numAnch *4  :. featureHeight :. featureWidth) cvt2 weights 
+
+    return (img, info, gt, lbls, targets, weights)
+
+-- toNDArray :: [((ImageTensor, ImageInfo, GTBoxes, Anchor.Labels, Anchor.Targets, Anchor.Weights))] ->
+--     IO ((NDArray Float, NDArray Float, NDArray Float), (NDArray Float, NDArray Float, NDArray Float))
+toNDArray dat = liftIO $ do
+    imagesC  <- convertToMX images
+    infosC   <- convertToMX infos
+    gtboxesC <- convertToMX [if V.null gt then emtpyGT else convertToRepa (V.toList gt) | gt <- gtboxes]
+    labelsC  <- convertToMX labels
+    targetsC <- convertToMX targets
+    weightsC <- convertToMX weights
+    return ((imagesC, infosC, gtboxesC), (labelsC, targetsC, weightsC))
+    where
+    (images, infos, gtboxes, labels, targets, weights) = unzip6 dat
+    emtpyGT = Repa.fromUnboxed (Z:.0:.5) UV.empty
+
+    convert :: Repa.Shape sh => [Array U sh Float] -> ([Int], UV.Vector Float)
+    convert xs = assert (not (null xs)) $ (ext, vec)
+        where
+        vec = UV.concat $ map Repa.toUnboxed xs
+        sh0 = Repa.extent (head xs)
+        ext = length xs : reverse (Repa.listOfShape sh0)
+        
+    convertToMX :: Repa.Shape sh => [Array U sh Float] -> IO (NDArray Float)
+    convertToMX   = uncurry fromVector . (_2 %~ UV.convert) . convert
+
+    -- shape, at the type level, are sequence of Int, although we wnat to append
+    -- a dimension at the head, we add Int at the tail, they are the same.
+    convertToRepa :: Repa.Shape sh => [Array U sh Float] -> Array U (sh :. Int) Float
+    convertToRepa = uncurry Repa.fromUnboxed . (_1 %~ Repa.shapeOfList . reverse) . convert
