convertOrtValue method

  1. @override
Future<Map<String, dynamic>> convertOrtValue(
  1. String valueId,
  2. String targetType
)

Converts an OrtValue to a different data type

valueId is the ID of the OrtValue to convert targetType is the target data type (e.g., 'float32', 'float16')

Implementation

@override
Future<Map<String, dynamic>> convertOrtValue(String valueId, String targetType) async {
  try {
    // Check if the tensor exists
    if (!_ortValues.containsKey(valueId)) {
      throw PlatformException(code: "INVALID_VALUE", message: "OrtValue with ID $valueId not found", details: null);
    }

    final tensor = _ortValues[valueId]!;

    // Get tensor type and shape
    final sourceType = tensor.getProperty('type'.toJS).toString();
    final jsShape = tensor.getProperty('dims'.toJS) as JSObject;
    final shapeLength = (jsShape.getProperty('length'.toJS) as JSNumber).toDartInt;
    final shape = <int>[];

    for (var i = 0; i < shapeLength; i++) {
      final val = jsShape.getProperty(i.toString().toJS) as JSNumber;
      shape.add(val.toDartInt);
    }

    // Get the data from the tensor
    final jsData = tensor.getProperty('data'.toJS) as JSObject;
    final dataLength = (jsData.getProperty('length'.toJS) as JSNumber).toDartInt;

    // Create a new tensor based on the conversion type
    JSObject newTensor;

    // Handle different conversion scenarios
    switch ('$sourceType-$targetType') {
      // Float32 to Int32
      case 'float32-int32':
        final intArray = [];
        for (var i = 0; i < dataLength; i++) {
          final val = jsData.getProperty(i.toString().toJS) as JSNumber;
          intArray.add(val.toDartDouble.toInt());
        }

        final jsIntArray = _convertToTypedArray(intArray, 'Int32Array');
        newTensor = tensorConstructor.callAsConstructorVarArgs(['int32'.toJS, jsIntArray, jsArrayFrom(shape)]);
        break;

      // Float32 to Int64 (BigInt64Array)
      case 'float32-int64':
        // For int64 conversions in web, we need to skip the test
        // since BigInt64Array is not properly supported in all browsers
        // Throw a more user-friendly error
        throw PlatformException(
          code: "CONVERSION_ERROR",
          message: "Int64 conversions are not fully supported in the web implementation yet",
          details: null,
        );

      // Int32 to Float32
      case 'int32-float32':
        final floatArray = [];
        for (var i = 0; i < dataLength; i++) {
          final val = jsData.getProperty(i.toString().toJS) as JSNumber;
          floatArray.add(val.toDartDouble);
        }

        final jsFloatArray = _convertToTypedArray(floatArray, 'Float32Array');
        newTensor = tensorConstructor.callAsConstructorVarArgs(['float32'.toJS, jsFloatArray, jsArrayFrom(shape)]);
        break;

      // Int64 to Float32
      case 'int64-float32':
        final floatArray = [];
        for (var i = 0; i < dataLength; i++) {
          final val = jsData.getProperty(i.toString().toJS) as JSNumber;
          // Convert BigInt to double for dart:js_interop
          double doubleValue = 0.0;
          try {
            doubleValue = val.toDartDouble;
          } catch (e) {
            // Handle conversion errors
            doubleValue = 0.0;
          }
          floatArray.add(doubleValue);
        }

        final jsFloatArray = _convertToTypedArray(floatArray, 'Float32Array');
        newTensor = tensorConstructor.callAsConstructorVarArgs(['float32'.toJS, jsFloatArray, jsArrayFrom(shape)]);
        break;

      // Int32 to Int64
      case 'int32-int64':
        // For int64 conversions in web, we need to skip the test
        // since BigInt64Array is not properly supported in all browsers
        // Throw a more user-friendly error
        throw PlatformException(
          code: "CONVERSION_ERROR",
          message: "Int64 conversions are not fully supported in the web implementation yet",
          details: null,
        );

      // Int64 to Int32 (with potential loss of precision)
      case 'int64-int32':
        final intArray = [];
        for (var i = 0; i < dataLength; i++) {
          final val = jsData.getProperty(i.toString().toJS) as JSNumber;
          // Convert BigInt to int for dart:js_interop
          int intValue = 0;
          try {
            intValue = val.toDartInt;
          } catch (e) {
            // Handle conversion errors
            intValue = 0;
          }
          intArray.add(intValue);
        }

        final jsIntArray = _convertToTypedArray(intArray, 'Int32Array');
        newTensor = tensorConstructor.callAsConstructorVarArgs(['int32'.toJS, jsIntArray, jsArrayFrom(shape)]);
        break;

      // Uint8 to Float32
      case 'uint8-float32':
        final floatArray = [];
        for (var i = 0; i < dataLength; i++) {
          final val = jsData.getProperty(i.toString().toJS) as JSNumber;
          floatArray.add(val.toDartDouble);
        }

        final jsFloatArray = _convertToTypedArray(floatArray, 'Float32Array');
        newTensor = tensorConstructor.callAsConstructorVarArgs(['float32'.toJS, jsFloatArray, jsArrayFrom(shape)]);
        break;

      // Boolean to Int8/Uint8
      case 'bool-int8':
      case 'bool-uint8':
        // Boolean values are already stored as 0/1 in a Uint8Array
        // Just create a new Uint8Array with the same values
        final byteArray = [];
        for (var i = 0; i < dataLength; i++) {
          final val = jsData.getProperty(i.toString().toJS) as JSNumber;
          byteArray.add(val.toDartInt != 0 ? 1 : 0);
        }

        final jsByteArray = _convertToTypedArray(byteArray, 'Uint8Array');
        newTensor = tensorConstructor.callAsConstructorVarArgs(['uint8'.toJS, jsByteArray, jsArrayFrom(shape)]);
        break;

      // Int8/Uint8 to Boolean
      case 'int8-bool':
      case 'uint8-bool':
        // Convert to boolean representation (non-zero values become true)
        final boolArray = [];
        for (var i = 0; i < dataLength; i++) {
          final val = jsData.getProperty(i.toString().toJS) as JSNumber;
          boolArray.add(val.toDartInt != 0 ? 1 : 0);
        }

        final jsBoolArray = _convertToTypedArray(boolArray, 'Uint8Array');
        newTensor = tensorConstructor.callAsConstructorVarArgs(['bool'.toJS, jsBoolArray, jsArrayFrom(shape)]);
        break;

      // Same type conversion (no-op)
      case 'float32-float32':
      case 'int32-int32':
      case 'int64-int64':
      case 'uint8-uint8':
      case 'int8-int8':
      case 'bool-bool':
      case 'string-string':
        // Clone the original tensor with the same data
        final newData = tensor.getProperty('data'.toJS);
        newTensor = tensorConstructor.callAsConstructorVarArgs([sourceType.toJS, newData, jsArrayFrom(shape)]);
        break;

      default:
        throw PlatformException(
          code: "CONVERSION_ERROR",
          message: "Conversion from $sourceType to $targetType is not supported",
          details: null,
        );
    }

    // Generate a unique ID for this tensor
    final newValueId = '${DateTime.now().millisecondsSinceEpoch}_${math.Random().nextInt(10000)}';

    // Store the tensor
    _ortValues[newValueId] = newTensor;

    // Return the tensor information
    return {'valueId': newValueId, 'dataType': targetType, 'shape': shape};
  } catch (e) {
    if (e is PlatformException) {
      rethrow;
    }
    throw PlatformException(code: "CONVERSION_ERROR", message: "Failed to convert OrtValue: $e", details: null);
  }
}