Проект chaincode1
1) Main Menu - VC# 2010 Express: File → New Project... → Visual Studio installed templates: Windows Forms Application
Name: chaincode1 → Location: C:\temp → Create directory for solution: отключить → OK
2) Стереть файлы: Form1.Designer.cs и Program.cs.
3) Правой кнопкой мыши кликнуть на Form1. В контекстном меню выбрать View Code.
Вы увидите заготовку кода в Form1.cs. Сотрите этот код.
Подготовка Изображения
Допишите следующий код в Form1.cs:
using System; using System.Drawing; using System.Windows.Forms; using System.Collections; public class Form1 : Form { [STAThread] static void Main() { Application.Run( new Form1() ); } const Int32 xSize = 11; const Int32 ySize = 12; Byte[,] i0 = new Byte[ySize ,xSize ]; Brush[] brush = new Brush[10]; Brush blackbrush = SystemBrushes.ControlText; Brush bluebrush = new SolidBrush( Color.Blue ); Pen redpen = new Pen( Color.Red, 5 ); Font arial10 = new Font( "Arial", 10 ); Int32 i, x, y, dx, dy; Byte threshold = 1; Button[] button = new Button[ySize]; Boolean CC4, CC8; Point start = new Point(); chaincode cc = new chaincode(); class chaincode { public Point start; public String cracks; public Int32 perimeter, area; } public Form1() { BackColor = Color.White; Text = "Chain Code"; SetStyle( ControlStyles.ResizeRedraw, true ); Width = 800; Height = 600; for ( i=0; i < 10; i++ ) brush[i] = new SolidBrush(Color.FromArgb( i*25, i*25, i*25 ) ); for ( y=0; y < ySize; y++ ) { button[y] = new Button(); Controls.Add(button[y]); button[y].BackColor = Color.Gray; button[y].Text = "nothing"; button[y].Name = y.ToString(); button[y].Click += new EventHandler( do_it ); } button[0].Name = button[0].Text = "Homunculus"; button[1].Name = "Threshold"; button[2].Name = button[2].Text = "Noise"; button[3].Name = button[3].Text = "Clear"; button[4].Name = button[4].Text = "Chain Code 4"; button[5].Name = button[5].Text = "Chain Code 8"; button[1].Text = String.Format( "Threshold={0:#}", threshold ); } protected override void OnPaint( PaintEventArgs e ) { Graphics g = e.Graphics; Rectangle r = ClientRectangle; dx = r.Width / (xSize+2); dy = (r.Height - FontHeight ) / ySize; for ( y=0; y < ySize; y++ ) { button[y].Top = y*dy+1; button[y].Left = xSize*dx+1; button[y].Width = r.Width - button[y].Left - 2; button[y].Height = dy-2; } for ( y=0; y < ySize; y++ ) for ( x=0; x < xSize; x++ ) g.FillRectangle( brush[i0[y,x]], x*dx, y*dy, dx, dy ); if ( cc.perimeter == 0 ) return; x = cc.start.X; y = cc.start.Y; for ( Int32 ii = 0; ii < cc.perimeter; ii++ ) switch ( cc.cracks[ii] ) { case 'e': g.DrawLine(redpen, x*dx, y*dy, (x+1)*dx, y*dy ); x++; break; case 's': g.DrawLine(redpen, x*dx, y*dy, x*dx, (y+1)*dy ); y++; break; case 'w': g.DrawLine(redpen, x*dx, y*dy, (x-1)*dx, y*dy ); x--; break; case 'n': g.DrawLine(redpen, x*dx, y*dy, x*dx, (y-1)*dy ); y--; break; } g.FillRectangle( bluebrush, x*dx-5, y*dy-5, 11, 11 ); String s = "(" + cc.start.X.ToString() + "/" + cc.start.Y.ToString() + ")" + cc.cracks + " P=" + cc.perimeter.ToString() + " A=" + cc.area.ToString(); g.DrawString(s, arial10, blackbrush, 0, button[ySize-1].Top + button[ySize-1].Height ); } protected void do_it( object sender, System.EventArgs e ) { switch( ((Button)sender).Name) { case "Homunculus"://*********************************** i0 = new Byte[,]{ {0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,9,9,9,0,0,0,0}, {0,0,0,0,9,0,9,0,0,0,0}, {0,0,0,0,9,8,9,0,0,0,0}, {1,0,0,0,0,7,0,0,0,0,1}, {0,2,6,6,6,6,6,6,6,2,0}, {1,0,0,0,5,5,5,0,0,0,1}, {0,0,0,0,4,4,4,0,0,0,0}, {0,0,0,0,3,0,3,0,0,0,0}, {0,0,0,0,2,0,2,0,0,0,0}, {0,0,0,0,1,0,1,0,0,0,0}, {0,0,0,0,1,0,1,0,0,0,0} }; break; } Invalidate(); } }
Запустите программу Debug → Start Without Debugging Ctrl F5. Кнопка "Homunculus" что-то делает - остальные пусты.
Threshold, Noise и Clear
Версия 2: закройте приложение cells1.
Допишите код под последней командой break; из case "Homunculus": в функции protected void do_it(...):
case "Threshold"://*************************************
if ( ++threshold > 9 ) threshold = 1; button[1].Text = "Threshold=" + threshold.ToString(); break; case "Noise"://**************************************** Random random = new Random(); for ( y=0; y < ySize; y++ ) for ( x=0; x < xSize; x++ ) { Int32 noise = random.Next() % 3 - 1;//gives -1 or 0 or +1 noise += i0[y,x];//add former gray value if (noise < 0) i0[y,x] = 0; else if (noise > 9) i0[y,x] = 9; else i0[y,x] = (Byte)noise; } break; case "Clear"://******************************************* for ( y=0; y < ySize; y++ ) for ( x=0; x < xSize; x++ ) i0[y,x] = 0; threshold = 1; button[1].Text = "Threshold=1"; cc.cracks = ""; cc.perimeter = 0; break;
Запустите программу Debug → Start Without Debugging Ctrl F5. Опробуйте кнопку Threshold (меняется название), Noise (несколько нажатий) и Clear.
Один Chain Code без обработки границ
Version3: Закройте программу chaincode1.
Напишите следующие Cases под последней командой break; из case "Clear": в обработчике событий protected void do_it(...):
case "Chain Code 4": CC4 = true; CC8 = false; chaincode_finder(); break; case "Chain Code 8": CC4 = false; CC8 = true; chaincode_finder(); break;
Допишите следующую функцию под Invalidate(); и под последней скобкой, закрывающей protected void do_it( object sender, System.EventArgs e), но еще перед самой последней скобкой:
private void chaincode_finder() { for ( x=0; x < xSize; x++ ) i0[0,x] = i0[ySize-1,x] = 0;//clear 1. & last row for ( y=0; y < ySize; y++ ) i0[y,0] = i0[y,xSize-1] = 0;//clear 1. & last column for ( y=1; y < ySize; y++ )//search for a start crack for ( x=1; x < xSize; x++ ) if ( i0[y,x-1] < threshold && i0[y,x] >= threshold ) { start.X = x; start.Y = y; goto found; } return; //no start crack found found: System.Text.StringBuilder cracks = new System.Text.StringBuilder(); cc.start = start; cracks.Append( 's' ); cc.area = 0; x = start.X; y = start.Y + 1; Char last_crack = 's'; do { if ( CC4 ) // 4-connectivity switch ( last_crack ) { case 'e': if ( i0[y-1,x ] < threshold) goto n; if ( i0[y ,x ] < threshold) goto e; goto s; case 's': if ( i0[y ,x ] < threshold) goto e; if ( i0[y ,x-1] < threshold) goto s; goto w; case 'w': if ( i0[y ,x-1] < threshold) goto s; if ( i0[y-1,x-1] < threshold) goto w; goto n; case 'n': if ( i0[y-1,x-1] < threshold) goto w; if ( i0[y-1,x ] < threshold) goto n; goto e; } else if ( CC8 ) // 8-connectivity switch ( last_crack ) { case 'e': if ( i0[y ,x ] >= threshold) goto s; if ( i0[y-1,x ] >= threshold) goto e; goto n; case 's': if ( i0[y ,x-1] >= threshold) goto w; if ( i0[y ,x ] >= threshold) goto s; goto e; case 'w': if ( i0[y-1,x-1] >= threshold) goto n; if ( i0[y ,x-1] >= threshold) goto w; goto s; case 'n': if ( i0[y-1,x ] >= threshold) goto e; if ( i0[y-1,x-1] >= threshold) goto n; goto w; } e: last_crack = 'e'; cracks.Append( 'e' ); x++; cc.area += y; continue; s: last_crack = 's'; cracks.Append( 's' ); y++; continue; w: last_crack = 'w'; cracks.Append( 'w' ); x--; cc.area -= y; continue; n: last_crack = 'n'; cracks.Append( 'n' ); y--; continue; } while ( x != start.X || y != start.Y ); //end of do cc.cracks = cracks.ToString(); cc.perimeter = cc.cracks.Length; }
Нажмите Debug → Start Without Debugging Ctrl F5. Опробуйте chaincode1 без и с использованием Noise.
Измените Homunculus-а так, чтобы при 4-х и 8-ми Соседях получались различные результаты.
Chain Code с обработкой границ
Version4: Закройте программу chaincode1.
Измените функцию private void chaincode_finder() так:
private void chaincode_finder() { Byte left; for ( y=0; y < ySize; y++ )//search for a start crack for ( x=0; x < xSize; x++ ) { if ( x > 0 ) left = i0[y,x-1]; else left = 0; if ( left < threshold && i0[y,x] >= threshold ) { start.X = x; start.Y = y; goto found; } } return; //no start crack found found: System.Text.StringBuilder cracks = new System.Text.StringBuilder(); cc.start = start; cracks.Append( 's' ); cc.area = 0; x = start.X; y = start.Y + 1; Char last_crack = 's'; do { if ( CC4 ) // 4-connectivity switch ( last_crack ) { case 'e': if ( x == xSize || i0[y-1,x ] < threshold) goto n; if ( y == ySize || i0[y ,x ] < threshold) goto e; goto s; case 's': if ( y == ySize || i0[y ,x ] < threshold) goto e; if ( x == 0 || i0[y ,x-1] < threshold) goto s; goto w; case 'w': if ( x == 0 || i0[y ,x-1] < threshold) goto s; if ( y == 0 || i0[y-1,x-1] < threshold) goto w; goto n; case 'n': if ( y == 0 || i0[y-1,x-1] < threshold) goto w; if ( x == xSize || i0[y-1,x ] < threshold) goto n; goto e; } else if ( CC8 ) // 8-connectivity switch ( last_crack ) { case 'e': if ( x == xSize ) goto n; if ( y < ySize && i0[y ,x ] >= threshold) goto s; if ( i0[y-1,x ] >= threshold) goto e; goto n; case 's': if ( y == ySize ) goto e; if ( x > 0 && i0[y ,x-1] >= threshold) goto w; if ( i0[y ,x ] >= threshold) goto s; goto e; case 'w': if ( x == 0 ) goto s; if ( y > 0 && i0[y-1,x-1] >= threshold) goto n; if ( i0[y ,x-1] >= threshold) goto w; goto s; case 'n': if ( y == 0 ) goto w; if ( x < xSize && i0[y-1,x ] >= threshold) goto e; if ( i0[y-1,x-1] >= threshold) goto n; goto w; } e: last_crack = 'e'; cracks.Append( 'e' ); x++; cc.area += y; continue; s: last_crack = 's'; cracks.Append( 's' ); y++; continue; w: last_crack = 'w'; cracks.Append( 'w' ); x--; cc.area -= y; continue; n: last_crack = 'n'; cracks.Append( 'n' ); y--; continue; } while ( x != start.X || y != start.Y ); //end of do cc.cracks = cracks.ToString(); cc.perimeter = cc.cracks.Length; }
Нажмите Debug → Start Without Debugging Ctrl F5. Опробуйте chaincode1 с и без использования Noise.
Проследите логику обработку границ для 4-х и 8-ми соседнего принципа.
Попробуйте понять как работает расчет area.
All Chain Codes с обработкой границ
Version5: Закройте программу chaincode1.
Добавьте следующие декларации в заголовок public class Form1 : System.Windows.Forms.Form:
Byte[,] c1v = new Byte[ySize ,xSize+1]; ArrayList all_chaincodes = new ArrayList();
Измените функцию protected override void OnPaint(PaintEventArgs e) чтобы она выглядела так:
protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; Rectangle r = ClientRectangle; dx = r.Width / (xSize+2); dy = (r.Height - all_chaincodes.Count * FontHeight ) / ySize; for ( y=0; y < ySize; y++ ) { button[y].Top = y*dy+1; button[y].Left = xSize*dx+1; button[y].Width = r.Width - button[y].Left - 2; button[y].Height = dy-2; } Int32 textfieldY = button[ySize-1].Top + button[ySize-1].Height + 2; for ( y=0; y < ySize; y++ ) for ( x=0; x < xSize; x++ ) g.FillRectangle( brush[i0[y,x]], x*dx, y*dy, dx, dy ); if ( all_chaincodes.Count == 0 ) return; for ( i=0; i < all_chaincodes.Count; i++ ) { chaincode c = (chaincode)all_chaincodes[i]; x = c.start.X; y = c.start.Y; for ( Int32 ii = 0; ii < c.perimeter; ii++ ) switch ( c.cracks[ii] ) { case 'e': g.DrawLine(redpen, x*dx, y*dy, (x+1)*dx, y*dy ); x++; break; case 's': g.DrawLine(redpen, x*dx, y*dy, x*dx, (y+1)*dy ); y++; break; case 'w': g.DrawLine(redpen, x*dx, y*dy, (x-1)*dx, y*dy ); x--; break; case 'n': g.DrawLine(redpen, x*dx, y*dy, x*dx, (y-1)*dy ); y--; break; } g.FillRectangle( bluebrush, x*dx-5, y*dy-5, 11, 11 ); String s = "(" + c.start.X.ToString() + "/" + c.start.Y.ToString() + ")" + c.cracks + " P=" + c.perimeter.ToString() + " A=" + c.area.ToString(); g.DrawString(s, arial10, blackbrush, 0, textfieldY ); textfieldY += FontHeight; } }
Измените функцию private void chaincode_finder() чтобы она выглядела так:
private void chaincode_finder() { all_chaincodes.Clear(); for ( y=0; y < ySize; y++ ) for ( x=0; x <= xSize; x++ ) c1v[y,x] = 0; start.X = start.Y = 0; for ( Byte cc_no = 1; start_crack_search(); cc_no++ ) { chaincode cc = new chaincode(); System.Text.StringBuilder cracks = new System.Text.StringBuilder(); cc.start = start; cracks.Append( 's' ); cc.area = 0; x = start.X; y = start.Y + 1; Char last_crack = 's'; do { if ( CC4 ) // 4-connectivity switch ( last_crack ) { case 'e': if ( x == xSize || i0[y-1,x ] < threshold) goto n; if ( y == ySize || i0[y ,x ] < threshold) goto e; goto s; case 's': if ( y == ySize || i0[y ,x ] < threshold) goto e; if ( x == 0 || i0[y ,x-1] < threshold) goto s; goto w; case 'w': if ( x == 0 || i0[y ,x-1] < threshold) goto s; if ( y == 0 || i0[y-1,x-1] < threshold) goto w; goto n; case 'n': if ( y == 0 || i0[y-1,x-1] < threshold) goto w; if ( x == xSize || i0[y-1,x ] < threshold) goto n; goto e; } else if ( CC8 ) // 8-connectivity switch ( last_crack ) { case 'e': if ( x == xSize ) goto n; if ( y < ySize && i0[y ,x ] >= threshold) goto s; if ( i0[y-1,x ] >= threshold) goto e; goto n; case 's': if ( y == ySize ) goto e; if ( x > 0 && i0[y ,x-1] >= threshold) goto w; if ( i0[y ,x ] >= threshold) goto s; goto e; case 'w': if ( x == 0 ) goto s; if ( y > 0 && i0[y-1,x-1] >= threshold) goto n; if ( i0[y ,x-1] >= threshold) goto w; goto s; case 'n': if ( y == 0 ) goto w; if ( x < xSize && i0[y-1,x ] >= threshold) goto e; if ( i0[y-1,x-1] >= threshold) goto n; goto w; } e: last_crack = 'e'; cracks.Append( 'e' ); x++; cc.area += y; continue; s: last_crack = 's'; cracks.Append( 's' ); y++; c1v[y-1,x] = cc_no; continue; w: last_crack = 'w'; cracks.Append( 'w' ); x--; cc.area -= y; continue; n: last_crack = 'n'; cracks.Append( 'n' ); y--; c1v[y ,x] = cc_no; continue; } while ( x != start.X || y != start.Y ); //end of do cc.cracks = cracks.ToString(); cc.perimeter = cc.cracks.Length; all_chaincodes.Add( cc ); }// end of for }
Напишите дополнительную функцию private Boolean start_crack_search() под последней скобкой функции private void chaincode_finder(), но еще до самой последней скобки, закрывающей программу:
private Boolean start_crack_search() { Byte left; for ( y=start.Y; y < ySize; y++ ) for ( x=0; x < xSize; x++ ) { if ( x > 0 ) left = i0[y,x-1]; else left = 0; if ( left < threshold && i0[y,x] >= threshold && c1v[y,x] == 0 ) { start.X = x; start.Y = y; c1v[y,x] = 1; return true; } } return false; }
Дополните в protected void do_it(...) в case "Homunculus", в case "Threshold", в case "Noise" и в case "Clear" (всего 4 раза) каждый раз перед закрывающим break; следующий код:
all_chaincodes.Clear();
Нажмите Debug → Start Without Debugging Ctrl F5. Опробуйте chaincode1.
Chain Code Animation
Version6: Закройте программу chaincode1.
Добавьте следующие декларации в public class Form1 : System.Windows.Forms.Form:
Pen pinkpen = new Pen( Color.FromArgb(255,128,128), 3 );
Добавьте следующие 2 команды в конце конструктора public Form1():
redpen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor; pinkpen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
Найдите в функции private void chaincode_finder() следующие 3 команды:
... Char last_crack = 's'; do { if ( CC4 ) // 4-connectivity ...
Замените эти 3 команды на следующий код:
... Char last_crack = 's'; Graphics g = this.CreateGraphics(); do { g.DrawLine( pinkpen, x*dx, y*dy, x*dx+dx/4, y*dy ); g.DrawLine( pinkpen, x*dx, y*dy, x*dx, y*dy+dy/4 ); g.DrawLine( pinkpen, x*dx, y*dy, x*dx-dx/4, y*dy ); g.DrawLine( pinkpen, x*dx, y*dy, x*dx, y*dy-dy/4 ); switch ( last_crack ) { case 'e': g.DrawLine( redpen, (x-1)*dx, y*dy, x*dx, y*dy ); break; case 's': g.DrawLine( redpen, x*dx, (y-1)*dy, x*dx, y*dy ); break; case 'w': g.DrawLine( redpen, (x+1)*dx, y*dy, x*dx, y*dy ); break; case 'n': g.DrawLine( redpen, x*dx, (y+1)*dy, x*dx, y*dy ); break; } Int64 ticks = DateTime.Now.Ticks + 2000000; do {} while ( ticks > DateTime.Now.Ticks ); if ( CC4 ) // 4-connectivity ...
Дорисуйте последний крэк для каждой области, дописав в конце функции private void chaincode_finder() сразу после конца do-цикла, то есть под строкой
} while ( x != start.X || y != start.Y ); //end of do
еще следующую конструкцию:
switch ( last_crack ) { case 'e': g.DrawLine( redpen, (x-1)*dx, y*dy, x*dx, y*dy ); break; case 'w': g.DrawLine( redpen, (x+1)*dx, y*dy, x*dx, y*dy ); break; }
Нажмите Debug → Start Without Debugging Ctrl F5. Опробуйте chaincode1.
Замедлите анимацию, изменив такты (Tick) с 2 Mio на 4, 8, 16 Mio (1 Tick = 100 Наносекунды).
Следующие задания
Создайте варианты.
Примеры:
(1) Замените 4 Направления через числа типа Byte: e = 0, s = 1, w = 2, n = 3.
(2) Нарисуйте Крэк Коды из версии 5 в различных цветах, которые циклически повторяются, если областей будет больше, чем цветов.
(3) Заполните области этими цветами используя матрицу c1v.
(4) Замените обработку границ в версиях 4/5 через try - catch Exception Handling.