private static SKBitmap AutoOrient(SKBitmap bitmap, SKEncodedOrigin origin) { Stopwatch orient = new Stopwatch("SkiaSharpOrient"); SKBitmap rotated; switch (origin) { case SKEncodedOrigin.BottomRight: rotated = new SKBitmap(bitmap.Height, bitmap.Width); using (var canvas = new SKCanvas(rotated)) { canvas.RotateDegrees(180, bitmap.Width / 2, bitmap.Height / 2); canvas.DrawBitmap(bitmap, 0, 0); } break; case SKEncodedOrigin.RightTop: rotated = new SKBitmap(bitmap.Height, bitmap.Width); using (var canvas = new SKCanvas(rotated)) { canvas.Translate(rotated.Width, 0); canvas.RotateDegrees(90); canvas.DrawBitmap(bitmap, 0, 0); } break; case SKEncodedOrigin.LeftBottom: rotated = new SKBitmap(bitmap.Height, bitmap.Width); using (var canvas = new SKCanvas(rotated)) { canvas.Translate(0, rotated.Height); canvas.RotateDegrees(270); canvas.DrawBitmap(bitmap, 0, 0); } break; default: rotated = bitmap.Copy(); break; } orient.Stop(); return(rotated); }
private BitmapOptions GetBitmapOptions(SKEncodedOrigin orientation) { switch (orientation) { case SKEncodedOrigin.BottomRight: return(new BitmapOptions(_bitmap.Width, _bitmap.Height, _bitmap.Width, _bitmap.Height, 180)); case SKEncodedOrigin.RightTop: return(new BitmapOptions(_bitmap.Height, _bitmap.Width, _bitmap.Height, 0, 90)); case SKEncodedOrigin.LeftBottom: return(new BitmapOptions(_bitmap.Height, _bitmap.Width, 0, _bitmap.Height, 270)); default: return(null); } }
private SKBitmap LoadBitmap(Stream stream, out SKEncodedOrigin origin) { using var managedStream = new SKManagedStream(stream); using var codec = SKCodec.Create(managedStream); origin = codec.EncodedOrigin; var info = codec.Info; var bitmap = new SKBitmap(info.Width, info.Height, SKImageInfo.PlatformColorType, info.IsOpaque ? SKAlphaType.Opaque : SKAlphaType.Premul); var result = codec.GetPixels(bitmap.Info, bitmap.GetPixels(out _)); if (result == SKCodecResult.Success || result == SKCodecResult.IncompleteInput) { return(bitmap); } throw new ArgumentException("Невозможно загрузить изображение из источника."); }
private static SKBitmap HandleOrientation(SKBitmap bitmap, SKEncodedOrigin orientation) { SKBitmap rotated; switch (orientation) { case SKEncodedOrigin.BottomRight: using (var surface = new SKCanvas(bitmap)) { surface.RotateDegrees(180, bitmap.Width / 2, bitmap.Height / 2); surface.DrawBitmap(bitmap.Copy(), 0, 0); } return(bitmap); case SKEncodedOrigin.RightTop: rotated = new SKBitmap(bitmap.Height, bitmap.Width); using (var surface = new SKCanvas(rotated)) { surface.Translate(rotated.Width, 0); surface.RotateDegrees(90); surface.DrawBitmap(bitmap, 0, 0); } return(rotated); case SKEncodedOrigin.LeftBottom: rotated = new SKBitmap(bitmap.Height, bitmap.Width); using (var surface = new SKCanvas(rotated)) { surface.Translate(0, rotated.Height); surface.RotateDegrees(270); surface.DrawBitmap(bitmap, 0, 0); } return(rotated); default: return(bitmap); } }
private SKBitmap LoadBitmap(Stream stream, out SKEncodedOrigin origin) { using SKManagedStream s = new SKManagedStream(stream); using SKCodec codec = SKCodec.Create(s); origin = codec.EncodedOrigin; SKImageInfo info = codec.Info; SKBitmap bitmap = new SKBitmap(info.Width, info.Height, SKImageInfo.PlatformColorType, info.IsOpaque ? SKAlphaType.Opaque : SKAlphaType.Premul); SKCodecResult result = codec.GetPixels(bitmap.Info, bitmap.GetPixels(out IntPtr length)); if (result == SKCodecResult.Success || result == SKCodecResult.IncompleteInput) { return(bitmap); } else { throw new ArgumentException("Unable to load bitmap from provided data"); } }
/// <summary> /// Sets the rotation-to-apply-property to the right enum value. Currently, this supports only non-flipping /// origins since the translator only supports non-flipped images as well. The will be extended if I can get an /// image, that is flipped, otherwise this is practically not testable /// </summary> /// <param name="origin"></param> protected void SetRotation(SKEncodedOrigin origin) { switch (origin) { case SKEncodedOrigin.Default: _rotationToApplyForFaceProcessing = RotateFlipType.RotateNoneFlipNone; break; case SKEncodedOrigin.RightTop: _rotationToApplyForFaceProcessing = RotateFlipType.Rotate90FlipNone; break; case SKEncodedOrigin.BottomRight: _rotationToApplyForFaceProcessing = RotateFlipType.Rotate180FlipNone; break; case SKEncodedOrigin.LeftBottom: _rotationToApplyForFaceProcessing = RotateFlipType.Rotate270FlipNone; break; } }
private SKBitmap LoadBitmap(Stream stream, out SKEncodedOrigin origin, out SKEncodedImageFormat format) { using (var s = new SKManagedStream(stream)) { using (var codec = SKCodec.Create(s)) { origin = codec.EncodedOrigin; format = codec.EncodedFormat; // decode the bitmap at the nearest size var bitmap = SKBitmap.Decode(codec); var result = codec.GetPixels(bitmap.Info, bitmap.GetPixels(out IntPtr length)); if (result == SKCodecResult.Success || result == SKCodecResult.IncompleteInput) { return(bitmap); } else { throw new ArgumentException("Unable to load bitmap from provided data."); } } } }
private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin) { //var transformations = { // 2: { rotate: 0, flip: true}, // 3: { rotate: 180, flip: false}, // 4: { rotate: 180, flip: true}, // 5: { rotate: 90, flip: true}, // 6: { rotate: 90, flip: false}, // 7: { rotate: 270, flip: true}, // 8: { rotate: 270, flip: false}, //} switch (origin) { case SKEncodedOrigin.TopRight: { var rotated = new SKBitmap(bitmap.Width, bitmap.Height); using (var surface = new SKCanvas(rotated)) { surface.Translate(rotated.Width, 0); surface.Scale(-1, 1); surface.DrawBitmap(bitmap, 0, 0); } return(rotated); } case SKEncodedOrigin.BottomRight: { var rotated = new SKBitmap(bitmap.Width, bitmap.Height); using (var surface = new SKCanvas(rotated)) { float px = (float)bitmap.Width / 2; float py = (float)bitmap.Height / 2; surface.RotateDegrees(180, px, py); surface.DrawBitmap(bitmap, 0, 0); } return(rotated); } case SKEncodedOrigin.BottomLeft: { var rotated = new SKBitmap(bitmap.Width, bitmap.Height); using (var surface = new SKCanvas(rotated)) { float px = (float)bitmap.Width / 2; float py = (float)bitmap.Height / 2; surface.Translate(rotated.Width, 0); surface.Scale(-1, 1); surface.RotateDegrees(180, px, py); surface.DrawBitmap(bitmap, 0, 0); } return(rotated); } case SKEncodedOrigin.LeftTop: { // TODO: Remove dual canvases, had trouble with flipping using (var rotated = new SKBitmap(bitmap.Height, bitmap.Width)) { using (var surface = new SKCanvas(rotated)) { surface.Translate(rotated.Width, 0); surface.RotateDegrees(90); surface.DrawBitmap(bitmap, 0, 0); } var flippedBitmap = new SKBitmap(rotated.Width, rotated.Height); using (var flippedCanvas = new SKCanvas(flippedBitmap)) { flippedCanvas.Translate(flippedBitmap.Width, 0); flippedCanvas.Scale(-1, 1); flippedCanvas.DrawBitmap(rotated, 0, 0); } return(flippedBitmap); } } case SKEncodedOrigin.RightTop: { var rotated = new SKBitmap(bitmap.Height, bitmap.Width); using (var surface = new SKCanvas(rotated)) { surface.Translate(rotated.Width, 0); surface.RotateDegrees(90); surface.DrawBitmap(bitmap, 0, 0); } return(rotated); } case SKEncodedOrigin.RightBottom: { // TODO: Remove dual canvases, had trouble with flipping using (var rotated = new SKBitmap(bitmap.Height, bitmap.Width)) { using (var surface = new SKCanvas(rotated)) { surface.Translate(0, rotated.Height); surface.RotateDegrees(270); surface.DrawBitmap(bitmap, 0, 0); } var flippedBitmap = new SKBitmap(rotated.Width, rotated.Height); using (var flippedCanvas = new SKCanvas(flippedBitmap)) { flippedCanvas.Translate(flippedBitmap.Width, 0); flippedCanvas.Scale(-1, 1); flippedCanvas.DrawBitmap(rotated, 0, 0); } return(flippedBitmap); } } case SKEncodedOrigin.LeftBottom: { var rotated = new SKBitmap(bitmap.Height, bitmap.Width); using (var surface = new SKCanvas(rotated)) { surface.Translate(0, rotated.Height); surface.RotateDegrees(270); surface.DrawBitmap(bitmap, 0, 0); } return(rotated); } default: return(bitmap); } }
private SKBitmap GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, ImageOrientation?orientation, out SKEncodedOrigin origin) { if (cropWhitespace) { using (var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin)) { return(CropWhiteSpace(bitmap)); } } return(Decode(path, forceAnalyzeBitmap, orientation, out origin)); }
internal SKBitmap Decode(string path, bool forceCleanBitmap, ImageOrientation?orientation, out SKEncodedOrigin origin) { if (!File.Exists(path)) { throw new FileNotFoundException("File not found", path); } var requiresTransparencyHack = _transparentImageTypes.Contains(Path.GetExtension(path)); if (requiresTransparencyHack || forceCleanBitmap) { using (var stream = new SKFileStream(NormalizePath(path))) using (var codec = SKCodec.Create(stream)) { if (codec == null) { origin = GetSKEncodedOrigin(orientation); return(null); } // create the bitmap var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack); // decode _ = codec.GetPixels(bitmap.Info, bitmap.GetPixels()); origin = codec.EncodedOrigin; return(bitmap); } } var resultBitmap = SKBitmap.Decode(NormalizePath(path)); if (resultBitmap == null) { return(Decode(path, true, orientation, out origin)); } // If we have to resize these they often end up distorted if (resultBitmap.ColorType == SKColorType.Gray8) { using (resultBitmap) { return(Decode(path, true, orientation, out origin)); } } origin = SKEncodedOrigin.TopLeft; return(resultBitmap); }
private SKBitmap RotateAndFlip(SKBitmap original, SKEncodedOrigin origin) { // these are the origins that represent a 90 degree turn in some fashion SKEncodedOrigin[] differentOrientations = new SKEncodedOrigin[] { SKEncodedOrigin.LeftBottom, SKEncodedOrigin.LeftTop, SKEncodedOrigin.RightBottom, SKEncodedOrigin.RightTop }; // check if we need to turn the image bool isDifferentOrientation = differentOrientations.Any(o => o == origin); // define new width/height int width = isDifferentOrientation ? original.Height : original.Width; int height = isDifferentOrientation ? original.Width : original.Height; SKBitmap bitmap = new SKBitmap(width, height, original.AlphaType == SKAlphaType.Opaque); // todo: the stuff in this switch statement should be rewritten to use pointers switch (origin) { case SKEncodedOrigin.LeftBottom: for (int x = 0; x < original.Width; x++) { for (int y = 0; y < original.Height; y++) { bitmap.SetPixel(y, original.Width - 1 - x, original.GetPixel(x, y)); } } break; case SKEncodedOrigin.RightTop: for (int x = 0; x < original.Width; x++) { for (int y = 0; y < original.Height; y++) { bitmap.SetPixel(original.Height - 1 - y, x, original.GetPixel(x, y)); } } break; case SKEncodedOrigin.RightBottom: for (int x = 0; x < original.Width; x++) { for (int y = 0; y < original.Height; y++) { bitmap.SetPixel(original.Height - 1 - y, original.Width - 1 - x, original.GetPixel(x, y)); } } break; case SKEncodedOrigin.LeftTop: for (int x = 0; x < original.Width; x++) { for (int y = 0; y < original.Height; y++) { bitmap.SetPixel(y, x, original.GetPixel(x, y)); } } break; case SKEncodedOrigin.BottomLeft: for (int x = 0; x < original.Width; x++) { for (int y = 0; y < original.Height; y++) { bitmap.SetPixel(x, original.Height - 1 - y, original.GetPixel(x, y)); } } break; case SKEncodedOrigin.BottomRight: for (int x = 0; x < original.Width; x++) { for (int y = 0; y < original.Height; y++) { bitmap.SetPixel(original.Width - 1 - x, original.Height - 1 - y, original.GetPixel(x, y)); } } break; case SKEncodedOrigin.TopRight: for (int x = 0; x < original.Width; x++) { for (int y = 0; y < original.Height; y++) { bitmap.SetPixel(original.Width - 1 - x, y, original.GetPixel(x, y)); } } break; } original.Dispose(); return(bitmap); }
public void CodecCanLoadCorrectOrigin(string image, SKEncodedOrigin origin) { var codec = SKCodec.Create(Path.Combine(PathToImages, image)); Assert.Equal(origin, codec.EncodedOrigin); }
/// <summary> /// Needed for auto-orienting the image after using the Scaled codec to load /// the image. /// </summary> /// <param name="original"></param> /// <param name="origin"></param> /// <returns></returns> private static SKBitmap AutoOrient(SKBitmap original, SKEncodedOrigin origin) { Stopwatch orient = new Stopwatch("SkiaSharpOrient"); var useWidth = original.Width; var useHeight = original.Height; Action <SKCanvas> transform = canvas => { }; switch (origin) { case SKEncodedOrigin.TopLeft: break; case SKEncodedOrigin.TopRight: // flip along the x-axis transform = canvas => canvas.Scale(-1, 1, useWidth / 2, useHeight / 2); break; case SKEncodedOrigin.BottomRight: transform = canvas => canvas.RotateDegrees(180, useWidth / 2, useHeight / 2); break; case SKEncodedOrigin.BottomLeft: // flip along the y-axis transform = canvas => canvas.Scale(1, -1, useWidth / 2, useHeight / 2); break; case SKEncodedOrigin.LeftTop: useWidth = original.Height; useHeight = original.Width; transform = canvas => { // Rotate 90 canvas.RotateDegrees(90, useWidth / 2, useHeight / 2); canvas.Scale(useHeight * 1.0f / useWidth, -useWidth * 1.0f / useHeight, useWidth / 2, useHeight / 2); }; break; case SKEncodedOrigin.RightTop: useWidth = original.Height; useHeight = original.Width; transform = canvas => { canvas.Translate(useWidth, 0); canvas.RotateDegrees(90); }; break; case SKEncodedOrigin.RightBottom: useWidth = original.Height; useHeight = original.Width; transform = canvas => { // Rotate 90 canvas.RotateDegrees(90, useWidth / 2, useHeight / 2); canvas.Scale(-useHeight * 1.0f / useWidth, useWidth * 1.0f / useHeight, useWidth / 2, useHeight / 2); }; break; case SKEncodedOrigin.LeftBottom: useWidth = original.Height; useHeight = original.Width; transform = canvas => { canvas.Translate(0, useHeight); canvas.RotateDegrees(270); }; break; default: break; } var rotated = new SKBitmap(useWidth, useHeight); using (var canvas = new SKCanvas(rotated)) { transform.Invoke(canvas); canvas.DrawBitmap(original, 0, 0); } orient.Stop(); return(rotated); }
private async Task <Stream> Pixelate(SKBitmap bitmap, SKEncodedOrigin origin, IFeatureExtractor featureExtractor) { // fix orientation if encoded origin is not TopLeft/Default bitmap = FixOrientation(bitmap, origin); // a surface is something like a table we need to actually draw stuff var surface = SKSurface.Create(new SKImageInfo(bitmap.Width, bitmap.Height)); // get the canvas where we actually draw onto var canvas = surface.Canvas; canvas.Clear(SKColors.Transparent); // apply the original image to the canvas canvas.DrawBitmap(bitmap, 0, 0); // extract the image features switch (FaceProcessing) { case FaceProcessing.PixelateFaces: { var faces = (await featureExtractor.ExtractFacesAsync()).ToArray(); Logger.OnExtractedFaces(_imagePath, faces); foreach (var face in faces) { if (FaceOutline != null) { Pixelate(canvas, SKRectI.Create(face.X, face.Y, face.Width, face.Height), bitmap, FaceOutline, PixelSizeFunction); } else { Pixelate(canvas, SKRectI.Create(face.X, face.Y, face.Width, face.Height), bitmap, PixelSizeFunction); } Logger.OnPixelatedFace(_imagePath, face); } break; } case FaceProcessing.PixelatePersons: { var persons = (await featureExtractor.ExtractPersonsAsync()).ToArray(); Logger.OnExtractedPersons(_imagePath, persons); foreach (var person in persons) { if (FaceOutline != null) { Pixelate(canvas, SKRectI.Create(person.X, person.Y, person.Width, person.Height), bitmap, FaceOutline, PixelSizeFunction); } else { Pixelate(canvas, SKRectI.Create(person.X, person.Y, person.Width, person.Height), bitmap, PixelSizeFunction); } Logger.OnPixelatedPerson(_imagePath, person); } break; } case FaceProcessing.Skip: break; default: throw new ArgumentOutOfRangeException(); } switch (CarProcessing) { case CarProcessing.PixelatePlatesAndTextOnCars: { var text = (await featureExtractor.ExtractTextAsync()).ToArray(); Logger.OnExtractedText(_imagePath, text); var cars = (await featureExtractor.ExtractCarsAsync()).ToArray(); Logger.OnExtractedCars(_imagePath, cars); var licensePlates = (await featureExtractor.ExtractLicensePlatesAsync()).ToArray(); Logger.OnExtractedLicensePlates(_imagePath, licensePlates); var mergeDistance = (int)(bitmap.Width * MergeFactor); var merged = ImagePatchClusterizer.Clusterize(text, mergeDistance).ToArray(); foreach (var car in cars) { foreach (var patch in merged) { // If patch is inside of the car borders if (patch.Y >= car.Y && patch.X >= car.X && patch.Y <= car.Y + car.Height && patch.X <= car.X + car.Width && patch.Width <= car.Width + car.X - patch.X && patch.Height <= car.Height + car.Y - patch.Y) { if (PlateOutline != null) { Pixelate(canvas, SKRectI.Create(patch.X, patch.Y, patch.Width, patch.Height), bitmap, PlateOutline, PixelSizeFunction); } else { Pixelate(canvas, SKRectI.Create(patch.X, patch.Y, patch.Width, patch.Height), bitmap, PixelSizeFunction); } Logger.OnPixelatedText(_imagePath, patch); } } } foreach (var plate in licensePlates) { if (PlateOutline != null) { Pixelate(canvas, SKRectI.Create(plate.X, plate.Y, plate.Width, plate.Height), bitmap, PlateOutline, PixelSizeFunction); } else { Pixelate(canvas, SKRectI.Create(plate.X, plate.Y, plate.Width, plate.Height), bitmap, PixelSizeFunction); } Logger.OnPixelatedLicensePlate(_imagePath, plate); } break; } case CarProcessing.PixelateCars: { var cars = (await featureExtractor.ExtractCarsAsync()).ToArray(); Logger.OnExtractedCars(_imagePath, cars); foreach (var car in cars) { if (PlateOutline != null) { Pixelate(canvas, SKRectI.Create(car.X, car.Y, car.Width, car.Height), bitmap, PlateOutline, PixelSizeFunction); } else { Pixelate(canvas, SKRectI.Create(car.X, car.Y, car.Width, car.Height), bitmap, PixelSizeFunction); } Logger.OnPixelatedCar(_imagePath, car); } break; } case CarProcessing.Skip: break; default: throw new ArgumentOutOfRangeException(); } return(surface.Snapshot().Encode(OutputFormat, OutputQuality).AsStream()); }