awolfbee
发表于 2020-12-26 21:07:36
侧面拍摄的图片应该如上图所示。如何识别A点在左边还是在右边,这应该是关键。
机械部分的设计,因识别方向不同,全部作废。看来停下机械部分的设计是正确的,应当先将最关键的部分,也就是识别部分搞定之后才能针对识别的位置和方向设计机械部分。本末不可倒置。
大白小白
发表于 2020-12-26 21:15:19
awolfbee 发表于 2020-12-22 20:25
最近在找工作,面试比较忙,天气也比较冷,冷人就开始犯懒,不想动了……惰性啊!
大侠怎么又在找工作?开了老板?
awolfbee
发表于 2021-1-2 09:20:48
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <stdlib.h>
#include <stdio.h>
#include <opencv2/highgui/highgui_c.h>
#include <math.h>
//#include "iostream"
//#include "cv.h"
//#include "highgui.h"
//#include "math.h"
using namespace cv;//命名空间CV
using namespace std;//命名空间 std
int threshold_value = 225;//启动程序时的阈值初始值,因为227能够完全提取轮廓,所以设置为225,实际上227也可以。
int threshold_type = 3; //启动程序的时候阈值类型,默认的为不足部分取零
int const max_value = 255;
int const max_type = 4;
int const max_BINARY_value = 255;
CvFont font;
uchar* ptr;
char label;
char label2;
Mat src, blured, src_e, src_gray, dst; //类定义几个图片变量,dst是最后转化阈值之后的图片,src.gray是灰度图
//在C语言中“char*”是声明一个字符类型du的指针,定义数据类型,char可以定义字符zhi有变量、数组、指针。dao
//例如:char *string="I love C#!"
//定义了一个字符指针变量string,用字符串常量"I love C#!",对它进行初始化。对字符指针变量初始化,实际上就是把字符串第1个元素的地址(即存放字符串的字符数组的首元素地址)赋给string。*/
Mat element = getStructuringElement(MORPH_RECT, Size(5,5)); //用于腐蚀的参数
char* window_name = "阈值演示程序20201121";
char* trackbar_type = "类型: \n 0: 二值化 \n 1: 反二值化 \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted";//
char* trackbar_value = "数值";
/// 自定义函数声明
void Threshold_Demo( int, void* );
/**
* @主函数
*/
int main( int argc, char** argv )
{
/// 读取一副图片,不改变图片本身的颜色类型(该读取方式为DOS运行模式)
src = imread("121.JPG", 1); //目前还未使用摄像头拍摄照片,暂时以直接读取文件的方式来测试。
erode (src, src_e, element); //对图片进行腐蚀,参数可调,正常为9或者10,过大则造成轮廓过小,应该再进行降噪
blur (src_e, blured, Size (3,3));//3*3内核降噪
imshow("腐蚀和降噪后的图片", blured);//显示图片
int width=blured.rows;//图像的行列
int height=blured.cols;//图像的列数量
cout<<width<<endl; //显示行列的具体像素
cout<<height<<endl;
int a; //定义整型数组,后面的1应该可以不要的
int b;//设置一维数组,用于判断曲线的切线斜率
/// 将图片转换成灰度图片 Mat img = imread("11.jpg", IMREAD_GRAYSCALE); //在读取图片的同时直接转化成灰度图, 下一步是要将像素亮度超过一定阈值的点提取出来,并找到该点的坐标,然后记录该点坐标,用于后期的比对
cvtColor( blured, src_gray, CV_RGB2GRAY );
/// 创建一个窗口显示图片
namedWindow( window_name, CV_WINDOW_AUTOSIZE );
/// 创建滑动条来控制阈值
createTrackbar( trackbar_type, window_name, &threshold_type, max_type, Threshold_Demo);
createTrackbar( trackbar_value, window_name, &threshold_value, max_value, Threshold_Demo);
/// 初始化自定义的阈值函数
Threshold_Demo( 0, 0 );
// Mat img=src;//暂时无用
//imshow("转化之后图片",dst);
//遍历图片的每个像素点,当像素点的阈值大于227时,将最左侧的该像素地址保存在二维数组中,在该行之后的像素点抛弃,如果阈值低于227,则向下遍历至该行末,然后从下一行开始对像素进行比较
//Mat BW = imread(imgName);
//int value = BW.at<uchar>(191, 51);
int width1=dst.rows;//处理之后图像的行列
int height1=dst.cols;//处理之后图像的列数量
for (int i=0 ; i<height1; i++)//从第一行开始应该从最后一行开始向上检索,这样可以减少计算量,一旦出现与之前曲线K值相反的方向,则确定是拐点,不用再考虑,但是要考虑出现切线斜率始终是减少的趋势,这种情况下往往是蒜尖
{
for (int j = 0; j < width1; j++) //从第一行的第一列开始
{
//int index = i * width + j;
int value = dst.at<uchar>(i,j); //读取给定坐标处的像素值
//if; //像素值
//int data = (int)dst.data;
if ( value >200) //如果像素值大于某个数值,则将其地址记录在数组内,且仅记录首像素,后面的舍弃
{
a=j; //数组值等于列数,便于后期对比
//cout<<i<<" --- "<<j<<endl; //i为行数
//cout<<i<<" -坐标-- "<<a<<endl;
if (i>1)
{//11
if (a<a)//如果第一行中大于某个阈值的像素地址与下一行相比靠右,也就是列数比上一行要大,则说明该曲线向左侧倾斜,说
//明是底部,如果曲线向右侧弯曲,则是蒜尖 (之所以用i-1,是因为总不能和没有像素的地址对比,所以必须向后取值)
{
b=0; //因此,当下一行的地址比上一行的地址小,则用1表示,如果下一行地址比上一行大,用0表示,1是蒜尾,0是蒜尖。
}
else if (a>=a)
{
b=1;
}
cout<<i<<" -标识符-- "<<b<<endl;
//cout<<j<<endl; //j为列数
} //11
break;
}
}
}
//开始对b数组进行分析,确定是否为头尾(但是需要对曲线进行圆滑处理)
for (int i=0 ; i<height1; i++)//对数组b进行遍历,检查是否存在误判的问题
//寻找拐点,找到用于判断的后段曲线的拐点
//对图形进行圆滑处理,除了最大的拐点之外,尽量不要出现折线
// int width=dst.rows;//图像的行列
//int height=dst.cols;//图像的列数量
cout<<width<<endl; //显示行列的具体像素
cout<<height<<endl;
//for (int i =height1-5 ; i>20; i--) //对收集的坐标数据进行分析
// 等待用户按键。如果是ESC健则退出等待过程。
while (true)
{
int c;
c = waitKey( 20 );
if( (char)c == 27 )
{ break; }
}
}
/**
* @自定义的阈值函数
*/
void Threshold_Demo( int, void* )
{
/* 0: 二进制阈值
1: 反二进制阈值
2: 截断阈值
3: 0阈值
4: 反0阈值
*/
threshold( src_gray, dst, threshold_value, max_BINARY_value,threshold_type );
imshow( window_name, dst );
}
/*
void main()
{
//读入彩色图像 单通道图像(CV_8UC1);CV 8位未指定的1通道的图像,backImg是单通道灰色图
//Mat img = imread("fruits.jpg");
Mat img = imread("11.jpg", IMREAD_GRAYSCALE); //在读取图片的同时直接转化成灰度图, 下一步是要将像素亮度超过一定阈值的点提取出来,并找到该点的坐标,然后记录该点坐标,用于后期的比对
imshow("灰度图", img);
//确定图像的尺寸,行列,
int width=img.rows; //图片的变量名加上行列,就是行列的数据
int height=img.cols;
cout << width << endl; //显示行列的数据本图片已经用358的像素值裁剪了,所以形成的就是高宽都是358
cout << height << endl;
//定义一个二维数组,a,其行数等于图像行数,列数就一列,用于记录图片上像素的亮度超过某个阈值的像素所在的列数,用于下一步的对比。
int a; //确定一个358的二维数组
//int height = backImg.rows; //backImg是图片变量名
//int width = backImg.cols;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
int index = i * width + j;
//像素值
int data = (int)img.data;
}
}
waitKey();
}
*/
awolfbee
发表于 2021-1-2 09:23:11
以上为使用OPENCV作为视觉识别的工具,目前测试的方式是以一道激光照射在蒜瓣上,根据激光产生的亮条的形状以及拐点判断蒜瓣的头尾,对于清理非常干净的蒜瓣来说,这样识别问题不是很大,基本上还是可以判断的,但是对于没有清理干净的蒜瓣,例如底部还有残留的块状根部,或者是还有蒜皮,这样就会对识别产生干扰,因此需要使用更复杂的算法排除干扰。
awolfbee
发表于 2021-1-2 09:24:55
目前仅用保存在电脑中的图片进行测试,暂时还没有使用摄像头连续拍照,关于如何排序,还得等识别没有问题之后再说。
元旦放几天假,总算有点时间研究下。
大白小白
发表于 2021-1-4 13:02:56
awolfbee 发表于 2021-1-2 09:20
#include
#include
#include
lzv5,不明觉厉!
这个需要安装opencv的什么软件,什么版本,
才能在C环境下编译测试?
awolfbee
发表于 2021-1-22 22:26:06
用一次性筷子做了个简易的相机支架,第一次居然做低了,不能拍摄大蒜的全貌,然后在上面增加了一个小的支架,双杠上再增加双杠。
拍摄的大蒜图片,背景色应该为黑色的比较好,还有就是光源没选择好,要在支架上再增加LED照明灯,设置好光源才行。搞视觉,的确麻烦。
相机的像素选择有点低,不过28元的摄像头,还能指望个啥,能拍摄照片就不错了。
awolfbee
发表于 2021-1-22 22:35:32
03年购买了一个飞利浦的剃须刀,用了几年后电池弹片上锈不能用了,于是就用锂电池装上,之前用的是一个巨大的翘板开关,而且是用不干胶带缠绕的,虽然实用,但是很难看。今天把剃须刀带到公司去,整理了下,还安装了一块锂电池充电板,焊接完毕后再用热熔胶固定下相对位置,把电池的触点用胶保护起来,这样就比较完美了。充电时是红色灯,充满了就转绿色灯。
实际上我有好几个剃须刀,但是就这个用的比较顺手点,所以就没舍得丢。到现在已经用了18年了,刀片在玻璃板上磨过几次,依然非常锋利。
孙启明
发表于 2021-1-24 11:34:26
awolfbee 发表于 2021-1-22 22:26
用一次性筷子做了个简易的相机支架,第一次居然做低了,不能拍摄大蒜的全貌,然后在上面增加了一个小的 ...
残存块根的蒜瓣,从形状上不好识别,可以从色差上面进行识别。
awolfbee
发表于 2021-1-31 22:09:17
用未解锁的手机屏幕作为背景,效果非常好。调节了下焦距,基本上能分辨出外观来。
先用带皮蒜瓣,然后剥了几个蒜瓣,写了一段程序,现在能够使用程序读取照片了。
准备搞一个黑色的绒布再加上玻璃板作为背景,然后要粘贴上定位条,这样每个蒜瓣的原点就确定了,便于后期的程序书写。