generateAnchors function

List<List<double>> generateAnchors(
  1. SSDAnchorOptions options
)

Generates SSD anchors based on the given options.

Returns a list of anchors where each anchor is [xCenter, yCenter, width, height] in normalized coordinates. For fixed-size anchors (SSDAnchorOptions.fixedAnchorSize is true), width and height are always 1.0.

This is a direct port of the Python generate_anchors function.

Implementation

List<List<double>> generateAnchors(SSDAnchorOptions options) {
  final anchors = <List<double>>[];
  int layerId = 0;
  final nStrides = options.strides.length;

  while (layerId < nStrides) {
    final anchorHeight = <double>[];
    final anchorWidth = <double>[];
    final aspectRatios = <double>[];
    final scales = <double>[];
    int lastSameStrideLayer = layerId;

    while (lastSameStrideLayer < nStrides &&
        options.strides[lastSameStrideLayer] == options.strides[layerId]) {
      final scale = _calculateScale(
        options.minScale,
        options.maxScale,
        lastSameStrideLayer,
        nStrides,
      );

      if (lastSameStrideLayer == 0 && options.reduceBoxesInLowestLayer) {
        aspectRatios.addAll([1.0, 2.0, 0.5]);
        scales.addAll([0.1, scale, scale]);
      } else {
        aspectRatios.addAll(options.aspectRatios);
        for (int i = 0; i < options.aspectRatios.length; i++) {
          scales.add(scale);
        }
        if (options.interpolatedScaleAspectRatio > 0) {
          double scaleNext;
          if (lastSameStrideLayer == nStrides - 1) {
            scaleNext = 1.0;
          } else {
            scaleNext = _calculateScale(
              options.minScale,
              options.maxScale,
              lastSameStrideLayer + 1,
              nStrides,
            );
          }
          scales.add(math.sqrt(scale * scaleNext));
          aspectRatios.add(options.interpolatedScaleAspectRatio);
        }
      }
      lastSameStrideLayer++;
    }

    for (int i = 0; i < aspectRatios.length; i++) {
      final ratioSqrt = math.sqrt(aspectRatios[i]);
      anchorHeight.add(scales[i] / ratioSqrt);
      anchorWidth.add(scales[i] * ratioSqrt);
    }

    final stride = options.strides[layerId];
    final featureMapHeight = (options.inputSizeHeight / stride).ceil();
    final featureMapWidth = (options.inputSizeWidth / stride).ceil();

    for (int y = 0; y < featureMapHeight; y++) {
      for (int x = 0; x < featureMapWidth; x++) {
        for (int anchorId = 0; anchorId < anchorHeight.length; anchorId++) {
          final xCenter = (x + options.anchorOffsetX) / featureMapWidth;
          final yCenter = (y + options.anchorOffsetY) / featureMapHeight;

          List<double> newAnchor;
          if (options.fixedAnchorSize) {
            newAnchor = [xCenter, yCenter, 1.0, 1.0];
          } else {
            newAnchor = [
              xCenter,
              yCenter,
              anchorWidth[anchorId],
              anchorHeight[anchorId],
            ];
          }
          anchors.add(newAnchor);
        }
      }
    }

    layerId = lastSameStrideLayer;
  }

  return anchors;
}