简体   繁体   中英

Blurring an uploaded image ASP.NET Core API

I'm writing an api using .NET 6.
As part of my requirements I need to :

  • Handle file uploads, supporting only images.
  • I need to convert the image to jpg (remove any alpha channel).
  • Resize (shrink) if needed.
  • Create a blurred copy of the image, and of course save both images.

I tried the following packages :

  • Magick.NET - It was painfully slow to blur images.
  • ImageSharp - Resulted in huge memory leak (10 Concurrent requests raised the memory from 140 MB to 600 MB, for 1 MB images).
  • OpenCV (EmguCV or OpenCVSharp) - A lot of hustle to work with byte stream.
  • ImageProcessor - Doesn't support .NET core or .NET 5+.

I tried tackling the blur problem first, I thought that if I could find a good blur supporting library I would find everything else... and I'm kind of stuck here.

This is what I have so far (using ImageSharp)

    [Route("api/[controller]")]
    [ApiController]
    public class PhotosController : ControllerBase
    {
        [HttpPost]
        [Consumes("multipart/form-data")]
        public async Task<IActionResult> UploadAsync([FromForm] IFormFile photo)
        {
            if (photo.Length > 0)
            {
                try
                {
                    var folderName = Path.Combine("Resources", "Images");
                    var pathToSave = Path.Combine(Directory.GetCurrentDirectory(), folderName);
                    var fileNameWithoutExt = Path.GetFileNameWithoutExtension(ContentDispositionHeaderValue.Parse(photo.ContentDisposition).FileName.Trim('"'));
                    var ext = ".jpg";
                    var fullPath = Path.Combine(pathToSave, fileNameWithoutExt) + ext;
                    var dbPath = Path.Combine(folderName, fileNameWithoutExt) + ext;

                    using (var stream = photo.OpenReadStream())
                    {
                        using (var outputStream = Blur(stream))
                        {
                            using(var fileStream = System.IO.File.Create(fullPath))
                            {
                                outputStream.CopyTo(fileStream);
                            }
                        }
                    }

                    return Ok(new { dbPath });
                }
                catch (Exception ex)
                {
                    return StatusCode(500, $"Internal server error: {ex}");
                }
            }
            else
            {
                return BadRequest();
            }
        }

        private static Stream Blur(Stream stream)
        {
            var outputStream = new MemoryStream();
            using (var image = Image.Load(stream, new PngDecoder()))
            {
                image.Mutate(ctx =>
                {
                    ctx.GaussianBlur(35);
                });
                image.SaveAsJpeg(outputStream);
            }
            return outputStream;
        }
    }

Thanks !

After searching some blur supporting libraries in asp.net core, I didn't find any useful. So here is a demo about Create a blurred copy of the image and of course save both images without any Third-party class library. I just use console.app to test, you just need to change a little code when you upload file in api.

 static void Main(string[] args)
        {
            //When you upload the image, You can save the image in the specified path, Then read it. For testing convience, I just read the exciting file here.
            string path = @"xxxxxxxxxxxx";

            //set the path of output image
            string path2 = @"xxxxxxxxxxxxxxxx";
            Bitmap bitmap = new Bitmap(path);
            bitmap = Blur(bitmap, 10);
            bitmap.Save(path2);
        }

        private static Bitmap Blur(Bitmap image, Int32 blurSize)
        {
            return Blur(image, new Rectangle(0, 0, image.Width, image.Height), blurSize);
        }

        private unsafe static Bitmap Blur(Bitmap image, Rectangle rectangle, Int32 blurSize)
        {
            Bitmap blurred = new Bitmap(image.Width, image.Height);

            // make an exact copy of the bitmap provided
            using (Graphics graphics = Graphics.FromImage(blurred))
                graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height),
                    new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);

            // Lock the bitmap's bits
            BitmapData blurredData = blurred.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, blurred.PixelFormat);

            // Get bits per pixel for current PixelFormat
            int bitsPerPixel = Image.GetPixelFormatSize(blurred.PixelFormat);

            // Get pointer to first line
            byte* scan0 = (byte*)blurredData.Scan0.ToPointer();

            // look at every pixel in the blur rectangle
            for (int xx = rectangle.X; xx < rectangle.X + rectangle.Width; xx++)
            {
                for (int yy = rectangle.Y; yy < rectangle.Y + rectangle.Height; yy++)
                {
                    int avgR = 0, avgG = 0, avgB = 0;
                    int blurPixelCount = 0;

                    // average the color of the red, green and blue for each pixel in the
                    // blur size while making sure you don't go outside the image bounds
                    for (int x = xx; (x < xx + blurSize && x < image.Width); x++)
                    {
                        for (int y = yy; (y < yy + blurSize && y < image.Height); y++)
                        {
                            // Get pointer to RGB
                            byte* data = scan0 + y * blurredData.Stride + x * bitsPerPixel / 8;

                            avgB += data[0]; // Blue
                            avgG += data[1]; // Green
                            avgR += data[2]; // Red

                            blurPixelCount++;
                        }
                    }

                    avgR = avgR / blurPixelCount;
                    avgG = avgG / blurPixelCount;
                    avgB = avgB / blurPixelCount;

                    // now that we know the average for the blur size, set each pixel to that color
                    for (int x = xx; x < xx + blurSize && x < image.Width && x < rectangle.Width; x++)
                    {
                        for (int y = yy; y < yy + blurSize && y < image.Height && y < rectangle.Height; y++)
                        {
                            // Get pointer to RGB
                            byte* data = scan0 + y * blurredData.Stride + x * bitsPerPixel / 8;

                            // Change values
                            data[0] = (byte)avgB;
                            data[1] = (byte)avgG;
                            data[2] = (byte)avgR;
                        }
                    }
                }
            }

            // Unlock the bits
            blurred.UnlockBits(blurredData);

            return blurred;
        }

If you think thses methods is slow, I think you can put them in background service.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM