一、DL现状、本例范畴


​​基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._System​​


本例显然属于object localization。


二、支撑环境和基本流程


这个基本上来说,就是采用百度自己提供的数据集(后期我这个桌面食物数据集也上传)和工具来做了。非常值得注意一点的是百度的标注工具,有智能标准的能力。我没有全部标注完,但是结果已经非常不错。


​​基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._System_02​​



首先是创建这个数据集。我采用“高拍仪拍摄3个松鼠食物”的方法来进行。共采集45张图片,其中训练的40张要有部分是比较难以识别的,检测的5张相对质量较好。


​​基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._System_03​​



​​基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_04​​


​​基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_05​​


​​基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_06​​


​​基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_07​​


这种小规模的数据集那是相当棒的结果了。



数据集训练好了,其实我更关心的是调用问题,好像Csharp有接口的,我来看看;其实对于以下任何一种方式来说,调用都是问题:


​​基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_08​​



​​基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_09​​




基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_10


充10块钱就可以了



基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_11

​​


使用之前的Csharp代码来进行调用实验。



这里的购买方式都是可选的,我认为这种方式未来肯定是很好的,但是要考虑消费者习惯;此外,本地OpenCV调用方式,肯定是需要的;还有公网这种调用方式,还需要研究研究才能够实现的。     


直接提供了H5的调用方式,在采集方式不统一的情况下给出较高结果,使得我认为这可能就是最后需要采用的方法。



基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_12



三、GOCW的引入


      希望能够用Csharp编写界面,因为它更好用;但是又不想引入EmguCV类似的库,因为里面很多东西不是我需要的。那么最直接的方法就是使用Csharp调用基于Opencv编写的类库文件(Dll)的,我取名叫做GreenOpenCsharpWarper(GOCW)


       经过比较长时间的探索研究,目前的GOCW已经可以直接以函数的形式在内存中传递bitmap和Mat对象,达到了函数级别的应用。因为这里涉及到托管代码编写,也就是CLR程序编写,所以有比较复杂的地方;为了展现GOCW的优良特性,我编写实现GOGPY项目,也就是一个"Csharp编写界面,OpenCV实现算法的实时视频处理程序”,相关细节都包含其中。之所以叫“GPY”,是采集硬件这块,我采用了成像质量较好的高拍仪设备(GaoPaiYi)。


 


基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_13

​​


     这里简单将最核心内容进行讲解。GOCW的核心问题,无非就是基于CLR之上的两个方向的数据流转换。核心函数为


Bitmap
^ GOClrClass
:
:testMethod(cli
:
:array
<
unsigned char
>
^ pCBuf1)

{
pin_ptr <System : :Byte
> p1
=
&pCBuf1[
0];

unsigned char
* pby1
= p1;

cv : :Mat img_data1(pCBuf1 -
>Length,
1,CV_8U,pby1);

cv : :Mat img_object = cv
:
:imdecode(img_data1,IMREAD_UNCHANGED);
//获得数据到img_object中去

//处理过程///
cvtColor(img_object,img_object, 40);

/
Bitmap ^ bb = MatToBitmap(img_object);
if ( !img_object.data)
return nullptr;
std : :vector <uchar
> buf;

cv : :imencode( ".jpg", img_object, buf);

return bb;
}


以及


System
:
:Drawing
:
:Bitmap
^ MatToBitmap(
const cv : :Mat & img)

{
if (img.type() != CV_8UC3)
{
throw gcnew NotSupportedException( "Only images of type CV_8UC3 are supported for conversion to Bitmap");
}
//create the bitmap and get the pointer to the data
PixelFormat fmt(PixelFormat : :Format24bppRgb);
Bitmap ^bmpimg = gcnew Bitmap(img.cols, img.rows, fmt);
BitmapData ^data = bmpimg -
>LockBits(System
:
:Drawing
:
:Rectangle(
0,
0, img.cols, img.rows), ImageLockMode
:
:WriteOnly, fmt);

//byte *dstData = reinterpret_cast<byte*>(data->Scan0.ToPointer());
Byte *dstData = reinterpret_cast
<Byte
*
>(data
-
>Scan0.ToPointer());

unsigned char *srcData = img.data;
for ( int row = 0; row < data
-
>Height;
++row)

{
memcpy( reinterpret_cast
<
void
*
>(
&dstData[row
*data
-
>Stride]),
reinterpret_cast
<
void
*
>(
&srcData[row
*img.step]), img.cols
*img.channels());

}
bmpimg - >UnlockBits(data);
return bmpimg;
}

而在csharp中,直接


Bitmap b 
=
new Bitmap(cam.Width, cam.Height, cam.Stride, PixelFormat.Format24bppRgb, m_ip);

// If the image is upsidedown

b.RotateFlip(RotateFlipType.RotateNoneFlipY);
srcImage = b;
if (picPreview.Image != null)
picPreview.Image.Dispose();

//调用clr+opencv图像处理模块

MemoryStream ms = new MemoryStream();
b.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] bytes = ms.GetBuffer();
Bitmap bitmap = client.testMethod(bytes);


就可以调用,并且获得结果。



 


四、本例的实现、训练和效果

4.1、重构解决方案



基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_14

基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_15


GOCVhelper做算法研究和函数封装;GOImage做dll;Csharp程序开发界面;


解决OpenCV版本问题,进行函数封装。


现在环境配置已经精简



基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_16

​​



基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_17

​​


此外将.dll拷贝到能够被访问的地方就可以。



基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._System_18

​​


下一步在保证效果不变的情况下,进行函数封装。OK可行;


4.2、GOCW封装


采用输入图片是Mat直接输入;输出结果还是ini外部存储的方式,最为有效。



基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_19

​​


因为有良好的积累,所以很快就完成了基本算法移植



基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_20

​​


但是这还不够,有两个界面操作,1个是框选、一个是圆的产生和去除。其中框选需要结合QML一起来想,圆操作现在应该可行。


很快算法集成成功,主要还是得益于之前的有效积累。



基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_21

​​


这里还有一个升级版本




基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._System_22

​​


基于GOCW的界面,成功打通EasyDL通道


private void button4_Click(object sender, EventArgs e)



{



//保存json



List<Dictionary<String, float>> listDic = new List<Dictionary<String, float>>();



for (int i=0;i<listCenter.Count;i++)



{



PointF pointf = listCenter[i];



Dictionary<String, float> dic = new Dictionary<String, float>()



{



{ "name",99999},



{ "x1",(pointf.X-3)},



{ "y1",(pointf.Y-3)},



{ "x2",(pointf.X+3)},



{ "y2",(pointf.Y+3)}



};



listDic.Add(dic);



}



String Jsondata = JsonConvert.SerializeObject(listDic);



Jsondata = Jsondata.Replace("99999.0", "\"pip\"");



Jsondata = "{\"labels\": " + Jsondata + "}";



StreamWriter writer = new StreamWriter(strFliePath.TrimEnd(".jpg".ToArray())+"_.json", false);



writer.Write(Jsondata);



writer.Close();



//在原目录保存缩放后的img



bmpCrop.Save(strFliePath.TrimEnd(".jpg".ToArray()) + "_.jpg");

其中这段:


  StreamWriter   writer  =  new   StreamWriter ( strFliePath . TrimEnd ( ".jpg" . ToArray ())+ "_.json" ,  false );


编码格式,卡了我一晚上。


最后通过比较工具才发现了编码不同。



基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_23

​​


要不断有计划地谁用过新工具


4.3在线训练、观察调参




基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_24

​​




基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_25

​​




基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_26

基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._OpenCV_27

基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_28

基于OpenCV实现“钢管计数”算法,基于Csharp编写界面,并实现算法融合【完成】..._数据集_29

方法应该是可行的,但是训练的过程肯定是需要系统方法。在没有足够标注数据的情况下,必须首先研究自动标注方法。
​https://ai.baidu.com/easydl/app/2/models/verify?modelId=54940&iterationId=81438​

五、小结


1、Csharp编写界面非常重要,是核心能力。但是目前标注仍然是很困难,需要3-5min一幅图;


2、什么样的特征需要标注?需要标注到什么程度?这些都是值得研究的,需要长期思考的工程问题;


3、建立用户参与的标注采集机制,是最终需要的,我们如何建立这个机制,关键是第一参与者的参与;


4、EasyDL是非常棒的BaseLine,它肯定不止采用了YOLO,它的效果是非常重要的参考;此外“智能标注”模式也值得参考。