ฉันเริ่มสัมผัสกับการประมวลผลภาพดิจิทัลครั้งแรกในโรงเรียนมัธยม ในเวลานั้น PHOTOSHOP ยังคงเป็น 4.0 อาจเป็นเพราะอคติของฉัน ฉันจึงยังไม่มีความสนใจในการเรียนรู้ 3DMAX และการก้าวกระโดดจาก 2D ไปสู่ 3D อาจไม่มีอะไรเกิดขึ้น ทำกับฉันแล้วฉันก็ทนไม่ไหวที่จะทิ้งสี่เหลี่ยมนั้นไว้เป็นลูกบาศก์เงินเดือนสูง.... 555
ตอนที่ฉันเรียนมหาวิทยาลัย ฉันเขียนโปรแกรมประมวลผลภาพกับเพื่อนร่วมชั้น ในเวลานั้น การเขียนโปรแกรมยังไม่เป็นทางการ และสิ่งที่ฉันคิดคือจะใช้งานมันอย่างไร ตอนนี้ดูเหมือนว่าเทคโนโลยีที่แท้จริงคือความสามารถในการเข้าใจภาพรวม สถานการณ์มากกว่าความมหัศจรรย์ของแรงบันดาลใจ เมื่อไม่กี่วันที่ผ่านมาฉันได้ติดต่อกับโปรแกรมประมวลผลภาพจากต่างประเทศ นี่คือบทสรุป ฉันคาดว่าฉันจะไม่ศึกษาการประมวลผลภาพโดยเฉพาะในอนาคต
อดีตเพื่อนร่วมชั้นเคยบอกฉันว่า .net ไม่มีคำแนะนำ ขณะนี้หลักสูตรฝึกอบรมหลายหลักสูตรดูเหมือนจะพูดแบบเดียวกัน จริงๆ แล้ว นี่เป็นความผิดพลาด เพียงแต่เฟรมเวิร์กไม่แนะนำให้ใช้พอยน์เตอร์ โดยเฉพาะอย่างยิ่งในการดำเนินการข้ามกระบวนการ เช่น บริการเว็บและรีโมต พอยน์เตอร์ไม่ปลอดภัย แต่ทุกคนที่เคยใช้ TC ควรประทับใจอย่างยิ่งกับประสิทธิภาพในการดำเนินการของพอยน์เตอร์ เนื่องจากความต้องการในการคำนวณข้อมูลขนาดใหญ่เป็นชุด พอยน์เตอร์จึงเป็นทางเลือกเดียว ดังนั้น .net จะคงตัวชี้ไว้อย่างชาญฉลาดและแสดงรายการไว้ในชุดวิธีการที่ไม่ปลอดภัย การใช้พอยน์เตอร์อย่างสมเหตุสมผลจะช่วยปรับปรุงประสิทธิภาพการดำเนินการได้อย่างมาก ฉันได้ทำการทดลองเพื่อดำเนินการแบบจุดต่อจุดบนรูปภาพขนาด 640*480 จะใช้เวลาหลายนาที ในขณะที่การดำเนินการของพอยน์เตอร์จะเสร็จสิ้นเกือบจะในทันที ดังนั้นอย่ากลัวที่จะใช้พอยน์เตอร์
อย่างที่สองคือคณิตศาสตร์ ผมแนะนำให้ทุกคนเข้าใจก่อนที่จะเขียนโปรแกรม วิชาคณิตศาสตร์ไม่ใช่เรื่องตลก...ถ้าไม่เข้าใจต้องนอนบนเตียงแล้วคิดทบทวนซ้ำแล้วซ้ำเล่า ว่าคณิตศาสตร์สามารถป้องกันโรคอัลไซเมอร์ได้
ใกล้บ้านมากขึ้น เรามาพูดถึงโครงสร้างของโปรแกรมกันดีกว่า:
โครงการสร้างภาพ (ฟิลเตอร์ พื้นผิว โหมดภาพ)
โครงงานคณิตศาสตร์ (อัลกอริทึม ขอบเขต การปรับแต่ง และวิธีการคำนวณทั่วไป)
โครงการหลักของโปรแกรม
ฉันขอยกตัวอย่างให้คุณฟัง ฉันจะทำการเขียนโปรแกรมเชิงอินเทอร์เฟ
ซสาธารณะ IFilter
ด้วย
-
ใช้บิตแมป (บิตแมป img );
}
เพื่อแสดงให้เห็น ฉันจะทำการเขียนโปรแกรมเชิงอินเทอร์เฟซด้วย ตัวกรองแต่ละตัวจะต้องใช้อินเทอร์เฟซนี้ คำจำกัดความของอินเทอร์เฟซยังรวมถึงคำจำกัดความที่ไม่ได้สร้างรูปภาพจริง แต่สร้างเฉพาะวัตถุไบนารี่เท่านั้น ซึ่งจะไม่ได้รับการพิจารณาที่นี่ในขณะนี้ สิ่งมีชีวิต. ยกตัวอย่างตัวกรองสีกลับด้าน
ใช้บิตแมปสาธารณะ (บิตแมป srcImg)
-
//รับขนาดรูปภาพต้นฉบับ
ความกว้าง int = srcImg.Width;
int height = srcImg.Height;
PixelFormat fmt = ( srcImg.PixelFormat == PixelFormat.Format8bppIndexed ) ?
PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb;
// ล็อคข้อมูลบิตแมปต้นฉบับ
BitmapData srcData = srcImg.LockBits(
สี่เหลี่ยมผืนผ้าใหม่ (0, 0, กว้าง, สูง)
ImageLockMode.ReadOnly, fmt );
// สร้างรูปภาพใหม่
บิตแมป dstImg = ( fmt == PixelFormat.Format8bppIndexed ) ?
AForge.Imaging.Image.CreateGrayscaleImage( ความกว้าง ความสูง ):
บิตแมปใหม่ (ความกว้าง, ความสูง, fmt);
// ล็อคข้อมูลบิตแมปปลายทาง
BitmapData dstData = dstImg.LockBits(
สี่เหลี่ยมผืนผ้าใหม่ (0, 0, กว้าง, สูง)
ImageLockMode.ReadWrite, fmt );
// คัดลอกรูปภาพ
Win32.memcpy( dstData.Scan0, srcData.Scan0, srcData.Stride * height );
// ประมวลผลตัวกรอง
ProcessFilter( dstData, fmt );
// ปลดล็อคทั้งสองภาพ
dstImg.UnlockBits(dstData);
srcImg.UnlockBits( srcData );
กลับ dstImg ;
-
เป็นทางเข้าสู่วิธีการกรองและทำงานเตรียมการให้เสร็จสิ้นก่อนการประมวลผล ProcessFilter ยังเรียกวิธี ProcessFilter ที่ใช้กันทั่วไปในแต่ละคลาสตัวกรอง และ ProcessFilter นี้เป็นกุญแจสำคัญในการตระหนักถึงฟังก์ชัน การดำเนินการแบบจุดต่อจุด หรือการทำงานของเทมเพลต
// ประมวลผลตัวกรอง
ส่วนตัวเป็นโมฆะ ProcessFilter ที่ไม่ปลอดภัย (ข้อมูล BitmapData, PixelFormat fmt)
-
int width = data.Width;
int height = data.Height;
int lineSize = width * ( ( fmt == PixelFormat.Format8bppIndexed ) ? 1 : 3 );
int offset = data.Stride - lineSize;
// ทำงาน
ไบต์ * ptr = (ไบต์ *) data.Scan0.ToPointer( );
// กลับด้าน
สำหรับ ( int y = 0; y < ความสูง; y++ )
-
สำหรับ ( int x = 0; x < lineSize; x++, ptr ++ )
-
// กลับแต่ละพิกเซล
*ptr = (ไบต์)( 255 - *ptr );
-
ptr += ชดเชย;
-
}
ในหมู่พวกเขา Format8bppIndexed ไม่จำเป็นต้องกังวลมากเกินไป โดยส่วนตัวแล้วฉันคิดว่าไม่จำเป็นต้องคำนึงถึงปัญหาความเข้ากันได้ในระยะแรกของการออกแบบ
เรามาพูดถึงพื้นผิวกันดีกว่า ฉันไม่เคยคิดมากเกี่ยวกับเรื่องนี้มาก่อน แต่ฉันพบว่าชาวต่างชาติชอบเล่นกับมันเพราะพื้นผิวมีพื้นที่ให้เล่นมากขึ้นในวิชาคณิตศาสตร์ ฉันไม่รู้ว่ามันเกิดขึ้นได้อย่างไร เป็นจริงตามจินตนาการ มันยาก บางทีหนึ่งในนั้นอาจค้นพบวิธีนี้เมื่อเล่นซอฟต์แวร์สร้างแบบจำลองทางคณิตศาสตร์ดังนั้นครูคณิตศาสตร์ระดับสูงจึงปฏิเสธที่จะยอมรับใครเลยและเล่นกับอัลกอริทึมอย่างหนัก อย่างไรก็ตามฉันคิดว่านั่นคือสิ่งที่เกิดขึ้น - -
อินเทอร์เฟซสาธารณะ ITextureGenerator
-
/***////// <สรุป>
/// สร้างพื้นผิว
/// </สรุป>
float[,] สร้าง( int width, int height );
/**////// <summary>
/// รีเซ็ต - สร้างตัวเลขสุ่มภายในใหม่
/// </สรุป>
โมฆะรีเซ็ต ( );
-
นี่คืออินเทอร์เฟซการใช้งานของเครื่องสร้างพื้นผิว เพื่อให้แน่ใจว่าพื้นผิวจะแตกต่างกันในแต่ละครั้ง จะต้องอัปเดตตัวเลขสุ่มเป็นพารามิเตอร์การคำนวณ
เสียง Math.PerlinNoise ส่วนตัว = Math.PerlinNoise ใหม่ ( 1.0 / 32, 0.05, 0.5, 8 );
การบรรลุรายละเอียดพื้นผิวยังต้องใช้สัญญาณรบกวน ดังนั้นจึงจำเป็นต้องมีการใช้สัญญาณรบกวนหลายประเภท
//คอนสตรัคเตอร์
WoodTexture สาธารณะ () : นี้ (12.0) { }
WoodTexture สาธารณะ (วงแหวนคู่)
-
this.rings = แหวน;
รีเซ็ต( );
}
Constructor จัดเตรียมการตั้งค่าเริ่มต้น ซึ่งเป็นขีดจำกัดของขนาดพื้นผิวของหน่วย
//สร้างพื้นผิว
โฟลตสาธารณะ [,] สร้าง ( int width, int height )
-
float[,] texture = new float[height, width];
int w2 = ความกว้าง / 2;
int h2 = ความสูง / 2;
สำหรับ ( int y = 0; y <ความสูง; y++)
-
สำหรับ (int x = 0; x <ความกว้าง; x++)
-
สองเท่า xv = (สองเท่า) ( x - w2 ) / ความกว้าง;
สองเท่า yv = (สองเท่า) ( y - h2 ) / ความสูง;
พื้นผิว[y, x] =
Math.Max( 0.0f, Math.Min ( 1.0f, (ลอย)
คณิต.เอบีเอส( คณิต.บาป(
( Math.Sqrt( xv * xv + yv * yv ) + noise.Function2D( x + r, y + r ) )
* Math.PI * 2 * วงแหวน
-
-
-
-
เนื้อกลับ;
-
แค่นั้นแหละ. - - ผลลัพธ์ของคณิตศาสตร์ที่ไม่ดีของฉัน ฉันไม่รู้ด้วยซ้ำว่าเธอกำลังพูดถึงอะไร ฉันเลือกค่าสูงสุดจากค่าต่ำสุด อัลกอริทึมนั้นหาได้ไม่ยาก สิ่งสำคัญคือการดูว่าโครงสร้างผสานรวมเข้าด้วยกันอย่างไร
โมฆะสาธารณะ รีเซ็ต( )
-
r = แรนด์ถัดไป (5,000 );
}อย่าลืมตัวเลขสุ่มนี้ ภาพของตัวเลขก็ต้องการความสวยงามตามธรรมชาติเช่นกัน
แนวคิดเชิงวัตถุในวิศวกรรมคณิตศาสตร์ไม่ใช่เรื่องง่ายที่จะนำไปใช้ ลองดูที่ PerlinNoise เพื่อสร้างแรงบันดาลใจให้ผู้อื่น
PerlinNoise สาธารณะ (ความถี่ init สองครั้ง, initAmplitude สองเท่า, การคงอยู่สองเท่า, int อ็อกเทฟ)
-
this.initFrequency = ความถี่เริ่มต้น;
this.initAmplitude = initAmplitude;
this.persistance = ความคงอยู่;
this.octaves = อ็อกเทฟ;
-
อันดับแรก เราต้องรวบรวมข้อมูล เนื่องจากการประมวลผลภาพเกี่ยวข้องกับสถานการณ์ในมิติเดียวและสองมิติ ดังนั้นวิธีการพื้นฐาน เช่น สัญญาณรบกวน จำเป็นต้องจัดเตรียมวิธีการที่สอดคล้องกันสำหรับทั้งสองสถานการณ์
/***////// <สรุป>
/// ฟังก์ชั่นเสียง Perlin 1-D
/// </สรุป>
ฟังก์ชั่นคู่สาธารณะ (สองเท่า x)
-
ความถี่สองเท่า = ความถี่เริ่มต้น;
แอมพลิจูดสองเท่า = initAmplitude;
ผลรวมสองเท่า = 0;
// อ็อกเทฟ
สำหรับ (int i = 0; i <อ็อกเทฟ; i++)
-
ผลรวม += SmoothedNoise ( x * ความถี่ ) * ความถี่
*= 2;
แอมพลิจูด *= ความคงอยู่;
-
จำนวนเงินที่ส่งคืน;
}
/**//// <สรุป>
/// ฟังก์ชั่นสัญญาณรบกวน Perlin 2-D
/// </สรุป>
สาธารณะ Double Function2D(double x, double y)
-
ความถี่สองเท่า = ความถี่เริ่มต้น;
แอมพลิจูดสองเท่า = initAmplitude;
ผลรวมสองเท่า = 0;
// อ็อกเทฟ
สำหรับ (int i = 0; i <อ็อกเทฟ; i++)
-
ผลรวม += SmoothedNoise ( x * ความถี่, y * ความถี่ ) * ความกว้าง;
ความถี่ *= 2;
แอมพลิจูด *= ความคงอยู่;
-
จำนวนเงินที่ส่งคืน;
-
อะไรคือความแตกต่างระหว่างมิติเดียวและสองมิติ เมื่อตอนที่ฉันอยู่มัธยมต้น ฉันได้เรียนรู้ว่าการเคลื่อนไหวของเส้นทำให้เกิดพื้นผิว เมื่อตอนที่ฉันอยู่ในวิทยาลัย ฉันได้เรียนรู้ว่าเส้นที่เป็นวัฏจักรและการเปลี่ยนแปลงสามารถเป็นตัวแทนของพื้นผิวได้ การจดจำขอบและการลับคม ฉันยังพบว่าก่อนหน้านี้ฉันประเมินเส้นต่ำเกินไป แต่จริงๆ แล้ว มันเป็นพารามิเตอร์ที่น้อยกว่าพื้นผิวเพียงตัวเดียว
/***////// <สรุป>
/// ฟังก์ชั่นเสียงรบกวนธรรมดา
/// </สรุป>
ป้องกันเสียงรบกวนสองเท่า (int x)
-
int n = ( x << 13 ) ^ x;
กลับ ( 1.0 - ( ( n * ( n * 15731 + 789221 ) + 1376312589 ) & 0x7ffffff ) / 1073741824.0 );
-
ป้องกันเสียงรบกวนสองเท่า (int x, int y)
-
int n = x + y * 57;
n = ( n << 13 ) ^ n ;
กลับ ( 1.0 - ( ( n * ( n * 15731 + 789221 ) + 1376312589 ) & 0x7ffffff ) / 1073741824.0 );
} พิสูจน์ย่อหน้าก่อนหน้านี้อีกครั้ง โดยส่วนตัวแล้ว ฉันรู้สึกว่า x+y*57 นี้มีความหมายแบบประมาณการเล็กน้อย รับค่าสัญญาณรบกวนที่สอดคล้องกัน แต่เสียงรบกวนไม่สามารถนำมาใช้โดยตรงได้
/***////// <สรุป>
///เสียงเบา
/// </สรุป>
ป้องกัน SmoothedNoise สองเท่า (สองเท่า x)
-
int xInt = (int) x;
xFrac สองเท่า = x - xInt;
return CosineInterpolate ( สัญญาณรบกวน ( xInt ) , สัญญาณรบกวน ( xInt + 1 ), xFrac );
-
ป้องกัน SmoothedNoise สองเท่า (สองเท่า x, สองเท่า y)
-
int xInt = (int) x;
int yInt = (int) y;
xFrac สองเท่า = x - xInt;
double yFrac = y - yInt;
// รับค่าสัญญาณรบกวนสี่ค่า
x0y0 สองเท่า = เสียงรบกวน ( xInt , yInt );
x1y0 สองเท่า = สัญญาณรบกวน ( xInt + 1, yInt );
x0y1 สองเท่า = เสียงรบกวน ( xInt , yInt + 1 );
x1y1 สองเท่า = สัญญาณรบกวน ( xInt + 1, yInt + 1) ;
// x การแก้ไข
v1 สองเท่า = โคไซน์อินเตอร์โพเลท ( x0y0, x1y0, xFrac );
ดับเบิ้ล v2 = โคไซน์อินเตอร์โพเลท( x0y1, x1y1, xFrac );
//yinterpolation
กลับโคไซน์อินเตอร์โพเลต (v1, v2, yFrac );
} สัญญาณรบกวนที่ราบรื่น ซึ่งดูเหมือนจะเรียกไม่เข้ากันเล็กน้อย ทำงานโดยการประมาณค่าโคไซน์ แทนที่จะเป็นโคไซน์แยก การแก้ไขโคไซน์คืออะไร? /***////// <สรุป>
/// การประมาณค่าโคไซน์
/// </สรุป>
ป้องกัน cosineInterpolate สองเท่า ( x1 สองเท่า, x2 สองเท่า, a สองเท่า)
-
ดับเบิล f = ( 1 - Math.Cos( a * Math.PI ) ) * 0.5;
กลับ x1 * ( 1 - f ) + x2 * f;
}แค่นั้นแหละ มีบางสิ่งที่เพียงพอแล้วที่ปรมาจารย์จะรู้ และคุณก็สามารถทำได้ตามนั้น เพราะเหตุใด เพราะคุณอาจไม่เข้าใจมันมาทั้งชีวิต แต่บางคนจะคิดออกเองตามธรรมชาติและความรู้ยังคงถูกถ่ายทอดต่อไป เช่นเดียวกับที่คุณไม่จำเป็นต้องรู้อัตราส่วนกรดในกระเพาะเพื่อเพลิดเพลินกับอาหารอร่อยและรสเผ็ด คุณก็ไม่ต้องกังวลเรื่องอาหารไม่ย่อยสำหรับคนรุ่นต่อๆ ไป ไม่ต้องฝืนอะไรมันก็เชิงลบนิดหน่อย 555
ภาพไม่ยากตราบใดที่คุณเข้าใจความสัมพันธ์การโทรและรูปแบบลอยเช่น Photoshop เป็นตัวเลือกที่ดีที่สุดฉันคิดว่า // กลับภาพ
โมฆะส่วนตัว invertColorFiltersItem_Click (ผู้ส่งวัตถุ System.EventArgs e)
-
ApplyFilter(กลับหัวใหม่());
}
// ใช้ฟิลเตอร์กับรูปภาพ
โมฆะส่วนตัว ApplyFilter (ตัวกรอง IFilter)
-
พยายาม
-
// ตั้งค่าเคอร์เซอร์รอ
this.Cursor = Cursors.WaitCursor;
// ใช้ตัวกรองกับรูปภาพ
บิตแมป newImage = filter.Apply (รูปภาพ)
;
-
// เปิดรูปภาพใหม่ในเอกสารใหม่
โฮสต์ NewDocument (ภาพใหม่);
-
อื่น
-
ถ้า (host.RememberOnChange)
-
// สำรองรูปภาพปัจจุบัน
ถ้า (สำรอง! = null)
backup.Dispose();
สำรองข้อมูล = รูปภาพ;
-
อื่น
-
// ปล่อยภาพปัจจุบัน
รูปภาพ.ทิ้ง();
}
image = newImage;
// อัพเดต
อัพเดตรูปภาพใหม่();
-
-
จับ (ArgumentException)
-
MessageBox.Show("ตัวกรองที่เลือกไม่สามารถใช้กับรูปภาพได้", "ข้อผิดพลาด", MessageBoxButtons.OK, MessageBoxIcon.Error);
-
ในที่สุด
-
// คืนค่าเคอร์เซอร์
this.Cursor = Cursors.Default;
-
}หากการโทรเป็นไปอย่างราบรื่น โค้ดจำนวนไม่มากก็จะดูยุ่งเหยิง สำหรับผู้เริ่มต้น ให้ใช้ภูมิภาคให้เกิดประโยชน์
นอกจากนี้ยังมีแนวคิดของ DocumentsHost สะดวกมากในการใช้โฮสต์ไฟล์รูปภาพและเชื่อมต่อรูปภาพและแบบฟอร์ม /**//// <summary>
/// อินเทอร์เฟซ IDocumentsHost
/// ให้การเชื่อมต่อระหว่างเอกสารและแม่หม้ายหลัก
/// </สรุป>
อินเทอร์เฟซสาธารณะ IDocumentsHost
-
บูล CreateNewDocumentOnChange{get;}
บูล RememberOnChange{get;}
บูล NewDocument (ภาพบิตแมป);
GetImage
)
(ผู้ส่งวัตถุ, ข้อความสตริง, ขนาดขนาด, รูปแบบ PixelFormat);
}ทุกคนยินดีที่จะพูดคุยกับฉัน