Saya pertama kali mengenal pengolahan gambar digital saat SMA. Saat itu PHOTOSHOP masih 4.0. Mungkin karena prasangka saya, saya masih kurang tertarik mempelajari 3DMAX dan sejenisnya. Lompatan dari 2D ke 3D mungkin tidak ada apa-apanya lakukan dengan saya, dan saya tidak tega meninggalkan kotak itu dengan gaji Kubik yang tinggi.... Haha.
Ketika saya masih kuliah, saya menulis beberapa program pengolah gambar dengan teman-teman sekelas saya. Saat itu, pemrograman masih sangat biasa-biasa saja, dan yang saya pikirkan hanyalah bagaimana mengimplementasikannya situasi, bukan keajaiban kilasan inspirasi. Saya bertemu dengan beberapa program pemrosesan gambar asing beberapa hari yang lalu. Berikut ringkasannya. Saya memperkirakan bahwa saya tidak akan mempelajari pemrosesan gambar secara khusus di masa mendatang.
Seorang mantan teman sekelas pernah mengatakan kepada saya bahwa .net tidak memiliki petunjuk. Sekarang banyak kursus pelatihan yang tampaknya mengatakan hal yang sama. Faktanya, ini adalah sebuah kesalahan. Hanya saja framework tidak merekomendasikan penggunaan pointer, terutama pada operasi lintas proses seperti webservise dan remoting, pointer tidak aman. Namun setiap orang yang pernah menggunakan TC pasti sangat terkesan dengan efisiensi eksekusi pointer. Di bawah permintaan penghitungan batch data berskala besar, pointer adalah satu-satunya pilihan. Oleh karena itu, .net dengan cerdik menyimpan penunjuk dan mencantumkannya dalam kumpulan metode tidak aman. Penggunaan pointer yang wajar akan sangat meningkatkan efisiensi eksekusi. Saya telah melakukan eksperimen untuk melakukan operasi titik demi titik pada gambar 640*480. Operasi non-pointer memakan waktu beberapa menit, sedangkan operasi pointer selesai hampir seketika. Jadi jangan takut untuk menggunakan petunjuk.
Yang kedua adalah matematika, saya menyarankan semua orang untuk memahaminya sebelum menulis sebuah program, kelas matematika bukanlah lelucon... Jika Anda tidak mengerti, Anda harus berbaring di tempat tidur dan memikirkannya berulang kali bahwa matematika dapat mencegah penyakit Alzheimer.
Lebih dekat ke rumah, mari kita bicara tentang struktur program:
Proyek pencitraan (filter, tekstur, mode gambar)
Proyek matematika (algoritma, batasan, penyesuaian, dan metode penghitungan umum)
Proyek program utama
Izinkan saya memberi Anda sebuah contoh. Saya juga akan melakukan beberapa pemrograman berorientasi antarmuka
IFilter
{
Terapkan Bitmap( Bitmap img );
}
Sebagai ilustrasi, saya juga akan melakukan pemrograman berorientasi antarmuka. Setiap filter harus mengimplementasikan antarmuka ini. Definisi antarmuka juga menyertakan definisi alasan yang tidak menghasilkan gambar sebenarnya, tetapi hanya menghasilkan objek biner, yang tidak akan dipertimbangkan di sini untuk saat ini. makhluk. Ambil filter warna invert sebagai contoh
Terapkan Bitmap publik( Bitmap srcImg )
{
//dapatkan ukuran gambar sumber
int lebar = srcImg.Lebar;
int tinggi = srcImg.Tinggi;
PixelFormat fmt = ( srcImg.PixelFormat == PixelFormat.Format8bppIndexed ) ?
PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb;
// mengunci data bitmap sumber
BitmapData srcData = srcImg.LockBits(
Persegi Panjang baru (0, 0, lebar, tinggi),
ImageLockMode.ReadOnly, fmt );
// buat gambar baru
Bitmap dstImg = ( fmt == PixelFormat.Format8bppIndexed ) ?
AForge.Imaging.Image.CreateGrayscaleImage (lebar, tinggi):
Bitmap baru( lebar, tinggi, fmt );
// mengunci data bitmap tujuan
BitmapData dstData = dstImg.LockBits(
Persegi Panjang baru (0, 0, lebar, tinggi),
ImageLockMode.ReadWrite, fmt );
// salin gambar
Win32.memcpy( dstData.Scan0, srcData.Scan0, srcData.Stride * height );
// memproses filter
ProcessFilter( dstData, fmt );
// membuka kunci kedua gambar
dstImg.UnlockBits(dstData);
srcImg.UnlockBits( srcData );
kembalikan dstImg;
}
Ini adalah pintu masuk ke metode filter dan menyelesaikan pekerjaan persiapan sebelum pemrosesan. ProcessFilter juga memanggil metode ProcessFilter yang umum di setiap kelas filter, dan ProcessFilter ini adalah kunci untuk mewujudkan fungsi, operasi titik demi titik, atau operasi templat.
// Proses filternya
Void ProcessFilter pribadi yang tidak aman (data BitmapData, PixelFormat fmt)
{
int lebar = data.Lebar;
int tinggi = data.Tinggi;
int ukuran garis = lebar * ( ( fmt == PixelFormat.Format8bppIndexed ) ? 1 : 3 );
int offset = data.Stride - lineSize;
// lakukan pekerjaan
byte * ptr = (byte *) data.Scan0.ToPointer( );
// membalikkan
untuk ( int y = 0; y < tinggi; y++ )
{
untuk ( int x = 0; x < Ukuran garis; x++, ptr ++ )
{
// membalikkan setiap piksel
*ptr = (bita)( 255 - *ptr );
}
ptr += offset;
}
}
Diantaranya, Format8bppIndexed tidak perlu terlalu dikhawatirkan. Menurut saya pribadi, masalah kompatibilitas dengannya tidak perlu dipertimbangkan pada tahap awal desain.
Sekarang mari kita bicara tentang tekstur. Saya belum terlalu memikirkan hal ini sebelumnya, tetapi saya menemukan bahwa orang asing suka bermain-main dengannya karena tekstur memiliki lebih banyak ruang untuk bermain dalam matematika menjadi kenyataan berdasarkan imajinasi. Mungkin salah satu dari mereka menemukan metode ini ketika memainkan perangkat lunak pemodelan matematika, sehingga guru matematika tingkat tinggi menolak untuk menerima siapa pun dan bermain-main dengan algoritma dengan sangat keras. Bagaimanapun, menurutku itulah yang terjadi. . .
antarmuka publik ITextureGenerator
{
/**//// <ringkasan>
/// Menghasilkan tekstur
/// </ringkasan>
float[,] Hasilkan( int lebar, int tinggi );
/**//// <ringkasan>
/// Reset - membuat ulang nomor acak internal
/// </ringkasan>
batal Atur Ulang( );
}
Ini adalah antarmuka implementasi generator tekstur. Untuk memastikan tekstur berbeda setiap kali, angka acak harus diperbarui sebagai parameter perhitungan.
kebisingan Math.PerlinNoise pribadi = Math.PerlinNoise baru( 1.0 / 32, 0.05, 0.5, 8 );
Mencapai detail tekstur juga memerlukan noise, sehingga banyak jenis noise yang perlu diterapkan.
//Konstruktor
Tekstur Kayu publik( ) : ini( 12.0 ) {}
Tekstur Kayu publik (cincin ganda)
{
this.rings = berdering;
Mengatur ulang( );
}
Konstruktor menyediakan pengaturan nilai default, yang merupakan batas ukuran unit tekstur.
//Hasilkan tekstur
public float[,] Hasilkan( int lebar, int tinggi )
{
float[,] tekstur = new float[tinggi, lebar];
int w2 = lebar / 2;
int h2 = tinggi / 2;
untuk ( int y = 0; y < tinggi; y++ )
{
untuk (int x = 0; x < lebar; x++)
{
ganda xv = (ganda) ( x - w2 ) / lebar;
ganda yv = (ganda) ( y - h2 ) / tinggi
;
Matematika.Max( 0.0f, Matematika.Min( 1.0f, (float)
Matematika.Abs( Matematika.Dosa(
( Math.Sqrt( xv * xv + yv * yv ) + noise.Function2D( x + r, y + r ) )
* Math.PI * 2 * berdering
))
));
}
}
mengembalikan tekstur;
}
Itu saja. . . Hasil matematika saya yang buruk. Saya bahkan tidak tahu apa yang dia bicarakan. Saya memilih nilai maksimum dari nilai minimum. Algoritma tidak sulit ditemukan, kuncinya adalah melihat bagaimana struktur mengintegrasikannya.
kekosongan publik Reset()
{
r = rand.Berikutnya( 5000 );
}Jangan lupa angka acak ini, gambar angkanya juga perlu keindahan alam.
Konsep berorientasi objek dalam teknik Matematika tidak mudah untuk diterapkan. Lihatlah PerlinNoise untuk menginspirasi orang lain.
PerlinNoise publik (frekuensi init ganda, amplitudo init ganda, ketekunan ganda, oktaf int)
{
this.initFrequency = initFrequency;
this.initAmplitudo = initAmplitudo;
this.persistance = ketekunan;
this.oktaf = oktaf;
}
Pertama, kita perlu mengumpulkan data, karena pemrosesan gambar melibatkan situasi satu dimensi dan dua dimensi, sehingga metode yang mendasari seperti noise perlu menyediakan metode yang sesuai untuk kedua situasi tersebut.
/**//// <ringkasan>
/// Fungsi kebisingan Perlin 1-D
/// </ringkasan>
Fungsi ganda publik (ganda x)
{
frekuensi ganda = frekuensi init;
amplitudo ganda = initAmplitudo;
jumlah ganda = 0;
// oktaf
for (int i = 0; i < oktaf; i++)
{
jumlah += Kebisingan yang Dihaluskan( x * frekuensi ) * amplitudo
;
amplitudo *= ketekunan;
}
jumlah pengembalian;
}
/**//// <ringkasan>
/// Fungsi kebisingan Perlin 2-D
/// </ringkasan>
Fungsi ganda publik2D(ganda x, ganda y)
{
frekuensi ganda = frekuensi init;
amplitudo ganda = initAmplitudo;
jumlah ganda = 0;
// oktaf
for (int i = 0; i < oktaf; i++)
{
jumlah += Kebisingan yang Dihaluskan( x * frekuensi, y * frekuensi ) * amplitudo
;
amplitudo *= ketekunan;
}
jumlah pengembalian;
}
Apa perbedaan antara satu dimensi dan dua dimensi? Ketika saya masih di sekolah menengah, saya belajar bahwa pergerakan garis menghasilkan permukaan. Tapi setelah saya belajar bahwa garis yang berputar dan berubah dapat mewakili permukaan pengenalan dan penajaman tepi, saya juga menemukan bahwa saya meremehkan garis sebelumnya, tetapi kenyataannya itu hanya satu parameter yang lebih sedikit daripada permukaan.
/**//// <ringkasan>
/// Fungsi kebisingan biasa
/// </ringkasan>
Kebisingan ganda yang dilindungi (int x)
{
int n = ( x << 13 ) ^ x;
kembali ( 1.0 - ( ( n * ( n * n * 15731 + 789221 ) + 1376312589 ) & 0x7ffffffff ) / 1073741824.0 );
}
Kebisingan ganda yang dilindungi (int x, int y)
{
int n = x + kamu * 57;
n = ( n << 13 ) ^ n ;
kembali ( 1.0 - ( ( n * ( n * n * 15731 + 789221 ) + 1376312589 ) & 0x7ffffffff ) / 1073741824.0 );
} Sekali lagi buktikan paragraf sebelumnya. Secara pribadi, menurut saya x+y*57 ini memiliki sedikit arti proyeksi. Dapatkan nilai kebisingan yang sesuai. Namun noise tidak bisa digunakan secara langsung.
/**//// <ringkasan>
/// Merapikan kebisingan
/// </ringkasan>
dilindungi ganda SmoothedNoise (ganda x)
{
int xInt = (int)x;
ganda xFrac = x - xInt;
kembalikan CosineInterpolate( Kebisingan( xInt ) , Kebisingan( xInt + 1 ), xFrac );
}
dilindungi ganda SmoothedNoise (ganda x, ganda y)
{
int xInt = (int) x;
int yInt = (int) y;
xFrac ganda = x - xInt;
double yFrac = y - yInt;
// dapatkan empat nilai noise
ganda x0y0 = Kebisingan( xInt , yInt );
ganda x1y0 = Kebisingan( xInt + 1, yInt );
ganda x0y1 = Kebisingan( xInt , yInt + 1 );
double x1y1 = Kebisingan( xInt + 1, yInt + 1) ;
// x interpolasi
double v1 = CosineInterpolate( x0y0, x1y0, xFrac );
ganda v2 = CosineInterpolate( x0y1, x1y1, xFrac );
//interpolasi
kembalikan CosineInterpolate( v1, v2, yFrac );
} Kebisingan halus, yang tampaknya agak aneh untuk disebut, beroperasi dengan interpolasi kosinus dan bukan kosinus diskrit. Apa itu interpolasi kosinus? /**//// <ringkasan>
/// Interpolasi kosinus
/// </ringkasan>
dilindungi CosineInterpolate ganda (ganda x1, ganda x2, ganda a)
{
ganda f = ( 1 - Math.Cos( a * Math.PI ) ) * 0,5;
kembalikan x1 * ( 1 - f ) + x2 * f;
}Itu saja, ada beberapa hal yang cukup untuk diketahui oleh master, dan Anda dapat melakukannya sesuai dengan itu. Mengapa? Karena Anda mungkin tidak memahaminya seumur hidup Anda, tetapi seseorang dengan sendirinya akan mengetahuinya, dan pengetahuan masih terus diwariskan. Sama seperti Anda tidak perlu mengetahui rasio asam lambung untuk menikmati makanan lezat dan pedas, Anda juga tidak perlu khawatir akan gangguan pencernaan pada generasi mendatang. Tidak perlu memaksakan beberapa hal, itu agak negatif, haha.
Gambarnya tidak sulit, selama Anda memahami hubungan pemanggilannya, dan bentuk mengambang seperti photoshop adalah pilihan terbaik, menurut saya, // Balikkan gambar
private void invertColorFiltersItem_Click(pengirim objek, System.EventArgs e)
{
ApplyFilter(Pembalikan baru());
}
// Terapkan filter pada gambar
kekosongan pribadi ApplyFilter(Filter IFilter)
{
mencoba
{
// atur kursor tunggu
this.Cursor = Cursors.WaitCursor;
// menerapkan filter pada gambar
Bitmap newImage = filter.Apply(gambar);
jika (host.CreateNewDocumentOnChange)
{
// buka gambar baru di dokumen baru
host.Dokumen Baru(Gambar baru);
}
kalau tidak
{
if (host.RememberOnChange)
{
// buat cadangan gambar saat ini
jika (cadangan!= null)
backup.Buang();
cadangan = gambar;
}
kalau tidak
{
// lepaskan gambar saat ini
gambar.Buang();
}
gambar = Gambar baru;
// perbarui
PerbaruiGambarBaru();
}
}
tangkapan(ArgumentException)
{
MessageBox.Show("Filter yang dipilih tidak dapat diterapkan pada gambar", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Akhirnya
{
// mengembalikan kursor
this.Cursor = Kursor.Default;
}
}Jika panggilannya lancar, jumlah kode apa pun tidak akan terasa berantakan. Untuk pemula, manfaatkan wilayah dengan baik.
Ada juga konsep DocumentsHost. Sangat nyaman menggunakannya untuk menghosting file gambar dan menghubungkan gambar dan formulir
/// Antarmuka IDDocumentsHost
/// Menyediakan koneksi antara dokumen dan jendela utama
/// </ringkasan>
antarmuka publik IDocumentsHost
{
bool CreateNewDocumentOnChange{dapatkan;}
bool RememberOnChange{get;}
bool NewDocument(Gambar bitmap);
bool NewDocument(Gambar ComplexImage);
Bitmap GetImage(pengirim objek, Teks string, Ukuran ukuran, format PixelFormat);
}Semua orang boleh berdiskusi dengan saya