public BasicStats GetStats(BitmapAdapter bitmap, ImageManipulationInfo manipulationInfo) { // Console.WriteLine(manipulationInfo.ToString()); var fast = new FastBitmap.FastBitmap(bitmap.GetSegment(manipulationInfo)); return(GetBasicStats(fast, new Rectangle(0, 0, fast.Width, fast.Height))); }
public IEnumerable <Bitmap> GetRefinedMatches2(FastBitmap.FastBitmap bitmap, ImageManipulationInfo manipulationInfo) { ImageSegments[] matches = _matchFinder.Value.GetMatches(_statsGenerator.GetStats(bitmap, manipulationInfo)); return(_matchFinder.Value.RefineMatches2(_statsGenerator.GetAdvancedStats(bitmap, manipulationInfo.Rectangle), matches, _loader, _statsGenerator) .Select(m => new { Image = new BitmapAdapter(m.Image.ToBitmap()), Segments = m.ManipulationInfos }) .SelectMany(m => m.Segments.Select(m.Image.GetSegment))); }
public Bitmap GetSegment(ImageManipulationInfo manipulationInfo) { var targetBitmap = new Bitmap(manipulationInfo.Width, manipulationInfo.Height); FastBitmap.FastBitmap segment = new FastBitmap.FastBitmap(targetBitmap); segment.Lock(); segment.CopyRegion(_bitmap, manipulationInfo.Rectangle, manipulationInfo.AsZeroBasedRectangleOfSameSize()); segment.Unlock(); return(targetBitmap); }
private ImageAndStats GetMatchableSegments(PhysicalImage physicalImage) { var img = _loader.LoadImage(physicalImage.ImagePath); // For movie stills with widescreen bars above/below, start further down and end higher up IEnumerable <Rectangle> rects = GetSegmentRectangles(new Rectangle(6, 6, img.Width - 12, img.Height - 12)); // IEnumerable<Rectangle> rects = GetSegmentRectangles(new Rectangle(0, 0, img.Width, img.Height)); var segmentStats = new List <SegmentAndStats>(); foreach (var rectangle in rects) { var imageManipulationInfo = new ImageManipulationInfo(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); Stats.BasicStats basicStats = GetStats(physicalImage, imageManipulationInfo); segmentStats.Add(new SegmentAndStats(imageManipulationInfo, basicStats)); } return(new ImageAndStats(physicalImage, segmentStats)); }
private void WriteSourceAndFill(SourceAndMatch match) { FastBitmap.FastBitmap fillImage = new FastBitmap.FastBitmap(match.ReplacementImage); fillImage.Lock(); ImageManipulationInfo hole = match.SourceSegment; for (int xOffset = 0; xOffset < hole.Width; xOffset++) { for (int yOffset = 0; yOffset < hole.Height; yOffset++) { Color holeColor = _target.GetPixel(hole.StartX + xOffset, hole.StartY + yOffset); Color fillColor = fillImage.GetPixel(xOffset, yOffset); _newImage.SetPixel(hole.StartX + xOffset, hole.StartY + yOffset, GetNewColor(holeColor, fillColor)); } } fillImage.Unlock(); }
public IEnumerable <Bitmap> GetRefinedMatches2(PhysicalImage image, ImageManipulationInfo manipulationInfo) { var bitmap = _loader.LoadImage(image.ImagePath); return(GetRefinedMatches2(bitmap, manipulationInfo)); }
public Bitmap GetBitmap(PhysicalImage physicalImage, ImageManipulationInfo manipulationInfo) { return(BitmapAdapter.FromPath(physicalImage.ImagePath, _loader) .GetSegment(manipulationInfo)); }
public SourceAndMatch(ImageManipulationInfo sourceSegment, Bitmap replacementImage) { SourceSegment = sourceSegment; ReplacementImage = replacementImage; }
public BasicStats GetStats(FastBitmap.FastBitmap bitmap, ImageManipulationInfo manipulationInfo) { return(GetBasicStats(bitmap, manipulationInfo.Rectangle)); }
public BasicStats GetStats(PhysicalImage image, ImageManipulationInfo manipulationInfo) { global::FastBitmap.FastBitmap bitmap = _loader.LoadImage(image.ImagePath); return(GetStats(bitmap, manipulationInfo)); }
/// <summary> /// Given some image data as a stream, transform the image to fit within the given size (of bytes) and save to the given savePath /// </summary> /// <param name="fileData"></param> /// <param name="maxSize"></param> /// <returns></returns> public async Task <ImageManipulationInfo> FitToSizeAndSave(Stream fileData, string savePath, int maxSize) //, double resizeFactor, double resizeFactorReduce) { var result = new ImageManipulationInfo { RenderCount = 0, LoadCount = 1, SizeInBytes = fileData.Length }; logger.LogTrace($"FitToSize called with size {maxSize}, image bytes {result.SizeInBytes}"); await SingleManipLock.WaitAsync(); try { using var memStream = new MemoryStream(); await fileData.CopyToAsync(memStream); fileData.Seek(0, SeekOrigin.Begin); IImageFormat?format; //This will throw an exception if it's not an image (most likely) using (var image = Image.Load(fileData, out format)) { double sizeFactor = ResizeFactor; result.Width = image.Width; result.Height = image.Height; result.MimeType = format.DefaultMimeType; //while (fileConfig.tryResize && imageByteCount > config.MaxSize && sizeFactor > 0) while (result.SizeInBytes > maxSize && sizeFactor > 0) { double resize = 1 / Math.Sqrt(result.SizeInBytes / (maxSize * sizeFactor)); logger.LogWarning($"User image too large ({result.SizeInBytes}), trying ONE resize by {resize}"); result.Width = (int)(result.Width * resize); result.Height = (int)(result.Height * resize); image.Mutate(x => x.Resize(result.Width, result.Height, KnownResamplers.Lanczos3)); result.RenderCount++; memStream.SetLength(0); image.Save(memStream, format); result.SizeInBytes = memStream.Length; //Keep targeting an EVEN more harsh error margin (even if it's incorrect because //it stacks with previous resizes), also this makes the loop guaranteed to end sizeFactor -= ResizeFactorReduce; } //If the image is still too big, bad ok bye if (result.SizeInBytes > maxSize) { throw new RequestException("File too large!"); } } logger.LogDebug($"New image size: {result.SizeInBytes}"); memStream.Seek(0, SeekOrigin.Begin); using (var stream = System.IO.File.Create(savePath)) { await memStream.CopyToAsync(stream); } } finally { SingleManipLock.Release(); } return(result); }
/// <summary> /// Given an image as a stream, perform the given modifications to it and save it to the given path /// </summary> /// <param name="fileData"></param> /// <param name="savePath"></param> /// <param name="modify"></param> /// <returns></returns> public async Task <ImageManipulationInfo> MakeThumbnailAndSave(Stream fileData, string savePath, GetFileModify modify) //, bool highQualityResize) { var result = new ImageManipulationInfo { RenderCount = 0, LoadCount = 1 }; await SingleManipLock.WaitAsync(); try { await Task.Run(() => { IImageFormat?format; using var image = Image.Load(fileData, out format); result.MimeType = format.DefaultMimeType; //var maxDim = Math.Max(image.Width, image.Height); var isGif = format.DefaultMimeType == GifMime; var isJpg = format.DefaultMimeType == JpegMime; //Square ALWAYS happens, it can happen before other things. if (modify.crop) { var minDim = Math.Min(image.Width, image.Height); image.Mutate(x => x.Crop(new Rectangle((image.Width - minDim) / 2, (image.Height - minDim) / 2, minDim, minDim))); result.RenderCount++; } //This must come after the crop! var isNowLarger = (modify.size > Math.Max(image.Width, image.Height)); //Saving as png also works, but this preserves the format (even if it's a little heavier compute, it's only a one time thing) if (modify.freeze && isGif) { while (image.Frames.Count > 1) { image.Frames.RemoveFrame(1); } result.RenderCount++; } if (modify.size > 0 && !(isGif && isNowLarger)) //&& (modify.size > image.Width || modify.size > image.Height))) { var width = 0; var height = 0; //Preserve aspect ratio when not square if (image.Width > image.Height) { width = modify.size; } else { height = modify.size; } if (HighQualityResize) { image.Mutate(x => x.Resize(width, height, isNowLarger ? KnownResamplers.Spline : KnownResamplers.Lanczos3)); } else { image.Mutate(x => x.Resize(width, height)); } result.RenderCount++; } result.Width = image.Width; result.Height = image.Height; using (var stream = System.IO.File.OpenWrite(savePath)) { IImageEncoder?encoder = null; if (HighQualityResize && modify.size <= MinJpegHighQualitySize && isJpg) { encoder = new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder() { Quality = JpegHighQuality, }; } if (encoder != null) { image.Save(stream, encoder); } else { image.Save(stream, format); } result.SizeInBytes = stream.Length; } }); } finally { SingleManipLock.Release(); } return(result); }