C# 이미지 처리의 기본: Bitmap과 GDI+
비트맵(Bitmap)은 그래픽 이미지를 구성하는 픽셀 데이터와 그 속성 정보를 함께 담고 있는 객체입니다. 비트맵을 파일로 저장할 때는 여러 표준 형식을 사용할 수 있으며, .NET의 GDI+는 BMP, GIF, EXIF, JPG(JPEG), PNG, TIFF 형식을 기본적으로 지원합니다.
Bitmap 클래스가 제공하는 다양한 생성자를 활용하면 파일, 스트림 등 여러 소스로부터 이미지 객체를 만들 수 있고, Save 메서드를 통해 이미지를 스트림이나 파일 시스템에 손쉽게 저장할 수 있습니다.
이미지를 압축해서 지정한 경로에 저장하기
아래 예제의 CompressAndSaveImageAsync 메서드는 입력받은 Bitmap 이미지를 JPEG 형식으로 압축한 뒤 지정된 경로에 저장합니다. 저장 시 Encoder.Quality 매개변수를 사용해 JPEG 품질(기본값 50)을 조절할 수 있습니다.
생성되는 새 이미지 파일 이름은 현재 사용자 ID와 현재 날짜·시간을 조합해 자동으로 만들어지므로 파일명 중복 문제를 피할 수 있습니다.
코드 예제
private async Task<string> CompressAndSaveImageAsync(Bitmap inputImage, int quality = 50)
{
string imageSavedPath = string.Empty;
try
{
// JPEG 인코더 가져오기
var jpgEncoder = await GetEncoderAsync(ImageFormat.Jpeg);
// 품질(quality) 인코더 매개변수 설정
var imageEncoder = Encoder.Quality;
var imageEncoderParameters = new EncoderParameters(1);
var imageEncoderParameter = new EncoderParameter(imageEncoder, quality);
imageEncoderParameters.Param[0] = imageEncoderParameter;
// 사용자 ID와 현재 시각을 조합해 고유한 파일명 생성
var userId = Regex.Replace(WindowsIdentity.GetCurrent().Name, @"[^0-9a-zA-Z]+", "_");
var currentDateTime = Regex.Replace(DateTimeOffset.Now.ToString().Split('+')[0].Trim(), @"[^0-9a-zA-Z]+", "_");
var imageName = $"{userId}_{currentDateTime}.jpg";
// 저장 경로 지정 후 이미지 저장
imageSavedPath = @"C:\Users\K\Desktop\Questions\Images";
inputImage.Save(imageSavedPath, jpgEncoder, imageEncoderParameters);
inputImage.Dispose();
}
catch (Exception ex)
{
throw;
}
return imageSavedPath;
}
private async Task<ImageCodecInfo> GetEncoderAsync(ImageFormat format)
{
ImageCodecInfo imageCodecInfo = null;
try
{
// 시스템에서 사용 가능한 이미지 인코더 목록 조회
var codecs = ImageCodecInfo.GetImageEncoders();
foreach (var codec in codecs)
{
if (codec.FormatID == format.Guid)
{
imageCodecInfo = codec;
}
}
}
catch (Exception ex)
{
throw;
}
return imageCodecInfo;
}
핵심 포인트 정리
- JPEG 품질 조절:
Encoder.Quality와EncoderParameter를 사용하면 품질을 0~100 사이 값으로 자유롭게 설정할 수 있습니다. 값이 작을수록 용량은 줄어들지만 화질이 저하됩니다. - 인코더 검색:
ImageCodecInfo.GetImageEncoders()로 시스템에 설치된 인코더 목록을 조회한 뒤,FormatID가 대상 형식(ImageFormat.Jpeg)의 GUID와 일치하는 인코더를 찾아 사용합니다. - 중복 방지 파일명: 사용자 ID와 타임스탬프를 정규식으로 정제해 조합하면 파일명 충돌 없이 이미지를 체계적으로 관리할 수 있습니다.
- 리소스 해제: 저장이 끝난 Bitmap은
Dispose()로 반드시 해제해 메모리 누수를 방지해야 합니다.
실제 프로젝트에서는 Path.Combine(imageSavedPath, imageName)처럼 경로와 파일명을 결합해 전체 저장 경로를 구성하는 것이 안전합니다. 또한 이 예제는 WindowsIdentity처럼 Windows 전용 API를 사용하므로, 크로스 플랫폼 환경이라면 다른 방식으로 사용자 식별 값을 얻어야 한다는 점도 참고하세요.