You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VB.NET实现无画质损失的JPG图片裁剪(仿MS Paint功能)

精准裁剪JPG至指定尺寸并保留原始DPI与画质(VB.NET实现)

现有一张略大于19"×23"的JPG图片,需用VB.NET实现精准裁剪至19"×23"并完全保留原始画质。

在MS Paint中可轻松完成该操作:打开一张2851×4651像素、200 DPI的JPG,通过「图像属性」将宽高修改为3800×4600像素(对应200 DPI下的19"×23"),最终仅裁剪右侧和底部各51像素,画质、压缩率与原图完全一致,仅文件大小略有减小。

但使用常规裁剪/缩放方法时,保存的图片始终默认96 DPI;即便调整像素尺寸使最终物理尺寸为19"×23",也会出现明显像素化,文件大小大幅降低,存在严重画质损失。

核心需求是实现类似MS Paint的无损裁剪——仅裁去侧边和底部的少量区域,同时必须保留原始DPI(如200 DPI),允许使用外部库。


存在画质损失的示例代码

以下代码虽能生成19"×23"的图片,但因硬编码96 DPI且涉及缩放,存在画质损失:

Public Shared Function ResizeImage(SourceImage As Drawing.Image, TargetWidthIn As Decimal, TargetHeightIn As Decimal) As Drawing.Bitmap

    'Dim TargetWidth As Integer = TargetWidthIn * SourceImage.HorizontalResolution
    'Dim TargetHeight As Integer = TargetHeightIn * SourceImage.VerticalResolution
    Dim TargetWidth As Integer = TargetWidthIn * 96
    Dim TargetHeight As Integer = TargetHeightIn * 96

    Dim bmSource = New Drawing.Bitmap(SourceImage)

    Dim bmDest As New Drawing.Bitmap(TargetWidth, TargetHeight, Drawing.Imaging.PixelFormat.Format32bppArgb)

    Dim nSourceAspectRatio = bmSource.Width / bmSource.Height
    Dim nDestAspectRatio = bmDest.Width / bmDest.Height

    Dim NewX = 0
    Dim NewY = 0
    Dim NewWidth = bmDest.Width
    Dim NewHeight = bmDest.Height

    If nDestAspectRatio = nSourceAspectRatio Then
        'same ratio
    ElseIf nDestAspectRatio > nSourceAspectRatio Then
        'Source is taller
        NewWidth = Convert.ToInt32(Math.Floor(nSourceAspectRatio * NewHeight))
        NewX = Convert.ToInt32(Math.Floor((bmDest.Width - NewWidth) / 2))
    Else
        'Source is wider
        NewHeight = Convert.ToInt32(Math.Floor((1 / nSourceAspectRatio) * NewWidth))
        NewY = Convert.ToInt32(Math.Floor((bmDest.Height - NewHeight) / 2))
    End If

    Using grDest = Drawing.Graphics.FromImage(bmDest)
        With grDest
            .CompositingQuality = Drawing.Drawing2D.CompositingQuality.HighQuality
            '.InterpolationMode = Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
            .InterpolationMode = Drawing.Drawing2D.InterpolationMode.NearestNeighbor
            .PixelOffsetMode = Drawing.Drawing2D.PixelOffsetMode.HighQuality
            .CompositingMode = Drawing.Drawing2D.CompositingMode.SourceCopy
            '.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias
            '.CompositingMode = Drawing.Drawing2D.CompositingMode.SourceOver
            .DrawImage(bmSource, NewX, NewY, NewWidth, NewHeight)
        End With
    End Using

    Return bmDest
End Function

无损裁剪解决方案

方案1:原生GDI+实现

核心思路:基于原始图片DPI计算目标像素尺寸,直接裁剪像素区域,保留原始DPI与压缩参数。

Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Linq

Public Shared Function CropImageToExactSize(sourceImage As Image, targetWidthInches As Decimal, targetHeightInches As Decimal) As Bitmap
    ' 基于原始DPI计算目标像素尺寸
    Dim targetWidth As Integer = CInt(Math.Round(targetWidthInches * sourceImage.HorizontalResolution))
    Dim targetHeight As Integer = CInt(Math.Round(targetHeightInches * sourceImage.VerticalResolution))

    ' 校验原始图片尺寸是否足够
    If sourceImage.Width < targetWidth OrElse sourceImage.Height < targetHeight Then
        Throw New ArgumentException("原始图片尺寸小于目标裁剪尺寸")
    End If

    ' 定义裁剪区域:此处裁剪右侧和底部,起始点为(0,0)
    ' 若需居中裁剪,可调整起始坐标为(sourceImage.Width - targetWidth)/2、(sourceImage.Height - targetHeight)/2
    Dim cropRect As New Rectangle(0, 0, targetWidth, targetHeight)

    ' 创建裁剪后的Bitmap并设置原始DPI
    Dim croppedBitmap As New Bitmap(targetWidth, targetHeight)
    croppedBitmap.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution)

    ' 绘制裁剪区域到新Bitmap
    Using g As Graphics = Graphics.FromImage(croppedBitmap)
        g.CompositingMode = Drawing2D.CompositingMode.SourceCopy
        g.DrawImage(sourceImage, New Rectangle(0, 0, targetWidth, targetHeight), cropRect, GraphicsUnit.Pixel)
    End Using

    Return croppedBitmap
End Function

' 保存裁剪后的图片,保留原始JPG压缩参数
Public Shared Sub SaveCroppedImage(croppedBitmap As Bitmap, originalImagePath As String, outputPath As String)
    ' 获取JPG编码器
    Dim jpgCodec As ImageCodecInfo = ImageCodecInfo.GetImageEncoders().FirstOrDefault(ici => ici.FormatID = ImageFormat.Jpeg.Guid)
    If jpgCodec Is Nothing Then
        Throw New NotSupportedException("不支持JPG格式")
    End If

    ' 读取原始图片的压缩质量参数
    Dim encoderParams As New EncoderParameters(1)
    Dim originalQuality As Long = 100 ' 默认最高质量
    Using originalImage As Image = Image.FromFile(originalImagePath)
        Dim qualityParam = originalImage.PropertyItems.FirstOrDefault(pi => pi.Id = &H1D)
        If qualityParam IsNot Nothing Then
            originalQuality = BitConverter.ToInt32(qualityParam.Value, 0)
        End If
    End Using

    encoderParams.Param(0) = New EncoderParameter(Encoder.Quality, originalQuality)
    ' 保存图片,保留DPI与压缩质量
    croppedBitmap.Save(outputPath, jpgCodec, encoderParams)
End Sub

' 使用示例
'Using sourceImg As Image = Image.FromFile("原始图片路径.jpg")
'    Using croppedBmp As Bitmap = CropImageToExactSize(sourceImg, 19, 23)
'        SaveCroppedImage(croppedBmp, "原始图片路径.jpg", "输出图片路径.jpg")
'    End Using
'End Using

方案2:使用ImageSharp库(推荐)

ImageSharp是跨平台的.NET图像处理库,无需依赖GDI+,处理DPI与压缩参数更简洁。

首先安装NuGet包:SixLabors.ImageSharp 和 SixLabors.ImageSharp.Formats.Jpeg

Imports SixLabors.ImageSharp
Imports SixLabors.ImageSharp.Formats.Jpeg
Imports SixLabors.ImageSharp.Processing

Public Shared Sub CropImageWithImageSharp(sourcePath As String, outputPath As String, targetWidthInches As Decimal, targetHeightInches As Decimal)
    Using image As Image = Image.Load(sourcePath)
        ' 获取原始DPI
        Dim dpiX As Double = image.Metadata.HorizontalResolution
        Dim dpiY As Double = image.Metadata.VerticalResolution

        ' 计算目标像素尺寸
        Dim targetWidth As Integer = CInt(Math.Round(targetWidthInches * dpiX))
        Dim targetHeight As Integer = CInt(Math.Round(targetHeightInches * dpiY))

        ' 裁剪右侧和底部区域
        image.Mutate(x => x.Crop(targetWidth, targetHeight))

        ' 保留原始DPI设置
        image.Metadata.HorizontalResolution = dpiX
        image.Metadata.VerticalResolution = dpiY

        ' 使用原始压缩质量保存(可读取原始参数,此处默认最高质量)
        Dim jpegEncoder = New JpegEncoder()
        jpegEncoder.Quality = 100

        image.Save(outputPath, jpegEncoder)
    End Using
End Sub

关键注意点

  • 必须基于原始图片的DPI计算目标像素尺寸,而非硬编码96 DPI
  • 裁剪操作直接截取像素区域,避免缩放,杜绝画质损失
  • 保存时务必保留原始图片的DPI设置与JPG压缩参数(质量、子采样等)

内容的提问来源于stack exchange,提问作者kpg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 09:55:31