기하학적 처리 C# 소스 코드
- 메뉴를 다음과 같이 추가한다.
- [영역_확대(픽셀복제)] 메뉴를 더블클릭한 후 다음코드를 완성한다.
12345678910111213141516171819202122232425262728293031private void 기하확대픽셀복제ToolStripMenuItem_Click(object sender, EventArgs e){Bitmap bmp = f_OpenBitmapFile();if (bmp == null) return;byte[] data = f_getDataFromImage(bmp);///////// Start Image Processing ////////////////double scale_x = 2;double scale_y = 2;int width = bmp.Width;int height = bmp.Height;int depth = data.Length / width / height;int newWidth = (int)(bmp.Width * scale_x);int newHeight = (int)(bmp.Height * scale_y);byte[] newdata = new byte[newWidth * newHeight * depth];for (int y = 0; y < newHeight; y++){for (int x = 0; x < newWidth; x++){for (int d = 0; d < depth; d++){newdata[(y * newWidth + x) * depth + d] = data[((int)(y / scale_y) * width + (int)(x / scale_x)) * depth + d];}}}///////// End Image Processing ////////////////Bitmap bmpResult = f_makeImageFromData(newWidth, newHeight, bmp.PixelFormat, newdata);f_drawImage(bmp, bmpResult);} - [기하_확대(양선형보간법)] 메뉴를 더블클릭한 후 다음코드를 완성한다.
12345678910111213141516171819202122232425262728293031323334353637///////// Start Image Processing ////////////////double scale_x = 2;double scale_y = 2;int newWidth = (int)(bmp.Width * scale_x);int newHeight = (int)(bmp.Height * scale_y);byte[] newdata = new byte[(int)(newWidth * newHeight)];for (int y = 0; y < newHeight; y++){for (int x = 0; x < newWidth; x++){// 원시 영상에서의 픽셀 좌표 계산double alpha = x / scale_x - (int)(x / scale_x);double beta = y / scale_y - (int)(y / scale_y);// 보간에 사용된 4 픽셀의 좌표 계산int Ax = (int)(x / scale_x);int Ay = (int)(y / scale_y);int Bx = Ax + 1;int By = Ay;int Cx = Ax;int Cy = Ay + 1;int Dx = Ax + 1;int Dy = Ay + 1;// 픽셀 위치가 영상의 경계를 벗어나는지 검사if (Bx > bmp.Width - 1) Bx = bmp.Width - 1;if (Dx > bmp.Width - 1) Dx = bmp.Width - 1;if (Cy > bmp.Height - 1) Cy = bmp.Height - 1;if (Dy > bmp.Height - 1) Dy = bmp.Height - 1;// x 방향으로 보간double E = data[Ay * bmp.Width + Ax] * (1 - alpha)+ data[By * bmp.Width + Bx] * alpha;double F = data[Cy * bmp.Width + Cx] * (1 - alpha)+ data[Dy * bmp.Width + Dx] * alpha;// y 방향으로 보간newdata[y * newWidth + x] = (byte)(E * (1 - beta) + F * beta);}}///////// End Image Processing ////////////////
- [기하_축소(서브샘플링)] 메뉴를 더블클릭한 후 다음코드를 완성한다.
123456789101112131415161718192021///////// Start Image Processing ////////////////double scale_x = 2;double scale_y = 1.5;int width = bmp.Width;int height = bmp.Height;int depth = data.Length / width / height;int newWidth = (int)(bmp.Width / scale_x);int newHeight = (int)(bmp.Height / scale_y);byte[] newdata = new byte[newWidth * newHeight * depth];for (int y = 0; y < newHeight; y++){for (int x = 0; x < newWidth; x++){for (int d = 0; d < depth; d++){newdata[(y * newWidth + x) * depth + d] = data[((int)(y * scale_y) * width + (int)(x * scale_x)) * depth + d];}}}///////// End Image Processing //////////////// - [기하_축소(평균값필터링)] 메뉴를 더블클릭한 후 다음코드를 완성한다.
123456789101112131415161718192021222324252627282930313233343536373839///////// Start Image Processing ////////////////double scale_x = 2; //scale_x 는 1 이상의 정수 값일 경우만 가능double scale_y = 2; //scale_y 는 1 이상의 정수 값일 경우만 가능int width = bmp.Width;int height = bmp.Height;int depth = data.Length / width / height;int newWidth = (int)(bmp.Width / scale_x);int newHeight = (int)(bmp.Height / scale_y);byte[] newdata = new byte[newWidth * newHeight * depth];for (int y = 0; y < newHeight; y++){for (int x = 0; x < newWidth; x++){// 필터 윈도우 내의 픽셀값 합계 계산int sum = 0;for (int yy = 0; yy < scale_y; yy++){for (int xx = 0; xx < scale_x; xx++){if (x * scale_x + xx > width - 1 || y * scale_y + yy > height - 1){sum += data[(int)(y * scale_y) * width + (int)(x * scale_x);}else{sum += data[(int)(y * scale_y + yy) * width + (int)(x * scale_x + xx)];}}}sum = (int)(sum / (scale_x * scale_y)); // 평균값 계산if (sum > 255) sum = 255;if (sum < 0) sum = 0;newdata[(y * newWidth + x) * depth + d] = (byte)sum;}}///////// End Image Processing ////////////////
- [기하_회전(0. 전방향사상)] 메뉴를 더블클릭한 후 그 위쪽에 2개의 함수를 추가한다.
- f_MatrixRotate(radian, x, y) : 좌표 (x. y)를 원점을 중심으로 radian 각만 큼 회전한 좌표
123456789private double[] f_MatrixRotate(double radian, double x, double y){double[] v = new double[2];v[0] = Math.Cos(radian) * x + Math.Sin(radian) * y;v[1] = -Math.Sin(radian) * x + Math.Cos(radian) * y;return v;}
- f_Rotate(rotateName, width, height, cx, cy, radian, byte[] data) : (cx, cy)를 중심으로 radian 각만큼 회전
123456789101112131415161718192021222324252627282930313233343536373839404142434445private byte[] f_Rotate(string rotateName, int width, int height, int cx, int cy, double radian, byte[] data){int depth = data.Length / width / height;if (rotateName.CompareTo("전방향사상") == 0 || rotateName.CompareTo("좌표계변환") == 0){radian = -radian;}byte[] newdata = new byte[data.Length];for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){double[] pos;if (rotateName.CompareTo("전방향사상") == 0 || rotateName.CompareTo("역방향사상") == 0){pos = f_MatrixRotate(radian, x, y);}else{pos = f_MatrixRotate(radian, x - cx, y - cy);pos[0] += cx;pos[1] += cy;}for (int d = 0; d < depth; d++){if (pos[0] >= 0 && pos[0] < width && pos[1] >= 0 && pos[1] < height){if (rotateName.CompareTo("전방향사상") == 0){newdata[((int)pos[1] * width + (int)pos[0]) * depth + d] = data[(y * width + x) * depth + d];}else{newdata[(y * width + x) * depth + d] = data[((int)pos[1] * width + (int)pos[0]) * depth + d];}}}}}return newdata;}
- f_MatrixRotate(radian, x, y) : 좌표 (x. y)를 원점을 중심으로 radian 각만 큼 회전한 좌표
- [기하_회전(0. 전방향사상)] 메뉴를 더블클릭한 후 다음코드를 완성한다.
1234///////// Start Image Processing ////////////////double radian = Math.PI / 180 * 30; //30도byte[] newdata = f_Rotate("전방향사상", bmp.Width, bmp.Height, 0, 0, radian, data);///////// End Image Processing ////////////////
- [기하_회전(1. 역방향사상)] 메뉴를 더블클릭한 후 다음코드를 완성한다.
1234///////// Start Image Processing ////////////////double radian = Math.PI / 180 * 30; //30도byte[] newdata = f_Rotate("역방향사상", bmp.Width, bmp.Height, 0, 0, radian, data);///////// End Image Processing ////////////////
- [기하_회전(2. 회전중심)] 메뉴를 더블클릭한 후 다음코드를 완성한다.
1234///////// Start Image Processing ////////////////double radian = Math.PI / 180 * 30; //30도byte[] newdata = f_Rotate("회전중심", bmp.Width, bmp.Height, bmp.Width/2, bmp.Height/2, radian, data);///////// End Image Processing ////////////////
- [기하_회전(3. 좌표계변환)] 메뉴를 더블클릭한 후 다음코드를 완성한다.
1234///////// Start Image Processing ////////////////double radian = Math.PI / 180 * 30; //30도byte[] newdata = f_Rotate("좌표계변환", bmp.Width, bmp.Height, bmp.Width / 2, bmp.Height / 2, radian, data);///////// End Image Processing ////////////////
- [기하_회전(4. 풀력영상크기)] 메뉴를 더블클릭한 후 다음코드를 완성한다.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546///////// Start Image Processing ////////////////double radian = Math.PI / 180 * 30; //30도int width = bmp.Width;int height = bmp.Height;int depth = data.Length / width / height;//newWidth와 newHeight 구하기 시작double[][] v = new double[4][];v[0] = new double[2]{ 0, 0 };v[1] = f_MatrixRotate(radian, width, height);v[2] = f_MatrixRotate(radian, 0, height);v[3] = f_MatrixRotate(radian, width, 0);double min_x = double.MaxValue;double min_y = double.MaxValue;double max_x = double.MinValue;double max_y = double.MinValue;for (int i = 0; i < v.Length; i++){if (v[i][0] < min_x) min_x = v[i][0];if (v[i][0] > max_x) max_x = v[i][0];if (v[i][1] < min_y) min_y = v[i][1];if (v[i][1] > max_y) max_y = v[i][1];}int newWidth = (int)max_x - (int)min_x;int newHeight = (int)max_y - (int)min_y;//newWidth와 newHeight 구하기 끝byte[] newdata = new byte[newWidth * newHeight * depth];for (int y = 0; y < newHeight; y++){for (int x = 0; x < newWidth; x++){double[] pos = f_MatrixRotate(radian, x - newWidth / 2, y - newHeight / 2);pos[0] += width / 2;pos[1] += height / 2;for (int d = 0; d < depth; d++){if (pos[0] >= 0 && pos[0] < width && pos[1] >= 0 && pos[1] < height){newdata[(y * newWidth + x) * depth + d] = data[((int)pos[1] * width + (int)pos[0]) * depth + d];}}}}///////// End Image Processing ////////////////
- [기하_좌우대칭] 메뉴를 더블클릭한 후 다음코드를 완성한다.
12345678910111213141516///////// Start Image Processing ////////////////int width = bmp.Width;int height = bmp.Height;int depth = data.Length / width / height;byte[] newdata = new byte[width * height * depth];for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){for (int d = 0; d < depth; d++){newdata[(y * width + x) * depth + d] = data[(y * width + width - x - 1) * depth + d];}}}///////// End Image Processing //////////////// - [기하_상하대칭] 메뉴를 더블클릭한 후 다음코드를 완성한다.
12345678910111213141516///////// Start Image Processing ////////////////int width = bmp.Width;int height = bmp.Height;int depth = data.Length / width / height;byte[] newdata = new byte[width * height * depth];for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){for (int d = 0; d < depth; d++){newdata[(y * width + x) * depth + d] = data[((height - y - 1) * width + x) * depth + d];}}}///////// End Image Processing //////////////// - [기하_워핑] 메뉴를 더블클릭한 후 그 위쪽에 f_Warping() 함수를 추가한다.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293private byte[] f_Warping(int width, int height, control_line[] source_lines, control_line[] dest_lines, byte[] data){int depth = data.Length / width / height;byte[] newdata = new byte[data.Length];double u; // 수직 교차점의 위치double h; // 제어선으로부터 픽셀의 수직 변위double d; // 제어선과 픽셀 사이의 거리double tx, ty; // 결과영상 픽셀에 대응되는 입력 영상 픽셀 사이의 변위의 합double xp, yp; // 각 제어선에 대해 계산된 입력 영상의 대응되는 픽셀 위치double weight; // 각 제어선의 가중치double totalWeight; // 가중치의 합double a = 0.001;double b = 2.0;double p = 0.75;int x1, x2, y1, y2;int src_x1, src_y1, src_x2, src_y2;double src_line_length, dest_line_length;int num_lines = 23; // 제어선의 수int line;int x, y;int source_x, source_y;int last_row, last_col;last_row = height - 1;last_col = width - 1;// 출력 영상의 각 픽셀에 대하여for (y = 0; y < height; y++){for (x = 0; x < width; x++){totalWeight = 0.0;tx = 0.0;ty = 0.0;// 각 제어선에 대하여for (line = 0; line < num_lines; line++){x1 = dest_lines[line].Px;y1 = dest_lines[line].Py;x2 = dest_lines[line].Qx;y2 = dest_lines[line].Qy;dest_line_length = Math.Sqrt((float)(x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));// 수직교차점의 위치 및 픽셀의 수직 변위 계산u = (double)((x - x1) * (x2 - x1) + (y - y1) * (y2 - y1)) /(double)((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));h = (double)((y - y1) * (x2 - x1) - (x - x1) * (y2 - y1)) / dest_line_length;// 제어선과 픽셀 사이의 거리 계산if (u < 0) d = Math.Sqrt((float)(x - x1) * (x - x1) + (y - y1) * (y - y1));else if (u > 1) d = Math.Sqrt((float)(x - x2) * (x - x2) + (y - y2) * (y - y2));else d = Math.Abs(h);src_x1 = source_lines[line].Px;src_y1 = source_lines[line].Py;src_x2 = source_lines[line].Qx;src_y2 = source_lines[line].Qy;src_line_length = Math.Sqrt((float)(src_x2 - src_x1) * (src_x2 - src_x1) +(src_y2 - src_y1) * (src_y2 - src_y1));// 입력 영상에서의 대응 픽셀 위치 계산xp = src_x1 + u * (src_x2 - src_x1) -h * (src_y2 - src_y1) / src_line_length;yp = src_y1 + u * (src_y2 - src_y1) +h * (src_x2 - src_x1) / src_line_length;// 제어선에 대한 가중치 계산weight = Math.Pow((Math.Pow((double)(dest_line_length), p) / (a + d)), b);// 대응 픽셀과의 변위 계산tx += (xp - x) * weight;ty += (yp - y) * weight;totalWeight += weight;}source_x = x + (int)(tx / totalWeight + 0.5);source_y = y + (int)(ty / totalWeight + 0.5);// 영상의 경계를 벗어나는지 검사if (source_x < 0) source_x = 0;if (source_x > last_col) source_x = last_col;if (source_y < 0) source_y = 0;if (source_y > last_row) source_y = last_row;for (int dt = 0; dt < depth; dt++){newdata[(y * width + x) * depth + dt] = data[(source_y * width + source_x) * depth + dt];}}}return newdata;}
- [기하_워핑] 메뉴를 더블클릭한 후 다음코드를 완성한다.
123456789101112131415161718192021222324252627282930313233343536private void 기하워핑ToolStripMenuItem_Click(object sender, EventArgs e){Bitmap bmp = f_OpenBitmapFile();if (bmp == null) return;byte[] data = f_getDataFromImage(bmp);///////// Start Image Processing ////////////////int[] source_init = new int[]{116,7,207,5, 34,109,90,21, 55,249,30,128, 118,320,65,261,123,321,171,321, 179,319,240,264, 247,251,282,135, 281,114,228,8,78,106,123,109, 187,115,235,114, 72,142,99,128, 74,150,122,154,108,127,123,146, 182,152,213,132, 183,159,229,157, 219,131,240,154,80,246,117,212, 127,222,146,223, 154,227,174,221, 228,252,183,213,114,255,186,257, 109,258,143,277, 152,278,190,262};int[] dest_init = new int[] {120,8,200,6, 12,93,96,16, 74,271,16,110, 126,336,96,290,142,337,181,335, 192,335,232,280, 244,259,288,108, 285,92,212,13,96,135,136,118, 194,119,223,125, 105,145,124,134, 110,146,138,151,131,133,139,146, 188,146,198,134, 189,153,218,146, 204,133,221,140,91,268,122,202, 149,206,159,209, 170,209,181,204, 235,265,208,199,121,280,205,284, 112,286,160,301, 166,301,214,287};control_line[] source_lines = new control_line[23];control_line[] dest_lines = new control_line[23];for (int i = 0; i < source_lines.Length; i++){source_lines[i] = new control_line(source_init[i * 4], source_init[i * 4 + 1], source_init[i * 4 + 2], source_init[i * 4 + 3]);dest_lines[i] = new control_line(dest_init[i * 4], dest_init[i * 4 + 1], dest_init[i * 4 + 2], dest_init[i * 4 + 3]);}byte[] newdata = f_Warping(bmp.Width, bmp.Height, source_lines, dest_lines, data);///////// End Image Processing ////////////////Bitmap bmpResult = f_makeImageFromData(bmp.Width, bmp.Height, bmp.PixelFormat, newdata);f_drawImage(bmp, bmpResult);}
- [기하_모핑] 메뉴를 더블클릭한 후 다음코드를 완성한다.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970private void 기하모핑ToolStripMenuItem_Click(object sender, EventArgs e){Bitmap bmp = f_OpenBitmapFile();if (bmp == null) return;byte[] data = f_getDataFromImage(bmp);Bitmap bmp2 = f_OpenBitmapFile();if (bmp2 == null) return;byte[] data2 = f_getDataFromImage(bmp2);///////// Start Image Processing ////////////////int[] source_init = new int[]{116,7,207,5, 34,109,90,21, 55,249,30,128, 118,320,65,261,123,321,171,321, 179,319,240,264, 247,251,282,135, 281,114,228,8,78,106,123,109, 187,115,235,114, 72,142,99,128, 74,150,122,154,108,127,123,146, 182,152,213,132, 183,159,229,157, 219,131,240,154,80,246,117,212, 127,222,146,223, 154,227,174,221, 228,252,183,213,114,255,186,257, 109,258,143,277, 152,278,190,262};int[] dest_init = new int[] {120,8,200,6, 12,93,96,16, 74,271,16,110, 126,336,96,290,142,337,181,335, 192,335,232,280, 244,259,288,108, 285,92,212,13,96,135,136,118, 194,119,223,125, 105,145,124,134, 110,146,138,151,131,133,139,146, 188,146,198,134, 189,153,218,146, 204,133,221,140,91,268,122,202, 149,206,159,209, 170,209,181,204, 235,265,208,199,121,280,205,284, 112,286,160,301, 166,301,214,287};control_line[] source_lines = new control_line[23];control_line[] dest_lines = new control_line[23];for (int i = 0; i < source_lines.Length; i++){source_lines[i] = new control_line(source_init[i * 4], source_init[i * 4 + 1], source_init[i * 4 + 2], source_init[i * 4 + 3]);dest_lines[i] = new control_line(dest_init[i * 4], dest_init[i * 4 + 1], dest_init[i * 4 + 2], dest_init[i * 4 + 3]);}int NUM_FRAMES = 10;control_line[] warp_lines = new control_line[23];for (int i = 0; i < warp_lines.Length; i++){warp_lines[i] = new control_line(0, 0, 0, 0);}for (int frame = 0; frame <= NUM_FRAMES; frame++){double fweight = (double)frame / NUM_FRAMES;for (int i = 0; i < warp_lines.Length; i++){warp_lines[i].Px = (int)(source_lines[i].Px + (dest_lines[i].Px - source_lines[i].Px) * fweight);warp_lines[i].Py = (int)(source_lines[i].Py + (dest_lines[i].Py - source_lines[i].Py) * fweight);warp_lines[i].Qx = (int)(source_lines[i].Qx + (dest_lines[i].Qx - source_lines[i].Qx) * fweight);warp_lines[i].Qy = (int)(source_lines[i].Qy + (dest_lines[i].Qy - source_lines[i].Qy) * fweight);}byte[] newdata = f_Warping(bmp.Width, bmp.Height, source_lines, warp_lines, data);byte[] newdata2 = f_Warping(bmp.Width, bmp.Height, dest_lines, warp_lines, data2);for (int i = 0; i < newdata.Length; i++){int val = (int)((1 - fweight) * newdata[i] + fweight * newdata2[i]);if (val > 255) val = 255;if (val < 0) val = 0;newdata[i] = (byte)val;}///////// End Image Processing ////////////////Bitmap bmpResult = f_makeImageFromData(bmp.Width, bmp.Height, bmp.PixelFormat, newdata);f_drawImage(bmp, bmp2, bmpResult);System.Threading.Thread.Sleep(500);}}