動機はこれでした。人々に関する情報を保存するプログラムには写真がありますが、写真は難しい方法でそこに到達します。 紙のオリジナル写真(人々がドキュメントと一緒に持ってきます)を撮り、それをファイルにスキャンし、簡単なエディターで開き、3x4 cmで切り取り、保存し、プログラムでその人のプロフィールを開きます。
現在、プログラムには数千人が参加しており、タスクはすべての写真を美しく撮影することです。なぜなら、彼らは単にひどい品質の写真を持ち、顔の塗りつぶしの割合が異なるからです。 事は小さいです:画面を吊るし、ライト、椅子、三脚、カメラ、USBをセットし...そして私たちのプログラム。
この機能を実装するための大きな計画がありました。
- 男が来て髪をとかし、カメラの前に座っている
- オペレーターはカメラを向け、人の髪の毛、衣服をまっすぐにします...
- プログラムに座って、「すべてを自動化する」ボタンを押します
- それだけです。
それまでの間、プログラムは次のことを行う必要があります。
- WIA経由でカメラに接続する
- カメラに写真を撮らせる
- カメラから新鮮なショットを取得します
- 回転データに従って回転します
- 刻まれている長方形を特定することで写真の顔を見つける
- 顔の長方形が300dpiの解像度で3x4cmフレームの80%を占めるようにスケーリングの割合を計算します
- 写真を拡大縮小して、白い背景に重ねます
- 写真を個人プロファイルに保存する
画像内の顔認識については多くのことが書かれていますが、私はまだこの段階に達していないので、次回はそれらについて話します。 最初の3つには十分な問題があります。 それらについて説明します。
Wia
Windows Image Acquisition(WIA)は、画像処理デバイス(カメラ、スキャナー、ビデオカメラなど)のほとんどのモデルを簡単に操作する方法です。 操作を成功させるには、少なくともデバイスがWindowsコントロールパネルの「スキャナーとカメラ」に表示されている必要があります。 これを実現するには、適切なドライバーが必要です。 一部のデバイスはそのまま使用できますが、 Canon EOSカメラの場合のように、他のデバイスはWIAドライバーを手動でインストールする必要があります。
WIAの問題に取り組むのに本当に役立つ最も役立つMSDNの記事は、サンプル記事です。 私は偶然一つにつまずいたに違いありません。
簡単なコード
理解を容易にするために、ここでVB.NET 2008およびC#2008のコードを提供し、それらを棚で分析します。 例を単純化するために、すべての主観的な方法とエラーチェックが省略されていることにすぐに注意を向けます。 誰もがアプリケーションのロジックを熟考する必要があります。 誰もがさまざまなタスクを持っているため、特に数行しか必要ない場合は、誰かのコードを理解するのは非常に困難です。 同僚は、好みの言語でコードを公開し、テキストの途中でアクセスできます。 手順はまったく同じです。
フォーム1のソースコードは、フォーム全体に1つのPictureBox1要素が引き伸ばされています。 ここで最も「難しい」ことは、プロジェクトパラメータにWIA COMオブジェクトへのリンクを追加することです。 開始する前に、デバイスの電源がオンになっていることを確認してください。
VB.NETコード
Imports WIA Public Class Form1 Dim Device1 As WIA.Device Dim CommonDialog1 As New WIA.CommonDialogClass Dim DeviceManager1 As New WIA.DeviceManager Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' 1. Device1 = CommonDialog1.ShowSelectDevice(WiaDeviceType.CameraDeviceType) ' 2. Device1.ExecuteCommand(WIA.CommandID.wiaCommandTakePicture) ' 4. Dim Device1a As WIA.Device = DeviceManager1.DeviceInfos(Device1.DeviceID).Connect ' 3. Dim newItem As WIA.Item = Device1a.Items(Device1a.Items.Count) ' 5. Dim newImage As WIA.ImageFile = CommonDialog1.ShowTransfer(newItem, WIA.FormatID.wiaFormatJPEG) ' 6. Dim newVector As WIA.Vector = newImage.FileData ' 7. , Dim bytBLOBData() As Byte = newVector.BinaryData ' 8. Dim stmBLOBData As New IO.MemoryStream(bytBLOBData) ' 9. PictureBox PictureBox1.Image = Image.FromStream(stmBLOBData) ' 10. Zoom ( ) PictureBox1.SizeMode = PictureBoxSizeMode.Zoom End Sub End Class
C#2008コード
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using WIA; using System.IO; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { WIA.DeviceManager DeviceManager1 = new DeviceManagerClass(); WIA.CommonDialogClass CommonDialog1 = new CommonDialogClass(); // 1. WIA.Device Device1 = CommonDialog1.ShowSelectDevice(WiaDeviceType.CameraDeviceType, true, false); // 2. Device1.ExecuteCommand(WIA.CommandID.wiaCommandTakePicture); // 3. WIA.Device Device1a = null; foreach (DeviceInfo dev_item in DeviceManager1.DeviceInfos) { // if (dev_item.DeviceID == Device1.DeviceID) { // , DeviceID Device1a = dev_item.Connect(); break; } } // 4. WIA.Item newItem = Device1a.Items[Device1a.Items.Count]; // 5. WIA.ImageFile newImage = (ImageFile)CommonDialog1.ShowTransfer(newItem, WIA.FormatID.wiaFormatJPEG, false); // 6. WIA.Vector newVector = newImage.FileData; // 7. , Byte[] bytBLOBData = (Byte[])newVector.get_BinaryData(); // 8. MemoryStream stmBLOBData = new MemoryStream(bytBLOBData); // 9. PictureBox pictureBox1.Image = Image.FromStream(stmBLOBData); // 10. Zoom ( ) pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; } } }
7番目のステップの後、画像に対して何でも自由に実行できます。ファイルへの保存、データベースへの保存、フォームへの表示、変換、フィルターの適用など。 主なものはあなたがそれを持っているということです。
私は、すべてが機能することを確認するために、フォームに表示しました。
すべてがシンプルに見える
ご覧のように、コードは短くて便利なアクションがいっぱいです。 しかし、熊手と落とし穴は、マイクロソフトの反対側にいるプログラマーの永遠の仲間です。 彼らはすべてが単純かつ明らかに行われたと考えましたが、私たちはいつものように理解していませんでした。
ステップ1
' 1. Device1 = CommonDialog1.ShowSelectDevice(WiaDeviceType.CameraDeviceType)
デバイスを選択する必要があります。 2つの方法があります:自分で選択するか、ユーザーに選択させます。 両方の方法の目的は同じです-デバイスIDを取得する-DeviceID。 次に、このDeviceIDをどこでも目的のデバイスへのリンクとして使用して、WIAを操作できます。
この例では、デバイスを選択するための標準のWIAウィンドウが開きます。 いくつかあるかもしれません。
ユーザーが使用するものを決定する必要がない場合は、WiaDeviceTypeパラメーターを使用して、使用可能なデバイスをフィルター処理できます。 例では、カメラ-CameraDeviceTypeを探しています。 ただし、カードリーダーの中にはカメラのふりをする人もいます。
ここでは、これを行うことができます。すべてのデバイスを循環させ、wiaCommandTakePicture関数をサポートしているかどうかを確認します(写真を撮る)。 したがって、1つも見つからなかったが、適切なものが少なくとも1つある場合、ユーザーは外に出ません。何を撮影するかを決定する必要があります。 これを行う方法は、MSDNの記事の例(上記を参照)で詳しく説明されています。
Stone№raz
wiaCommandTakePictureをサポートするデバイスはないことが判明する場合があります。 これは、特にCanonを使用していて、リモートシューティング(USB経由のリモートシューティング)用のプログラムを含む、すべてのプログラムをディスクから持っている場合、混乱を招きます。 その中で、彼は写真を撮るが、WIAを通してはしたくない。 ここでは、Canonに手を出し、少数のSDKを頼んでモジュールを作成するか、ユーザーに手動で写真を撮らせてから、すべてを自動的に行うことができます。 しかし、全体として、このゲームには価値があります。 キヤノンには多くのユニットがあり、それぞれの地域でSDKを配布していることに注意してください。 ロシアの場合、すべてが悲しいですが、ヨーロッパのオフィスに連絡してみてください。 彼らは私にメールで答えたようなものです。
ステップ2
' 2. Device1.ExecuteCommand(WIA.CommandID.wiaCommandTakePicture)
さて、私たちは幸運で写真を撮ることができるとしましょう。 えっと...ええと...どんなaf * ck?
設定なし。 写真しか撮れません。 カメラ(読み取り-レンズ)が終了し、フルオートマチックモード(またはスイッチに設定されているもの)でクリックして元に戻ります。 まあ、ありがとう。
正直なところ、私はカメラをリモートでプリセットする問題について掘り下げることはしなかったが、人々による試みはグーグルで気づかれた。 たぶん誰かが解決策を見つけることができます。
カメラはフレームを取得し、メモリまたはカードに保存します。 今、私たちはそこからそれを取り出す必要があります!
ステップ3
' 3. Dim Device1a As WIA.Device = DeviceManager1.DeviceInfos(Device1.DeviceID).Connect
もう一度、別のモードでカメラに接続します。 C#では、このステップはもう少し長くなります。 ここでは、ファイルのコンテナとして接続します。 警戒してください! ファイル構造は接続時に記憶されます! これについては後で...
ステップ4
' 4. Dim newItem As WIA.Item = Device1.Items(Device1.Items.Count)
記事を書いている間、彼はStone No. 2の問題の原因を理解したようです。 ありがとう、Harb!
このステップでは、カメラに保存されている最後のファイルのIDを覚えています。 Device1.Itemsオブジェクトには、カメラのメモリに保存されているすべてのファイルとフォルダーに関する情報が保存されます。 はい、はい、フォルダも-注意してください。 このオブジェクトを使用して、ファイル名などの情報を取得できます。 これはすべてMSDNの例に含まれています(上記を参照)。 ここでは、単純なオブジェクトカウンターが役立ちます。 私たちの新しい写真が最後に撮られたのは論理的ですが、常に真実ではありません。 そのため、ここでのエラーチェックも挿入する必要があります。
ステップ5
' 5. Dim newImage As WIA.ImageFile = CommonDialog1.ShowTransfer(newItem, WIA.FormatID.wiaFormatJPEG)
そして、ポインタを記憶するファイルを取得します。
ここでは、写真のアップロードプロセスが、プログレスバーを備えた標準のWIAインターフェイスで表示されます。
関数のパラメーターの1つは、転送されたファイルの形式です。 wiaFormatJPEGが必要です。 これは、カメラが突然TIFFをくれないようにするためです。 (冗談)
ストーンNo. 2
むしろ、ここでのレーキは、最後に撮影した写真を発行する機能が接続時のカメラファイルの構造を記憶しているという事実にあります。 それが新しい接続が使用される理由です-写真を撮った後にファイル構造を更新する必要があります。
ステップ6
' 6. Dim newVector As WIA.Vector = newImage.FileData
写真を取得する唯一の文書化された方法は、ベクターインターフェイスを介して取得することです。 結果として、このオブジェクトには多くのものがありますが、関心があるのは画像を含むBinaryDataバイト配列だけです。
ステップ7、8、9
' 7. , Dim bytBLOBData() As Byte = newVector.BinaryData ' 8. Dim stmBLOBData As New IO.MemoryStream(bytBLOBData) ' 9. PictureBox PictureBox1.Image = Image.FromStream(stmBLOBData)
このBinaryDataが本格的な画像になるには、いくつかの変換が必要です。 ストリーミングするバイト、ストリームを画像に。 そして、受け取ったオブジェクトに対して(リスト内のコメントに従って)何でもできます。
ちなみに、指定された方法では、画像はDBMSに保存するために変換されます(変換のみが逆方向に行われます)。
ステップ10
PictureBox1.SizeMode = PictureBoxSizeMode.Zoom
それぞれに独自のパスがありますが、わかりやすくするために、PictureBox1要素のImageプロパティに画像を割り当てました。 ぬるぬるした自分を見ます。
エラー処理を提供するためだけに残っています。 WIAは非常に気まぐれであり、多くの状況があります(エラーの場合のデバッグ中に、コードは例外をスローしませんが、エラーが発生した場所から単に実行されないため、それらのほとんどはx64システムの開発中です)。 たとえば、カメラは長時間使用されるまで眠りに落ちることがあります。 多くのことを予見する必要があります。
多くの場合、この技術は写真を使用した「パスオフィス」のように動作するために使用されます。
議論の問題について:
- Canon SDKを持っている人はいますか? 非商用使用に必要なDLLを共有できますか?
- OpenCVとその.NETのラッパーを使用して、写真の顔のある長方形を文字通り10行で認識できます 。 私は近い将来何をしますか。
ご清聴ありがとうございました!
UPD: C#に例を追加しました。 手順に関する混乱を取り除きました。