顏色識別是這個程式另一個技術瓶頸,在網路上找到藍色小舖所提供的方法比較簡單,它提供全螢幕抓點,共有兩項技術要點:螢幕座標計算以及顏色判別。
首先,如何計算螢幕上特定位置座標?
網路上沒有適當的範例說明這個螢幕抓點方法,所以我想了很久想出這個座標計算法來解決。
螢幕座標的原點在左上角,橫向 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 秒會產生中斷,可配合影像擷取自動判斷顏色,
座標轉換方面,可用
回覆刪除PictureBox1.PointToScreen(X3,X3)
將PixtureBox1上的座標轉換到螢幕座標系(X,Y)。
PictureBox1.PointToClient(X,X)
則有相反地效果。
讀取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物件中讀取,不需自行解析。
希望對你有幫助。^^
Zeal,
回覆刪除我已經很久沒寫 VB,以前較常寫 VB6,
對 VB.net 沒有那麼熟,
非常感謝您留言提供好方法,
另外對本部格有任何建言,歡迎告訴我。
Bridan ^_^
Zeal,
回覆刪除這些天很忙,直到今天有空測試您的方法,結果不行。
希望您看到留言後,再測看看你的方法二,在我的電腦上 RGB 三色都是 128 不會變化。
如果可以的話,請上傳範例程式參考,謝謝。
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#),如果有我幫得上忙得地方請告訴我。
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 才能對這問題除錯。
研發工作就是這樣,總會遇到無法預期的狀況,無論如何,很高興能認識您,謝謝。
看來我真該找個webcam試試了...
回覆刪除阿~~~我看到一個致命的錯誤了,我忘記刪掉這一行
回覆刪除g.FillRectangle(Brushes.RosyBrown, ctrl.DisplayRectangle)
這是我測試實用的!用來填滿整個區域,真是糟糕~~刪掉這行後應該就正常了!
我還是會找個webcam試試.
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
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拍攝的影像資料,需要再研究看看 @@...
Zeal,
回覆刪除非常感謝您提供這兩個取色函式並詳細的解說,它們皆能正常執行,確實目前的程式,如果被其它視窗遮蔽會影響抓圖,不過就目前的應用還可以接受。
由這個小程式可知,做研發工作挑戰度很高,每天都有不同大小問題等著解決。
這是我首次碰觸影像處理問題,所以就用最笨但是最可靠的方式解決,最後謝謝您,就像您的英文名字一樣,熱心地解決 WebCam 問題,那天有空來台北,寫 e-mail 給我,請您吃飯。^_^
哈囉~打擾你了~
回覆刪除小弟是VB6的初學者
最近在想如何將一張已知的曲線圖
藉由VB截取其顏色為黑色的座標點
然後再curve fitting獲得曲線多項式
簡單的說 就是
將一張曲線圖中的曲線轉成多項式
一直在困擾中 就發現你的部落閣
心中盼望你能指點一二
如果你忙 也沒關係啦
^^~
Hi TIM,
回覆刪除關於您的問題,我的答覆請見 Curve fitting
如有問題請再留言。