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
用未解锁的手机屏幕作为背景,效果非常好。调节了下焦距,基本上能分辨出外观来。
先用带皮蒜瓣,然后剥了几个蒜瓣,写了一段程序,现在能够使用程序读取照片了。
准备搞一个黑色的绒布再加上玻璃板作为背景,然后要粘贴上定位条,这样每个蒜瓣的原点就确定了,便于后期的程序书写。
awolfbee
发表于 2021-1-31 22:11:51
感觉按照蒜瓣这样原始的状态识别很麻烦,上周在工作的地方写程序的时候就在考虑这个不一致性的问题,因为蒜头碾碎成蒜瓣之后会存在很多蒜皮以及蒜根之类的不规则物品,对视觉识别会造成很大的干扰,这个可能要设计个设备,在把蒜瓣种子送入到钟子箱之前就要预处理下,把从蒜瓣上剥离的蒜皮清理掉,把蒜根进一步去除,要么就在识别时将无法识别的丢弃到废料箱中,后期人工再处理。
awolfbee
发表于 2021-2-2 22:27:15
通过视觉软件各种算法,取出蒜瓣的外形图,现在就是要对这个外形图进行处理,得出头尾的结论。貌似这个也简单,因为都带有个小尾巴,但是我把外面的蒜皮剥掉之后是怎样的呢?下面开始剥皮。
其中的小点应该是灰尘,可以在计算过程中把面积小于多少的点排除,这样就可以得出蒜瓣的净轮廓图像。