@@ -81,7 +81,7 @@ public static Task<VideoInfo> LoadVideoInfoAsync(string filename, int thumbSize
8181 /// <param name="heightOverride">The height.</param>
8282 /// <param name="frameRateOverride">The frame rate.</param>
8383 /// <returns>VideoTensor.</returns>
84- internal static VideoTensor LoadVideoTensor ( string videoFile , int ? widthOverride = default , int ? heightOverride = default , float ? frameRateOverride = default , ResizeMode resizeMode = ResizeMode . Crop )
84+ public static VideoTensor LoadVideoTensor ( string videoFile , int ? widthOverride = default , int ? heightOverride = default , float ? frameRateOverride = default , ResizeMode resizeMode = ResizeMode . Crop )
8585 {
8686 return ReadVideo ( videoFile , widthOverride , heightOverride , frameRateOverride , resizeMode ) ;
8787 }
@@ -96,7 +96,7 @@ internal static VideoTensor LoadVideoTensor(string videoFile, int? widthOverride
9696 /// <param name="height">The height.</param>
9797 /// <param name="cancellationToken">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
9898 /// <returns>Task<VideoTensor>.</returns>
99- internal static Task < VideoTensor > LoadVideoTensorAsync ( string videoFile , int ? widthOverride = default , int ? heightOverride = default , float ? frameRateOverride = default , ResizeMode resizeMode = ResizeMode . Crop , CancellationToken cancellationToken = default )
99+ public static Task < VideoTensor > LoadVideoTensorAsync ( string videoFile , int ? widthOverride = default , int ? heightOverride = default , float ? frameRateOverride = default , ResizeMode resizeMode = ResizeMode . Crop , CancellationToken cancellationToken = default )
100100 {
101101 return Task . Run ( ( ) => ReadVideo ( videoFile , widthOverride , heightOverride , frameRateOverride , resizeMode , cancellationToken ) ) ;
102102 }
@@ -455,5 +455,133 @@ private static async Task WriteVideoFramesAsync(string videoOutputFile, IAsyncEn
455455 // Block
456456 await Task . WhenAll ( readerTask , processTask , writerTask ) ;
457457 }
458+
459+
460+
461+ /// <summary>
462+ /// Converts Matrix to Tensor.
463+ /// </summary>
464+ /// <param name="matrix">The matrix.</param>
465+ /// <returns>Tensor<System.Single>.</returns>
466+ internal static unsafe ImageTensor ToTensor ( this Mat matrix , Size cropSize = default )
467+ {
468+ int cropX = 0 ;
469+ int cropY = 0 ;
470+ int height = matrix . Rows ;
471+ int width = matrix . Cols ;
472+
473+ if ( cropSize != default )
474+ {
475+ if ( width == cropSize . Width )
476+ {
477+ cropY = ( height - cropSize . Height ) / 2 ;
478+ height = cropSize . Height ;
479+ }
480+ else if ( height == cropSize . Height )
481+ {
482+ cropX = ( width - cropSize . Width ) / 2 ;
483+ width = cropSize . Width ;
484+ }
485+ }
486+
487+ var imageTensor = new ImageTensor ( [ 1 , 4 , height , width ] ) ;
488+ var destination = imageTensor . Memory . Span ;
489+
490+ unsafe
491+ {
492+ var source = matrix . DataPointer ;
493+ int srcStride = matrix . Cols * 3 ;
494+ int dstStride = height * width ;
495+ for ( int y = 0 ; y < height ; y ++ )
496+ {
497+ for ( int x = 0 ; x < width ; x ++ )
498+ {
499+ int srcIndex = ( ( y + cropY ) * matrix . Cols + ( x + cropX ) ) * 3 ;
500+ int dstIndex = y * width + x ;
501+
502+ destination [ 0 * dstStride + dstIndex ] = GetFloatValue ( source [ srcIndex + 2 ] ) ; // R
503+ destination [ 1 * dstStride + dstIndex ] = GetFloatValue ( source [ srcIndex + 1 ] ) ; // G
504+ destination [ 2 * dstStride + dstIndex ] = GetFloatValue ( source [ srcIndex + 0 ] ) ; // B
505+ destination [ 3 * dstStride + dstIndex ] = GetFloatValue ( byte . MaxValue ) ; // A
506+ }
507+ }
508+ }
509+
510+ return imageTensor ;
511+ }
512+
513+
514+ /// <summary>
515+ /// Converts Tensor to OpenCv Matrix.
516+ /// </summary>
517+ /// <param name="tensor">The tensor.</param>
518+ /// <returns>Mat.</returns>
519+ internal static unsafe Mat ToMatrix ( this Tensor < float > tensor )
520+ {
521+ var channels = tensor . Dimensions [ 1 ] ;
522+ var height = tensor . Dimensions [ 2 ] ;
523+ var width = tensor . Dimensions [ 3 ] ;
524+
525+ var matrix = new Mat ( height , width , MatType . CV_8UC3 ) ;
526+ var source = tensor . Span ;
527+ var destination = matrix . DataPointer ;
528+ for ( int y = 0 ; y < height ; y ++ )
529+ {
530+ for ( int x = 0 ; x < width ; x ++ )
531+ {
532+ int offset = y * width + x ;
533+
534+ if ( channels == 1 )
535+ {
536+ byte gray = GetByteValue ( source [ offset ] ) ;
537+ destination [ offset * 3 + 0 ] = gray ; // B
538+ destination [ offset * 3 + 1 ] = gray ; // G
539+ destination [ offset * 3 + 2 ] = gray ; // R
540+ }
541+ else
542+ {
543+ destination [ offset * 3 + 0 ] = GetByteValue ( source [ 2 * width * height + offset ] ) ; // B
544+ destination [ offset * 3 + 1 ] = GetByteValue ( source [ 1 * width * height + offset ] ) ; // G
545+ destination [ offset * 3 + 2 ] = GetByteValue ( source [ 0 * width * height + offset ] ) ; // R
546+ }
547+ }
548+ }
549+
550+ return matrix ;
551+ }
552+
553+
554+ /// <summary>
555+ /// Gets the normalized byte value.
556+ /// </summary>
557+ /// <param name="value">The value.</param>
558+ internal static byte GetByteValue ( this float value )
559+ {
560+ return ( byte ) Math . Clamp ( ( value + 1.0f ) * 0.5f * 255f , 0 , 255 ) ;
561+ }
562+
563+
564+ /// <summary>
565+ /// Gets the normalized float value.
566+ /// </summary>
567+ /// <param name="value">The value.</param>
568+ internal static float GetFloatValue ( this byte value )
569+ {
570+ return ( value / 255f ) * 2.0f - 1.0f ;
571+ }
572+
573+
574+ /// <summary>
575+ /// Null if zero.
576+ /// </summary>
577+ /// <param name="value">The value.</param>
578+ /// <returns>System.Nullable<System.Int32>.</returns>
579+ internal static int ? NullIfZero ( this int ? value )
580+ {
581+ if ( value . HasValue && value . Value == 0 )
582+ return null ;
583+
584+ return value ;
585+ }
458586 }
459587}
0 commit comments