推荐 最新
神机妙算

halcon学习和实践(从halcon转化为opencv)

halcon本身还是商业软件,从学习的角度来使用无可厚非,但是如果用在商业上面,那么就必须要获得halcon的授权,这是底线。那么既然学了halcon,又不想支付高额的授权费用,那么只有两条路了。第一个就是自己写一个image的库,另外一个就是寻找开源库,比如opencv就是其中的一种开源库。        从最早的opencv2开始,目前最新的opencv已经进化到了opencv4。opencv目前支持的语言主要是c++和python两种。c++基础比较好的同学,完全可以用c++来进行开发,这主要体现在最终部署上面。而在平时学习的时候,最好使用python来学习,这样比较方便,也比较快捷。因为在图像处理的过程中,一个很重要的部分,就是参数的标定工作。如果每次都是用c++来测试的话,那么标定一次,就要重新编译一次。这就显得很麻烦。当然也有比较讨巧的办法,就是把标定的数据变成字符串,这样不重新编译c++,直接修改exe文件也可以达成调参不重编的目的。        还是以之前audi2的例子为证,我们看一下如果需要变成opencv应该怎么做。        首先,还是看一下halcon的代码,read_image (Audi2, 'audi2') fill_interlace (Audi2, ImageFilled, 'odd') threshold (ImageFilled, Region, 0, 90) connection (Region, ConnectedRegions) select_shape (ConnectedRegions, SelectedRegions, 'width', 'and', 30, 70) select_shape (SelectedRegions, Letters, 'height', 'and', 60, 110) dev_clear_window () dev_set_colored (12) dev_display (ImageFilled) dev_display (Letters)        这段代码相信大家看了很多遍,早已经熟记在心。那么,下面需要做的就是一个一个找到对应的opencv函数即可, read_image ===>cv2.imread threshold===>cv2.threshold connection===>cv2.findContours select_shape===>cv2.boundingRect dev_display===>cv2.drawContours & cv2.imshow        对于算法中的参数,基本上也可以作为参考。当然,这里因为暂时没有找到原来的图片,所以在参数上面稍做了修改。经过这些处理之后,最终获得的opencv代码如下所示,import numpy as np import cv2 img = cv2.imread('./audi2.png') imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret1, img_thresh = cv2.threshold(imgGray, 90, 255, cv2.THRESH_BINARY) _, contours, hierarchy = cv2.findContours(img_thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) for i,c in enumerate(contours): x,y,w,h = cv2.boundingRect(c) if w < 10 or w > 60: continue if h < 40 or h > 150: continue cv2.drawContours(img, contours, i, (0, 0, 255), 3) cv2.imshow('image', img) cv2.waitKey(0) cv2.destroyAllWindows()        利用python命令,直接运行代码,python demo.py        不出意外,我们就可以获得这样的运行效果,基本可以满足我们的要求,

0
0
0
浏览量2011
神机妙算

halcon学习和实践(导出cpp文件)

       halcon除了编写脚本文件之外,还可以导出cpp文件。因为,最终的功能总是要给客户使用的,相关的软件也要部署到客户的机器上的,总不能把hdevelop软件安装在客户的电脑上吧。这也不是很合适。        还是以上次的脚本为例,看一下如何导出cpp代码。导出cpp的方法也很简单,选择文件-》导出即可,         格式选择cpp,然后单击导出就可以了,         目前来说,总共支持4种格式的导出方式,分别是c文件、cpp文件、vb文件、c#文件。因为c#编写上位机比较方便,所以需要的同学可以导出为c#文件。当然对c和cpp比较熟悉的朋友,可以导出为对应的文件,界面部分用mfc或者qt编写效果也是一样的。        我们可以阅读一下这个生成的cpp文件,源代码如下所示,/// // File generated by HDevelop for HALCON/C++ Version 13.0.2 /// #ifndef __APPLE__ # include "HalconCpp.h" # include "HDevThread.h" # if defined(__linux__) && !defined(__arm__) && !defined(NO_EXPORT_APP_MAIN) # include <X11/Xlib.h> # endif #else # ifndef HC_LARGE_IMAGES # include <HALCONCpp/HalconCpp.h> # include <HALCONCpp/HDevThread.h> # else # include <HALCONCppxl/HalconCpp.h> # include <HALCONCppxl/HDevThread.h> # endif # include <stdio.h> # include <HALCON/HpThread.h> # include <CoreFoundation/CFRunLoop.h> #endif using namespace HalconCpp; #ifndef NO_EXPORT_MAIN // Main procedure void action() { // Local iconic variables HObject ho_Audi2, ho_ImageFilled, ho_Region; HObject ho_ConnectedRegions, ho_SelectedRegions, ho_Letters; ReadImage(&ho_Audi2, "audi2"); FillInterlace(ho_Audi2, &ho_ImageFilled, "odd"); Threshold(ho_ImageFilled, &ho_Region, 0, 90); Connection(ho_Region, &ho_ConnectedRegions); SelectShape(ho_ConnectedRegions, &ho_SelectedRegions, "width", "and", 30, 70); SelectShape(ho_SelectedRegions, &ho_Letters, "height", "and", 60, 110); if (HDevWindowStack::IsOpen()) ClearWindow(HDevWindowStack::GetActive()); if (HDevWindowStack::IsOpen()) SetColored(HDevWindowStack::GetActive(),12); if (HDevWindowStack::IsOpen()) DispObj(ho_ImageFilled, HDevWindowStack::GetActive()); if (HDevWindowStack::IsOpen()) DispObj(ho_Letters, HDevWindowStack::GetActive()); } #ifndef NO_EXPORT_APP_MAIN #ifdef __APPLE__ // On OS X systems, we must have a CFRunLoop running on the main thread in // order for the HALCON graphics operators to work correctly, and run the // action function in a separate thread. A CFRunLoopTimer is used to make sure // the action function is not called before the CFRunLoop is running. // Note that starting with macOS 10.12, the run loop may be stopped when a // window is closed, so we need to put the call to CFRunLoopRun() into a loop // of its own. HTuple gStartMutex; H_pthread_t gActionThread; HBOOL gTerminate = FALSE; static void timer_callback(CFRunLoopTimerRef timer, void *info) { UnlockMutex(gStartMutex); } static Herror apple_action(void **parameters) { // Wait until the timer has fired to start processing. LockMutex(gStartMutex); UnlockMutex(gStartMutex); try { action(); } catch (HException &exception) { fprintf(stderr," Error #%u in %s: %s\n", exception.ErrorCode(), (const char *)exception.ProcName(), (const char *)exception.ErrorMessage()); } // Tell the main thread to terminate itself. LockMutex(gStartMutex); gTerminate = TRUE; UnlockMutex(gStartMutex); CFRunLoopStop(CFRunLoopGetMain()); return H_MSG_OK; } static int apple_main(int argc, char *argv[]) { Herror error; CFRunLoopTimerRef Timer; CFRunLoopTimerContext TimerContext = { 0, 0, 0, 0, 0 }; CreateMutex("type","sleep",&gStartMutex); LockMutex(gStartMutex); error = HpThreadHandleAlloc(&gActionThread); if (H_MSG_OK != error) { fprintf(stderr,"HpThreadHandleAlloc failed: %d\n", error); exit(1); } error = HpThreadCreate(gActionThread,0,apple_action); if (H_MSG_OK != error) { fprintf(stderr,"HpThreadCreate failed: %d\n", error); exit(1); } Timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(),0,0,0, timer_callback,&TimerContext); if (!Timer) { fprintf(stderr,"CFRunLoopTimerCreate failed\n"); exit(1); } CFRunLoopAddTimer(CFRunLoopGetCurrent(),Timer,kCFRunLoopCommonModes); for (;;) { HBOOL terminate; CFRunLoopRun(); LockMutex(gStartMutex); terminate = gTerminate; UnlockMutex(gStartMutex); if (terminate) break; } CFRunLoopRemoveTimer(CFRunLoopGetCurrent(),Timer,kCFRunLoopCommonModes); CFRelease(Timer); error = HpThreadHandleFree(gActionThread); if (H_MSG_OK != error) { fprintf(stderr,"HpThreadHandleFree failed: %d\n", error); exit(1); } ClearMutex(gStartMutex); return 0; } #endif int main(int argc, char *argv[]) { int ret = 0; try { #if defined(_WIN32) SetSystem("use_window_thread", "true"); #elif defined(__linux__) && !defined(__arm__) XInitThreads(); #endif // Default settings used in HDevelop (can be omitted) SetSystem("width", 512); SetSystem("height", 512); #ifndef __APPLE__ action(); #else ret = apple_main(argc,argv); #endif } catch (HException &exception) { fprintf(stderr," Error #%u in %s: %s\n", exception.ErrorCode(), (const char *)exception.ProcName(), (const char *)exception.ErrorMessage()); ret = 1; } return ret; } #endif #endif        整个代码的长度大约200行,不算很长,在可以接受的范围内。通过观察发现,主要的图像操作代码都被翻译到了action这个函数里面。        代码从main函数开始。分成两种情况,一种是没有定义__APPLE__宏,也就是mac电脑的宏;一种是定义了__APPLE__宏。如果没有定义这个宏,那么使用就比较简单,main函数直接调用action函数即可。        如果定义了__APPLE__宏,那么流程稍微复杂一点,首先它会调用apple_main函数,在apple_main函数里面会创建一个入口是apple_action函数的线程。这个线程没有立即开始执行,直到apple_main创建一个Timer的定时器,并且timer_callback回调函数中释放gStartMutex之后,apple_action才开始执行。在apple_action中会继续调用action函数,调用结束后,会设置g_Terminate为true,这样app_main函数就可以跳出循环等待,整个操作也就这样完成了。        一般编译c、cpp代码都需要include目录、lib目录和dll目录,这部分和平台有关,相差不大,剩下来的工作就是把这段代码集成到自己的上位机软件中即可。1)include目录位置C:\Program Files\MVTec\HALCON-13.0\include2)lib目录位置C:\Program Files\MVTec\HALCON-13.0\lib\x64-win643)dll位置C:\Program Files\MVTec\HALCON-13.0\bin\x64-win64

0
0
0
浏览量2014
神机妙算

halcon学习和实践(svm模型训练实战)

在halcon软件中,除了可以做传统的视觉分析、物体测量之外,另外一个重要的功能就是ocr。说到ocr,也就是光学字符识别,就不得不谈到机器学习和深度学习。虽然深度学习有着很好的识别效果,但是本身对样本的要求也很多,比如就需要足够多的训练样本。而传统的机器学习,识别的原理、方法具有很强的物理意义,也就是说方法本身是可以说清楚为什么会出现这个识别效果的,在特定的应用场景也同样发挥着重要的作用。        目前halcon软件中本身提供了很多的模型,当然这些模型不能覆盖所有的场景,所以本身也提供了模型训练的方法,这边可以找个例子看一下。事实上,开源软件中,比如python的sklearn同样提供了基础的机器学习算法,大家也可以学习一下。        这里不失一般性,可以选用train_characters_ocr.hdev这个文件来进行分析,* This example program trains a font that consists * of the characters A to G. The font is used in * the example program classify_characters_ocr.hdev * to read the corresponding characters. * read_image (Image, 'ocr/chars_training_01.png') get_image_pointer1 (Image, Pointer, Type, Width, Height) dev_close_window () dev_open_window (0, 0, Width, Height, 'white', WindowHandle) set_display_font (WindowHandle, 12, 'mono', 'true', 'false') dev_set_draw ('margin') dev_set_line_width (2) dev_display (Image) dev_update_window ('off') dev_update_pc ('off') dev_update_var ('off') * * Create an SVM classifier for the characters A to G ClassNames := ['A','B','C','D','E','F','G'] create_ocr_class_svm (8, 10, 'constant', ['convexity','num_holes','projection_horizontal','projection_vertical'], ClassNames, 'rbf', 0.02, 0.05, 'one-versus-one', 'normalization', 10, OCRHandle) * * Read the training images and store the regions * of the characters to a training file for I := 1 to 7 by 1 read_image (Image, 'ocr/chars_training_' + I$'.2d') dev_display (Image) get_regions (Image, SortedRegions) count_obj (SortedRegions, NumberObjects) for J := 1 to NumberObjects by 1 select_obj (SortedRegions, ObjectSelected, J) if (I == 1 and J == 1) write_ocr_trainf (ObjectSelected, Image, ClassNames[J - 1], 'train_characters_ocr.trf') else append_ocr_trainf (ObjectSelected, Image, ClassNames[J - 1], 'train_characters_ocr.trf') endif dev_set_color ('gray') dev_display (ObjectSelected) disp_message (WindowHandle, ClassNames[J - 1], 'window', 10, 10 + (J * 20), 'black', 'true') * stop () endfor disp_continue_message (WindowHandle, 'black', 'true') stop () endfor * * Check if the content of the training file is correct read_ocr_trainf (Characters, 'train_characters_ocr.trf', CharacterNames) count_obj (Characters, NumberCharacters) for I := 1 to NumberCharacters by 1 select_obj (Characters, CharacterSelected, I) dev_clear_window () dev_display (CharacterSelected) disp_message (WindowHandle, CharacterNames[I - 1], 'window', 10, 10, 'black', 'true') * stop () endfor * * Train the font, write the font to file, * and clear the classifier from memory trainf_ocr_class_svm (OCRHandle, 'train_characters_ocr.trf', 0.001, 'default') reduce_ocr_class_svm (OCRHandle, 'bottom_up', 2, 0.001, OCRHandleReduced) write_ocr_class_svm (OCRHandleReduced, 'font_characters_ocr') * clear_ocr_class_svm (OCRHandle) clear_ocr_class_svm (OCRHandleReduced)        因为涉及到的代码较多,这里分析的主要是涉及svm的几个函数,        第20行,create_ocr_class_svm,准备创建一个svm模型,        第32行,write_ocr_trainf,将训练文件写入train_characters_ocr.trf,        第34行,append_ocr_trainf,将训练文件添加到append_ocr_trainf.trf,        第58行,trainf_ocr_class_svm,开始训练svm模型,        第59行,reduce_ocr_class_svm,优化裁剪svm模型,        第60行,write_ocr_class_svm,保存生成的svm模型,模型名为font_characters_ocr,        第62行,clear_ocr_class_svm,关闭初始生成svm模型句柄,        第63行,clear_ocr_class_svm,关闭优化裁剪svm模型句柄。        初始学习的时候,主要有两个方面需要注意下。第一个,就是训练svm模型的流程。一开始的时候可能难以理解,个中的参数也不知道什么意义,这个可以在后续的学习过程中不断演进。第二个,就是了解append_ocr_trainf.trf、font_characters_ocr这两个最重要的参数,前者是为了准备训练文件,后者是生成的模型名。模型生成之后,就不需要再次训练了,只需要每次加载font_characters_ocr这个模型,就可以开始分类预测了。以上就是需要注意的两个地方。

0
0
0
浏览量1800
神机妙算

halcon学习和实践(第一个范例threshold.hdev)

对于halcon学习,一个比较好的学习方法就是学习它的范例,效果虽然慢,但是作用是实实在在的。范例中不仅包含了很多的应用case,而且可以通过这些范例,可以学习问题解决的思路是什么样子的。只要有了这些思路,遇到问题的时候才不会那么慌张。         打开halcon之后,只要输入ctrl+E,或者是依次输入文件-》浏览HDevelop示例程序,就可以看到对应的窗口,比如像这样,        左边是各种各样分类的方法,右边是具体的案例。不失一般性,我们可以找一个blob分析,然后选择一个threashold.hdev的案例来进行分析,         打开后似乎什么也没有,没关系,这个时候只需要单击上面的执行按钮就可以看到效果了,         不出意外的话,你就会看到这样的执行结果,         上面整个截图有四个部分,左上是一副图片,上面有几个彩色的部分,看上去所有的操作都是为了为了找到这几个彩色的连通区域;左下有很多小图片,看上去应该是执行过程中的中间结果;右上是查找执行函数使用的;右下是执行的脚本。我们可以进一步分析这个脚本是做什么的,read_image (Audi2, 'audi2') fill_interlace (Audi2, ImageFilled, 'odd') threshold (ImageFilled, Region, 0, 90) connection (Region, ConnectedRegions) select_shape (ConnectedRegions, SelectedRegions, 'width', 'and', 30, 70) select_shape (SelectedRegions, Letters, 'height', 'and', 60, 110) dev_clear_window () dev_set_colored (12) dev_display (ImageFilled) dev_display (Letters)         脚本内容看上去有点像python脚本,当然这不重要。第一行read_image,顾名思义,应该是读取图片;第二行fill_interlace,不太懂,暂且认为是图像增强的功能吧;第三行threshold,这个好理解,就是二值化;第四行connection,听名字好像是区域连接,姑且认为是分割吧;第五行和第六行是select_shape,两个函数一样,但是参数不一样,一个是width,一个是height,从字面上看,应该是从之前的分割区域中筛选出合适的区域。第七行dev_clear_window,从名字上看是清除窗口。第八行dev_set_colored,字面上看是设置颜色;第九行和第十行都是dev_display,很明显这是一个显示函数,只不过一个显示的是原图像,一个显示的是分割后的图像,两者叠加在一起。       有了上面的分析,我们可以清楚的知道,这就是一个车牌识别的程序。       也许 有的同学会说,能不能单步调试呢,其实这是完全可以的,也是比较推荐的调试方法,选择F2重新执行,         接着就是单步执行,可以输入F6,或者是单击这个按钮,         上面这些步骤,大家都可以在安装halcon之后,在自己的电脑上完成。学习图像固然需要花费一些时间,但是只要一步一步去做,总能学会的。

0
0
0
浏览量1533
神机妙算

halcon学习和实践(开篇)

大学里面开设数字图像处理课程的学校很多,选用的书籍一般就是冈萨雷斯那本《数字图像处理》。编程实践的话,一般是三种方法,一种是老师自己写一个dll库,里面包含了基本的read image、write image方法,学生专注于算法的编写就好了;一种是用python + opencv库编写算法;还有一种是matlab编写,大体就是这三种。        我们不能说这种教学是错误的,但是有点脱离生产实际,这是肯定的。有几个方面,这个与实际应用场景是严重脱离的,       1、教学使用的图片一般都是来自于公开图片库,这给人一个错觉,那就是好的图像似乎是天然获得的。但是实际上,在工业生产上为了获得一张符合要求的图片需要付出很多的努力,比如说选择好的光源,选择合适的镜头,选择匹配的传感器等等。此外,对相机的安装位置也有一定的要求。有了高质量的图片,对算法要求自然也就低很多。很多时候,大家会发现,工业生产上面花费在算法的时间上不一定很多,但是花在获取高质量图片的功夫上面需要花费很多的时间,而且很多时候这个时间都是少不了的。       2、工业生产的算力并不是无限供给的,而且对时间有一定的要求。一般教学中,都是拿PC或者笔记本直接对图像处理,用的都是最新的cpu、最新的显卡、最新的硬件。但是工业或者消费品中,用的可能是嵌入式板子,而且嵌入式板子的算力都是一定的。不管如此,就是这算力不高的开发板,对于其结果的输出还是有性能要求的。比如汽车、机器人系统,如果图像输出结果不能在一定时间内完成,那么即使后面给出了结果,也基本上没有了意义。为了加速图像处理,你可能还需要掌握dsp、fpga、gpu的处理方法,这都是cpu开发之外的必须的工作量。不光如此,你可能还需要掌握硬件知识,比如网络、dma、v4l2、neon汇编等等,这一切的一切都是为了加速算法的执行。        3、学术中,为了提高对图像的处理能力,可能会设计出各种各样的算法。而在生产中,很多时候一个系统,是需要多种传感器配合才能获得最终结果的。比如图像可能和激光雷达、3d结构光camera、毫米波雷达、超声波雷达来进行配合。只要对最终的系统有利,我们有必要选用各种传感器组合在一起,而不拘泥于摄像头本身。图像本身没有深度信息,此时除了深度学习之外,另外一个稳妥的办法,就是匹配一个深度检测传感器,距离短可以用3d结构光,距离长可以用lidar,现在这方面都已经越来越成熟了。        上面说的三点只是生产中常见的几个误区。此外,在工业当中,实际使用的软件,也不是opencv,而是halcon和vision pro。拿halcon举例,用户自己本身不需要掌握复杂的算法,通过脚本就可以实现功能的开发。在脚本完成后,可以导出为c#或者c++代码,结合dll文件,可以最终部署到自己的项目中。本身halcon也有windows版本和linux版本。只是halcon本身是商业软件,作为学习使用一般问题不大,如果是大范围的做商业部署,最好还是用opencv+定制算法+硬件优化来解决大多数问题,这样比较稳妥。

0
0
0
浏览量1284
神机妙算

halcon学习和实践(hdev脚本学习技巧)

上一节我们提到了第一个halcon的脚本,也就是threshold.hdev。有过编码经验的同学看到这段代码,可能不会感到学习上有什么难度。但是对于一些plc开发的同学来说,特别是熟悉梯形图开发的朋友来说,学习上或许还是有一定的困难的。所以,这里提供一个hdev脚本的技巧。        首先,还是重新看一下这个脚本,read_image (Audi2, 'audi2') fill_interlace (Audi2, ImageFilled, 'odd') threshold (ImageFilled, Region, 0, 90) connection (Region, ConnectedRegions) select_shape (ConnectedRegions, SelectedRegions, 'width', 'and', 30, 70) select_shape (SelectedRegions, Letters, 'height', 'and', 60, 110) dev_clear_window () dev_set_colored (12) dev_display (ImageFilled) dev_display (Letters)        我们知道每一个算子都有它的输入和输出,所以,这里可以先确定一个模型,          从上面的算子来说,每一个算子都有一个或者若干个输入,但是输出一般只有一个。对于function1来说,它的输入就是input1,它的输出是output1。对于function2呢,function1的output1就是它的input1,它自己的输出就是output2,这里output1只是input2的一部分,可能function2还有其他输入。function3也是这样,依次类推。        回到上面的脚本代码,第一行代码read_image (Audi2, 'audi2')        很明显,输入是图片名称'audi2',输出是图片数据Audi2。        紧接着分析第二行代码,fill_interlace (Audi2, ImageFilled, 'odd')        这里我们看到第一行的输出Audi2,当然也多了一个输入'odd',输出为ImageFilled。        继续分析第三行代码,threshold (ImageFilled, Region, 0, 90)        经过观察发现,第二行的输出ImageFilled变成了这里的输入,除此之外,多了两个输入,一个是0,一个是90,输出是Region。        第四行到第六行代码,大家可以自己去分析。        接着分析第七行代码,dev_clear_window ()        这里既没有看到输入,也没有看到输出,因此可以看成是全局设定。          分析第八行代码,dev_set_colored (12)        这里只有一个12输入,同样没有输出,所以可以看成是全局设定。        继续分析第九行、第十行代码,dev_display (ImageFilled) dev_display (Letters)        上面两行代码只有输入,没有输出。进一步查找发现,ImageFilled是第二行的输出结果,Letters是第六行输出结果。所以,这里也可以近似看成是全局设定,主要是为了显示处理后的图像结果。因为不涉及到图像的继续处理,所以也就没有输出。当然如果说有输出的话,那么输出的对象就是窗口屏幕,这也就是讲得通的。        所以,不管hdev脚本多么复杂,基本都是参考上面的逻辑来执行的。只要掌握了上面给出的模型,掌握hdev脚本不会很复杂的。

0
0
0
浏览量773
神机妙算

halcon学习和实践(颜色筛选)

图像处理中,颜色是一个很重要的属性。很多同学都以为颜色只有红、橙、黄、绿、青、蓝、紫这几种颜色。事实上并不是这样的。如果我们以红、蓝、绿为基色,其他颜色都是以这三种颜色为基础搭配而成的话,那么颜色的种类应该有256 * 256* 256这么多。这就是为什么,很多知名的消费品每一次在产品发布会的时候都要精心挑选几种颜色供大家挑选。        那么,作为图像处理来说,如果要识别筛选某种颜色的话,是不是直接从r、g、b里面筛选一个范围就可以实现这个目标呢?这样其实是可以的,但是不准确。一种更好的办法是先将图像从rgb转化为h、s、v。这里的h是色度,s是饱和度,v是亮度。如果需要挑选一个颜色的话,那么选择对应的h、s、v范围即可。        还记得在大学本科论文设计的时候,当时的论文题目叫彩色图像的增强显示。那个时候只是知道r、g、b,并不知道h、s、v,所以就在rgb通道上面做了各种增强方法,比如说直方图均衡等等,虽然也有一定的效果,但是那个效果真是惨不忍睹。        hsv本身是为了提取特定的区域范围,这部分相当于代替了算子运算、二值化运算,后期的区域分割、属性过滤、ocr识别、发送命令,这部分还是一样,和之前的流程没有什么差别。下面说说应该怎么做一个颜色提取,首先需要明确颜色范围,大家可以参考这篇blog,地址在这,https://blog.csdn.net/zhangdongren/article/details/98941445        接着就可以自己写一个python+opencv的代码来进行处理,比如像这样,原来代码的地址在这,https://www.cnblogs.com/puheng/p/9228473.htmlimport cv2 import numpy as np lower_blue=np.array([110,50,50]) upper_blue=np.array([130,255,255]) img1=cv2.imread("car.png") xsize,ysize,channel=img1.shape img1=cv2.resize(img1,(ysize,xsize)) frame=cv2.cvtColor(img1,cv2.COLOR_BGR2HSV) mask_blue=cv2.inRange(frame,lower_blue,upper_blue) res_blue=cv2.bitwise_and(frame,frame,mask=mask_blue) res_blue=cv2.cvtColor(res_blue,cv2.COLOR_HSV2BGR) cv2.imshow("mask_blue",mask_blue) cv2.imshow("res_blue",res_blue) cv2.waitKey(0) cv2.destroyAllWindows()        先准备一个有蓝色车牌的图片,命名为car.png,比如像这样         直接输入python执行语句,比如说python demo.py,不出意外就可以得到这样的结果,         这样,通过一个颜色,就可以比较容易的将需要的物体从图像中识别出来了。

0
0
0
浏览量703
神机妙算

halcon学习和实践(设备要求)

和学校里面的只注重图像算法本身不同,真正的机器视觉是以应用为前提的。就算它使用的工业摄像头,也和我们平常pc笔记本上使用的摄像头不一样的。不失一般性,我们可以看下一般机器视觉需要哪些设备,可以用画一个简单的流程图来表示,这样更方便理解,         蓝色的部分,就是机器视觉所需要的设备,分别是光源控制器、光源、镜头、图像传感器、PC工控机或者嵌入式开发板。这里面,光源控制器、图像传感器、PC都是可以执行算法控制的。等所有流程都执行完毕之后,一般会输出对应的控制命令,传递给机器人或者其他机械装置,也就是图中黄色的部分。1、光源控制器        光源控制器一般是控制光的强度,作为补光使用,自然光很多时候并不能满足实际的拍摄需求。此外光源控制器会有通道选择、粗调、细调这些功能,对外提供的控制接口有rs232、rs485。当然这取决于工厂用什么开发板。2、光源        光源是一门大学问。按照形状分,可以有平面光源、环形光源;按照颜色分,可以有白光、蓝光、红光等各色光源;按照照射方法,可以有正面照射和背面照射;此外如果希望夜里也能看见物体,这个时候就需要发射红外光等等。很多同学都把精力花在算法上面,但是对光源了解很少,其实这大可不必。设计好的光源,可以让物体特征和属性更明显地暴露出来,反而有利于简化算法的设计,甚至有时候都不需要复杂的算子,就可以出来很好的处理效果。3、镜头        镜头这个大家都很熟悉,玩摄影的同学更是对徕卡、蔡司这些名字如数家珍。工业上的镜头也很讲究,比如是否需要加偏振片,选择什么样尺寸的镜头,是否需要广角镜头,视场角多少,焦距选择多少的镜头合适,这都需要自己好好测算下的。工业视觉里面,传感器和镜头是分开来的,毕竟不同的场景下的需求是不一样的。这里面两者的接口也有很多种,主要分为卡口和螺口,常用的螺口又分c口、cs口等等,买的时候一定要注意清楚。4、传感器      传感器这部分,大家比较了解的可能就是分辨率。一般认为,分辨率越高越好。当然除了分辨率之外,曝光时间、帧率、对外接口形式、isp设置这些也是需要考虑清楚的。特别是测量和定位,对于不同精度的要求,直接决定了我们需要选择什么样的镜头和传感器。另外,在windows平台上面有一个很好用的软件AMCAP,上面可以很清晰地告诉我们,当前图像传感器支持哪些属性配置,  5、PC工控机或者嵌入式开发板        摄像头连接到算法处理设备上,一般是ethernet或者usb居多。如果分辨率很高,那么ethernet用的多一些,价格也高一些,反之则usb多一点。PC的好处就是资源多,如果资源不够,还可以添加gpu显卡。而嵌入式的好处是性价比比较高,但是开发难度也稍微高一点,如果对性能和成本有一定的要求,那么用嵌入式设备开发也是不错的一个选择。        在一开始学习的时候,可以只用pc的摄像头进行测试和分析。等到有了一定开发经验之后,一定要购买一些设备,上实际环境测试,这才是比较好的做法。

0
0
0
浏览量502
神机妙算

halcon学习和实践(ocr识别)

  ocr是工业视觉里面重要的一个环节。大家可以试想一下,目前工业视觉里面,除了定位和测量、残次品识别之外,另外一个重要的应用领域就是ocr。ocr,全称是optical character recognition,也就是光学字符识别。比如,别人已经贴好的标签,打印好的文字、发票,身份证信息,这些都可以用ocr识别的算法来解决。        目前识别的方法主要有两种,一种是传统的机器学习算法,这部分有决策树、多层感知机、svm算法等等;另外一种就是基于cnn、rnn的深度学习,虽然算法本身类似于黑盒,但是识别效果很好,目前使用的也越来越多。当然,不管是哪种算法,都要包含这样几个步骤,        1、挑选好一定数量的样本,一般来说,样本越多越好;        2、将样本分成训练集和测试集,训练集是专门用于训练使用,测试集是训练后使用;        3、将训练好的模型部署到现场,进行实际生产的使用。        在halcon的样例代码中,也包含了很多的ocr用例,ocr_wafer_semi_font.hdev就是其中一个。今天可以分析一下这个算法,* * This example describes one step from the semiconductor product chain. * In the front-end-of-line step, the ICs are printed on the wafer. To * tag a single wafer from the production life line, each wafer receives * an ID number, printed with the SEMI font. This ID number is read here. * dev_update_off () dev_close_window () read_image (Image, 'ocr/wafer_semi_font_01') dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) dev_set_draw ('margin') set_display_font (WindowHandle, 16, 'mono', 'true', 'false') dev_set_line_width (2) dev_set_colored (12) * read_ocr_class_mlp ('SEMI_NoRej.omc', OCRHandle) NumImages := 10 for Index := 1 to NumImages by 1 * * Segment characters read_image (Image, 'ocr/wafer_semi_font_' + Index$'02') * Characters must be black-on-white, i.e., dark characters on a light background invert_image (Image, ImageInvert) mean_image (Image, ImageMean, 31, 31) dyn_threshold (Image, ImageMean, RegionDynThresh, 7, 'light') * Characters are often dotted. Therefore, we first merge close dots * that belong to the same character just before calling the operator connection closing_circle (RegionDynThresh, RegionClosing, 2.0) connection (RegionClosing, ConnectedRegions) * Filter out characters based on two facts: * 1. Characters are printed in SEMI-12. Therefore we can make strong assumptions * on the dimensions of the characters * 2. Characters are printed along a straight line select_shape (ConnectedRegions, SelectedRegions1, ['height','width'], 'and', [29,15], [60,40]) area_center (SelectedRegions1, Area, RowCh, ColumnCh) MedianRow := median(RowCh) select_shape (SelectedRegions1, Chars, 'row', 'and', MedianRow - 30, MedianRow + 30) * * Read out segmented characters enhance_contrast (Chars, ImageInvert, ImageRead) sort_region (Chars, CharsSorted, 'character', 'true', 'column') do_ocr_multi_class_mlp (CharsSorted, ImageRead, OCRHandle, Class, Confidence) * dev_display (ImageInvert) dev_display (CharsSorted) area_center (CharsSorted, Area1, Row, Column) MeanRow := mean(Row) disp_message (WindowHandle, Class, 'image', MeanRow + 42, Column - 11, 'yellow', 'false') disp_message (WindowHandle, Class, 'image', MeanRow + 40, Column - 10, 'slate blue', 'false') if (Index != NumImages) disp_continue_message (WindowHandle, 'black', 'true') stop () endif endfor clear_ocr_class_mlp (OCRHandle)        整个代码有55行,下面开始逐步进行分析。        代码从第7行开始,第7行到第14行的部分主要是验证一下图片是否可以读出。这里挑选了一张图片进行读取检验。因为后续循环的时候会不断读取图片,所以在此之前先用一张图片试试水,确保图片的读取路径没有问题。当然,中间还对字体、线宽、颜色做了设置。        第16行,读入OCRHandle,可以理解为加载好训练的模型。        第17-18行,开始准备循环读入图片,总共10张,        第21行,读取图片,        第23行,反转图片,主要是像素反转,        第24行,中值滤波,        第25行,二值化处理,        第28行,闭运算,        第29行,图像分割,        第34行,根据高度和宽度筛选合适的区域,        第35-37行,先计算一个平均高度,然后在平均高度的(-30,30)范围内筛选,        第40行,图像增强,        第41行,对应区域根据列排序,        第42行,利用之前读到的OCRHandle识别,        第44-49行,显示图片,显示识别好的字符,        第51行,显示continue信息,        第52行,暂停运行,直到输入F5,        第55行,卸载识别模型OCRHandle。        上面就是识别的基本流程。如果有了对应的模型,那么识别是比较简单的一件事情,所要做的就是把对应的字符分割出来。如果没有这个模型,就需要自己训练模型了,这个相对来说是比较麻烦的一件事情。

0
0
0
浏览量453
神机妙算

halcon学习和实践(工业视觉套路)

       和学术领域为了追求正确率尝试各种算法不同,工业视觉领域更多的是一种算法的套路。这些套路有它自己的道理,也有它内在的逻辑,整个过程也比较好解释,所以在工业上面其实用的很广。大家可以来看一看,         在工作当中,会有一个有趣的现象。那就是,早期的很多工业视觉工程师,虽然没有系统学过相关图形图像的原理,但是这并不妨碍他们在实际生产和工作中大量使用工业视觉的相关算法。究其原因,主要是他们了解算法使用的套路,知道怎么把需求和算法匹配起来,所以熟悉了算法理论的同学只要掌握了这个套路,也是可以很快搭建出自己需要的视觉工业开发软件的。1、图像获取        工业图像获取目前主要是usb3和ethernet。ethernet因为可以分布式部署,处理图像的设备可以和摄像头离的很远,camera部署也比较方便,因此在生产中得到了广泛引用。此外,图像获取的这部分工作主要都是由设备厂商来完成的,开发算法的同学只需要复用厂家给出的demo代码即可。2、彩色转黑白        如果选用的是黑白相机,那么获取的图像直接就是黑白图片。反之,如果本身是彩色相机,算法分割对色彩没有特殊要求的话,那么大概率需要自己转换成黑白图像。黑白图像处理数据少一些,但是细节部分还是保留下来了,计算量也小很多,后续的主要算法也是基于黑白图像来完成的。3、降噪        自然界的图片总是会存在噪声的,用合理的算法剔除这些噪声是不错的一个方法。当然,降噪还可以通过控制曝光时间、添加滤光片等方法来解决,不仅仅是降噪算法这一个选项。4、边缘检测        边缘检测是图像处理的一个重要环节。通过边缘检测的解算,大部分边缘细节都被完整保留下来了,那些边缘不是特别明显的部分往往就被忽略了。5、二值化        二值化的意思,后面的黑白图片中,像素值不是0,就是255,没有了中间地带。所以,二值化后的图像,信息进一步聚集,相应的计算量也开始进一步减小了。6、腐蚀        腐蚀运算是非常有用的一个操作。如果本来两个区域,因为一些噪声莫名其妙粘连在一起了,那么用腐蚀运算就可以重新把两个区域分开。7、膨胀        和腐蚀操作对应的就是膨胀了。举个例子,假设本来是一个区域,同样因为噪声分成了两个区域,那么就可以用膨胀操作来解决这个问题。实际操作中,一般用开运算和闭运算来代替。开运算是先腐蚀、再膨胀;闭运算是先膨胀、再腐蚀,具体用哪个看实际情况。8、分割        经过上面的操作之后,图像一般会被分割成若干个小区域。halcon里面用的是connection函数完成,opencv里面用的是findContours函数完成。9、过滤筛选        分割后的区域,并不是每一部分都是符合我们要求的。所以这个时候,只能根据一些特定的属性去筛选和过滤了,比如说长度、宽度、面积、圆度、半径、长宽比等等。halcon用的是select_shape函数,opencv就只能自己写了。10、测量        经过上面的操作之后,这个时候剩下来的区域往往就是自己需要的连通域了。从图像中可以获取很多的信息,比如长度、宽度、位置、大小、面积等等,这些都是可以通过转换测算出来的。11、ocr、分类、预测        随着深度学习的发展,传统的测量和定位已经越来越不能满足客户的需求了。所以,在分割好图像好之后,可以利用模版做进一步的匹配识别操作。此外,对于不规则的图像,可以通过训练模型的方法,进一步从图像中获取故障信息、ocr信息和异常信息等等,这些都是从前的传统图像算法做不了的。即使做,也是基于图像+传统的机器学习算法来做的,比如hog+svm等等,但是它在准确率和效率上面,和深度学习还是比不了的。12、给机器人发送命令        图像识别的目的,主要还是为了获取信息,根据信息生成命令通知给控制机构,或者直接一点就是机械手,进一步完成后面的动作,这样一个工业视觉软件算法才算是真正做完了

0
0
0
浏览量142