Visual Studio 2010에서 C#으로 영상처리 프로그램 시작
0. 소스코드 다운로드 : 다운로드
1. Visual Studio 2010에서 [파일] – [새로 만들기] – [프로젝트] 메뉴 순으로 선택한다.
2. Form1의 속성에서 Size 속성을 800, 600으로 설정
3. 도구상자에서 MenuStrip을 Form1로 드래그하여 추가
4. 메뉴에 다음 메뉴 추가
5. [픽셀_산술덧셈]을 더블클릭한 후 해당 코드 앞부분에 다음 코드를 추가한다.
private void 픽셀열기OToolStripMenuItem_Click(object sender, EventArgs e) 위쪽에 코드를 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
// 파일 열기 대화상자를 띄운 후 RAW 또는 PGM, JPG, PNG, BMP 파일을 선택하면 파일을 읽어와서 Bitmap 으로 만들어 리턴하는 함수 private Bitmap f_OpenBitmapFile() { OpenFileDialog ofnDlg = new OpenFileDialog(); ofnDlg.Filter = "raw files (*.raw)|*.raw|pgm files (*.pgm)|*.pgm|jpg files (*.jpg)|*.jpg|png files (*.png)|*.png|bmp files (*.bmp)|*.bmp|All files (*.*)|*.*"; ofnDlg.FilterIndex = 1; ofnDlg.RestoreDirectory = true; if (ofnDlg.ShowDialog() == DialogResult.OK) { if(ofnDlg.FileName.ToUpper().Contains(".RAW")){ FileStream fs = new FileStream(ofnDlg.FileName, FileMode.Open, FileAccess.Read); BinaryReader br = new BinaryReader(fs); byte[] data = br.ReadBytes((int)br.BaseStream.Length); br.Close(); return f_makeImageFromData(256, 256, data); } if (ofnDlg.FileName.ToUpper().Contains(".PGM")) { StreamReader sr = new StreamReader(ofnDlg.FileName); string pgmType = sr.ReadLine(); string buf = ""; do { buf = sr.ReadLine(); } while (buf.StartsWith("#")); int width = Convert.ToInt32(buf.Substring(0, buf.IndexOf(" "))); int height = Convert.ToInt32(buf.Substring(buf.IndexOf(" ")+1)); int data_size = width * height; buf = sr.ReadLine(); //최대 명암도 읽음 sr.Close(); FileStream fs = new FileStream(ofnDlg.FileName, FileMode.Open, FileAccess.Read); BinaryReader br = new BinaryReader(fs); br.ReadBytes((int)fs.Length - data_size); byte[] data = br.ReadBytes(data_size); br.Close(); return f_makeImageFromData(width, height, data); } if (ofnDlg.FileName.ToUpper().Contains(".JPG") || ofnDlg.FileName.ToUpper().Contains(".BMP") || ofnDlg.FileName.ToUpper().Contains(".PNG") || ofnDlg.FileName.ToUpper().Contains(".GIF") || ofnDlg.FileName.ToUpper().Contains(".PCX")) { return (Bitmap)Image.FromFile(ofnDlg.FileName); } } MessageBox.Show("파일을 선택하지 않았습니다"); return null; } |
6. 계속해서 다음 함수를 추가한다.
※ f_makeImageFromData() 함수에서 특정한 파일에서는 오류 발생,
※ 발생 이유는 Bitmap 형식에서 저장 방식의 특성문제(1 row(행)은 4바이트 단위로 저장됨)
※ 오류 수정은 스스로 해보길 바람, 또한 RGB 컬러 파일도 저장할 수 있도록 수정도 해보길..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// byte형의 영상 메모리를 Bitmap 형식으로 변환하여 리턴하는 함수 private Bitmap f_makeImageFromData(int width, int height, byte[] data) { Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed); //8bit Indexed 형 Bitmap의 팔레트를 GrayScale 형으로 변환하는 코드 ColorPalette grayPalette = bmp.Palette; for (int i = 0; i < grayPalette.Entries.Length; i++) { grayPalette.Entries[i] = Color.FromArgb(i, i, i); } bmp.Palette = grayPalette; BitmapData bmpData = bmp.LockBits( new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat); System.Runtime.InteropServices.Marshal.Copy(data, 0, bmpData.Scan0, data.Length); bmp.UnlockBits(bmpData); return bmp; } |
7. 계속해서 다음 함수를 추가한다.
※ f_getDataFromImage() 함수에서 특정한 파일에서는 오류 발생,
※ 발생 이유는 Bitmap 형식에서 저장 방식의 특성문제(1 row(행)은 4바이트 단위로 저장됨)
※ 오류 수정은 스스로 해보길 바람, 또한 RGB 컬러 파일도 읽을 수 있도록 수정도 해보길..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//Bitmap 데이터에서 영상이 저장된 메모리만 byte형으로 읽어서 리턴하는 함수 private byte[] f_getDataFromImage(Bitmap bmp) { if (bmp == null) return null; //byte[] data = new byte[mBmp.Width * mBmp.Height * 3]; //3바이트색상모드 : System.Drawing.Imaging.PixelFormat.Format24bppRgb byte[] data = new byte[bmp.Width * bmp.Height]; //1바이트색상모드 : System.Drawing.Imaging.PixelFormat.Format8bppIndexed BitmapData bmpReadData = bmp.LockBits( new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed); unsafe { byte* p = (byte*)(void*)bmpReadData.Scan0.ToPointer(); for (int i = 0; i < data.Length; i++) { data[i] = (byte)*(p + i); } } bmp.UnlockBits(bmpReadData); return data; } |
8. 위 코드를 입력한 후 [빌드]하면 unsafe에서 오류가 발생한다. 오류를 수정하기 위해서는 [프로젝트] – [??? 속성] 메뉴를 클릭한 후 [빌트] 탭에서 그림과 같이 [안전하지 않은 코드 허용(F)]를 체크하면 오류가 나타나지 않음
9. 계속해서 다음 함수를 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// Bitmap 영상의 개수에 따라 각각 Form에 영상을 출력하는 함수(오버로딩 사용) private void f_drawImage(Bitmap bmp) { Graphics g = this.CreateGraphics(); g.Clear(SystemColors.Control); g.DrawImage(bmp, 10, 60); g.Dispose(); } private void f_drawImage(Bitmap bmp, Bitmap bmp2) { Graphics g = this.CreateGraphics(); g.Clear(SystemColors.Control); g.DrawImage(bmp, 10, 60); g.DrawImage(bmp2, bmp.Width + 20, 60); g.Dispose(); } private void f_drawImage(Bitmap bmp, Bitmap bmp2, Bitmap bmp3) { Graphics g = this.CreateGraphics(); g.Clear(SystemColors.Control); g.DrawImage(bmp, 10, 60); g.DrawImage(bmp2, bmp.Width + 20, 60); g.DrawImage(bmp3, bmp.Width + bmp2.Width + 30, 60); g.Dispose(); } |
9-2. PictureBox를 이용해서 출력한다면
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
//Form에 pictureBox1, pictureBox2, pictureBox3을 만들어 놓은 뒤에 소스 코드 추가 // Bitmap 영상의 개수에 따라 각각 pictureBox에 영상을 출력하는 함수(오버로딩 사용) private void f_drawImage(Bitmap bmp) { pictureBox1.Location = new Point(10, 60); pictureBox1.Width = bmp.Width; pictureBox1.Height = bmp.Height; pictureBox1.Image = bmp; pictureBox1.Visible = true; pictureBox2.Visible = false; pictureBox3.Visible = false; } private void f_drawImage(Bitmap bmp, Bitmap bmp2) { pictureBox1.Location = new Point(10, 60); pictureBox1.Width = bmp.Width; pictureBox1.Height = bmp.Height; pictureBox1.Image = bmp; pictureBox2.Location = new Point(bmp.Width + 20, 60); pictureBox2.Width = bmp.Width; pictureBox2.Height = bmp.Height; pictureBox2.Image = bmp2; pictureBox1.Visible = true; pictureBox2.Visible = true; pictureBox3.Visible = false; } private void f_drawImage(Bitmap bmp, Bitmap bmp2, Bitmap bmp3) { pictureBox1.Location = new Point(10, 60); pictureBox1.Width = bmp.Width; pictureBox1.Height = bmp.Height; pictureBox1.Image = bmp; pictureBox2.Location = new Point(bmp.Width + 20, 60); pictureBox2.Width = bmp.Width; pictureBox2.Height = bmp.Height; pictureBox2.Image = bmp2; pictureBox3.Location = new Point(bmp.Width + bmp2.Width + 30, 60); pictureBox3.Width = bmp.Width; pictureBox3.Height = bmp.Height; pictureBox3.Image = bmp3; pictureBox1.Visible = true; pictureBox2.Visible = true; pictureBox3.Visible = true; } |
9-3. pictureBox의 그림을 파일로 저장하려면
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
private void pictureBox1_DoubleClick(object sender, EventArgs e) { pictureBox1.Image.Save("c:\\\\picture1.jpg", ImageFormat.Jpeg); } private void pictureBox2_DoubleClick(object sender, EventArgs e) { pictureBox2.Image.Save("c:\\\\picture2.jpg", ImageFormat.Jpeg); if(pictureBox2.Image.PixelFormat == PixelFormat.Format8bppIndexed){ byte[] data = f_getDataFromImage((Bitmap)(pictureBox2.Image)); FileStream fs = new FileStream("c:\\\\picture2.raw", FileMode.Create, FileAccess.Write); BinaryWriter bw = new BinaryWriter(fs); bw.Write(data); bw.Close(); } } private void pictureBox3_DoubleClick(object sender, EventArgs e) { pictureBox3.Image.Save("c:\\\\picture3.jpg", ImageFormat.Jpeg); if (pictureBox3.Image.PixelFormat == PixelFormat.Format8bppIndexed) { byte[] data = f_getDataFromImage((Bitmap)(pictureBox3.Image)); FileStream fs = new FileStream("c:\\\\picture3.raw", FileMode.Create, FileAccess.Write); BinaryWriter bw = new BinaryWriter(fs); bw.Write(data); bw.Close(); } } |
10. [픽셀_산술덧셈]을 클릭했을 때 추가되었던 함수를 다음과 같이 완성한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
private void 픽셀산술덧셈ToolStripMenuItem_Click(object sender, EventArgs e) { Bitmap bmp = f_OpenBitmapFile(); //그림파일 불러오기 if (bmp == null) return; byte[] data = f_getDataFromImage(bmp); //Bitmap 형식에서 byte 데이터만 가져오기 ///////// Start Image Processing //////////////// //영상처리 시작(산술덧셈) int addValue = 50; for (int i = 0; i < data.Length; i++) { if (data[i] + addValue > 255) { data[i] = 255; } else { data[i] = (byte)(data[i] + addValue); } } ///////// End Image Processing //////////////// //영상처리 끝(산술덧셈) Bitmap bmp2 = f_makeImageFromData(256, 256, data); //byte형 데이터를 Bitmap 형식으로 변환하기 f_drawImage(bmp, bmp2); //Form에 Bitmap 그림 출력하기 } |
11. 계속해서 [픽셀_산술뺄셈] 메뉴를 더블클릭한 후 다음코드를 완성한다.
산술 덧셈과 산술 뺄셈 코드는 대부분이 똑같고 약간의 차이만 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
private void 픽셀산술뺄셈ToolStripMenuItem_Click(object sender, EventArgs e) { Bitmap bmp = f_OpenBitmapFile(); //그림파일 불러오기 if (bmp == null) return; byte[] data = f_getDataFromImage(bmp); //Bitmap 형식에서 byte 데이터만 가져오기 ///////// Start Image Processing //////////////// //영상처리 시작(산술뺄셈) int subValue = 50; for (int i = 0; i < data.Length; i++) { if (data[i] - subValue < 0) { data[i] = 0; } else { data[i] = (byte)(data[i] - subValue); } } ///////// End Image Processing //////////////// //영상처리 끝(산술뺄셈) Bitmap bmp2 = f_makeImageFromData(256, 256, data); //byte형 데이터를 Bitmap 형식으로 변환하기 f_drawImage(bmp, bmp2); //Form에 Bitmap 그림 출력하기 } |
12. 실행시킨 후에 [픽셀처리] – [픽셀_산술덧셈] 메뉴를 클릭한 후 [Lenna.raw] 파일을 선택하면 Lenna와 더 밝아진 그림이 출력된다.
13. 계속해서 [픽셀처리] – [픽셀_산술뺄셈] 메뉴를 클릭한 후 [Lenna.raw] 파일을 선택하면 Lenna와 더 어두어진 그림이 출력된다.