初めまして、RYOと言います。
本当ならCでコーディングするべきなんでしょうが仕様上どうしても
以下の環境で、コーディングのみで作成する必要があります。^^;
環境
Windows2000/XP
VisualBasic 6.0(SP5)
・フリー、製品版問わずライブラリ(dllなど)は使用不可。
作成したいのはカラー人物画像をピクセル単位で類似比較する
プログラムです。
おおまかな処理の流れですが・・
JPGファイルの読み込みはOleLoadPictureFromFileを利用しています。
読み込んだバイナリは2つのPictureBoxを使い、StretchBitBltで
64×64ピクセルに変換後GetDiBitsでRGB配列にして、動作中常にメモリ上に
保持しています。しかし非常にメモリを喰います(2GB以上・・)。
類似画像の比較はRGB値ピクセル単位で比較してパーセントによる
類似度を算出しています。
現在50000枚程度(1枚は50KB程度のファイル)の比較で
1枚あたり比較時間が20秒かかっていますが、実行速度を上げる方法は
何かありませんでしょうか・・長々書いてしまいましたが
よろしくお願いします。
比較する部分に一番時間がかかっているなら、改善すべきところは
その部分じゃないでしょうか。
そこだけCでDLLとして作るとか
まあ、質問が抽象的なので回答も抽象的になってすみませんが。
123さんアドバイスを有り難うございます。
比較箇所ですか・・・
以下のようなプログラムなんですが・・
DLLは使用できないのでVBとAPIのみで実装する必要があるんです^^;
Public Sub FILE_COMPARE_ALL(ByVal lCr As Long, ByVal lCt As Long)
'(lCr:CompareRate lCt:ColorTolerance)
Dim sCf(1) As String
Dim lRp As Long
Dim lDp As Long
Dim lTp As Long
Dim lWt As Long
Dim lA As Long
Dim lB As Long
Dim AC As Long
Dim DC As Long
Dim ltFFD As Long
Dim PA() As RGBQUAD
Dim PB() As RGBQUAD
Dim RedA As Byte
Dim BlueA As Byte
Dim GreenA As Byte
Dim RedB As Byte
Dim BlueB As Byte
Dim GreenB As Byte
Dim bF As Byte
DC = -1
lCr = lCr - 1
lTp = ((UBound(typFFD(0).bytImageData) + 1))
lCt = (100 - lCt)
ltFFD = UBound(typFFD)
For lA = 0 To ltFFD
lWt = GetTickCount
If typFFD(lA).bytFileReadErrorFlag = 0 Then
sCf(0) = typFFD(lA).strFileFullPath
PA = typFFD(lA).bytImageData
For lB = lA To ltFFD
If typFFD(lB).bytFileReadErrorFlag = 0 Then
sCf(1) = typFFD(lB).strFileFullPath
If sCf(0) <> sCf(1) Then
bF = 0
lDp = 0
PB = typFFD(lB).bytImageData
For AC = 0 To lTp - 1
BlueA = PA(AC).rgbBlue
GreenA = PA(AC).rgbGreen
RedA = PA(AC).rgbRed
BlueB = PB(AC).rgbBlue
GreenB = PB(AC).rgbGreen
RedB = PB(AC).rgbRed
If RedA > RedB + lCt Then
lDp = lDp + 1
If Int(((lTp - lDp) / lTp) * 100) < lCr Then
bF = 1
Exit For 'AC
End If
ElseIf BlueA > BlueB + lCt Then
lDp = lDp + 1
If Int(((lTp - lDp) / lTp) * 100) < lCr Then
bF = 1
Exit For 'AC
End If
ElseIf GreenA > GreenB + lCt Then
lDp = lDp + 1
If Int(((lTp - lDp) / lTp) * 100) < lCr Then
bF = 1
Exit For 'AC
End If
ElseIf RedA < RedB - lCt Then
lDp = lDp + 1
If Int(((lTp - lDp) / lTp) * 100) < lCr Then
bF = 1
Exit For 'AC
End If
ElseIf BlueA < BlueB - lCt Then
lDp = lDp + 1
If Int(((lTp - lDp) / lTp) * 100) < lCr Then
bF = 1
Exit For 'AC
End If
ElseIf GreenA < GreenB - lCt Then
lDp = lDp + 1
If Int(((lTp - lDp) / lTp) * 100) < lCr Then
bF = 1
Exit For 'AC
End If
End If
Next AC
lRp = 100 - Int((lDp / lTp) * 100)
If bF = 0 Then
If lRp > lCr Then
DC = DC + 1
ReDim Preserve typCFD(DC)
With typCFD(DC)
.lngDupResultPercent = lRp
.lngFFDIndexA = lA
.lngFFDIndexB = lB
.lngTotalDifferentPixels = lDp
.lngTotalPixels = lTp
.lngTotalSimilarPixels = lTp - lDp
End With
End If
End If
End If
End If
Next lB
End If
Next lA
End Sub
長いソースですみません^^;;
DLL化がいけないのにどんな理由があるか分かりませんが
VBでやるには限界がありますね。
以前、512×512ピクセルのRGB画像2枚のサブストラクション
(2枚の画像のRGBの引き算結果を画面に描画)
を行うのにDLLを作ってやってみたのですが、Pentium4 2.7GHzのPCで
ほとんど瞬時に近い速度で計算できましたよ。
昔、全てVBで実装したDIBのライブラリを作ってみました。
それで200*100サイズの2枚の画像をアルファブレンドするのに、
P42.53GHz メモリ512MBのPCで
1000回平均で0.018734375秒しかかかりませんでした。
処理がかなりゴチャゴチャしているように見えますが、
テーブルを用意するなど、関数やメンバ参照をなるたけ使わない処理には出来ませんか?
※汚くて何をしているのかサッパリな上に、
知らない定義の名前が出てきているので詳しく見ていませんので、
誤爆しているかも…
アドバイスを回答頂いた皆様すみませんでした^^;
わかりずらい説明ですみません・・・
実際にプログラムを動作させるマシンはPentiumIII、500MHz(メモリは512MB)
なんですが、行いたい事は、JPGファイルを1PixelずつRGB値で比較して
類似画像(顔写真)の検出です。
確かに2枚の画像比較(1対1)ならほぼ一瞬(平均0.02秒)で終わっているのですが・・・
APIでメモリに読み込んだRGBのバイト型データをある程度色の誤差も考慮して比較しているためか、1000枚以上の全画像比較になると使い物にならない遅さになってしまいます。
上に貼ったソースの中で実際のPixel辺りのRGB値比較は以下のような感じです。
実際はループ内の処理です。
RedA、RedBにはRGB値がバイト型で格納されています。
lCtは類似色を許容するための誤差です(0〜100で0なら許容なし)
lCrは不一致Pixelを全体の何%まで許容するかの値です。
lTpは画像トータル(画像Pixel数×3(RGB))です
If RedA > RedB + lCt Then
lDp = lDp + 1’一致しないPixel数のカウント
If Int(((lTp - lDp) / lTp) * 100) < lCr Then
'一定以上不一致Pixelがあればループを抜ける
Exit For 'AC(ループを抜ける)
End If
上のような比較をRGBの3色ごとに行っているのですが
これをもっと早く処理できるような書き方があるとありがたいです・・
わからない点などおっしゃって頂ければがんばって説明しますので
どなたかアドバイスの方をよろしくお願いします。
> If Int(((lTp - lDp) / lTp) * 100) < lCr Then
>
これをもっと簡単にするのはどうですか。
要はlTp, lCr共にループ突入前に値が固定されてるわけです。
変化するのはlDpだけなので
if lDp > lDpMax Then
と比較できれば計算しない分だけ早くなるのではと思うけど実際どのくらい効果あるのか。
lDpMaxはループ突入前に
lDpMax = Int(lTp/(100/(100-lCr)))
で求めればよいかな。(小数点分はどうするか分からないので要確認)
> DC = DC + 1
> ReDim Preserve typCFD(DC)
>
あとは↑を一気に取得するようにしてはいかがですか。
例えば1000単位に「がばっ!」と取得するとか。関数抜ける前に最終調整すれば問題ないし。
'ループはいる前に
DCMax = 1000
ReDim Preserve typCFD(DCMax)
:
If lRp > lCr Then
DC = DC + 1
If DC > DCMax Then
DCMax = DCMax + 1000
ReDim Preserve typCFD(DCMax)
End If
With typCFD(DC)
:
End If
:
'関数抜ける前に最終再調整
If DC = -1 Then
Erase typCFD
Else
ReDim Preserve typCFD(DC)
End If
#メモリの確保回数によっては効果ないかも。
GOD様貴重なアドバイスをありがとうございます。
早速試して見た所、200枚の画像でテストした場合5秒ほど処理時間を短縮できました!
ただ配列を一度に1000とか確保すると、参照に時間がかかるのか
あまり効果はありませんでした^^;
とりあえず25ずつ確保するようにしてあります。
他に高速化できそうな箇所を今探していますがなかなか難しいですね^^;;
他にも高速化できそうなアドバイスがありましたら
お手数ですが引き続きよろしくお願いします。
配列を一気に取得しておくのはあまり効果なかったですか。(TT
そうすると配列に入ってくるデータというのは少ないんですかね。
他に小手先の技としては
(1)
>For lA = 0 To ltFFD
> For lB = lA To ltFFD
↑は
>For lA = 0 To ltFFD - 1
> For lB = lA + 1 To ltFFD
に変更しても良いのでは?
(2)
> lRp = 100 - Int((lDp / lTp) * 100)
↑も変化分はlDpだけなので予め計算で求められませんか?
(3)
>bF
↑のフラグはいらないように思えるのですがいかがでしょう。
if lDp > lDpMax Then
または
If AC < lTp Then '途中でFor文を抜けているか?
これは私の見えない所でフラグがたつようにできているのかな?
# (2), (3)は既にやってそう。
# 計算式(処理)は極力ループ外に追い出した方がいいです。
ちょっと間違っていたので修正。
bFの必要性は感じないけど^^
(3)
> If bF = 0 Then
if lDp <= lDpMax Then 'Exit Forの条件を満たしていない
または
If AC >= lTp Then '途中でFor文を抜けていないか?
↑のどちらかの式でいいのでは?
全然方向性違いますが
最適化の詳細設定を全部チェックしたらはやくなりますかね?
| ツイート |
|