-
-
Notifications
You must be signed in to change notification settings - Fork 516
[Canary] Transcoding support using NetVips #3978
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: canary
Are you sure you want to change the base?
Changes from 8 commits
a1ce3e2
dbba9aa
b21baaf
33b62a1
31fcbc9
995c47d
7bfde6a
bb67141
39a6fe5
ec60add
c738137
900c20d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,15 @@ | ||
| using System; | ||
| using System; | ||
| using System.IO; | ||
| using System.IO.Abstractions; | ||
| using API.Entities.Enums; | ||
| using Microsoft.Extensions.Logging.Abstractions; | ||
| using API.Services; | ||
| using API.Services.ImageServices; | ||
| using BenchmarkDotNet.Attributes; | ||
| using BenchmarkDotNet.Order; | ||
| using EasyCaching.Core; | ||
| using NSubstitute; | ||
| using SixLabors.ImageSharp; | ||
| using SixLabors.ImageSharp.Formats.Png; | ||
| using SixLabors.ImageSharp.Formats.Webp; | ||
| using SixLabors.ImageSharp.Processing; | ||
|
|
||
|
|
||
| namespace API.Benchmark; | ||
|
|
||
|
|
@@ -24,16 +23,22 @@ public class ArchiveServiceBenchmark | |
| private readonly ArchiveService _archiveService; | ||
| private readonly IDirectoryService _directoryService; | ||
| private readonly IImageService _imageService; | ||
| private readonly PngEncoder _pngEncoder = new PngEncoder(); | ||
| private readonly WebpEncoder _webPEncoder = new WebpEncoder(); | ||
| private readonly IImageFactory _imageFactory; | ||
| private const string SourceImage = "C:/Users/josep/Pictures/obey_by_grrsa-d6llkaa_colored_by_me.png"; | ||
|
|
||
|
|
||
| public ArchiveServiceBenchmark() | ||
| { | ||
| #if ImageMagick | ||
| _imageFactory = new API.Services.ImageServices.ImageMagick.ImageMagickImageFactory(); | ||
| #else | ||
| _imageFactory = new API.Services.ImageServices.NetVips.NetVipsImageFactory(); | ||
| #endif | ||
|
|
||
| _directoryService = new DirectoryService(null, new FileSystem()); | ||
| _imageService = new ImageService(null, _directoryService); | ||
| _imageService = new ImageService(null, _directoryService,_imageFactory); | ||
| _archiveService = new ArchiveService(new NullLogger<ArchiveService>(), _directoryService, _imageService, Substitute.For<IMediaErrorService>()); | ||
|
|
||
| } | ||
|
|
||
| [Benchmark(Baseline = true)] | ||
|
|
@@ -61,50 +66,39 @@ public void TestGetComicInfo_outside_root() | |
| } | ||
|
|
||
| [Benchmark] | ||
| public void ImageSharp_ExtractImage_PNG() | ||
| public void ImageFactory_ExtractImage_PNG() | ||
| { | ||
| var outputDirectory = "C:/Users/josep/Pictures/imagesharp/"; | ||
| var outputDirectory = "C:/Users/josep/Pictures/netvips/"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to refactor this to use the TestData folder within the project and not paths on my dev machine. (
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will do. |
||
| _directoryService.ExistOrCreate(outputDirectory); | ||
|
|
||
| using var stream = new FileStream(SourceImage, FileMode.Open); | ||
| using var thumbnail2 = SixLabors.ImageSharp.Image.Load(stream); | ||
| thumbnail2.Mutate(x => x.Resize(320, 0)); | ||
| thumbnail2.Save(_directoryService.FileSystem.Path.Join(outputDirectory, "imagesharp.png"), _pngEncoder); | ||
| using var thumbnail2 = _imageFactory.Create(stream); | ||
| thumbnail2.Resize(320, 0); | ||
| thumbnail2.Save(_directoryService.FileSystem.Path.Join(outputDirectory, "imagesharp.png"), EncodeFormat.PNG); | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public void ImageSharp_ExtractImage_WebP() | ||
| private void Resize320(IImage image) | ||
| { | ||
| var outputDirectory = "C:/Users/josep/Pictures/imagesharp/"; | ||
| _directoryService.ExistOrCreate(outputDirectory); | ||
|
|
||
| using var stream = new FileStream(SourceImage, FileMode.Open); | ||
| using var thumbnail2 = SixLabors.ImageSharp.Image.Load(stream); | ||
| thumbnail2.Mutate(x => x.Resize(320, 0)); | ||
| thumbnail2.Save(_directoryService.FileSystem.Path.Join(outputDirectory, "imagesharp.webp"), _webPEncoder); | ||
| int originalWidth = image.Width; | ||
| int originalHeight = image.Height; | ||
| int targetWidth = 320; | ||
| int targetHeight = (int)Math.Round((double)originalHeight * targetWidth / originalWidth); | ||
majora2007 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| image.Resize(targetWidth, targetHeight); | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public void NetVips_ExtractImage_PNG() | ||
| public void ImageSharp_ExtractImage_WebP() | ||
| { | ||
| var outputDirectory = "C:/Users/josep/Pictures/netvips/"; | ||
| _directoryService.ExistOrCreate(outputDirectory); | ||
|
|
||
| using var stream = new FileStream(SourceImage, FileMode.Open); | ||
| using var thumbnail = NetVips.Image.ThumbnailStream(stream, 320); | ||
| thumbnail.WriteToFile(_directoryService.FileSystem.Path.Join(outputDirectory, "netvips.png")); | ||
| using var thumbnail2 = _imageFactory.Create(stream); | ||
| Resize320(thumbnail2); | ||
| thumbnail2.Save(_directoryService.FileSystem.Path.Join(outputDirectory, "imagesharp.webp"), EncodeFormat.WEBP); | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public void NetVips_ExtractImage_WebP() | ||
| { | ||
| var outputDirectory = "C:/Users/josep/Pictures/netvips/"; | ||
| _directoryService.ExistOrCreate(outputDirectory); | ||
|
|
||
| using var stream = new FileStream(SourceImage, FileMode.Open); | ||
| using var thumbnail = NetVips.Image.ThumbnailStream(stream, 320); | ||
| thumbnail.WriteToFile(_directoryService.FileSystem.Path.Join(outputDirectory, "netvips.webp")); | ||
| } | ||
|
|
||
| // Benchmark to test default GetNumberOfPages from archive | ||
| // vs a new method where I try to open the archive and return said stream | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| using System.Diagnostics; | ||
| using System.Diagnostics; | ||
| using System.IO; | ||
| using System.IO.Abstractions; | ||
| using System.IO.Abstractions.TestingHelpers; | ||
|
|
@@ -7,8 +7,8 @@ | |
| using API.Archive; | ||
| using API.Entities.Enums; | ||
| using API.Services; | ||
| using API.Services.ImageServices; | ||
| using Microsoft.Extensions.Logging; | ||
| using NetVips; | ||
| using NSubstitute; | ||
| using NSubstitute.Extensions; | ||
| using Xunit; | ||
|
|
@@ -23,12 +23,17 @@ public class ArchiveServiceTests | |
| private readonly ILogger<ArchiveService> _logger = Substitute.For<ILogger<ArchiveService>>(); | ||
| private readonly ILogger<DirectoryService> _directoryServiceLogger = Substitute.For<ILogger<DirectoryService>>(); | ||
| private readonly IDirectoryService _directoryService = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), new FileSystem()); | ||
|
|
||
| private readonly IImageFactory _imageFactory; | ||
| public ArchiveServiceTests(ITestOutputHelper testOutputHelper) | ||
| { | ||
| #if ImageMagick | ||
| _imageFactory = new API.Services.ImageServices.ImageMagick.ImageMagickImageFactory(); | ||
| #else | ||
| _imageFactory = new API.Services.ImageServices.NetVips.NetVipsImageFactory(); | ||
| #endif | ||
| _testOutputHelper = testOutputHelper; | ||
| _archiveService = new ArchiveService(_logger, _directoryService, | ||
| new ImageService(Substitute.For<ILogger<ImageService>>(), _directoryService), | ||
| new ImageService(Substitute.For<ILogger<ImageService>>(), _directoryService, _imageFactory), | ||
| Substitute.For<IMediaErrorService>()); | ||
| } | ||
|
|
||
|
|
@@ -166,11 +171,17 @@ public void FindFirstEntry(string[] files, string expected) | |
| public void GetCoverImage_Default_Test(string inputFile, string expectedOutputFile) | ||
| { | ||
| var ds = Substitute.For<DirectoryService>(_directoryServiceLogger, new FileSystem()); | ||
| var imageService = new ImageService(Substitute.For<ILogger<ImageService>>(), ds); | ||
| var imageService = new ImageService(Substitute.For<ILogger<ImageService>>(), ds, _imageFactory); | ||
| var archiveService = Substitute.For<ArchiveService>(_logger, ds, imageService, Substitute.For<IMediaErrorService>()); | ||
|
|
||
| var testDirectory = Path.GetFullPath(Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ArchiveService/CoverImages")); | ||
| var expectedBytes = Image.Thumbnail(Path.Join(testDirectory, expectedOutputFile), 320).WriteToBuffer(".png"); | ||
| var thumbnail = imageService.ImageFactory.Create(Path.Join(testDirectory, expectedOutputFile)); | ||
| int width = 320; | ||
| int height =(int)(thumbnail.Height * (width / (double)thumbnail.Width)); | ||
| thumbnail = thumbnail.Thumbnail(width, height); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we not wrap this in the Image Interface so it's not so manual across the tests? Looks like a duplication of logic to me.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should also refactor this to use
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On second thought, I don't see the use case for this, archiveService.GetCoverImage would internally make the same calls anyway. |
||
| using MemoryStream stream = new MemoryStream(); | ||
| thumbnail.Save(stream, EncodeFormat.PNG); | ||
| var expectedBytes = stream.ToArray(); | ||
|
|
||
| archiveService.Configure().CanOpen(Path.Join(testDirectory, inputFile)).Returns(ArchiveLibrary.Default); | ||
|
|
||
|
|
@@ -197,7 +208,7 @@ public void GetCoverImage_Default_Test(string inputFile, string expectedOutputFi | |
| [InlineData("sorting.zip", "sorting.expected.png")] | ||
| public void GetCoverImage_SharpCompress_Test(string inputFile, string expectedOutputFile) | ||
| { | ||
| var imageService = new ImageService(Substitute.For<ILogger<ImageService>>(), _directoryService); | ||
| var imageService = new ImageService(Substitute.For<ILogger<ImageService>>(), _directoryService, _imageFactory); | ||
| var archiveService = Substitute.For<ArchiveService>(_logger, | ||
| new DirectoryService(_directoryServiceLogger, new FileSystem()), imageService, | ||
| Substitute.For<IMediaErrorService>()); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,12 @@ | ||
| using System.IO; | ||
| using System.IO; | ||
| using System.IO.Abstractions; | ||
| using System.Reflection; | ||
| using System.Threading.Tasks; | ||
| using API.Constants; | ||
| using API.Entities.Enums; | ||
| using API.Extensions; | ||
| using API.Services; | ||
| using API.Services.ImageServices; | ||
| using API.Services.Tasks.Metadata; | ||
| using API.SignalR; | ||
| using EasyCaching.Core; | ||
|
|
@@ -22,6 +23,7 @@ public class CoverDbServiceTests : AbstractDbTest | |
| private readonly DirectoryService _directoryService; | ||
| private readonly IEasyCachingProviderFactory _cacheFactory = Substitute.For<IEasyCachingProviderFactory>(); | ||
| private readonly ICoverDbService _coverDbService; | ||
| private readonly IImageService _imageService; | ||
|
|
||
| private static readonly string FaviconPath = Path.Join(Directory.GetCurrentDirectory(), | ||
| "../../../Services/Test Data/CoverDbService/Favicons"); | ||
|
|
@@ -34,10 +36,16 @@ public class CoverDbServiceTests : AbstractDbTest | |
| public CoverDbServiceTests() | ||
| { | ||
| _directoryService = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), CreateFileSystem()); | ||
| var imageService = new ImageService(Substitute.For<ILogger<ImageService>>(), _directoryService); | ||
| #if ImageMagick | ||
| IImageFactory imageFactory = new API.Services.ImageServices.ImageMagick.ImageMagickImageFactory(); | ||
| #else | ||
| IImageFactory imageFactory = new API.Services.ImageServices.NetVips.NetVipsImageFactory(); | ||
| #endif | ||
|
|
||
| _imageService = new ImageService(Substitute.For<ILogger<ImageService>>(), _directoryService, imageFactory); | ||
|
|
||
| _coverDbService = new CoverDbService(Substitute.For<ILogger<CoverDbService>>(), _directoryService, _cacheFactory, | ||
| Substitute.For<IHostEnvironment>(), imageService, UnitOfWork, Substitute.For<IEventHub>()); | ||
| Substitute.For<IHostEnvironment>(), _imageService, UnitOfWork, Substitute.For<IEventHub>()); | ||
| } | ||
|
|
||
| protected override Task ResetDb() | ||
|
|
@@ -89,7 +97,7 @@ public async Task DownloadFaviconAsync_ShouldDownloadAndMatchExpectedFavicon() | |
|
|
||
| // Load and compare similarity | ||
|
|
||
| var similarity = expectedFaviconPath.CalculateSimilarity(actualFaviconPath); // Assuming you have this extension | ||
| var similarity = _imageService.ImageFactory.CalculateSimilarity(expectedFaviconPath, actualFaviconPath); // Assuming you have this extension | ||
|
||
| Assert.True(similarity > 0.9f, $"Image similarity too low: {similarity}"); | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whatever test this is, we probably need to recreate or remove it. I dont even have this file anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just ported it over, should remove the test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah