1. 功能说明
通过摄像头识别特定颜色(红、绿、蓝)。摄像头采集图像信息并通过WiFi将信息传递给PC端,然后PC端根据比例判断出目标颜色在色盘上的所属颜色后,指针便会指向对应颜色。
2. 电子硬件
本实验中采用了以下硬件:
主控板 | Basra主控板(兼容Arduino Uno) |
扩展板 | Bigfish2.1扩展板 |
电池 | 7.4V锂电池 |
通信 | 2510通信转接板 |
WiFi路由器 | |
其它 | 摄像头 |
配置OpenCV的Visual Studio 2015.net环境的计算机一台 |
3. 功能实现
工作原理:
① 摄像头采集图像信息;
② 通过WiFi将信息传递给PC端(VS2015配置的OpenCV环境);
③ 在PC端修改红色色域范围,用于判断摄像范围内的红色像素;
采用HSV颜色模型
④ 计算检测在显示的摄像范围内的红色像素区域所占比例=红色像素范围/显示的摄像范围;
⑤ 根据比例判断目标颜色在色盘上所属颜色;
⑥ 指针指向对应颜色。
3.1硬件连接
将摄像头与路由器连接,启动路由器,将PC连接到路由器的WIFI网络。
本实验不需要用到主控板作为下位机,可直接通过WiFi将图像信号传递给PC端,所以无需下位机编程。
主控板与WiFi正常连线,给WiFi路由器模块通电。
接线说明:
① 将2510通信转接板连接到扩展板的扩展坞上面;
② 找到1根USB线,一端连接到2510通信转接板接口上,另一端连接到WiFi路由器USB接口上;
③ 将摄像头线连接到WiFi路由器接口上。
3.2示例程序
下面提供一个可以进行3个颜色(红、绿、蓝)识别的参考例程(MainWindow.xaml.cs):
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;
using System.Threading;
using OpenCvSharp;
using System.Drawing;
using System.Drawing.Imaging;
namespace Color_Detect
{
/// <summary>
/// Color_Detect
/// </summary>
public partial class MainWindow : System.Windows.Window
{
/*
* 指针角度对应各颜色
* 25 -> 红色
* 90 -> 绿色
* 150 -> 蓝色
*/
int ANGLE_RED = 0;
int ANGLE_GREEN = 0;
int ANGLE_BLUE = 0;
//各颜色像素所占窗口的比例
double numOfred = 0.0;
double numOfgreen = 0.0;
double numOfblue = 0.0;
//创建视频图像实例
VideoCapture capture = new VideoCapture("http://192.168.8.1:8083/?action=stream");
Mat frame = new Mat(); //存储视频每一帧图像像素
Mat resultColor = new Mat(); //存储检测后的颜色像素
//视频显示切换变量
Boolean isChange = false;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ANGLE_RED = 25;
ANGLE_GREEN = 90;
ANGLE_BLUE = 150;
}
//颜色指示动画函数
int angelCurrent = 0;
private void ColorIndicate(int where) {
RotateTransform rt = new RotateTransform();
rt.CenterX = 150;
rt.CenterY = 185;
this.indicatorPin.RenderTransform = rt;
double timeAnimation = Math.Abs(angelCurrent - where) * 5;
DoubleAnimation da = new DoubleAnimation(angelCurrent, where, new Duration(TimeSpan.FromMilliseconds(timeAnimation)));
da.AccelerationRatio = 0.8;
rt.BeginAnimation(RotateTransform.AngleProperty, da);
switch (where) {
case 25:
colorDisplay.Content = "红色";
break;
case 90:
colorDisplay.Content = "绿色";
break;
case 150:
colorDisplay.Content = "蓝色";
break;
default:
colorDisplay.Content = "颜色指示";
break;
}
angelCurrent = where;
}
/// <summary>
/// MatToBitmap(Mat image)
/// </summary>
public static Bitmap MatToBitmap(Mat image)
{
return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(image);
}
/// <summary>
/// BitmapToBitmapImage(System.Drawing.Bitmap bitmap)
/// </summary>
public static BitmapImage BitmapToBitmapImage(Bitmap bitmap)
{
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Png); //格式选Bmp时,不带透明度
stream.Position = 0;
BitmapImage result = new BitmapImage();
result.BeginInit();
// According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed."
// Force the bitmap to load right now so we can dispose the stream.
result.CacheOption = BitmapCacheOption.OnLoad;
result.StreamSource = stream;
result.EndInit();
result.Freeze();
return result;
}
}
//颜色检测函数
private void filterColor() {
Mat hsvImage = frame.CvtColor(ColorConversionCodes.BGR2HSV);
resultColor = new Mat(hsvImage.Rows, hsvImage.Cols, MatType.CV_8UC3, Scalar.All(255));
double H = 0.0, S = 0.0, V = 0.0;
float area = (float)(hsvImage.Rows * hsvImage.Cols);
float rateOfred = 0, rateOfgreen = 0, rateOfblue = 0;
for (int i = 0; i < hsvImage.Rows; i++) {
for (int j = 0; j < hsvImage.Cols; j++) {
H = hsvImage.Get<Vec3b>(i, j)[0];
S = hsvImage.Get<Vec3b>(i, j)[1];
V = hsvImage.Get<Vec3b>(i, j)[2];
var color = frame.Get<Vec3b>(i, j);
if (((H >= 0 && H <= 10) || (H >= 125 && H <= 180)) && S >= 43 && V >= 46) //红色像素所在hsv范围
{
resultColor.Set<Vec3b>(i, j, color);
numOfred++;
}
else if ((H >= 33 && H <= 83) && S >= 43 && V >= 46) //绿色像素所在hsv范围
{
resultColor.Set<Vec3b>(i, j, color);
numOfgreen++;
}
else if ((H > 100 && H < 124) && S >= 43 && V >= 46) //蓝色像素所在hsv范围
{
resultColor.Set<Vec3b>(i, j, color);
numOfblue++;
}
}
}
rateOfred = (float)(numOfred) / area * 100;
rateOfgreen = (float)(numOfgreen) / area * 100;
rateOfblue = (float)(numOfblue) / area * 100;
if (rateOfred > 85)
{
ColorIndicate(ANGLE_RED);
}
else if (rateOfgreen > 85)
{
ColorIndicate(ANGLE_GREEN);
}
else if (rateOfblue > 85) {
ColorIndicate(ANGLE_BLUE);
}
numOfred = 0;
numOfgreen = 0;
numOfblue = 0;
}
//视频显示函数
private void ThreadCapShow()
{
while (true)
{
try
{
capture.Read(frame); // same as cvQueryFrame
if (frame.Empty())
break;
this.Dispatcher.Invoke(
new Action(
delegate
{
if (isChange)
{
filterColor();
originImage.Source = BitmapToBitmapImage(MatToBitmap(resultColor));
resultColor = null;
}
else {
originImage.Source = BitmapToBitmapImage(MatToBitmap(frame));
}
}
));
//Cv2.WaitKey(100);
//bitimg = null;
}
catch { }
}
}
//加载视频
private void loadBtn_Click(object sender, RoutedEventArgs e)
{
if (originImage.Source != null) return;
Thread m_thread = new Thread(ThreadCapShow);
m_thread.IsBackground = true;
m_thread.Start();
}
//切换视频显示,显示检测结果
private void changeBtn_Click(object sender, RoutedEventArgs e)
{
if (!isChange)
{
isChange = true;
changeBtn.Content = "返回";
}
else {
isChange = false;
changeBtn.Content = "切换";
//指针角度归零
ColorIndicate(0);
}
}
}
}
程序设定的颜色为红色、绿色、蓝色,可以使用色卡或者特定颜色的物体来检测。
注意:程序中的比例值设置为85%时,可以进行三种颜色的识别判断,建议测试的色块距离小一些,识别效果会更好。
4. 资料内容
识别颜色-例程源代码
资料内容详见 如何实现视觉识别-识别颜色