Skip to content

Commit 83bdc24

Browse files
committed
Fix video input/output codec fields
1 parent b77382a commit 83bdc24

File tree

9 files changed

+187
-89
lines changed

9 files changed

+187
-89
lines changed

TensorStack.Image.BitmapImage/Extensions.cs

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,12 @@ public static Task SaveAsync(this ImageInput imageTensor, string filename)
6969
/// <param name="filePath">The file path.</param>
7070
internal static void Save(this WriteableBitmap bitmap, string filePath)
7171
{
72-
ImageService.DefaultDispatcher.Invoke(() =>
72+
var encoder = new PngBitmapEncoder();
73+
encoder.Frames.Add(BitmapFrame.Create(bitmap));
74+
using (FileStream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
7375
{
74-
var encoder = new PngBitmapEncoder();
75-
encoder.Frames.Add(BitmapFrame.Create(bitmap));
76-
using (FileStream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
77-
{
78-
encoder.Save(stream);
79-
}
80-
});
76+
encoder.Save(stream);
77+
}
8178
}
8279

8380

@@ -91,29 +88,27 @@ internal static WriteableBitmap ToBitmapImage(this ImageTensor imageTensor)
9188
var channels = imageTensor.Dimensions[1];
9289
var height = imageTensor.Dimensions[2];
9390
var width = imageTensor.Dimensions[3];
94-
return ImageService.DefaultDispatcher.Invoke(() =>
95-
{
96-
if (channels == 1)
97-
return imageTensor.ToSingleChannelImage();
9891

99-
var stride = width * 4;
100-
var pixelBuffer = new byte[height * stride];
101-
var writeableBitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
102-
for (int y = 0; y < height; y++)
92+
if (channels == 1)
93+
return imageTensor.ToSingleChannelImage();
94+
95+
var stride = width * 4;
96+
var pixelBuffer = new byte[height * stride];
97+
var writeableBitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
98+
for (int y = 0; y < height; y++)
99+
{
100+
for (int x = 0; x < width; x++)
103101
{
104-
for (int x = 0; x < width; x++)
105-
{
106-
int pixelIndex = (y * width + x) * 4;
107-
pixelBuffer[pixelIndex + 0] = GetByteValue(imageTensor[0, 2, y, x]); // B
108-
pixelBuffer[pixelIndex + 1] = GetByteValue(imageTensor[0, 1, y, x]); // G
109-
pixelBuffer[pixelIndex + 2] = GetByteValue(imageTensor[0, 0, y, x]); // R
110-
pixelBuffer[pixelIndex + 3] = channels == 4 ? GetByteValue(imageTensor[0, 3, y, x]) : byte.MaxValue; // A
111-
}
102+
int pixelIndex = (y * width + x) * 4;
103+
pixelBuffer[pixelIndex + 0] = GetByteValue(imageTensor[0, 2, y, x]); // B
104+
pixelBuffer[pixelIndex + 1] = GetByteValue(imageTensor[0, 1, y, x]); // G
105+
pixelBuffer[pixelIndex + 2] = GetByteValue(imageTensor[0, 0, y, x]); // R
106+
pixelBuffer[pixelIndex + 3] = channels == 4 ? GetByteValue(imageTensor[0, 3, y, x]) : byte.MaxValue; // A
112107
}
113-
writeableBitmap.WritePixels(new Int32Rect(0, 0, width, height), pixelBuffer, stride, 0);
114-
writeableBitmap.Freeze();
115-
return writeableBitmap;
116-
});
108+
}
109+
writeableBitmap.WritePixels(new Int32Rect(0, 0, width, height), pixelBuffer, stride, 0);
110+
writeableBitmap.Freeze();
111+
return writeableBitmap;
117112
}
118113

119114

TensorStack.Image.BitmapImage/ImageInput.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public ImageInput(WriteableBitmap image)
3131
: base(image.ToTensor())
3232
{
3333
_image = image;
34+
if (!_image.IsFrozen)
35+
_image.Freeze();
3436
}
3537

3638
/// <summary>
@@ -56,7 +58,7 @@ public ImageInput(string filename)
5658
/// <param name="width">The width.</param>
5759
/// <param name="height">The height.</param>
5860
/// <param name="resizeMode">The resize mode.</param>
59-
public ImageInput(string filename, int width, int height, ResizeMode resizeMode = ResizeMode.Stretch)
61+
public ImageInput(string filename, int width, int height, ResizeMode resizeMode = ResizeMode.Stretch)
6062
: this(ImageService.Load(filename))
6163
{
6264
Resize(width, height, resizeMode);

TensorStack.Image.BitmapImage/ImageService.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@
44
using System.Windows;
55
using System.Windows.Media;
66
using System.Windows.Media.Imaging;
7-
using System.Windows.Threading;
87

98
namespace TensorStack.Image
109
{
1110
public static class ImageService
1211
{
1312
const string RotationQuery = "System.Photo.Orientation";
14-
internal static Dispatcher DefaultDispatcher => Application.Current.Dispatcher;
15-
16-
13+
1714
/// <summary>
1815
/// Loads image from file.
1916
/// </summary>

TensorStack.TextGeneration/Pipelines/Florence/FlorenceOptions.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ public record FlorenceOptions : GenerateOptions
1313
public CoordinateBox<float> Region { get; set; }
1414
}
1515

16-
public record FlorenceSearchOptions : FlorenceOptions;
16+
public record FlorenceSearchOptions : FlorenceOptions
17+
{
18+
public FlorenceSearchOptions(){ }
19+
public FlorenceSearchOptions(FlorenceOptions options) : base(options) { }
20+
}
1721

1822

1923

TensorStack.Video/Extensions.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ public static class Extensions
2424
/// <param name="height">The height.</param>
2525
/// <param name="videoCodec">The video codec.</param>
2626
/// <param name="cancellationToken">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
27-
public static async Task SaveAync(this IAsyncEnumerable<ImageTensor> imageFrames, string videoFile, float framerate, int? widthOverride = null, int? heightOverride = null, string videoCodec = "mp4v", CancellationToken cancellationToken = default)
27+
public static async Task SaveAync(this IAsyncEnumerable<ImageTensor> imageFrames, string videoFile, float framerate, string videoCodec = "mp4v", int? widthOverride = null, int? heightOverride = null, CancellationToken cancellationToken = default)
2828
{
2929
var videoFrames = imageFrames.AsVideoFrames(framerate, cancellationToken);
30-
await VideoService.WriteVideoStreamAsync(videoFile, videoFrames, widthOverride, heightOverride, framerate, videoCodec, cancellationToken);
30+
await VideoService.WriteVideoStreamAsync(videoFile, videoFrames, videoCodec, widthOverride, heightOverride, framerate, cancellationToken);
3131
}
3232

3333

@@ -39,9 +39,9 @@ public static async Task SaveAync(this IAsyncEnumerable<ImageTensor> imageFrames
3939
/// <param name="framerate">The framerate.</param>
4040
/// <param name="videoCodec">The video codec.</param>
4141
/// <param name="cancellationToken">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
42-
public static async Task SaveAync(this IAsyncEnumerable<VideoFrame> videoFrames, string videoFile, int? widthOverride = null, int? heightOverride = null, float? frameRateOverride = null, string videoCodec = "mp4v", CancellationToken cancellationToken = default)
42+
public static async Task SaveAync(this IAsyncEnumerable<VideoFrame> videoFrames, string videoFile, string videoCodec = "mp4v", int? widthOverride = null, int? heightOverride = null, float? frameRateOverride = null, CancellationToken cancellationToken = default)
4343
{
44-
await VideoService.WriteVideoStreamAsync(videoFile, videoFrames, widthOverride, heightOverride, frameRateOverride, videoCodec, cancellationToken);
44+
await VideoService.WriteVideoStreamAsync(videoFile, videoFrames, videoCodec, widthOverride, heightOverride, frameRateOverride, cancellationToken);
4545
}
4646

4747

@@ -59,11 +59,11 @@ public static async Task SaveAync(this IAsyncEnumerable<VideoFrame> videoFrames,
5959
/// <param name="frameRateOverride">The output frame rate override.</param>
6060
/// <param name="videoCodec">The video codec.</param>
6161
/// <param name="cancellationToken">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
62-
public static async Task<VideoInputStream> SaveAync(this VideoInputStream videoInput, string videoFile, Func<VideoFrame, Task<VideoFrame>> frameProcessor, int readBuffer = 16, int writeBuffer = 16, int? widthOverride = null, int? heightOverride = null, float? frameRateOverride = null, string videoCodec = "mp4v", CancellationToken cancellationToken = default)
62+
public static async Task<VideoInputStream> SaveAync(this VideoInputStream videoInput, string videoFile, Func<VideoFrame, Task<VideoFrame>> frameProcessor, int readBuffer = 16, int writeBuffer = 16, string videoCodec = "mp4v", int? widthOverride = null, int? heightOverride = null, float? frameRateOverride = null, CancellationToken cancellationToken = default)
6363
{
6464
var videoFrames = videoInput.GetAsync(cancellationToken: cancellationToken);
65-
await VideoService.WriteVideoStreamAsync(videoFile, videoFrames, frameProcessor, readBuffer, writeBuffer, widthOverride, heightOverride, frameRateOverride, videoCodec, cancellationToken);
66-
return new VideoInputStream(videoFile, videoCodec);
65+
await VideoService.WriteVideoStreamAsync(videoFile, videoFrames, frameProcessor, readBuffer, writeBuffer, videoCodec, widthOverride, heightOverride, frameRateOverride, cancellationToken);
66+
return await VideoInputStream.CreateAsync(videoFile);
6767
}
6868

6969

TensorStack.Video/VideoInfo.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,15 @@
55

66
namespace TensorStack.Video
77
{
8-
public readonly record struct VideoInfo(string FileName, int Width, int Height, float FrameRate, int FrameCount, ImageTensor Thumbnail)
8+
public record VideoInfo
99
{
10+
public string FileName { get; init; }
11+
public int Width { get; init; }
12+
public int Height { get; init; }
13+
public float FrameRate { get; init; }
14+
public int FrameCount { get; init; }
15+
public string VideoCodec { get; init; }
16+
public ImageTensor Thumbnail { get; init; }
1017
public TimeSpan Duration => TimeSpan.FromSeconds(FrameCount / FrameRate);
1118
}
1219
}

TensorStack.Video/VideoInput.cs

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ namespace TensorStack.Video
1212
/// </summary>
1313
public class VideoInput : VideoTensor
1414
{
15-
private readonly string _videoCodec;
1615
private string _filename;
1716

1817
/// <summary>
@@ -23,8 +22,8 @@ public class VideoInput : VideoTensor
2322
/// <param name="height">The height.</param>
2423
/// <param name="frameRate">The frame rate.</param>
2524
/// <param name="videoCodec">The video codec.</param>
26-
public VideoInput(string filename, int? widthOverride = default, int? heightOverride = default, float? frameRateOverride = default, ResizeMode resizeMode = ResizeMode.Crop, string videoCodec = "mp4v")
27-
: this(VideoService.LoadVideoTensor(filename, widthOverride, heightOverride, frameRateOverride, resizeMode), videoCodec)
25+
public VideoInput(string filename, int? widthOverride = default, int? heightOverride = default, float? frameRateOverride = default, ResizeMode resizeMode = ResizeMode.Crop)
26+
: this(VideoService.LoadVideoTensor(filename, widthOverride, heightOverride, frameRateOverride, resizeMode))
2827
{
2928
_filename = filename;
3029
}
@@ -33,11 +32,7 @@ public VideoInput(string filename, int? widthOverride = default, int? heightOver
3332
/// Initializes a new instance of the <see cref="VideoInput"/> class.
3433
/// </summary>
3534
/// <param name="videoTensor">The video tensor.</param>
36-
public VideoInput(VideoTensor videoTensor, string videoCodec = "mp4v")
37-
: base(videoTensor, videoTensor.FrameRate)
38-
{
39-
_videoCodec = videoCodec;
40-
}
35+
public VideoInput(VideoTensor videoTensor) : base(videoTensor, videoTensor.FrameRate) { }
4136

4237
/// <summary>
4338
/// Gets the filename.
@@ -53,34 +48,29 @@ public VideoInput(VideoTensor videoTensor, string videoCodec = "mp4v")
5348
/// <param name="framerateOverride">The framerate.</param>
5449
/// <param name="cancellationToken">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
5550
/// <returns>A Task representing the asynchronous operation.</returns>
56-
public async Task SaveAsync(string filename, float? frameRateOverride = default, CancellationToken cancellationToken = default)
51+
public async Task SaveAsync(string filename, string videoCodec = "mp4v", float? frameRateOverride = default, CancellationToken cancellationToken = default)
5752
{
58-
await VideoService.SaveVideoTensorAync(filename, this, frameRateOverride, _videoCodec, cancellationToken);
53+
await VideoService.SaveVideoTensorAync(filename, this, videoCodec, frameRateOverride, cancellationToken);
5954
}
6055

6156

6257
/// <summary>
63-
/// Load as VideoInput asynchronously
58+
/// Creates the stream.
6459
/// </summary>
65-
/// <param name="filename">The filename.</param>
66-
/// <param name="widthOverride">The width.</param>
67-
/// <param name="heightOverride">The height.</param>
68-
/// <param name="frameRateOverride ">The frame rate.</param>
69-
/// <param name="cancellationToken">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
70-
/// <returns>A Task&lt;VideoInput&gt; representing the asynchronous operation.</returns>
71-
public static async Task<VideoInput> LoadAsync(string filename, int? widthOverride = default, int? heightOverride = default, float? frameRateOverride = default, ResizeMode resizeMode = ResizeMode.Crop, CancellationToken cancellationToken = default)
60+
/// <returns>VideoStream.</returns>
61+
public VideoInputStream CreateStream()
7262
{
73-
return new VideoInput(await VideoService.LoadVideoTensorAsync(filename, widthOverride, heightOverride, frameRateOverride, resizeMode, cancellationToken));
63+
return new VideoInputStream(_filename);
7464
}
7565

7666

7767
/// <summary>
78-
/// Creates the stream.
68+
/// Creates the stream asynchronously.
7969
/// </summary>
80-
/// <returns>VideoStream.</returns>
81-
public VideoInputStream CreateStream()
70+
/// <returns>Task&lt;VideoInputStream&gt;.</returns>
71+
public Task<VideoInputStream> CreateStreamAsync()
8272
{
83-
return new VideoInputStream(_filename, _videoCodec);
73+
return VideoInputStream.CreateAsync(_filename);
8474
}
8575

8676

@@ -93,5 +83,19 @@ public void SetFilename(string filename)
9383
_filename = filename;
9484
}
9585

86+
87+
/// <summary>
88+
/// Create a VideoInput asynchronously
89+
/// </summary>
90+
/// <param name="filename">The filename.</param>
91+
/// <param name="widthOverride">The width.</param>
92+
/// <param name="heightOverride">The height.</param>
93+
/// <param name="frameRateOverride ">The frame rate.</param>
94+
/// <param name="cancellationToken">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
95+
/// <returns>A Task&lt;VideoInput&gt; representing the asynchronous operation.</returns>
96+
public static async Task<VideoInput> CreateAsync(string filename, int? widthOverride = default, int? heightOverride = default, float? frameRateOverride = default, ResizeMode resizeMode = ResizeMode.Crop, CancellationToken cancellationToken = default)
97+
{
98+
return new VideoInput(await VideoService.LoadVideoTensorAsync(filename, widthOverride, heightOverride, frameRateOverride, resizeMode, cancellationToken));
99+
}
96100
}
97101
}

0 commit comments

Comments
 (0)