public void doFilter(INyARRaster i_input, INyARRaster i_output, NyARIntSize i_size)
        throws NyARException {
      assert (i_input.isEqualBufferType(NyARBufferType.INT1D_GRAY_8));
      assert (i_output.isEqualBufferType(NyARBufferType.INT1D_GRAY_8));
      int[] in_ptr = (int[]) i_input.getBuffer();
      int[] out_ptr = (int[]) i_output.getBuffer();
      int width = i_size.w;
      int height = i_size.h;
      int col0, col1, col2;
      int bptr = 0;
      // 1行目
      col1 = in_ptr[bptr] * 2 + in_ptr[bptr + width];
      col2 = in_ptr[bptr + 1] * 2 + in_ptr[bptr + width + 1];
      out_ptr[bptr] = (col1 * 2 + col2) / 9;
      bptr++;
      for (int x = 0; x < width - 2; x++) {
        col0 = col1;
        col1 = col2;
        col2 = in_ptr[bptr + 1] * 2 + in_ptr[bptr + width + 1];
        out_ptr[bptr] = (col0 + col1 * 2 + col2) / 12;
        bptr++;
      }
      out_ptr[bptr] = (col1 + col2) / 9;
      bptr++;
      // 2行目-末行-1

      for (int y = 0; y < height - 2; y++) {
        // 左端
        col1 = in_ptr[bptr] * 2 + in_ptr[bptr - width] + in_ptr[bptr + width];
        col2 = in_ptr[bptr + 1] * 2 + in_ptr[bptr - width + 1] + in_ptr[bptr + width + 1];
        out_ptr[bptr] = (col1 + col2) / 12;
        bptr++;
        for (int x = 0; x < width - 2; x++) {
          col0 = col1;
          col1 = col2;
          col2 = in_ptr[bptr + 1] * 2 + in_ptr[bptr - width + 1] + in_ptr[bptr + width + 1];
          out_ptr[bptr] = (col0 + col1 * 2 + col2) / 16;
          bptr++;
        }
        // 右端
        out_ptr[bptr] = (col1 * 2 + col2) / 12;
        bptr++;
      }
      // 末行目
      col1 = in_ptr[bptr] * 2 + in_ptr[bptr - width];
      col2 = in_ptr[bptr + 1] * 2 + in_ptr[bptr - width + 1];
      out_ptr[bptr] = (col1 + col2) / 9;
      bptr++;
      for (int x = 0; x < width - 2; x++) {
        col0 = col1;
        col1 = col2;
        col2 = in_ptr[bptr + 1] * 2 + in_ptr[bptr - width + 1];
        out_ptr[bptr] = (col0 + col1 * 2 + col2) / 12;
        bptr++;
      }
      out_ptr[bptr] = (col1 * 2 + col2) / 9;
      bptr++;
      return;
    }
 public int createHistogram(
     INyARRaster i_reader, NyARIntSize i_size, int[] o_histogram, int i_skip) {
   assert (i_reader.isEqualBufferType(NyARBufferType.WORD1D_R5G6B5_16LE));
   short[] input = (short[]) i_reader.getBuffer();
   int pix_count = i_size.w;
   int pix_mod_part = pix_count - (pix_count % 8);
   for (int y = i_size.h - 1; y >= 0; y -= i_skip) {
     int pt = y * i_size.w;
     int x, v;
     for (x = pix_count - 1; x >= pix_mod_part; x--) {
       v = (int) input[pt];
       v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3)) / 3;
       o_histogram[v]++;
       pt++;
     }
     // タイリング
     for (; x >= 0; x -= 8) {
       v = (int) input[pt];
       pt++;
       v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3)) / 3;
       o_histogram[v]++;
       v = (int) input[pt];
       pt++;
       v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3)) / 3;
       o_histogram[v]++;
       v = (int) input[pt];
       pt++;
       v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3)) / 3;
       o_histogram[v]++;
       v = (int) input[pt];
       pt++;
       v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3)) / 3;
       o_histogram[v]++;
       v = (int) input[pt];
       pt++;
       v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3)) / 3;
       o_histogram[v]++;
       v = (int) input[pt];
       pt++;
       v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3)) / 3;
       o_histogram[v]++;
       v = (int) input[pt];
       pt++;
       v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3)) / 3;
       o_histogram[v]++;
       v = (int) input[pt];
       pt++;
       v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3)) / 3;
       o_histogram[v]++;
     }
   }
   return i_size.w * i_size.h;
 }
 public int createHistogram(
     INyARRaster i_reader, NyARIntSize i_size, int[] o_histogram, int i_skip) {
   assert (i_reader.isEqualBufferType(NyARBufferType.INT1D_GRAY_8));
   final int[] input = (int[]) i_reader.getBuffer();
   for (int y = i_size.h - 1; y >= 0; y -= i_skip) {
     int pt = y * i_size.w;
     for (int x = i_size.w - 1; x >= 0; x--) {
       o_histogram[input[pt]]++;
       pt++;
     }
   }
   return i_size.w * i_size.h;
 }
 public int createHistogram(
     INyARRaster i_reader, NyARIntSize i_size, int[] o_histogram, int i_skip) {
   assert (i_reader.isEqualBufferType(NyARBufferType.BYTE1D_B8G8R8_24)
       || i_reader.isEqualBufferType(NyARBufferType.BYTE1D_R8G8B8_24));
   final byte[] input = (byte[]) i_reader.getBuffer();
   final int pix_count = i_size.w;
   final int pix_mod_part = pix_count - (pix_count % 8);
   for (int y = i_size.h - 1; y >= 0; y -= i_skip) {
     int pt = y * i_size.w * 3;
     int x, v;
     for (x = pix_count - 1; x >= pix_mod_part; x--) {
       v = ((input[pt + 0] & 0xff) + (input[pt + 1] & 0xff) + (input[pt + 2] & 0xff)) / 3;
       o_histogram[v]++;
       pt += 3;
     }
     // タイリング
     for (; x >= 0; x -= 8) {
       v = ((input[pt + 0] & 0xff) + (input[pt + 1] & 0xff) + (input[pt + 2] & 0xff)) / 3;
       o_histogram[v]++;
       v = ((input[pt + 3] & 0xff) + (input[pt + 4] & 0xff) + (input[pt + 5] & 0xff)) / 3;
       o_histogram[v]++;
       v = ((input[pt + 6] & 0xff) + (input[pt + 7] & 0xff) + (input[pt + 8] & 0xff)) / 3;
       o_histogram[v]++;
       v = ((input[pt + 9] & 0xff) + (input[pt + 10] & 0xff) + (input[pt + 11] & 0xff)) / 3;
       o_histogram[v]++;
       v = ((input[pt + 12] & 0xff) + (input[pt + 13] & 0xff) + (input[pt + 14] & 0xff)) / 3;
       o_histogram[v]++;
       v = ((input[pt + 15] & 0xff) + (input[pt + 16] & 0xff) + (input[pt + 17] & 0xff)) / 3;
       o_histogram[v]++;
       v = ((input[pt + 18] & 0xff) + (input[pt + 19] & 0xff) + (input[pt + 20] & 0xff)) / 3;
       o_histogram[v]++;
       v = ((input[pt + 21] & 0xff) + (input[pt + 22] & 0xff) + (input[pt + 23] & 0xff)) / 3;
       o_histogram[v]++;
       pt += 3 * 8;
     }
   }
   return i_size.w * i_size.h;
 }
  /**
   * 輪郭線抽出関数の実体です。
   *
   * @param i_raster
   * @param i_l
   * @param i_t
   * @param i_r
   * @param i_b
   * @param i_th
   * @param i_entry_x
   * @param i_entry_y
   * @param o_coord
   * @return
   * @throws NyARException
   */
  private boolean impl_getContour(
      INyARRaster i_raster,
      int i_l,
      int i_t,
      int i_r,
      int i_b,
      int i_th,
      int i_entry_x,
      int i_entry_y,
      NyARIntCoordinates o_coord)
      throws NyARException {
    assert (i_t <= i_entry_x);
    NyARIntPoint2d[] coord = o_coord.items;
    final int[] xdir = _getContour_xdir; // static int xdir[8] = { 0, 1, 1, 1, 0,-1,-1,-1};
    final int[] ydir = _getContour_ydir; // static int ydir[8] = {-1,-1, 0, 1, 1, 1, 0,-1};

    final int[] buf = (int[]) i_raster.getBuffer();
    final int width = i_raster.getWidth();
    // クリップ領域の上端に接しているポイントを得る。

    int max_coord = o_coord.items.length;
    int coord_num = 1;
    coord[0].x = i_entry_x;
    coord[0].y = i_entry_y;
    int dir = 5;

    int c = i_entry_x;
    int r = i_entry_y;
    for (; ; ) {
      dir = (dir + 5) % 8; // dirの正規化
      // ここは頑張ればもっと最適化できると思うよ。
      // 4隅以外の境界接地の場合に、境界チェックを省略するとかね。
      if (c > i_l && c < i_r && r > i_t && r < i_b) {
        for (; ; ) { // gotoのエミュレート用のfor文
          // 境界に接していないとき(暗点判定)
          if (buf[(r + ydir[dir]) * width + (c + xdir[dir])] <= i_th) {
            break;
          }
          dir++;
          if (buf[(r + ydir[dir]) * width + (c + xdir[dir])] <= i_th) {
            break;
          }
          dir++;
          if (buf[(r + ydir[dir]) * width + (c + xdir[dir])] <= i_th) {
            break;
          }
          dir++;
          if (buf[(r + ydir[dir]) * width + (c + xdir[dir])] <= i_th) {
            break;
          }
          dir++;
          if (buf[(r + ydir[dir]) * width + (c + xdir[dir])] <= i_th) {
            break;
          }
          dir++;
          if (buf[(r + ydir[dir]) * width + (c + xdir[dir])] <= i_th) {
            break;
          }
          dir++;
          if (buf[(r + ydir[dir]) * width + (c + xdir[dir])] <= i_th) {
            break;
          }
          dir++;
          if (buf[(r + ydir[dir]) * width + (c + xdir[dir])] <= i_th) {
            break;
          }

          // 8方向全て調べたけどラベルが無いよ?
          throw new NyARException();
        }
      } else {
        // 境界に接しているとき
        int i;
        for (i = 0; i < 8; i++) {
          final int x = c + xdir[dir];
          final int y = r + ydir[dir];
          // 境界チェック
          if (x >= i_l && x <= i_r && y >= i_t && y <= i_b) {
            if (buf[(y) * width + (x)] <= i_th) {
              break;
            }
          }
          dir++; // 倍長テーブルを参照するので問題なし
        }
        if (i == 8) {
          // 8方向全て調べたけどラベルが無いよ?
          throw new NyARException(); // return(-1);
        }
      }

      // xcoordとycoordをc,rにも保存
      c = c + xdir[dir];
      r = r + ydir[dir];
      coord[coord_num].x = c;
      coord[coord_num].y = r;
      // 終了条件判定
      if (c == i_entry_x && r == i_entry_y) {
        // 開始点と同じピクセルに到達したら、終点の可能性がある。
        coord_num++;
        // 末端のチェック
        if (coord_num == max_coord) {
          // 輪郭bufが末端に達した
          return false;
        }
        // 末端候補の次のピクセルを調べる
        dir = (dir + 5) % 8; // dirの正規化
        int i;
        for (i = 0; i < 8; i++) {
          final int x = c + xdir[dir];
          final int y = r + ydir[dir];
          // 境界チェック
          if (x >= i_l && x <= i_r && y >= i_t && y <= i_b) {
            if (buf[(y) * width + (x)] <= i_th) {
              break;
            }
          }
          dir++; // 倍長テーブルを参照するので問題なし
        }
        if (i == 8) {
          // 8方向全て調べたけどラベルが無いよ?
          throw new NyARException();
        }
        // 得たピクセルが、[1]と同じならば、末端である。
        c = c + xdir[dir];
        r = r + ydir[dir];
        if (coord[1].x == c && coord[1].y == r) {
          // 終点に達している。
          o_coord.length = coord_num;
          break;
        } else {
          // 終点ではない。
          coord[coord_num].x = c;
          coord[coord_num].y = r;
        }
      }
      coord_num++;
      // 末端のチェック
      if (coord_num == max_coord) {
        // 輪郭が末端に達した
        return false;
      }
    }
    return true;
  }