private async Task UpdateThumbStatus(Image image, ImageProcessResult conversionResult) { Logging.LogTrace($" - Updating metadata for {image.ImageId}"); try { using var db = new ImageContext(); if (image.MetaData != null) { db.Attach(image.MetaData); image.MetaData.ThumbLastUpdated = DateTime.UtcNow; db.ImageMetaData.Update(image.MetaData); } else { var metadata = new ImageMetaData { ImageId = image.ImageId, ThumbLastUpdated = DateTime.UtcNow }; db.ImageMetaData.Add(metadata); image.MetaData = metadata; } await db.SaveChangesAsync("ThumbUpdate"); } catch (Exception ex) { Logging.LogWarning($"Unable to update DB thumb for ID {image.ImageId}: {ex.Message}"); } }
/// 对云上数据进行图片处理 public void ProcessWithPicOperation() { string bucket = "examplebucket-1250000000"; //存储桶,格式:BucketName-APPID string key = "exampleobject"; //对象键 string srcPath = @"temp-source-file"; //本地文件绝对路径 //.cssg-snippet-body-start:[process-with-pic-operation] JObject o = new JObject(); // 不返回原图 o["is_pic_info"] = 0; JArray rules = new JArray(); JObject rule = new JObject(); rule["bucket"] = bucket; rule["fileid"] = "desample_photo.jpg"; //处理参数,规则参见:https://cloud.tencent.com/document/product/460/19017 rule["rule"] = "imageMogr2/thumbnail/400x400"; rules.Add(rule); o["rules"] = rules; string ruleString = o.ToString(Formatting.None); ImageProcessRequest request = new ImageProcessRequest(bucket, key, ruleString); ImageProcessResult result = cosXml.ImageProcess(request); //.cssg-snippet-body-end }
/// <summary> /// Resize using SixLabors ImageSharp, which can do 100 images in about 59s (2020 MacBook Air i5) /// </summary> /// <param name="source"></param> /// <param name="destFiles"></param> public async Task <ImageProcessResult> CreateThumbs(FileInfo source, IDictionary <FileInfo, ThumbConfig> destFiles) { var result = new ImageProcessResult(); Stopwatch load = new Stopwatch("ImageSharpLoad"); // Image.Load(string path) is a shortcut for our default type. // Other pixel formats use Image.Load<TPixel>(string path)) using var image = await Image.LoadAsync <Rgba32>(source.FullName); load.Stop(); // We've got the image in memory. Create the hash. result.ImageHash = GetHash(image); Stopwatch orient = new Stopwatch("ImageSharpOrient"); image.Mutate(x => x.AutoOrient()); orient.Stop(); Stopwatch thumbs = new Stopwatch("ImageSharpThumbs"); foreach (var pair in destFiles) { var dest = pair.Key; var config = pair.Value; var mode = ResizeMode.Max; var size = new Size { Height = config.height, Width = config.width }; Logging.LogTrace("Generating thumbnail for {0}: {1}x{2}", source.Name, size.Width, size.Height); if (config.cropToRatio) { // For the smallest thumbs, we crop to fix the aspect exactly. mode = ResizeMode.Crop; } var opts = new ResizeOptions { Mode = mode, Size = size, Sampler = KnownResamplers.Lanczos8 }; // Note, we don't clone and resize from the original image, because that's expensive. // So we always resize the previous image, which will be faster for each iteration // because each previous image is progressively smaller. image.Mutate(x => x.Resize(opts)); await image.SaveAsync(dest.FullName); result.ThumbsGenerated = true; } thumbs.Stop(); return(result); }
public void ImageProcess() { string key = photoKey; JObject o = new JObject(); // 返回原图 o["is_pic_info"] = 1; JArray rules = new JArray(); JObject rule = new JObject(); rule["bucket"] = bucket; rule["fileid"] = "desample_photo.png"; //处理参数,规则参见:https://cloud.tencent.com/document/product/460/19017 rule["rule"] = "imageMogr2/thumbnail/400x400"; rules.Add(rule); o["rules"] = rules; string ruleString = o.ToString(Formatting.None); ImageProcessRequest request = new ImageProcessRequest(bucket, key, ruleString); ImageProcessResult result = QCloudServer.Instance().cosXml.ImageProcess(request); var uploadResult = result.uploadResult; // Console.WriteLine(result.GetResultInfo()); Assert.IsNotEmpty((result.GetResultInfo())); Assert.True(result.IsSuccessful()); Assert.NotNull(uploadResult); Assert.NotNull(uploadResult.originalInfo); Assert.NotNull(uploadResult.originalInfo.ETag); Assert.NotNull(uploadResult.originalInfo.Key); Assert.NotNull(uploadResult.originalInfo.Location); Assert.NotNull(uploadResult.originalInfo.imageInfo.Ave); Assert.NotNull(uploadResult.originalInfo.imageInfo.Format); Assert.NotNull(uploadResult.originalInfo.imageInfo.Orientation); Assert.NotZero(uploadResult.originalInfo.imageInfo.Width); Assert.NotZero(uploadResult.originalInfo.imageInfo.Height); Assert.NotZero(uploadResult.originalInfo.imageInfo.Quality); Assert.NotNull(uploadResult.processResults); Assert.NotZero(uploadResult.processResults.results.Count); Assert.True(uploadResult.processResults.results[0].Width <= 400); Assert.True(uploadResult.processResults.results[0].Height <= 400); Assert.NotNull(uploadResult.processResults.results[0].ETag); Assert.NotNull(uploadResult.processResults.results[0].Format); Assert.NotNull(uploadResult.processResults.results[0].Key); Assert.NotNull(uploadResult.processResults.results[0].Location); Assert.NotZero(uploadResult.processResults.results[0].Quality); Assert.NotZero(uploadResult.processResults.results[0].Size); Assert.Zero(uploadResult.processResults.results[0].WatermarkStatus); }
/// <summary> /// Resize using SkiaSharp - this can do 100 images in about 30s (2020 i5 MacBook Air). /// </summary> /// <param name="source"></param> /// <param name="destFiles"></param> public Task <ImageProcessResult> CreateThumbs(FileInfo source, IDictionary <FileInfo, ThumbConfig> destFiles) { Stopwatch load, hashThumb, scale, save, thumbs; ImageProcessResult result = new ImageProcessResult { ThumbsGenerated = false }; try { thumbs = new Stopwatch("GenThumbs"); int desiredWidth = destFiles.Max(x => x.Value.width); load = new Stopwatch("LoadThumb"); using var sourceBitmap = LoadOrientedBitmap(source, desiredWidth); load.Stop(); hashThumb = new Stopwatch("HashThumb"); result.ImageHash = GetHash(sourceBitmap); hashThumb.Stop(); // Dropping this from High to Low doesn't have that much of an effect // in terms of image quality. var quality = SKFilterQuality.Medium; var srcBitmap = sourceBitmap; foreach (var pair in destFiles.OrderByDescending(x => x.Value.width)) { var dest = pair.Key; var config = pair.Value; scale = new Stopwatch("ScaleThumb"); float widthScaleFactor = (float)srcBitmap.Width / (float)config.width; float heighScaleFactor = (float)srcBitmap.Height / (float)config.height; float scaleFactor = Math.Min(widthScaleFactor, heighScaleFactor); using var scaledImage = new SKBitmap((int)(srcBitmap.Width / scaleFactor), (int)(srcBitmap.Height / scaleFactor)); srcBitmap.ScalePixels(scaledImage.PeekPixels(), quality); var cropSize = new SKSize { Height = config.height, Width = config.width }; using var cropped = config.cropToRatio ? Crop(scaledImage, cropSize) : scaledImage; using SKData data = cropped.Encode(SKEncodedImageFormat.Jpeg, 90); scale.Stop(); save = new Stopwatch("SaveThumb"); // TODO: For configs flagged batchcreate == false, perhaps don't write to disk // and just pass back the stream? using (var stream = new FileStream(dest.FullName, FileMode.Create, FileAccess.Write)) data.SaveTo(stream); save.Stop(); // Now, use the previous scaled image as the basis for the // next smaller thumbnail. This should reduce processing // time as we only work on the large image on the first // iteration if (destFiles.Count > 1) { srcBitmap = scaledImage.Copy(); } result.ThumbsGenerated = true; // TODO: Dispose if (pair.Value.size == ThumbSize.ExtraLarge) { Logging.Log($"{pair.Value.size} thumb created for {source.Name} [load: {load.ElapsedTime}ms, scale: {scale.ElapsedTime}ms, save: {save.ElapsedTime}ms]"); } } thumbs.Stop(); } catch (Exception ex) { Logging.Log($"Exception during Thumbnail processing: {ex.Message}"); throw; } return(Task.FromResult(result)); }
/// <summary> /// Convert the files to thumbnails by shelling out to either ImageMagick /// or the faster GraphicsMagick. /// </summary> /// <param name="source">Source.</param> /// <param name="sizes">Sizes.</param> public async Task <ImageProcessResult> CreateThumbs(FileInfo source, IDictionary <FileInfo, ThumbConfig> destFiles) { // This processor doesn't support hash creation ImageProcessResult result = new ImageProcessResult { ThumbsGenerated = false, ImageHash = string.Empty }; // Some useful unsharp and quality settings, plus by defining the max size of the JPEG, it // makes imagemagic more efficient with its memory allocation, so significantly faster. string args; string exeToUse = s_useGraphicsMagick ? graphicsMagickExe : imageMagickExe; int maxHeight = destFiles.Max(x => x.Value.height); int maxWidth = destFiles.Max(x => x.Value.width); if (s_useGraphicsMagick) { args = string.Format(" convert -size {0}x{1} \"{2}\" -quality 90 -unsharp 0.5x0.5+1.25+0.0 ", maxHeight, maxWidth, source.FullName); } else { args = string.Format(" -define jpeg:size={0}x{1} \"{2}\" -quality 90 -unsharp 0.5x0.5+1.25+0.0 ", maxHeight, maxWidth, source.FullName); } FileInfo?altSource = null; List <string> argsList = new List <string>(); // First pre-check whether the thumbs exist - don't want to create them if they don't. foreach (var pair in destFiles.OrderByDescending(x => x.Value.width)) { var dest = pair.Key; var config = pair.Value; // File didn't exist, so add it to the command-line. if (s_useGraphicsMagick) { argsList.Add(string.Format("-thumbnail {0}x{1} -auto-orient -write \"{2}\" ", config.height, config.width, dest.FullName)); } else { argsList.Add(string.Format("-thumbnail {0}x{1} -auto-orient -write \"{2}\" ", config.height, config.width, dest.FullName)); } } if (argsList.Any()) { var lastArg = argsList.Last(); lastArg = lastArg.Replace(" -write ", " "); argsList[argsList.Count() - 1] = lastArg; args += string.Join(" ", argsList); if (altSource != null) { source = altSource; Logging.LogVerbose("File {0} exists - using it as source for smaller thumbs.", altSource.Name); } Logging.LogVerbose("Converting file {0}", source); Process process = new Process(); process.StartInfo.FileName = exeToUse; process.StartInfo.Arguments = args; process.StartInfo.RedirectStandardError = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.UseShellExecute = false; process.OutputDataReceived += Process_OutputDataReceived; process.ErrorDataReceived += Process_OutputDataReceived; try { Logging.LogVerbose(" Executing: {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); bool success = process.Start(); if (success) { process.BeginErrorReadLine(); process.BeginOutputReadLine(); await process.WaitForExitAsync(); if (process.ExitCode == 0) { result.ThumbsGenerated = true; Logging.LogVerbose("Execution complete."); } else { throw new Exception("Failed"); } } } catch (Exception ex) { Logging.LogError("Conversion failed. Unable to start process: {0}", ex.Message); Logging.LogError($"Failed commandline was: {exeToUse} {args}"); } } else { Logging.LogVerbose("Thumbs already exist in all resolutions. Skipping..."); } return(result); }
/// <summary> /// Resize using SkiaSharp - this can do 100 images in about 30s (2020 i5 MacBook Air). /// </summary> /// <param name="source"></param> /// <param name="destFiles"></param> public Task <ImageProcessResult> CreateThumbs(FileInfo source, IDictionary <FileInfo, ThumbConfig> destFiles) { ImageProcessResult result = new ImageProcessResult { ThumbsGenerated = false }; try { int desiredWidth = destFiles.Max(x => x.Value.width); using var sourceBitmap = LoadOrientedBitmap(source, desiredWidth); result.ImageHash = GetHash(sourceBitmap); Stopwatch thumbs = new Stopwatch("SkiaSharpThumbs"); // Dropping this from High to Low doesn't have that much of an effect // in terms of performance. var quality = SKFilterQuality.Low; var srcBitmap = sourceBitmap; foreach (var pair in destFiles.OrderByDescending(x => x.Value.width)) { var dest = pair.Key; var config = pair.Value; float widthScaleFactor = (float)srcBitmap.Width / (float)config.width; float heighScaleFactor = (float)srcBitmap.Height / (float)config.height; float scaleFactor = Math.Min(widthScaleFactor, heighScaleFactor); using var scaledImage = new SKBitmap((int)(srcBitmap.Width / scaleFactor), (int)(srcBitmap.Height / scaleFactor)); srcBitmap.ScalePixels(scaledImage.PeekPixels(), quality); var cropSize = new SKSize { Height = config.height, Width = config.width }; using var cropped = config.cropToRatio ? Crop(scaledImage, cropSize) : scaledImage; using SKData data = cropped.Encode(SKEncodedImageFormat.Jpeg, 90); using (var stream = new FileStream(dest.FullName, FileMode.Create, FileAccess.Write)) data.SaveTo(stream); // Now, use the previous scaled image as the basis for the // next smaller thumbnail. This should reduce processing // time as we only work on the large image on the first // iteration srcBitmap = scaledImage.Copy(); result.ThumbsGenerated = true; // TODO: Dispose } thumbs.Stop(); } catch (Exception ex) { Logging.Log($"Exception during Skia processing: {ex.Message}"); throw; } return(Task.FromResult(result)); }