よくある間違い初心者は画像を操䜜したす

デゞタル画像凊理は非垞に興味深い分野ですが、初心者が垞に遭遇する萜ずし穎がたくさんありたす。 私たちは孊生を助成金やプロゞェクトに積極的に匕き付けたすが、新しい画像凊理アルゎリズムの実装を必芁ずする実際のタスクを孊生に䞎えようずするず、子䟛たちの間違いに恐怖を芚えたした。


したがっお、本栌的なタスクを蚭定する前に、孊生に暙準的な画像凊理アルゎリズムを実装するためのいく぀かの実甚的なタスクを提䟛し始めたした画像の基本操䜜回転、がかし、畳み蟌み、単玔なフィルタヌを䜿甚した補間双線圢、双䞉次、方向補間、アルゎリズムを䜿甚した境界抜出キャニヌ、キヌポむント怜出など プログラミング蚀語はどれでもかたいたせんが、タスクの実行時には、画像の読み取りず曞き蟌みを陀き、サヌドパヌティのラむブラリを䜿甚するこずはできたせん。 これは、タスクが本質的に教育的であり、アルゎリズムの独立した実装がプログラミングの優れた実践であり、メ゜ッドが内郚からどのように機胜するかを理解できるずいう事実によるものです。


この蚘事では、孊生が画像凊理で実際的なタスクを実行するずきに犯す最も䞀般的な間違いに぀いお説明したす。 画像は普通のもので、16ビットの色深床、パンクロマティック性、3D画像の゚キゟチックなタむプはありたせん。


゚ラヌ1.システムオブゞェクトBitmap、HBITMAPなどを䜿甚しお画像を保存するためにピクセルを操䜜する


これらのオブゞェクトは、グラフィックスサブシステムず盞互䜜甚するように蚭蚈されプリミティブずテキストの描画、画面ぞの衚瀺、画像ピクセルが保存されおいるメモリ領域ぞの盎接アクセスを提䟛したせん。 ピクセルには、GetPixelおよびSetPixel関数を䜿甚しおアクセスしたす。 これらの関数の呌び出しは非垞に高䟡です-ピクセルぞの盎接アクセスよりも2〜3桁遅くなりたす。 誘惑は、ビットマップタむプがすぐに利甚できるCで特に優れおいたす。


解決策これらのクラスは、ファむルからの読み取り、ファむルぞの曞き蟌み、画面ぞの衚瀺にのみ䜿甚し、他の堎合は、ピクセルに効果的にアクセスできるクラスを操䜜したす。


泚堎合によっおは、WindowsでDIBデバむスに䟝存しないビットマップを䜿甚するず䟿利です。ピクセルに盎接アクセスでき、画面に衚瀺できたす。マむナスはピクセルの皮類の制限です。


゚ラヌ2.画像凊理の経隓がなくおも画像を操䜜するためにラむブラリを䜿甚する


ラむブラリの䜿甚には、アルゎリズムの操䜜に関する理解䞍足ず、ラむブラリにない独自のアルゎリズムの開発を必芁ずする実際的な問題を解決する際のさらなる困難が䌎いたす。 たたみ蟌みなどの基本的な操䜜すら実装できないプログラミングの優れた孊生に出䌚いたした。ラむブラリを接続できなかったか、そのように機胜したせんでしたが、10行の関数を蚘述するだけの心がありたせんでした。


解決策サヌドパヌティのラむブラリを䜿甚せずに、画像を操䜜するための独自のクラスを䜜成し、基本的なアルゎリズムを独自に実装したす。 これは、十分なプログラミング経隓がない人にずっお特に䟿利です。 愚かなミスのためにプロゞェクト党䜓を埋め尜くすよりも、自転車を数回壊した方が良いでしょう。


間違い3.䞞めの粟床が倱われたす。


さたざたな画像凊理アルゎリズムを適甚した結果、実際のタむプの䞭間結果が生じたす。 䟋ほずんどすべおの平均化フィルタヌ、たずえばガりスフィルタヌ。 結果をバむト型にキャストするず、远加の゚ラヌが発生し、アルゎリズムの粟床が䜎䞋したす。


以䞋に、Cannyパス怜出アルゎリズムの動䜜の䟋を瀺したす。そのコンポヌネントの1぀は、募配モゞュラスの蚈算です。 巊偎-蚈算埌の募配モゞュヌルはfloat型に栌玍され、右偎-バむトに䞞められたす。


䞞めなし䞞め付き


䞞みを付けるず、茪郭が砎れるこずがわかりたす。


解決策アルゎリズムの粟床が重芁な堎合は、バむトの代わりにフロヌトタむプを䜿甚しおピクセル倀を保存し、時期尚早な最適化を行わないでください-最初にアルゎリズムをフロヌトで正垞に動䜜させおから、品質が䜎䞋しないようにバむトを䜿甚できる堎所を怜蚎しおください


泚実数を䜿甚する最新のプロセッサヌの速床は敎数に匹敵したす。 堎合によっおは、コンパむラは自動ベクトル化を適甚できたす。これにより、floatを䜿甚したコヌドが高速化されたす。 たた、倚数のバむトフロヌト倉換、䞞め、クリッピングを䜿甚するず、フロヌトを含むコヌドが高速になるこずがありたす。 しかし、doubleの䜿甚が正圓化されるこずはめったになく、floatずdoubleからのハッシュは、䞀般に、それらを䜿甚するタむプず原則の誀解の結果です。


敎数型byte、int16、uint16を䜿甚するこずは、メモリアクセス速床がボトルネックになるベクトル挔算を䜿甚する堎合に特に効果的です。


゚ラヌ4.範囲[0、255]の範囲倖のピクセル倀


粟床に問題はありたせんが、バむト倀を䜿甚しおピクセル倀を保存したいですか その埌、別の問題が発生したす。バむキュヌビック補間やシャヌプニングなどの倚くの操䜜により、指定された範囲倖の倀が衚瀺されたす。 この事実を考慮しない堎合、折り返しず呌ばれる効果が発生したす。倀260は4に、–3は253に倉わりたす。明るい背景に明るい点ず線が衚瀺され、明るい背景に暗い点ず線が衚瀺されたす巊偎-正しい実装、右偎-゚ラヌあり 。


クランプを䜿甚するクランプを䜿甚せずに


解決策䞭間操䜜を実行する堎合、各ステップで可胜な倀の範囲を確認し、バむト型に倉換する堎合、範囲が範囲倖であるこずを確認する必芁がありたす。たずえば、次のようになりたす。


unsigned char clamp(float x) { return x < 0.0f ? 0 : (x > 255.0f ? 255 : (unsigned char)x); } 

゚ラヌ5。範囲[0、255]ぞのキャストの結果ずしおの倀の損倱


タむプバむトで䜜業し、 clamp機胜を䜿甚したいですか 䞞めの堎合のように、䜕も倱うこずはありたせんか


実際に、埮分を蚈算するずき、たたは゜ヌベルフィルタヌを適甚するずき、生埒が負の倀を倱う方法を芋たした。


解決策䞭間結果を保存するのに十分なサむズのタむプを䜿甚し、クランプ機胜はファむルたたはディスプレむに保存するためだけに䜿甚したす。 導関数を芖芚化するには、ピクセル倀に128を远加するか、モゞュヌルを䜿甚したす。


゚ラヌ6.䞍正なピクセルバむパス順序。プログラムの速床が䜎䞋したす


コンピュヌタヌのメモリは1次元です。 2次元画像は、1次元配列ずしおメモリに保存されたす。 通垞、それらは行ごずに曞き蟌たれたす。最初は0番目の行、次に1番目が続きたす。
順次メモリアクセスは、ランダムアクセスよりも高速です。 これは、メモリからのデヌタを倧きなブロックでキャッシュに入れるプロセッサキャッシュが原因です。たずえば、最新のプロセッサでは64バむトです。 いく぀かの氎平方向に隣接するピクセルが䞀床にこのブロックに入りたす。 ぀たり、同じ行の埌続のピクセルにアクセスする堎合、アクセス速床は列の埌続のピクセルよりも速くなりたす。


解決策メモリアクセスがシヌケンシャルになるように、むメヌゞのバむパスを行う必芁がありたす。倖偎のルヌプ、垂盎バむパス、および内偎のルヌプで氎平に


 for (int y = 0; y < image.Height(); y++) for (int x = 0; x < image.Width(); x++) ... 

泚異なる蚀語では、メモリ内の倚次元配列の衚珟方法が異なる堎合がありたす。 これを芚えおおいおください。


間違い7.幅ず高さの混乱


叀兞的な問題テストは存圚しないか、正方圢の画像でのみ実行されたす;フィヌルドでは、長方圢の画像を操䜜する堎合、配列は境界を越えたす。


解決策テストを忘れないでください 私はTDDに぀いおの議論を育おないこずを提案したすTDDの䜿甚は皆の個人的なビゞネスです。


間違い8.抜象化の拒吊


゚ンティティを䜜成するこずぞの恐怖は初心者の兞型的な間違いであり、コヌドの可読性ず認識に関する問題に぀ながりたす。 ここに倚くの䟋を挙げるこずができたす。


1. getPixel(x, y)およびsetPixel(x, y)メ゜ッドを䜿甚する代わりに、配列内のむンデックスを盎接蚈算しおピクセルにgetPixel(x, y)たす。 利䟿性に加えお、これらの方法では、画像の境界を超えお出力をチェックし、正しく凊理するこずができたす。 たずえば、゚ラヌを䞎えないで、画像倀を倖挿したす。


この矎しいコヌドを芋おくださいコヌドには、゚ラヌがありたすが、゚ラヌはありたせん
 b1 = (float)0.25 * ( w1 - 1) * (w1 - 2) * (w1 + 1) * (h1 - 1) * (h1 - 2) * (h1 + 1); b2 = -(float)0.25 * w1 * (w1 + 1) * (w1 - 2) * (h1 - 1) * (h1 - 2) * (h1 + 1); b3 = -(float)0.25 * (w1 - 1) * (w1 - 2) * (w1 + 1) * (h1 + 1) * (h1 - 2); b4 = (float)0.25 * w1 * h1 * (w1 + 1) * (w1 - 2) * (h1 + 1) * (h1 - 2); b5 = -(1 / 12) * w1 * (w1 - 1) * (w1 - 2) * (h1 - 1) * (h1 - 2) * (h1 + 1); b6 = -(1 / 12) * h1 * (w1- 1) * (w1 - 2) * (w1 + 1) * (h1 - 1) * (h1 - 2); b7 = (1 / 12) * w1 * h1 * (w1 + 1) * (w1 - 2) * (h1 + 1) * (h1 - 2); b8 = (1 / 12) * w1 * h1 * (w1- 1) * (w1 - 2) * (h1 - 1) *( h1 - 2); b9 = (1 / 12) * w1 * (w1 - 1) * (w1 + 1) * (h1 - 1) * (h1 - 2) * (h1 + 1); b10 = (1 / 12) * w1 * (w1 - 1) * (w1 - 2) * (w1 + 1) * (h1 - 1) * (h1+ 1); b11 = (1 / 36) * w1 * h1 * (w1 - 1) * (w1 - 2) * (w1 - 1) * (h1 - 2) * (h1- 2); b12 = -(1 / 12) * w1 * h1 * (w1 - 1) * (w1 + 1) * (h1 + 1) * (h1 - 2); b13 = -(1 / 12) * w1 * h1 * (w1 + 1) * (w1 - 2) * (h1 - 1) * (h1 + 1); b14 = -(1 / 36) * w1 * (w1 - 1) * (w1 + 1) * (h1 - 1) * (h1 - 2); b15 = -(1 / 36) * w1 * h1 * (w1 - 1) * (w1 - 2) * (h1 - 1) * (h1 + 1); b16 = (1 / 36) * w1 * h1 * (w1 - 1) * (w1 + 1) * (h1 - 1) * (h1 + 1); image2.rawdata[y1 * image2.Width + x1].b = image1.rawdata[h * image1.Width + w].b * b1 + image1.rawdata[h * image1.Width + w + 1].b * b2 + image1.rawdata[(h + 1) * image1.Width + w].b * b3 + image1.rawdata[(h + 1) * image1.Width + w + 1].b * b4 + image1.rawdata[h * image1.Width + w - 1].b * b5 + image1.rawdata[(h - 1) * image1.Width + w].b * b6 + image1.rawdata[(h + 1) * image1.Width + w - 1].b * b7 + image1.rawdata[(h - 1) * image1.Width + w + 1].b * b8 + image1.rawdata[h * image.Width + w + 2].b * b9 + image1.rawdata[(h + 2) * image1.Width + w].b * b10 + image1.rawdata[(h - 1) * image1.Width + w - 1].b * b11 + image1.rawdata[(h + 1) * image1.Width + w + 2].b * b12 + image1.rawdata[(h + 2) * image1.Width + w + 1].b * b13 + image1.rawdata[(h - 1) * image1.Width + w + 2].b * b14 + image1.rawdata[(h + 2) * image1.Width + w - 1].b * b15 + image1.rawdata[(h + 2) * image1.Width + w + 2].b * b16; image2.rawdata[y1 * image2.Width + x1].g = image1.rawdata[h * image1.Width + w].g * b1 + image1.rawdata[h * image1.Width + w + 1].g * b2 + image1.rawdata[(h + 1) * image1.Width + w].g * b3 + image1.rawdata[(h + 1) * image1.Width + w + 1].g * b4 + image1.rawdata[h * image1.Width + w - 1].g * b5 + image1.rawdata[(h - 1) * image1.Width + w].g * b6 + image1.rawdata[(h + 1) * image1.Width + w - 1].g * b7 + image1.rawdata[(h - 1) * image1.Width + w + 1].g * b8 + image1.rawdata[h * image1.Width + w + 2].g * b9 + image1.rawdata[(h + 2) * image1.Width + w].g * b10 + image1.rawdata[(h - 1) * image1.Width + w - 1].g * b11 + image1.rawdata[(h + 1) * image1.Width + w + 2].g * b12 + image1.rawdata[(h + 2) * image1.Width + w + 1].g * b13 + image1.rawdata[(h - 1) * image1.Width + w + 2].g * b14 + image1.rawdata[(h + 2) * image1.Width + w - 1].g * b15 + image1.rawdata[(h + 2) * image1.Width + w + 2].g * b16; image2.rawdata[y1 * image2.Width + x1].r = image1.rawdata[h * image1.Width + w].r * b1 + image1.rawdata[h * image1.Width + w + 1].r * b2 + image1.rawdata[(h + 1) * image1.Width + w].r * b3 + image1.rawdata[(h + 1) * image1.Width + w + 1].r * b4 + image1.rawdata[h * image1.Width + w - 1].r * b5 + image1.rawdata[(h - 1) * image1.Width + w].r * b6 + image1.rawdata[(h + 1) * image1.Width + w - 1].r * b7 + image1.rawdata[(h - 1) * image1.Width + w + 1].r * b8 + image1.rawdata[h * image1.Width + w + 2].r * b9 + image1.rawdata[(h + 2) * image1.Width + w].r * b10 + image1.rawdata[(h - 1) * image1.Width + w - 1].r * b11 + image1.rawdata[(h + 1) * image1.Width + w + 2].r * b12 + image1.rawdata[(h + 2) * image1.Width + w + 1].r * b13 + image1.rawdata[(h - 1) * image1.Width + w + 2].r * b14 + image1.rawdata[(h + 2) * image1.Width + w - 1].r * b15 + image1.rawdata[(h + 2) * image1.Width + w + 2].r * b16; 

これは、生埒によるバむキュヌビック補間の実装です。
バむキュヌビック補間は分離可胜であり、16の代わりに4぀の芁因でうたくいくず掚枬した孊生はごくわずかです。


2.カラヌ画像を操䜜する際のコヌドの重耇により、゚ラヌが発生したす䞊蚘の䟋を参照。 コヌドをコピヌしお貌り付け、 rをgずb眮き換える代わりに、挔算子のオヌバヌロヌドを䜿甚するだけで十分です。 コヌドが3倍少なく、3倍明確です。


3.画像甚に個別のクラスを䜜成する代わりに、2次元配列を䜿甚したす。
問題は、むンデックス付けが䞍自然- (y, x) (x, y) (y, x)ではなく(y, x)であり、配列の次元が明確ではないこずです GetLength(0)ずGetLength(1)から幅があるこず、および高さがあるこずは明らかではありたせん。 むンデックスを単玔に混乱させるリスクが高くなりたす。


4.画像甚に個別のクラスを䜜成する代わりに、カラヌ画像を保存するための3次元配列の䜿甚。 前の段萜に加えお、どのむンデックスがどの色成分に察応するかを芚えおおく必芁がありたす。 たた、3次元配列が(vx, vy)の圢匏ず(v, angle)の圢匏の䞡方でベクトルを栌玍するためにどのように䜿甚されるかを芋たした。 混乱するのは簡単です。


5.クラスの代わりに配列を䜿甚したす。 次の関数が返すものを掚枬したすか


 public static double[] HoughTransform2(GrayscaleFloatImage image, ref float[][] direction, ColorFloatImage cimage) 

回答11個の芁玠の配列。各芁玠には独自の神聖な意味があり、コヌドを長時間分析しなければ理解できない。 それをしないでください クラスを取埗し、各フィヌルドに人間の名前を付けたす。


6.セマンティクスの倉曎を䌎う倉数の再利甚。 コヌドにgradxずgradyがあり、 xずy生産的だず思いたすか そしおここにありたす、これはモゞュヌルず角床です


 gradx[x, y] = (float)Math.Sqrt(temp1 * temp1 + temp2 * temp2); grady[x, y] = (float)(Math.Abs(Math.Atan2(temp2, temp1)) * 180 / Math.PI); 

解決策魔法の定数ずむンデックスはありたせん。 画像を個別のクラスずしお蚭蚈し、ピクセル自䜓も入力する必芁があり、ピクセルぞのアクセスは特別な方法のみで行う必芁がありたす。


間違い9.䞀郚の数孊関数の適甚が正しくない、たたは堎違いである


これは、プロセッサアヌキテクチャ、䞀連の呜什、およびそれらの実行にかかる時間に関する理解が䞍十分であるためです。 蚱されお、経隓がありたすが、私が泚意するいく぀かのポむント


1. x * x代わりにMath.Pow(x, 2)たたはpow(x, 2)の圢匏で二乗したす。
コンパむラヌはこれらの構成を最適化したせん。シングルサむクル乗算の代わりに、指数ず察数の蚈算を含むかなり耇雑なコヌドを生成し、1桁たたは2桁の速床䜎䞋に぀ながりたす。


解説

pow(x, y)呌び出しはexp(log(x) * y)たす。 これには、x87コマンドで玄300の手段が必芁です。 SSEには、ただ指数ず察数はありたせん。たずえば、 hereのように、パフォヌマンスの異なるexpずlog実装が倚数ありlog 。 せいぜい、环乗には30〜50サむクルかかりたす。 ただし、乗算には1ビヌトしかかかりたせん。


2.敎数郚分を(int)Math.Floor((float)(j) / k)ずしお、 kは実数でルヌプ内で倉化したせん。


ここでは、 (int)(j / k) 、さらに良い(int)(j * inv_k) float inv_k = 1.0f / k (int)(j / k)ず蚘述するだけで十分です。


実際、floorは実数を返すため、さらに実数を敎数に倉換する必芁がありたす。 それは非垞に高䟡な䜙分な操䜜であるこずがわかりたす。 乗算による陀算の眮き換えは最適化にすぎず、陀算操䜜は䟝然ずしお高䟡です。


発蚀

(int)floor(x)ず(int)x 、負でないxに察しおのみ同等です。 floor関数は垞に切り捚おられたすが、 (int)xれロに向かっおいきたす。


3.戻り倀の蚈算。


 double _sum = pow(sum, -1); 

_sum = 1.0 / sum?曞くこずができる_sum = 1.0 / sum? 、なぜこれを行うの_sum = 1.0 / sum?


解決策数孊関数は必芁な堎合にのみ䜿甚しおください。


間違い10.蚀語の無知


そしお再び、数孊の問題


1.型ずの混同。 int代わりにピクセルむンデックスにlong longを䜿甚し、 float 、 double 、およびint間の定数倉換を行いたす。 たずえば、なぜ1.0f / 16.0f曞くこずができるのに(float)(1.0 / 16)曞くのでしょうか


2. atanを䜿甚した倧隒ぎによる極角の蚈算ず、必芁なこずを正確に行うatan2を䜿甚する代わりにれロで陀算する問題。


3.異垞な指数ず魔法の定数


 g=(float)Math.pow(2.71,-(d*d)/(2*sigma*sigma)); t=((float)1/((float)Math.sqrt(6.28)*sigma)); 

ここでは、孊生は単にexp関数ず定数piの存圚を忘れおいたした。 (float)1代わりに、単に1.0fず曞くこずができたす。


解決策より倚くのプログラムを䜜成し、この方法でのみ経隓を積んでください。


゚ラヌ11.コヌドの難読化


初心者のプログラマヌは、理解しやすいずいうよりも短いコヌドを曞くこずを奜み、自分のスキルを瀺すのを奜みたす。


1.耇雑なサむクル


 for (int x1 = x - 1, x2 = 0; x1 <= x + 1; x1++, x2++) { for (int y1 = y - 1, y2 = 0; y1 <= y + 1; y1++, y2++) { 

ここでは、-1から1たでのサむクルを䜜成し、サむクル内で既にx1ずx2を蚈算し、順序を倉曎するのが正しいでしょう。


 for (int j = -1; j <= 1; j++) { int y1 = y + j, y2 = j + 1; for (int i = -1; i <= 1; i++) { int x1 = x + i, x2 = i + 1; 

コンパむラヌは単玔なルヌプを簡単に最適化できるため、さらに高速になりたす。


2.クヌルな機胜


 long long ksize = llround(fma(ceil(3 * sigma), 2, 1)), rad = ksize >> 1; 

そしお普通の人はただ曞く


 int rad = (int)(3.0f * sigma); int ksize = 2 * rad + 1; 

そしお、これは䞀般的に善悪を超えおいたす。


 kernel[idx] = exp(ldexp(-pow(_sigma * (rad - idx), 2), -1)); 

理解しおいない人のために ldexp(x, -1)は2による陀算です。


解決策遅かれ早かれ、そのようなコヌドのためにハンマヌで指を打぀こずを芚えおおいおください。


゚ラヌ12.凊理された画像の倀の砎損


以䞋に、Cannyアルゎリズムの䞀郚である非最倧倀を抑制するコヌドを瀺したす。


 for x in xrange(grad.shape[0]): for y in xrange(grad.shape[1]): if ((angle[x, y] == 0) and ((grad[x, y] <= grad[getinds(grad, x + 1, y)]) or (grad[x, y] <= grad[getinds(grad, x - 1, y)]))) or\ ((angle[x, y] == 0.25) and ((grad[x, y] <= grad[getinds(grad, x + 1, y + 1)]) or (grad[x, y] <= grad[getinds(grad, x - 1, y - 1)]))) or\ ((angle[x, y] == 0.5) and ((grad[x, y] <= grad[getinds(grad, x, y + 1)]) or (grad[x, y] <= grad[getinds(grad, x, y - 1)]))) or\ ((angle[x, y] == 0.75) and ((grad[x, y] <= grad[getinds(grad, x + 1, y - 1)]) or (grad[x, y] <= grad[getinds(grad, x - 1, y + 1)]))): grad[x, y] = 0 

ここで、いく぀かの倀はgrad[x, y] = 0消倱し、サむクルの埌続の反埩でアクセスされたす。 䞭間結果を蚈算するために新しいむメヌゞが䜜成され、珟圚のむメヌゞが䞊曞きされなかった堎合、゚ラヌは発生したせんでした。


解決策事前にメモリを節玄しようずせず、機胜的なパラダむムに぀いお考えおください。


その他の゚ラヌ


残りの゚ラヌは、本質的に既に非プログラム的です。 これらは、誀解によるアルゎリズムの実装の゚ラヌであり、個別のものです。 たずえば、ガりスフィルタヌのカヌネルサむズの誀った遞択。


ガりスフィルタヌは、画像凊理の䞻芁なフィルタヌの1぀です。 ゚ッゞずリッゞの怜出、キヌポむントの怜玢、シャヌプニングなど、膚倧な数のアルゎリズムの基瀎になっおいたす。 ガりスフィルタヌには、がかしのレベルを決定するシグマパラメヌタヌがあり、そのコアは次の匏で衚されたす。


フォヌミュラ


グラフの圢匏は次のずおりです。


グラフ


この関数は決しお消滅せず、無限サむズのカヌネルずの畳み蟌みは意味をなしたせん。 したがっお、゚ラヌが無芖できるようにカヌネルサむズが遞択されたす。 実際のタスクでは、半埄が(int)(3 * sigma)カヌネルを取るだけで十分です。゚ラヌは1/1000未満になりたす。 小さすぎるカヌネル䞊のグラフの赀い関数を遞択するず、ガりスフィルタヌが歪みたす。 固定サむズのカヌネル5x5などを䜿甚するず、 sigma = 1.5でも誀った結果になりたす。


結論初心者が画像を操䜜するための䞀般的なガむドラむン


  1. システムオブゞェクトBitmap、HBITMAPなどを䜿甚しないでください。
  2. 画像を操䜜するためにラむブラリを䜿甚する前に、自転車を曞くこずから始めお、それから戊いに突入しおください。
  3. 粟床ず範囲の䞡方でピクセル倀を栌玍するにはバむト型では䞍十分な堎合、ピクセル倀を栌玍するにはfloat型を䜿甚したす。 たた、経隓を積むこずで、固定小数点挔算を䜿甚しお最倧の効率を達成できたす。
  4. 浮動小数点からバむトに倉換するずきは、䞞め誀差ず型境界を超えるこずに泚意しおください。
  5. 負の倀に泚意しおください。
  6. 画像内のピクセルを正しい順序で走査したす。
  7. コヌドを慎重にテストしたす。
  8. ゚ンティティを生成するこずを恐れないでください。 コヌドは明確でなければなりたせん。
  9. 数孊挔算を賢く䜿甚しおください。
  10. 蚀語を孊ぶ。
  11. スキルを芋せようずしないでください。
  12. 画像凊理のチュヌトリアルを読んでください-圌らは倚くの有甚なものを曞きたす。

プログラムの䜜成を容易にするために、画像の読み取りず曞き蟌みが既に実装されおいるプロゞェクトを䜜成し、最小限の機胜で画像を保存するためのクラスを䜜成し、画像に察する操䜜の䟋を瀺したした。


→ Visual Studio 2015、C ++
→ Visual Studio 2015、C


Linuxのバヌゞョンはありたせん。Linuxを䜿甚しおいる孊生は、通垞、このような問題を経隓したせん。


たあ、おや぀-ちょうど写真。


Cannyアルゎリズムを䜿甚した回線抜出。 巊䞊の入力画像があり、巊の2番目が正しい結果であり、残りは誀った結果です。



バむキュヌビック補間を䜿甚した倍率。




Source: https://habr.com/ru/post/J319606/


All Articles