[C#] 이미지를 다루는 방법(이미지 포멧 변경, 이미지 합성, 이미지 태그 수정)
안녕하세요. 명월입니다.
이 글은 C#에서 이미지를 다루는 방법(이미지 포멧 변경, 이미지 합성, 이미지 태그 수정)에 대한 글입니다.
예전에 C/C++ 시절에는 이미지를 다루려면 이미지 포멧을 알고 구조체를 알아야 화면에 그리거나 사용할 수 있었는데 C#은 이런 기본적인 구조체 등은 다 정의되어 있고 그냥 가져다 사용만 할 수 있다는 게 너무 편한 것 같습니다.
이러한 개발 생산성 때문에 C#을 계속 쓰는 지도 모르겠습니다.
프로그램의 영상 처리 공부를 하면 가장 많이 사용하는 레나 사진을 통해서 설명하겠습니다.
출처 - https://namu.wiki/w/레나 포르센
그리고 처음에 이미지를 다루는 이해를 돕기 위해서는 콘솔 환경보다는 윈도우 환경이 좋기 때문에 윈도우 폼를 만들어 보겠습니다.
using System;
using System.Windows.Forms;
namespace WindowForm
{
// Form 클래스를 상속받는다.
public class Program : Form
{
// 싱글 스레드 어트리뷰트
[STAThread]
// 실행 함수
static void Main(string[] args)
{
// 콘솔 출력
Console.WriteLine("Program start");
// 환경에 맞는 스타일 설정
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 메시지 루프에 인스턴스를 생성한다.
Application.Run(new Program());
}
}
}
저는 콘솔 환경에서 윈도우 폼을 만들었기 때문에 뒤에 콘솔 창도 실행이 됩니다.
이제 이미지를 다룰 준비는 끝났고 위 이미지를 읽어서 먼저 윈도우 폼에 그려보겠습니다.
먼저 이미지를 사용하기 위해서는 System.Drawing과 System.Drawing.Design을 추가합니다.
소스 작성을 합니다.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowForm
{
// Form 클래스를 상속받는다.
public class Program : Form
{
// Graphic 객체 - C/C++이라고 치면 DC(Device Context)와 비슷한 녀석
private readonly Graphics graphics;
// 이미지 객체
private Image image;
// 생성자
public Program()
{
// Form으로 부터 Graphic 객체를 생성한다.
graphics = this.CreateGraphics();
// 이미지를 읽어 온다.
image = Bitmap.FromFile(@"d:\work\lena.png");
}
// 윈도우 메시지 루프에 Paint 요청 때마다.
protected override void OnPaint(PaintEventArgs e)
{
// 상위 클랫를 실행
base.OnPaint(e);
// 사각형 만들기 - 위치는 0,0 크기는 윈도우 사이즈 만큼
var rect = new Rectangle(new Point(0, 0), this.Size);
// 이미지를 그린다.
graphics.DrawImage(image, rect);
}
// 싱글 스레드 어트리뷰트
[STAThread]
// 실행 함수
static void Main(string[] args)
{
// 콘솔 출력
Console.WriteLine("Program start");
// 환경에 맞는 스타일 설정
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 메시지 루프에 인스턴스를 생성한다.
Application.Run(new Program());
}
}
}
여기서 화면에 이미지가 깔끔하게 그려집니다.
위 소스에서는 이미지를 가져와서 Image 객체를 만들고 Graphic 객체(DC)를 이용해서 윈도우에 그렸습니다.
이미지는 여러가지 포멧이 있습니다. PNG 포멧이 있고 JPG, GIF, BMP 등등 여러가지 이미지 포멧이 있습니다만, 사실 이 포멧들은 데이터 구조이 완전 달라서 같은 이미지라도 포멧에 따라 용량의 차이가 생깁니다.
보통은 BMP 파일이 가장 크고 PNG, JPG 순으로 갑니다. 반드시 그런건 아닙니다만, BMP파일은 각 픽셀(Pixel)마다의 색의 bit값을 넣어서 표현한것이고 PNG와 JPG는 데이터 양자 기법과 치환 기법을 사용해서 비손실, 손실 압축 포멧입니다.
(예전에는 그 구분을 확실히 알았는데 요즘에는 객체가 다 구현이 되어 있으니 다 잊어버렸네요..)
결론은 파일 포멧이 다릅니다. 예전 C/C++ 같으면 최근에는 Boost나 내장 함수도 많이 발전해서 이제는 다 구현이 되어 있을 거라 생각되지만 제가 공부할 때까지만 해도 이 구조체 만들거나 인터넷에서 퍼와서 변환할 때 그에 맞는 변환 함수를 만들었어야 하는 기억이 있습니다.
그런데 C#은? .Net framework에 구현이 되어 있습니다. 그냥 명시만 해주면 끝입니다.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace WindowForm
{
// Form 클래스를 상속받는다.
public class Program : Form
{
// Graphic 객체 - C/C++이라고 치면 DC(Device Context)와 비슷한 녀석
private readonly Graphics graphics;
// 이미지 객체
private Image image;
// 생성자
public Program()
{
// Form으로 부터 Graphic 객체를 생성한다.
graphics = this.CreateGraphics();
// 이미지를 읽어 온다.
image = Bitmap.FromFile(@"d:\work\lena.png");
// png를 jpg파일로 변환
image.Save(@"d:\work\lena.jpg", ImageFormat.Jpeg);
// png를 bmp파일로 변환
image.Save(@"d:\work\lena.bmp", ImageFormat.Bmp);
// png를 ico파일로 변환
image.Save(@"d:\work\lena.ico", ImageFormat.Icon);
}
// 윈도우 메시지 루프에 Paint 요청 때마다.
protected override void OnPaint(PaintEventArgs e)
{
// 상위 클랫를 실행
base.OnPaint(e);
// 사각형 만들기 - 위치는 0,0 크기는 윈도우 사이즈 만큼
var rect = new Rectangle(new Point(0, 0), this.Size);
// 이미지를 그린다.
graphics.DrawImage(image, rect);
}
// 싱글 스레드 어트리뷰트
[STAThread]
// 실행 함수
static void Main(string[] args)
{
// 콘솔 출력
Console.WriteLine("Program start");
// 환경에 맞는 스타일 설정
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 메시지 루프에 인스턴스를 생성한다.
Application.Run(new Program());
}
}
}
결과를 보시면 사이즈가 다 다른 이미지가 만들어 집니다. ICO 파일로도 변환이 되는 것보니 이제부터는 이미지 파일을 ICO로 변환해주는 사이트를 찾을 필요가 없을 것 같습니다.
C#에서 제공하는 이미지 포멧은 위와 같습니다. 제가 모르는 이미지 포멧도 있습니다. 이 정도면 개인적으로 이미지 포멧을 몰라도 변환하고 읽어 오는데는 문제 없을 것 같네요..
이미지 포멧 변경은 되었으니 이번에는 이미지를 조금 편집해 봅시다.
블로그를 운영하다보면 이미지에다가 저작권을 넣고 싶거나 워터 마크를 넣고 싶을 때가 있습니다. 글을 넣고 워터 마크를 넣어봅시다.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace WindowForm
{
// Form 클래스를 상속받는다.
public class Program : Form
{
// Graphic 객체 - C/C++이라고 치면 DC(Device Context)와 비슷한 녀석
private readonly Graphics graphics;
// 이미지 객체
private Image image;
// 워터 마크 이미지 객체
private Image watermark;
// 이미지 옵션
private ImageAttributes imageAttribute;
// 생성자
public Program()
{
// Form으로 부터 Graphic 객체를 생성한다.
graphics = this.CreateGraphics();
// 이미지를 읽어 온다.
image = Bitmap.FromFile(@"d:\work\lena.png");
// 워터 마크 이미지를 읽어 온다.
watermark = Bitmap.FromFile(@"d:\work\tistory.png");
// 컬러 매트릭스 인스턴스 생성
ColorMatrix colormatrix = new ColorMatrix();
// 투명도 설정
colormatrix.Matrix33 = 0.4F;
// 이미지 옵션 인스턴스 생성
imageAttribute = new ImageAttributes();
// 기본은 그대로 투명도만 설정한다.
imageAttribute.SetColorMatrix(colormatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
}
// 윈도우 메시지 루프에 Paint 요청 때마다.
protected override void OnPaint(PaintEventArgs e)
{
// 상위 클랫를 실행
base.OnPaint(e);
// 사각형 만들기 - 위치는 0,0 크기는 윈도우 사이즈 만큼
var rect = new Rectangle(new Point(0, 0), new Size(this.Width, this.Height - 39));
// 이미지를 그린다.
graphics.DrawImage(image, rect);
// 워터 마크 그리기 (이미지, DC 위치, 이미지 이동 x,y 이미지 늘리기 위한 크기 width, height, 유닛 타입, 옵션 설정)
graphics.DrawImage(watermark, rect, 0, 0, watermark.Width, watermark.Height, GraphicsUnit.Pixel, imageAttribute);
// 저작권 표시를 위한 글씨를 표시한다.
graphics.DrawString("nowonbun.tistory.com", new Font(new FontFamily("Arial"), 20, FontStyle.Bold, GraphicsUnit.Pixel), Brushes.Black, new Point(0, this.Height - 60));
}
// 싱글 스레드 어트리뷰트
[STAThread]
// 실행 함수
static void Main(string[] args)
{
// 콘솔 출력
Console.WriteLine("Program start");
// 환경에 맞는 스타일 설정
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 메시지 루프에 인스턴스를 생성한다.
Application.Run(new Program());
}
}
}
윈도우 화면을 보면 윈도우 화면에 워터마크와 저작권 표시가 생겼습니다. 우리는 이걸 윈도우에 표시하는 게 주 목적이 아니기 때문에 파일로 출력을 합시다.
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace WindowForm
{
public class Program
{
// 실행 함수
static void Main(string[] args)
{
// 이미지를 읽어온다.
var image = Bitmap.FromFile(@"d:\work\lena.png");
// 워터 마크 이미지를 읽어 온다.
var watermark = Bitmap.FromFile(@"d:\work\tistory.png");
// 컬러 매트릭스 인스턴스 생성
ColorMatrix colormatrix = new ColorMatrix();
// 투명도 설정
colormatrix.Matrix33 = 0.4F;
// 이미지 옵션 인스턴스 생성
var imageAttribute = new ImageAttributes();
// 기본은 그대로 투명도만 설정한다.
imageAttribute.SetColorMatrix(colormatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
// image로 부터 메모리 DC를 생성한다.
var graphics = Graphics.FromImage(image);
// 사각형 만들기 - 위치는 0,0 크기는 윈도우 사이즈 만큼
var rect = new Rectangle(new Point(0, 0), new Size(image.Width, image.Height - 39));
// 워터 마크 그리기 (이미지, DC 위치, 이미지 이동 x,y 이미지 늘리기 위한 크기 width, height, 유닛 타입, 옵션 설정)
graphics.DrawImage(watermark, rect, 0, 0, watermark.Width, watermark.Height, GraphicsUnit.Pixel, imageAttribute);
// 저작권 표시를 위한 글씨를 표시한다.
graphics.DrawString("nowonbun.tistory.com", new Font(new FontFamily("Arial"), 20, FontStyle.Bold, GraphicsUnit.Pixel), Brushes.Black, new Point(0, image.Height - 30));
// DC에 저장 -> image 객체로 이동(실제 물리 파일은 영향없음)
graphics.Save();
// 파일로 저장한다.
image.Save(@"d:\work\lena2.png");
// 콘솔 출력
Console.WriteLine("Press any key...");
Console.ReadKey();
}
}
}
Form에 관한 소스를 제거하고 image를 읽어오고 그리고 저장, 그리고 파일로 출력까지 합니다.
출력이 되어 표기가 되었습니다. 블로그에 글을 쓸 때, 이미지 자동 변환 툴을 만들면 참 편리하게 저작권 관리등이 될 것같네요..
이미지 자체를 수정하는 소스를 작성했으니 이번에는 이미지 태그를 수정해 보겠습니다.
이미지 포멧 별로 지원하는 태그가 존재합니다.
이미지 Property로 하면 각 이미지 포멧에 대한 태그 정보가 나옵니다.(죄송합니다. 제 OS가 일본 버젼이라 일본어로 표시되네요.. 양해해 주세요.)
이 태그 정보는 이미지 포멧마다 다릅니다만, 위 이미지는 JPG에 대한 포멧 정보입니다.
보통 이런 포멧 정보는 스마트 폰이나 카메라로 촬영을 하게 되면 기본적으로 작성이 됩니다. 그러나 우리는 이걸 수정하려고 합니다.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
namespace WindowForm
{
public class Program
{
// 태그 번호
enum PropertyTag : int
{
PropertyTagGpsVer = 0x0000,
PropertyTagGpsLatitudeRef = 0x0001,
PropertyTagGpsLatitude = 0x0002,
PropertyTagGpsLongitudeRef = 0x0003,
PropertyTagGpsLongitude = 0x0004,
PropertyTagGpsAltitudeRef = 0x0005,
PropertyTagGpsAltitude = 0x0006,
PropertyTagGpsGpsTime = 0x0007,
PropertyTagGpsGpsSatellites = 0x0008,
PropertyTagGpsGpsStatus = 0x0009,
PropertyTagGpsGpsMeasureMode = 0x000A,
PropertyTagGpsGpsDop = 0x000B,
PropertyTagGpsSpeedRef = 0x000C,
PropertyTagGpsSpeed = 0x000D,
PropertyTagGpsTrackRef = 0x000E,
PropertyTagGpsTrack = 0x000F,
PropertyTagGpsImgDirRef = 0x0010,
PropertyTagGpsImgDir = 0x0011,
PropertyTagGpsMapDatum = 0x0012,
PropertyTagGpsDestLatRef = 0x0013,
PropertyTagGpsDestLat = 0x0014,
PropertyTagGpsDestLongRef = 0x0015,
PropertyTagGpsDestLong = 0x0016,
PropertyTagGpsDestBearRef = 0x0017,
PropertyTagGpsDestBear = 0x0018,
PropertyTagGpsDestDistRef = 0x0019,
PropertyTagGpsDestDist = 0x001A,
PropertyTagNewSubfileType = 0x00FE,
PropertyTagSubfileType = 0x00FF,
PropertyTagImageWidth = 0x0100,
PropertyTagImageHeight = 0x0101,
PropertyTagBitsPerSample = 0x0102,
PropertyTagCompression = 0x0103,
PropertyTagPhotometricInterp = 0x0106,
PropertyTagThreshHolding = 0x0107,
PropertyTagCellWidth = 0x0108,
PropertyTagCellHeight = 0x0109,
PropertyTagFillOrder = 0x010A,
PropertyTagDocumentName = 0x010D,
PropertyTagImageDescription = 0x010E,
PropertyTagEquipMake = 0x010F,
PropertyTagEquipModel = 0x0110,
PropertyTagStripOffsets = 0x0111,
PropertyTagOrientation = 0x0112,
PropertyTagSamplesPerPixel = 0x0115,
PropertyTagRowsPerStrip = 0x0116,
PropertyTagStripBytesCount = 0x0117,
PropertyTagMinSampleValue = 0x0118,
PropertyTagMaxSampleValue = 0x0119,
PropertyTagXResolution = 0x011A,
PropertyTagYResolution = 0x011B,
PropertyTagPlanarConfig = 0x011C,
PropertyTagPageName = 0x011D,
PropertyTagXPosition = 0x011E,
PropertyTagYPosition = 0x011F,
PropertyTagFreeOffset = 0x0120,
PropertyTagFreeByteCounts = 0x0121,
PropertyTagGrayResponseUnit = 0x0122,
PropertyTagGrayResponseCurve = 0x0123,
PropertyTagT4Option = 0x0124,
PropertyTagT6Option = 0x0125,
PropertyTagResolutionUnit = 0x0128,
PropertyTagPageNumber = 0x0129,
PropertyTagTransferFunction = 0x012D,
PropertyTagSoftwareUsed = 0x0131,
PropertyTagDateTime = 0x0132,
PropertyTagArtist = 0x013B,
PropertyTagHostComputer = 0x013C,
PropertyTagPredictor = 0x013D,
PropertyTagWhitePoint = 0x013E,
PropertyTagPrimaryChromaticities = 0x013F,
PropertyTagColorMap = 0x0140,
PropertyTagHalftoneHints = 0x0141,
PropertyTagTileWidth = 0x0142,
PropertyTagTileLength = 0x0143,
PropertyTagTileOffset = 0x0144,
PropertyTagTileByteCounts = 0x0145,
PropertyTagInkSet = 0x014C,
PropertyTagInkNames = 0x014D,
PropertyTagNumberOfInks = 0x014E,
PropertyTagDotRange = 0x0150,
PropertyTagTargetPrinter = 0x0151,
PropertyTagExtraSamples = 0x0152,
PropertyTagSampleFormat = 0x0153,
PropertyTagSMinSampleValue = 0x0154,
PropertyTagSMaxSampleValue = 0x0155,
PropertyTagTransferRange = 0x0156,
PropertyTagJPEGProc = 0x0200,
PropertyTagJPEGInterFormat = 0x0201,
PropertyTagJPEGInterLength = 0x0202,
PropertyTagJPEGRestartInterval = 0x0203,
PropertyTagJPEGLosslessPredictors = 0x0205,
PropertyTagJPEGPointTransforms = 0x0206,
PropertyTagJPEGQTables = 0x0207,
PropertyTagJPEGDCTables = 0x0208,
PropertyTagJPEGACTables = 0x0209,
PropertyTagYCbCrCoefficients = 0x0211,
PropertyTagYCbCrSubsampling = 0x0212,
PropertyTagYCbCrPositioning = 0x0213,
PropertyTagREFBlackWhite = 0x0214,
PropertyTagGamma = 0x0301,
PropertyTagICCProfileDescriptor = 0x0302,
PropertyTagSRGBRenderingIntent = 0x0303,
PropertyTagImageTitle = 0x0320,
PropertyTagResolutionXUnit = 0x5001,
PropertyTagResolutionYUnit = 0x5002,
PropertyTagResolutionXLengthUnit = 0x5003,
PropertyTagResolutionYLengthUnit = 0x5004,
PropertyTagPrintFlags = 0x5005,
PropertyTagPrintFlagsVersion = 0x5006,
PropertyTagPrintFlagsCrop = 0x5007,
PropertyTagPrintFlagsBleedWidth = 0x5008,
PropertyTagPrintFlagsBleedWidthScale = 0x5009,
PropertyTagHalftoneLPI = 0x500A,
PropertyTagHalftoneLPIUnit = 0x500B,
PropertyTagHalftoneDegree = 0x500C,
PropertyTagHalftoneShape = 0x500D,
PropertyTagHalftoneMisc = 0x500E,
PropertyTagHalftoneScreen = 0x500F,
PropertyTagJPEGQuality = 0x5010,
PropertyTagGridSize = 0x5011,
PropertyTagThumbnailFormat = 0x5012,
PropertyTagThumbnailWidth = 0x5013,
PropertyTagThumbnailHeight = 0x5014,
PropertyTagThumbnailColorDepth = 0x5015,
PropertyTagThumbnailPlanes = 0x5016,
PropertyTagThumbnailRawBytes = 0x5017,
PropertyTagThumbnailSize = 0x5018,
PropertyTagThumbnailCompressedSize = 0x5019,
PropertyTagColorTransferFunction = 0x501A,
PropertyTagThumbnailData = 0x501B,
PropertyTagThumbnailImageWidth = 0x5020,
PropertyTagThumbnailImageHeight = 0x5021,
PropertyTagThumbnailBitsPerSample = 0x5022,
PropertyTagThumbnailCompression = 0x5023,
PropertyTagThumbnailPhotometricInterp = 0x5024,
PropertyTagThumbnailImageDescription = 0x5025,
PropertyTagThumbnailEquipMake = 0x5026,
PropertyTagThumbnailEquipModel = 0x5027,
PropertyTagThumbnailStripOffsets = 0x5028,
PropertyTagThumbnailOrientation = 0x5029,
PropertyTagThumbnailSamplesPerPixel = 0x502A,
PropertyTagThumbnailRowsPerStrip = 0x502B,
PropertyTagThumbnailStripBytesCount = 0x502C,
PropertyTagThumbnailResolutionX = 0x502D,
PropertyTagThumbnailResolutionY = 0x502E,
PropertyTagThumbnailPlanarConfig = 0x502F,
PropertyTagThumbnailResolutionUnit = 0x5030,
PropertyTagThumbnailTransferFunction = 0x5031,
PropertyTagThumbnailSoftwareUsed = 0x5032,
PropertyTagThumbnailDateTime = 0x5033,
PropertyTagThumbnailArtist = 0x5034,
PropertyTagThumbnailWhitePoint = 0x5035,
PropertyTagThumbnailPrimaryChromaticities = 0x5036,
PropertyTagThumbnailYCbCrCoefficients = 0x5037,
PropertyTagThumbnailYCbCrSubsampling = 0x5038,
PropertyTagThumbnailYCbCrPositioning = 0x5039,
PropertyTagThumbnailRefBlackWhite = 0x503A,
PropertyTagThumbnailCopyRight = 0x503B,
PropertyTagLuminanceTable = 0x5090,
PropertyTagChrominanceTable = 0x5091,
PropertyTagFrameDelay = 0x5100,
PropertyTagLoopCount = 0x5101,
PropertyTagGlobalPalette = 0x5102,
PropertyTagIndexBackground = 0x5103,
PropertyTagIndexTransparent = 0x5104,
PropertyTagPixelUnit = 0x5110,
PropertyTagPixelPerUnitX = 0x5111,
PropertyTagPixelPerUnitY = 0x5112,
PropertyTagPaletteHistogram = 0x5113,
PropertyTagCopyright = 0x8298,
PropertyTagExifExposureTime = 0x829A,
PropertyTagExifFNumber = 0x829D,
PropertyTagExifIFD = 0x8769,
PropertyTagICCProfile = 0x8773,
PropertyTagExifExposureProg = 0x8822,
PropertyTagExifSpectralSense = 0x8824,
PropertyTagGpsIFD = 0x8825,
PropertyTagExifISOSpeed = 0x8827,
PropertyTagExifOECF = 0x8828,
PropertyTagExifVer = 0x9000,
PropertyTagExifDTOrig = 0x9003,
PropertyTagExifDTDigitized = 0x9004,
PropertyTagExifCompConfig = 0x9101,
PropertyTagExifCompBPP = 0x9102,
PropertyTagExifShutterSpeed = 0x9201,
PropertyTagExifAperture = 0x9202,
PropertyTagExifBrightness = 0x9203,
PropertyTagExifExposureBias = 0x9204,
PropertyTagExifMaxAperture = 0x9205,
PropertyTagExifSubjectDist = 0x9206,
PropertyTagExifMeteringMode = 0x9207,
PropertyTagExifLightSource = 0x9208,
PropertyTagExifFlash = 0x9209,
PropertyTagExifFocalLength = 0x920A,
PropertyTagExifMakerNote = 0x927C,
PropertyTagExifUserComment = 0x9286,
PropertyTagExifDTSubsec = 0x9290,
PropertyTagExifDTOrigSS = 0x9291,
PropertyTagExifDTDigSS = 0x9292,
PropertyTagExifFPXVer = 0xA000,
PropertyTagExifColorSpace = 0xA001,
PropertyTagExifPixXDim = 0xA002,
PropertyTagExifPixYDim = 0xA003,
PropertyTagExifRelatedWav = 0xA004,
PropertyTagExifInterop = 0xA005,
PropertyTagExifFlashEnergy = 0xA20B,
PropertyTagExifSpatialFR = 0xA20C,
PropertyTagExifFocalXRes = 0xA20E,
PropertyTagExifFocalYRes = 0xA20F,
PropertyTagExifFocalResUnit = 0xA210,
PropertyTagExifSubjectLoc = 0xA214,
PropertyTagExifExposureIndex = 0xA215,
PropertyTagExifSensingMethod = 0xA217,
PropertyTagExifFileSource = 0xA300,
PropertyTagExifSceneType = 0xA301,
PropertyTagExifCfaPattern = 0xA302
}
// 이미지 타입 번호
static dynamic TranData(short type, byte[] value)
{
switch (type)
{
case 1:
return value;
case 2:
return Encoding.ASCII.GetString(value);
case 3:
return BitConverter.ToInt16(value, 0);
case 4:
case 5:
return BitConverter.ToInt32(value, 0);
case 10:
return BitConverter.ToInt64(value, 0);
}
return null;
}
// 실행 함수
static void Main(string[] args)
{
// 이미지를 읽어온다.
var image = Image.FromFile(@"d:\work\lena.jpg");
// 프로퍼티를 읽어 온다.
foreach (var property in image.PropertyItems)
{
// 타입별로
var tag = (PropertyTag)property.Id;
// 콘솔 출력
Console.WriteLine($"{tag.ToString()} - {TranData(property.Type, property.Value)}");
}
// 콘솔 출력
Console.WriteLine("Press any key...");
Console.ReadKey();
}
}
}
이미지를 읽어와서 태그 정보(EXIF)를 읽어 왔습니다. 잘 읽어 오는 것 같습니다.
참고로 태그 번호와 이미지 타입 번호는 MSDN을 참고 했습니다.
링크 - PropertyItem.Type Property
읽어 왔으니 이번에는 태그 정보를 넣어 보겠습니다.
using System;
using System.Drawing;
using System.Text;
namespace WindowForm
{
public class Program
{
// 태그 번호
enum PropertyTag : int
{
PropertyTagArtist = 0x013B
}
// 실행 함수
static void Main(string[] args)
{
// 이미지를 읽어온다.
var image = Image.FromFile(@"d:\work\lena.jpg");
// 0번쨰 PropertyItem을 취득.. 왜인지 PropertyItem클래스는 인스턴스 생성이 되지 않는다.
var item = image.PropertyItems[0];
// 작성자
item.Id = (int)PropertyTag.PropertyTagArtist;
// ASCII 타입으로 설정
item.Type = 2;
// nowonbun을 ASCII byte배열로 변경
item.Value = Encoding.ASCII.GetBytes("nowonbun\0");
// 길이 넣기
item.Len = item.Value.Length;
// Property 세팅
image.SetPropertyItem(item);
// 저장
image.Save(@"d:\work\lena3.jpg");
// 콘솔 출력
Console.WriteLine("Press any key...");
Console.ReadKey();
}
}
}
에러 없이 실행되었습니다. 이미지 태그 정보를 확인해 봅시다.
태그 정보가 추가되었는 것을 확인할 수 있습니다.
생각보다 이미지 태그 번호가 엄청나게 많네요.. 하나하나 다 설명하고 싶지만 너무 많아서 여기서 설명하기가 힘들꺼 같네요.. 필요하신 분들은 직접 디버깅해보면서 태그 데이터를 넣어보며 확인하는 수밖에 없을 것 같습니다.
여기까지 C#에서 이미지를 다루는 방법(이미지 포멧 변경, 이미지 합성, 이미지 태그 수정)에 대한 글이었습니다.
궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.