2009年5月23日 星期六

VB - Color Identification

http://4rdp.blogspot.com/2009/05/vb-color-identification.html?m=0

顏色識別是這個程式另一個技術瓶頸,在網路上找到藍色小舖所提供的方法比較簡單,它提供全螢幕抓點,共有兩項技術要點:螢幕座標計算以及顏色判別。

首先,如何計算螢幕上特定位置座標?
網路上沒有適當的範例說明這個螢幕抓點方法,所以我想了很久想出這個座標計算法來解決。



螢幕座標的原點在左上角,橫向 X 軸由左而右,縱向 Y 軸由上而下,程式視窗座標也一樣,這裡提供的範例是擷取 WebCam 畫面正中央的點,座標的單位為像素 pixel,

X = X1 + X2 + X3
Y = Y1 + Y2 + Y3

(X1,Y1) 螢幕到程式視窗 = (Me.Location.X , Me.Location.Y)
(X2,Y2) 程式視窗到影像視窗 = (PictureBox1.Location.X , PictureBox1.Location.Y)
(X3,Y3) 影像視窗到影像中央 = (PictureBox1.Size.Width / 2 , PictureBox1.Size.Height / 2)

電腦螢幕顯示顏色原理,就是利用紅綠藍 (RGB) 三光混色,每一種原色數值範圍在 0 ~ 255 (8 bites)。現在就是設計出程式以偵測畫面上中央圖點顏色數值,再判別出魔術方塊六種顏色。依據我測試的結果,WebCam 距離魔術方塊約二十公分距離較佳,如果兩者距離太近影像有白化情形,也就是黃色與橘色會變成白色,因為較淺顏色會因反光較強而讓顏色難以識別。

我的程式判別顏色的方法,採用先確認主色,再比較其它副色差異
紅主色包含 紅、黃、橙三色
綠主色只有 綠色
藍主色含有 藍、白兩色
其它情形則 黃、白或是無法識別

採用這方法的原因,主要為降低受環境光源影響,不論外部光線或強或弱,大部分情形應該可以正確判別,聰明的讀者也可以再改良它或是自創其它方式判斷,下面程式是我用的方法:

        If colorR > colorB And colorR > colorG Then
            If colorB >= colorG Or colorG < 60 Then
                Label3.Text = "Red"
            ElseIf colorR - colorG < colorG - colorB Then
                Label3.Text = "Yellow"
            Else
                Label3.Text = "Orange"
            End If
        ElseIf colorG > colorR And colorG > colorB Then
            Label3.Text = "Green"
        ElseIf colorB > colorR And colorB > colorG Then
            If colorR > 70 Then
                If colorB > colorR + 50 Then
                    Label3.Text = "Blue"
                Else
                    Label3.Text = "White"
                End If
            Else
                Label3.Text = "Blue"
            End If
        Else
            If colorR = 255 And colorG = 255 Then
                Label3.Text = "Yellow"
            ElseIf colorG = 255 And colorB = 255 Then
                Label3.Text = "White"
            Else
                Label3.Text = "Unknow"
            End If
        End If
 



另外這 VB 程式有加入 Timer,每 0.1 秒會產生中斷,可配合影像擷取自動判斷顏色,研發養成所線上論壇放有範例程式,有興趣的人請加入群組會員下載。(原連結已遺失)

13 則留言:

  1. 座標轉換方面,可用
    PictureBox1.PointToScreen(X3,X3)
    將PixtureBox1上的座標轉換到螢幕座標系(X,Y)。

    PictureBox1.PointToClient(X,X)
    則有相反地效果。

    回覆刪除
  2. 讀取RGB值,可用下列方式
    Dim colorR As Integer
    Dim colorG As Integer
    Dim colorB As Integer

    Dim bmp As Bitmap = New Bitmap(PictureBox1.Width, PictureBox1.Height)
    PictureBox1.DrawToBitmap(bmp, PictureBox1.DisplayRectangle)
    Dim col As Color = bmp.GetPixel(bmp.Width / 2, bmp.Height / 2)
    bmp.Dispose()

    Label2.Text = col.ToString()
    colorR = col.R
    colorG = col.G
    colorB = col.B

    其中bmp 建議宣告為Form1的成員。rgb值可直接從color物件中讀取,不需自行解析。

    希望對你有幫助。^^

    回覆刪除
  3. Zeal,

    我已經很久沒寫 VB,以前較常寫 VB6,
    對 VB.net 沒有那麼熟,
    非常感謝您留言提供好方法,
    另外對本部格有任何建言,歡迎告訴我。

    Bridan ^_^

    回覆刪除
  4. Zeal,

    這些天很忙,直到今天有空測試您的方法,結果不行。

    希望您看到留言後,再測看看你的方法二,在我的電腦上 RGB 三色都是 128 不會變化。

    如果可以的話,請上傳範例程式參考,謝謝。

    回覆刪除
  5. hello,
    抱歉給了一個不能用的方式。
    因為我手邊沒有webcam,所以我是用直接設定picturebox.ImageLocation的方式進行測試,測試時事ok的。
    我後來又測試使用graphics繪圖的方式,結果就如你所測試的一樣,RGB都是128。所以,這個方法不能運用於這個程式中。
    另外提供一個方法,這是修改Quick_GetPixel(),將輸入參數改為控制件、X、Y,其中XY都是在控制件的座標系上,可讀取元件上的xy位置的色彩。

    Public Function Quick_GetPixel_FromControl(ByVal ctrl As System.Windows.Forms.Control, ByVal x As Int16, ByVal y As Int16) As Color

    Dim g As Graphics = ctrl.CreateGraphics()
    g.FillRectangle(Brushes.RosyBrown, ctrl.DisplayRectangle)
    Dim hdc As IntPtr = g.GetHdc()

    Dim kk As Int32 = GetPixel(hdc, x, y)
    Dim Color As Color = ColorTranslator.FromWin32(kk)

    g.ReleaseHdc(hdc)
    g.Dispose()
    Return Color

    End Function


    過去我都是在bitmap上繪製影像,再傳給picturebox,所以未發現DrawToBitmap()有這樣的特性,還好這次被測試出來,謝謝!^^
    對了,我是從事影像分析的(C++、C#),如果有我幫得上忙得地方請告訴我。

    回覆刪除
  6. Zeal,

    依據您提供的 Quick_GetPixel_FromControl 函式,我想呼叫的方法應該如下,

    Dim colorR As Integer
    Dim colorG As Integer
    Dim colorB As Integer
    Dim col As Color

    col = Quick_GetPixel_FromControl(PictureBox1, PictureBox1.Size.Width / 2, PictureBox1.Size.Height / 2)

    colorR = col.R
    colorG = col.G
    colorB = col.B

    但是程式執行後,RGB 三色會變成 255,我想您可能需要取得一個 WebCam 才能對這問題除錯。

    研發工作就是這樣,總會遇到無法預期的狀況,無論如何,很高興能認識您,謝謝。

    回覆刪除
  7. 看來我真該找個webcam試試了...

    回覆刪除
  8. 阿~~~我看到一個致命的錯誤了,我忘記刪掉這一行

    g.FillRectangle(Brushes.RosyBrown, ctrl.DisplayRectangle)

    這是我測試實用的!用來填滿整個區域,真是糟糕~~刪掉這行後應該就正常了!

    我還是會找個webcam試試.

    回覆刪除
  9. Zeal,

    刪掉 g.FillRectangle(Brushes.RosyBrown, ctrl.DisplayRectangle),只是讓 PictureBox1 執行前顯示原有的灰色,問題仍在 Quick_GetPixel_FromControl 函式,不過謝謝您學到一招,原有的程式可以改寫成,

    Dim colorR As Integer
    Dim colorG As Integer
    Dim colorB As Integer
    Dim col As Color

    col = Quick_GetPixel(Me.Location.X + PictureBox1.Location.X + PictureBox1.Size.Width / 2, Me.Location.Y + PictureBox1.Location.Y + +PictureBox1.Size.Height / 2)

    colorR = col.R
    colorG = col.G
    colorB = col.B

    回覆刪除
  10. hello,
    終於找到webcam了...
    測試後,我猜測之前所提供的函式之所以取得的RGB都是255的原因應該是因為取像函式是將影像繪製到Screen上而不是picturebox1,所以若針對picturebox1攫取色彩無法得想要得結果。
    我修改了兩個函式,一個是使用.net的物件攫取色彩,另一個是從修改自Quick_GetPixel()。兩個函式都是利用從screen攫取色彩,只是過程不同而已。

    Public Function Quick_GetPixel_FromControl_1(ByVal ctrl As System.Windows.Forms.Control, ByVal x As Int16, ByVal y As Int16) As Color

    Dim tmpBmp As Bitmap = New Bitmap(1, 1)
    Dim g As Graphics = Graphics.FromImage(tmpBmp)
    g.CopyFromScreen(ctrl.PointToScreen(New Point(x, y)), New Point(0, 0), tmpBmp.Size, CopyPixelOperation.SourceCopy)

    Dim col As Color = tmpBmp.GetPixel(0, 0)
    tmpBmp.Dispose()
    Return col
    End Function

    Public Function Quick_GetPixel_FromControl_2(ByVal ctrl As System.Windows.Forms.Control, ByVal x As Int16, ByVal y As Int16) As Color

    Dim poInScreen As Point = ctrl.PointToScreen(New Point(x, y))

    Dim hdc As IntPtr = GetDC(0)
    Dim kk As Int32 = GetPixel(hdc, poInScreen.X, poInScreen.Y)
    Dim col As Color = ColorTranslator.FromWin32(kk)
    ReleaseDC(0, hdc)

    Return col
    End Function

    但是從screen取得色彩會有一個bug,當程式被其他視窗覆蓋,或是縮小視窗時,就無法取的正確的色彩值。據我所知應該可以透過取像函式直接取得webcam拍攝的影像資料,需要再研究看看 @@...

    回覆刪除
  11. Zeal,

    非常感謝您提供這兩個取色函式並詳細的解說,它們皆能正常執行,確實目前的程式,如果被其它視窗遮蔽會影響抓圖,不過就目前的應用還可以接受。

    由這個小程式可知,做研發工作挑戰度很高,每天都有不同大小問題等著解決。

    這是我首次碰觸影像處理問題,所以就用最笨但是最可靠的方式解決,最後謝謝您,就像您的英文名字一樣,熱心地解決 WebCam 問題,那天有空來台北,寫 e-mail 給我,請您吃飯。^_^

    回覆刪除
  12. 哈囉~打擾你了~
    小弟是VB6的初學者
    最近在想如何將一張已知的曲線圖
    藉由VB截取其顏色為黑色的座標點
    然後再curve fitting獲得曲線多項式
    簡單的說 就是
    將一張曲線圖中的曲線轉成多項式

    一直在困擾中 就發現你的部落閣
    心中盼望你能指點一二
    如果你忙 也沒關係啦
    ^^~

    回覆刪除
  13. Hi TIM,

    關於您的問題,我的答覆請見 Curve fitting
    如有問題請再留言。

    回覆刪除