11using Microsoft . ML . OnnxRuntime ;
22using Microsoft . ML . OnnxRuntime . Tensors ;
33using OnnxStack . Core ;
4+ using OnnxStack . Core . Image ;
5+ using OnnxStack . ImageUpscaler . Models ;
46using SixLabors . ImageSharp ;
57using SixLabors . ImageSharp . PixelFormats ;
68using System ;
9+ using System . Collections . Generic ;
710
811namespace OnnxStack . ImageUpscaler . Extensions
912{
1013 internal static class ImageExtensions
1114 {
15+ /// <summary>
16+ /// Generates the image tiles.
17+ /// </summary>
18+ /// <param name="imageSource">The image source.</param>
19+ /// <param name="sampleSize">Maximum size of the tile.</param>
20+ /// <param name="scaleFactor">The scale factor.</param>
21+ /// <returns></returns>
22+ public static List < ImageTile > GenerateTiles ( this Image < Rgba32 > imageSource , int sampleSize , int scaleFactor )
23+ {
24+ var tiles = new List < ImageTile > ( ) ;
25+ var tileSizeX = Math . Min ( sampleSize , imageSource . Width ) ;
26+ var tileSizeY = Math . Min ( sampleSize , imageSource . Height ) ;
27+ var columns = ( int ) Math . Ceiling ( ( double ) imageSource . Width / tileSizeX ) ;
28+ var rows = ( int ) Math . Ceiling ( ( double ) imageSource . Height / tileSizeY ) ;
29+ var tileWidth = imageSource . Width / columns ;
30+ var tileHeight = imageSource . Height / rows ;
31+
32+ for ( int y = 0 ; y < rows ; y ++ )
33+ {
34+ for ( int x = 0 ; x < columns ; x ++ )
35+ {
36+ var tileRect = new Rectangle ( x * tileWidth , y * tileHeight , tileWidth , tileHeight ) ;
37+ var tileDest = new Rectangle ( tileRect . X * scaleFactor , tileRect . Y * scaleFactor , tileWidth * scaleFactor , tileHeight * scaleFactor ) ;
38+ var tileImage = ExtractTile ( imageSource , tileRect ) ;
39+ tiles . Add ( new ImageTile ( tileImage , tileDest ) ) ;
40+ }
41+ }
42+ return tiles ;
43+ }
44+
45+
46+ /// <summary>
47+ /// Extracts an image tile from a source image.
48+ /// </summary>
49+ /// <param name="sourceImage">The source image.</param>
50+ /// <param name="sourceArea">The source area.</param>
51+ /// <returns></returns>
52+ public static Image < Rgba32 > ExtractTile ( this Image < Rgba32 > sourceImage , Rectangle sourceArea )
53+ {
54+ var height = sourceArea . Height ;
55+ var targetImage = new Image < Rgba32 > ( sourceArea . Width , sourceArea . Height ) ;
56+ sourceImage . ProcessPixelRows ( targetImage , ( sourceAccessor , targetAccessor ) =>
57+ {
58+ for ( int i = 0 ; i < height ; i ++ )
59+ {
60+ var sourceRow = sourceAccessor . GetRowSpan ( sourceArea . Y + i ) ;
61+ var targetRow = targetAccessor . GetRowSpan ( i ) ;
62+ sourceRow . Slice ( sourceArea . X , sourceArea . Width ) . CopyTo ( targetRow ) ;
63+ }
64+ } ) ;
65+ return targetImage ;
66+ }
67+
68+
69+ /// <summary>
70+ /// Converts to InputImage to an Image<Rgba32>
71+ /// </summary>
72+ /// <param name="inputImage">The input image.</param>
73+ /// <returns></returns>
74+ public static Image < Rgba32 > ToImage ( this InputImage inputImage )
75+ {
76+ if ( ! string . IsNullOrEmpty ( inputImage . ImageBase64 ) )
77+ return Image . Load < Rgba32 > ( Convert . FromBase64String ( inputImage . ImageBase64 . Split ( ',' ) [ 1 ] ) ) ;
78+ if ( inputImage . ImageBytes != null )
79+ return Image . Load < Rgba32 > ( inputImage . ImageBytes ) ;
80+ if ( inputImage . ImageStream != null )
81+ return Image . Load < Rgba32 > ( inputImage . ImageStream ) ;
82+ if ( inputImage . ImageTensor != null )
83+ return inputImage . ImageTensor . ToImage ( ) ;
84+
85+ return inputImage . Image ;
86+ }
1287
1388 /// <summary>
1489 /// Converts to DenseTensor.
@@ -20,7 +95,7 @@ public static DenseTensor<float> ToDenseTensor(this Image<Rgba32> image, ReadOnl
2095 {
2196 using ( image )
2297 {
23- return ProcessPixels ( image , dimensions ) ;
98+ return NormalizeToZeroToOne ( image , dimensions ) ;
2499 }
25100 }
26101
@@ -51,23 +126,24 @@ public static Image<Rgba32> ToImage(this DenseTensor<float> imageTensor)
51126 for ( var x = 0 ; x < width ; x ++ )
52127 {
53128 result [ x , y ] = new Rgba32 (
54- CalculateByte ( imageTensor , 0 , y , x ) ,
55- CalculateByte ( imageTensor , 1 , y , x ) ,
56- CalculateByte ( imageTensor , 2 , y , x )
129+ DenormalizeZeroToOneToByte ( imageTensor , 0 , y , x ) ,
130+ DenormalizeZeroToOneToByte ( imageTensor , 1 , y , x ) ,
131+ DenormalizeZeroToOneToByte ( imageTensor , 2 , y , x )
57132 ) ;
58133 }
59134 }
60135 return result ;
61136 }
62137
63138
139+
64140 /// <summary>
65- /// Processes the pixels.
141+ /// Normalizes the pixels from 0-255 to 0-1
66142 /// </summary>
67143 /// <param name="image">The image.</param>
68144 /// <param name="dimensions">The dimensions.</param>
69145 /// <returns></returns>
70- private static DenseTensor < float > ProcessPixels ( Image < Rgba32 > image , ReadOnlySpan < int > dimensions )
146+ private static DenseTensor < float > NormalizeToZeroToOne ( Image < Rgba32 > image , ReadOnlySpan < int > dimensions )
71147 {
72148 var width = dimensions [ 3 ] ;
73149 var height = dimensions [ 2 ] ;
@@ -90,15 +166,16 @@ private static DenseTensor<float> ProcessPixels(Image<Rgba32> image, ReadOnlySpa
90166 }
91167
92168
169+
93170 /// <summary>
94- /// Calculates the byte.
171+ /// Denormalizes the pixels from 0-1 to 0-255
95172 /// </summary>
96173 /// <param name="imageTensor">The image tensor.</param>
97174 /// <param name="index">The index.</param>
98175 /// <param name="y">The y.</param>
99176 /// <param name="x">The x.</param>
100177 /// <returns></returns>
101- private static byte CalculateByte ( Tensor < float > imageTensor , int index , int y , int x )
178+ private static byte DenormalizeZeroToOneToByte ( DenseTensor < float > imageTensor , int index , int y , int x )
102179 {
103180 return ( byte ) Math . Round ( Math . Clamp ( imageTensor [ 0 , index , y , x ] , 0 , 1 ) * 255 ) ;
104181 }
0 commit comments